I want to keep the convention of naming loggers for their module with logger = logging.getLogger(__name__), but have a single logger that captures them all.
Is that possible, and what should the logger's name be?
(I do not want to switch everything to logger = logging.getLogger('django'))
Yup, the default logger is the root logger '', you can get it via:
logging.getLogger()
logging.getLogger('')
Note the different type:
>>> logging.getLogger('')
<logging.RootLogger object at 0x7f0b9521ec18>
in Django you can add handlers to the root logger easily:
LOGGING = {
...
'loggers': {
'': {
'level': 'DEBUG',
'handlers': ['system_log', 'debug_log', 'sentry'],
},
},
}
If you attach a handler to the root logger, it will see ALL the logs. This is not always wanted. So if you attach some handlers to a more specialised logger, you can then set the other one to propagate=False so that the message doesn't reach the root logger.
'loggers': {
'': {
'level': 'DEBUG',
'handlers': ['system_log', 'debug_log', 'sentry'],
},
'django': {
'level': 'INFO',
'handlers': ['another_handler_just_for_django'],
'propagate': False,
},
},
Related
I have encountered a strange behavior of Django Loggers.
I am developing a front end application using Django. During the login service, I make some requests to certain components and use log.warning() calls to see the flow of the requests.
The logs worked perfectly, until I decided to add a LOGGING configuration to print the output of the logs in a file, as I want to deploy the application via Docker and I want to periodically check the log files.
When I added the following Django configuration concerning logging:
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'detailed': {
'class': 'logging.Formatter',
'format': "[%(asctime)s] - [%(name)s:%(lineno)s] - [%(levelname)s] %(message)s",
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'detailed',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': "{}/am.log".format(BASE_DIR),
'mode': 'w',
'formatter': 'detailed',
'level': 'INFO',
'maxBytes': 2024 * 2024,
'backupCount': 5,
},
},
'loggers': {
'am': {
'level': 'INFO',
'handlers': ['console', 'file']
},
}
}
The logging stops working. The file specified in the logging configuration, am.log, is indeed created but nothing gets printed to this file. Even the console logging does not take place.
I have taken this logging configuration from one of my Django projects for the backend of this application, and there it works perfectly. I really don't understand what I am doing wrong. Could you please help me or guide me in the right direction. I would be very grateful.
I wish you all a good day!
By using the key "am" in your 'loggers' configuration, you're defining one logger with name "am":
'loggers': {
'am': { # <-- name of the logger
'level': 'INFO',
'handlers': ['console', 'file']
},
}
So to use that logger, you have to get it by that name:
logger = logging.getLogger("am")
logger.warning("This is a warning")
If you name your loggers by the name of the module in which you're running, which is recommended practice, then you need to define each module logger:
logger = logging.getLogger(__name__) # <-- this logger will be named after the module, e.g. your app name.
Then in your logging configuration you can specify logging behavior per module (per app):
'loggers': {
'my_app': { # <-- logging for my app
'level': 'INFO',
'handlers': ['console', 'file']
},
'django': { # <-- logging for Django module
'level': 'WARNING',
'handlers': ['console', 'file']
},
}
Or if you just want to log everything the same, use the root ('') logger, which doesn't have a name, just empty string:
'loggers': {
'': { # <-- root logger
'level': 'INFO',
'handlers': ['console', 'file']
},
}
I am performing a simple Python Django TestCase. The question is simple: I want to use loggers to redirect information (stdout) to one file and failures/errors (stderr) to another.
I know I can print stdout to a file simply by defining the logger and using it to print the messages (i.e. logger.info('my debug message')); however, I have not found a way to log a failure/error. I can redirect the output of the entire test run using Bash (of which I am semi-successful), but I know there is a way to simplify it and use loggers to do all that backend work for me.
Here is an example of my logger:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s, %(module)s at line %(lineno)d:\n%(message)s'
},
},
'handlers': {
'app': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': 'logs/app.log',
'formatter': 'verbose'
},
'test': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': 'logs/test.log',
'formatter': 'verbose'
},
'test-fail': {
'level': 'ERROR',
'class': 'logging.FileHandler',
'filename': 'logs/test-fail.log',
'formatter': 'verbose',
}
},
'loggers': {
'app': {
'handlers': ['app'],
'level': 'DEBUG',
'propagate': True,
},
'test': {
'handlers': ['test', 'test-fail'],
'level': 'DEBUG',
'propagate': True,
},
},
}
And let's just say I'm trying to log the following test case to the logs/test-fail.log file specified in my logger:
logger = logging.getLogger('test')
def test_this(self):
logger.debug('This message will print to the logs/test.log file')
self.assertTrue(False) # will fail
In short, I want to print errors/failures from a Django TestCase using the logger and not using Bash. Also note that I'm fine with using the default logger, but I want to define an error logger case for only test cases, and I don't want the test error case being used by default.
why not try changing the logger and handler level to ERROR ? (more info in case https://docs.djangoproject.com/en/2.2/topics/logging/#topic-logging-parts-loggers)
In my project i deployed my django-project with tornado server, and my tornado main function is:
def main():
tornado.options.options.logging = None
tornado.options.parse_command_line()
os.environ['DJANGO_SETTINGS_MODULE'] = 'Zero.settings'
application = get_wsgi_application()
container = tornado.wsgi.WSGIContainer(application)
http_server = tornado.httpserver.HTTPServer(container, xheaders=True)
http_server.listen(tornado.options.options.port)
tornado.ioloop.IOLoop.current().start()
When i use tornado.options.options.logging = None to disabled tornado logging output, but it still output the log message in my console with twice, my django logging config is:
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'standard': {
'format': '%(asctime)s [%(threadName)s] [%(name)s:%(funcName)s] [%(levelname)s]- %(message)s'}
},
'filters': {
},
'handlers': {
'error': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR, 'log', 'error.log'),
'maxBytes': 1024*1024*5,
'backupCount': 5,
'formatter': 'standard',
},
'console':{
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'standard'
},
},
'loggers': {
'django': {
'handlers': ['console'],
'level': 'INFO',
'propagate': True
},
}
}
The final result is:
2018-06-15 17:40:55,724 [MainThread] [base_views:get] [INFO]- get message correct
INFO:base_views:get message correct
so what can i do to solve this problem.
Thank U.
You've only configured the django logger, not the root logger. When Tornado sees that the root logger is not configured, it adds its own last-resort handler (using logging.basicConfig instead of tornado.log). Because of the way python loggers propagate, this results in any other loggers being duplicated.
When you use tornado.options.options.logging = None, you should make sure that you configure the root logger yourself, or configure all of your other handlers with propagate=False. In this case, move the loggers.django section of your config to a new root section:
'handlers': {...}
'root': {
'handlers': ['console'],
'level': 'INFO',
},
I'm using Django 1.6 and would like to add to—not replace—Django's default logging. Specifically, I want to make it so that in addition to Django's default logging behavior, any log record with a log level of DEBUG or higher gets written to a log file that gets automatically rotated.
Based on what I've found through searches and my own experimenting, it seems like you have to redefine all of the logging (Django's defaults plus my rotating file logging), even when using 'disable_existing_loggers': False. I figure I'm probably doing something wrong.
I'd like to see an example of what to put in my settings.py file that will allow me to accomplish this.
settings.py is just Python, so we can do:
from django.utils.log import DEFAULT_LOGGING
# Use defaults as the basis for our logging setup
LOGGING = DEFAULT_LOGGING
# We need some formatters. These ones are from the docs.
LOGGING['formatters'] = {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(message)s'
},
}
# A log file handler that rotates periodically
LOGGING['handlers'].update({
'rotate': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'formatter': 'verbose',
'filename': '/tmp/debug.log',
}
})
# The default 'django' logger is a catch-all that does nothing. We replace it with
# a rotating file handler.
LOGGING['loggers'].update({
'django': {
'handlers': ['rotate'],
'propagate': True,
'level': 'DEBUG',
}
})
# If you don't want to completely replace the django handler, you could do something
# like this instead:
#LOGGING['loggers']['django']['handlers'] += ['rotate']
This will add your rotating file handler to the existing handlers, define the basic formatters, and replace the catch-all logger (which does nothing) with one that replace the default logger
Based on your comments I tried this. It seems to do what you're asking.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'rotate': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/tmp/debug.log',
}
},
'loggers': {
'django': {
'handlers': ['rotate'],
'propagate': True,
'level': 'DEBUG',
}
},
}
I have a Django 1.3 application. I have several management commands. I want each command to write, to a different logfile, instead of default filename defined in settings.py. I run these commands as part of cron everyday. Our current logfile looks like the example given here https://docs.djangoproject.com/en/dev/topics/logging/. And, we use
logger = logging.getLogger(__name__)
Thanks
You'll need to do this by adding a logger and a handler for each package:
'handlers': {
'my_command_handler': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/path/to/my_command_log',
},
...
},
'loggers': {
'my_pkg.management.commands.my_command': {
'level': 'DEBUG',
'handlers': ['my_command_handler'],
},
...
}
You may also want to consider adding 'propagate': False to the command loggers, if you don't want the messages to get to other loggers.