python: difference between logging.Logger and logging.getLogger - python

Yes, I see python doc says: "Loggers are never instantiated directly, but always through the module-level function logging.getLogger(name)", but I have an issue to debug and want to know the root cause.
here is the example:
#!/usr/bin/python
import logging
logger = logging.getLogger("test")
format = "%(asctime)s [%(levelname)-8s] %(message)s"
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(format, datefmt="%Y-%m-%d %H:%M:%S"))
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.info("test")
Using logging.getLogger("test") here, log message will not be printed.
If I change logging.getLogger("test") to logging.Logger("test"), the log message will be printed.
#!/usr/bin/python
import logging
logger = logging.Logger("test")
format = "%(asctime)s [%(levelname)-8s] %(message)s"
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(format, datefmt="%Y-%m-%d %H:%M:%S"))
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.info("test")
Or we can using logging.getLogger("test") and set logger level to logging.DEBUG.
#!/usr/bin/python
import logging
logger = logging.getLogger("test")
format = "%(asctime)s [%(levelname)-8s] %(message)s"
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(format, datefmt="%Y-%m-%d %H:%M:%S"))
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
logger.info("test")

The method .getLogger("test") is looking for any existing logger configurations for the name "test" while the .Logger("test") is creating a default logger with the name "test" and sets the default log level to 0. If the getLogger method doesn't find a logger class by that name then it will create a basic logger that will have an effective level of 30 (https://docs.python.org/3/library/logging.html#logging-levels) which will ignore your DEBUG message. You can check via logger.getEffectiveLevel() to notice the difference.
Ideally you would create loggers and set them based on the proper naming/configurations instead of accepting the default configuration.

Related

How to reset root logger config in Python

I originally used logging.basicConfig(filename='logs/example.log') to create a log file. After reading the docs I found that it is not recommended to modify the class attributes of logging. Now that I've changed these attributes how can I change them back/reset the logger module?
Output of the code below creates two log files, app.log and example.log. The latter is an artifact of .basicConfig() being called when I first tried to set up the logger.
UPDATE:
grep -R "example.log" /lib/python3.8/ does not output anything so I'm not sure what was changed in the source code of logging to cause the example.log file to be created every time
import logging
import logging.handlers
LOG_FILENAME = 'logs/app.log'
# https://stackoverflow.com/questions/3630774/logging-remove-inspect-modify-handlers-configured-by-fileconfig
# need to run this every time since I changed class attributes of logger
# still creates example.log file
print(logging.getLogger())
root_log = logging.getLogger()
for handler in root_log.handlers:
root_log.removeHandler(handler)
# OR
# logging.basicConfig(force=True) #one way to reset root logger format
# create logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# create console and file handler and set level to info
fh = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=250000, backupCount=5)
fh.setLevel(logging.INFO)
# ch = logging.StreamHandler()
# ch.setLevel(logging.INFO)
# create formatter
ffh = logging.Formatter('%(asctime)s : %(name)-12s : %(levelname)-8s : %(message)s')
# fch = logging.Formatter('%(name)-12s : %(levelname)-8s : %(message)s')
# add formatter handlers
fh.setFormatter(ffh)
# ch.setFormatter(fch)
# add handler to logger
logger.addHandler(fh)
# logger.addHandler(ch)
logger.info('instance logger')
# logging.shutdown()

Location of Python log file should be changed

I am using logger in my python 2.7 project on a legacy code. I want to create logs on specific location but python logging module creates the log files at the default place i.e. from where it is executed.
Is there is any way to change this default location?
Below is the initialization of the logger.
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
formatter = logging.Formatter('%(message)s')
file_handler = logging.FileHandler('file.log')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
Below is an example of how you can set the location and other properties of Python logger:
You can define a get_logger function as follows:
import logging
import os
LOG_DIR = 'log_dir'
LOG_FORMATTER = logging.Formatter('[%(asctime)s] %(levelname)s %(name)s: %(message)s')
def get_logger(log_name, log_dir = LOG_DIR):
if not os.path.isdir(log_dir):
os.mkdir(log_dir)
logger = logging.getLogger(log_name)
logging.basicConfig(level = logging.INFO)
log_handler = logging.FileHandler(os.path.join(log_dir, log_name))
logger.addHandler(log_handler)
log_handler.setFormatter(LOG_FORMATTER)
log_handler.setLevel('INFO')
return logger
Then in the file that you want to make logs, you can do as follows:
logger = get_logger('filename')
If you want to make a logging message, you can then do as follows:
logger.info('logging information!')

Why does log.debug() not log when the level is set to logging.DEBUG?

I was expecting that the following code would output both log lines
import logging
log = logging.getLogger('hello')
log.setLevel(logging.DEBUG)
print(log.getEffectiveLevel())
log.debug('debug log')
log.critical('critical log')
The output is
10
critical log
The level is correctly set to 10 (which corresponds to DEBUG) and despite this log.debug('debug log') does not output anything - why?
You haven't configured the logging system, so it's still using the defaults (level WARN for the root logger).
https://docs.python.org/3/library/logging.html#logging.basicConfig says basicConfig
Does basic configuration for the logging system by creating a
StreamHandler with a default Formatter and adding it to the root
logger.
Configuring the logging system with basicConfig first will create a handler and formatter that your logger will use:
logging.basicConfig()
log = logging.getLogger('hello')
log.setLevel(logging.DEBUG)
print(log.getEffectiveLevel())
log.debug('debug log')
log.critical('critical log')
Outputs:
10
DEBUG:hello:debug log
CRITICAL:hello:critical log
In Logger.callHandlers, each handler compares the log record's level with its level. If there aren't any handlers, it will use the default of WARNING.
With a StreamHandler it will
sends logging output to streams such as sys.stdout, sys.stderr or any file-like object"
according to the documentation
import logging
import sys
# Initialize Logger and set Level to DEBUG
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# Initialize a Handler to print to stdout
handler = logging.StreamHandler(sys.stdout)
# Format Handler output
logFormatter = logging.Formatter(
"%(asctime)s %(message)s", datefmt="%m/%d/%Y %I:%M:%S %p"
)
handler.setFormatter(logFormatter)
# Set Handler Level to DEBUG
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
logger.debug('Debug Info')
>>> 09/19/2020 09:01:00 PM Debug Info
You need to add stream handler
import logging
log = logging.getLogger('hello')
log.setLevel(logging.DEBUG)
# # Create a file handler to store the logs
file_handler = logging.FileHandler('test.log')
log.addHandler(file_handler)
# # Send output to terminal
stream_handler = logging.StreamHandler()
log.addHandler(stream_handler)
log.debug('debug log')

How do I use logging in python tornado and write to file

Currently I'm using logging.getLogger().setLevel(logging.DEBUG) what I think is logging everything where logging level is => DEBUG Is that a correct assumption? I can see a difference when I set logging.DEBUG to logging.ERROR so I guess I'm correct.
Also how do I write these logging rows to a file?
This is a exmaple write log to file
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# create a file handler
handler = logging.FileHandler('hello.log')
handler.setLevel(logging.INFO)
# create a logging format
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# add the handlers to the logger
logger.addHandler(handler)
logger.info('Hello baby')
More detail:
http://victorlin.me/posts/2012/08/good-logging-practice-in-python/

Global Python Logger with file Rotator

I have create a global logger using the following:
def logini():
logfile='/var/log/cs_status.log'
import logging
import logging.handlers
global logger
logger = logging.getLogger()
logging.basicConfig(filename=logfile,filemode='a',format='%(asctime)s %(name)s %(levelname)s %(message)s',datefmt='%y%m%d-%H:%M:%S',level=logging.DEBUG,propagate=0)
handler = logging.handlers.RotatingFileHandler(logfile, maxBytes=2000000, backupCount=5)
logger.addHandler(handler)
__builtins__.logger = logger
It works, however I am getting 2 outputs for every log, one with the formatting and one without.
I realize that this is being caused by the file rotater as I can comment out the 2 lines of the handler code and then I get a single outputted correct log entry.
How can I prevent the log rotator from outputting a second entry ?
Currently you're configuring two file loggers that point to the same logfile. To only use the RotatingFileHandler, get rid of the basicConfig call:
logger = logging.getLogger()
handler = logging.handlers.RotatingFileHandler(logfile, maxBytes=2000000,
backupCount=5)
formatter = logging.Formatter(fmt='%(asctime)s %(name)s %(levelname)s %(message)s',
datefmt='%y%m%d-%H:%M:%S')
handler.setFormatter(formatter)
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
All basicConfig does for you is to provide an easy way to instantiate either a StreamHandler (default) or a FileHandler and set its loglevel and formats (see the docs for more information). If you need a handler other than these two, you should instantiate and configure it yourself.

Categories