django logging in script near manage.py - messages are just ignored - django

I'm using Django 1.4.1.
There's a script tmp.py in the same direcotry as manage.py, it does some routime operatons and is started from cron with command like python /path/to/project/tmp.py
Here is tmp.py:
import os
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "MYPROJECT.settings")
if __name__ == "__main__":
import logging
logger = logging.getLogger('parser.test')
logger.info('Hello, world')
This script makes a log entry, but this entry is just ignored by logger.
When I put same 3 lines into any of my views, log entry appears in my log file as expected.
Logger config from setings.py:
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'standard': {
'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
'datefmt' : "%d/%b/%Y %H:%M:%S"
},
},
'handlers': {
'null': {
'level':'DEBUG',
'class':'django.utils.log.NullHandler',
},
'logfile': {
'level':'DEBUG',
'class':'logging.handlers.RotatingFileHandler',
'filename': "/hosting/MYPROJECTS/logs/logfile",
'maxBytes': 50000,
'backupCount': 2,
'formatter': 'standard',
},
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
'formatter': 'standard'
},
},
'loggers': {
'django': {
'handlers':['console'],
'propagate': True,
'level':'WARN',
},
'django.db.backends': {
'handlers': ['console'],
'level': 'DEBUG',
'propagate': False,
},
'parser': {
'handlers': ['console', 'logfile'],
'level': 'DEBUG',
},
}
}
Why this logger works only in views? Maybe logger is still not configured after os.environ.setdefault("DJANGO_SETTINGS_MODULE", "MYPROJECT.settings") in my tmp.py?

I can't solve this problem but I found alternative solution: write a custom admin command instead of separate script near manage.py
https://docs.djangoproject.com/en/dev/howto/custom-management-commands/
It logs works fine in admin commands.

I managed to get Django logging settings to work for my non-Django script.
First, let's orient to the Django directory: <myproject>/<src>/<myproject>/settings.py & <myproject>/<src>/manage.py
# FILE: <myproject>/<src>/scripts/save_report_2_mongo.py
[...]
import logging
logger = logging.getLogger(__name__)
[...]
At the top of my script I import logging. Then I create the logging object. In my case __name__ == scripts.save_report_2_mongo. If the OP's setup is anywhere near to mine, then by this example name != "main", and the logger is never instantiated. Right?
Finally, inside settings.py
# FILE: <myproject>/<src>/<myproject>/settings.py
[...]
LOGGING = {
[...]
'formatters' : {...},
'handlers' : {...},
'loggers': {
'django': {
'handlers': ['console', 'fileInfo', 'fileDebug'],
'level': 'DEBUG',
'propagate': True,
},
'scripts.save_report_2_mongo': {
'handlers': ['sr2m_handler'],
'level': 'DEBUG',
},
}
I believe this works because of this passage in Python docs: docs.python.org > Logging HOWTO > Advanced Logging Tutorial
Advanced Logging Tutorial
The logging library takes a modular approach and offers several
categories of components: loggers, handlers, filters, and formatters.
Loggers expose the interface that application code directly uses.
[...]
Logging is performed by calling methods on instances of the Logger
class (hereafter called loggers). Each instance has a name, and they
are conceptually arranged in a namespace hierarchy using dots
(periods) as separators. For example, a logger named ‘scan’ is the
parent of loggers ‘scan.text’, ‘scan.html’ and ‘scan.pdf’. Logger
names can be anything you want, and indicate the area of an
application in which a logged message originates.
A good convention to use when naming loggers is to use a module-level
logger, in each module which uses logging, named as follows:
logger = logging.getLogger(__name__)
This means that logger names track the package/module hierarchy, and
it’s intuitively obvious where events are logged just from the logger
name.
My emphasis--logger names track the package/module hierarchy. In this case I obtained the value of __name__ inside my script and then named the logger in the loggers section of settings.py.

Related

Django LOGGING not printing to console and file

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']
},
}

Logging output running django tests under Pycharm

When running/debugging individual tests using django.test.TestCase under PyCharm logging.logger messages are not shown. I've tried setting logging.basicConfig(level=logging.DEBUG) as suggested by How can I see log messages when unit testing in PyCharm? but that didn't help either. I suspect it might be django's TestCase setup interferring.
Is there some other way in test setup or runner configuration that I can turn on debug logging for the test run?
The logging I have set up in my settings.py right now is:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console': {
'class': 'logging.StreamHandler',
'formatter': 'verbose'
},
'file': {
'level': 'DEBUG',
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': '/var/log/em/mcqueen-dev.log',
'when': 'midnight',
'formatter': 'verbose',
},
},
'formatters': {
'verbose': {
'format': '%(asctime)s.%(msecs).03d - %(process)d - %(thread)d - %(levelname)8s - %(filename)s:%(lineno)d - %(funcName)s - %(message)s'
},
'simple': {
'format': '%(asctime)s - %(levelname)s %(message)s'
},
},
'loggers': {
'mcqueen_api': {
'handlers': ['console', 'file'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG')
},
'mcqueen_app': {
'handlers': ['console', 'file'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG')
},
'mcqueen_base': {
'handlers': ['console', 'file'],
'level': os.getenv('DJANGO_LOG_LEVEL', 'DEBUG')
},
},
}
When I want to see logs while working on tests for Django project in PyCharm I'm putting this snippet of code in the file with test:
import logging
logger = logging.getLogger(__name__)
logging.disable(logging.NOTSET)
logger.setLevel(logging.DEBUG)
While running Django tests level for disabling logs is set high (to 50), lowering it (as in line #3 in code above) will cause that logs will be displayed (or saved to file - depending on log handler that is in use).
This thread on stackoverflow explains the probable reason your logging output not showing to console. Apparently, django's unittest runner replaces the global sys.stdout/sys.stderr, but the StreamHandler specified from the django settings is still bound up with the original sys.stdout/sys.stderr. The fix is to add a stream handler to your logger in the test module, based on the values of sys.stdout/sys.stderr during execution.
If you want the logger to log to console for all methods of your test case, then you're probably best using a custom base class (see linked to thread for more details) to wrap the logic for adding/removing at setUp/tearDown.
I prefer to use decorators over the individual test methods for wrapping. For example (using the 'django_test' logger config provided by Sơn Lâm's answer):
import logging
import sys
from contextlib import contextmanager
from django.test import TestCase
#contextmanager
def streamhandler_to_console(lggr):
# Use 'up to date' value of sys.stdout for StreamHandler,
# as set by test runner.
stream_handler = logging.StreamHandler(sys.stdout)
lggr.addHandler(stream_handler)
yield
lggr.removeHandler(stream_handler)
def testcase_log_console(lggr):
def testcase_decorator(func):
def testcase_log_console(*args, **kwargs):
with streamhandler_to_console(lggr):
return func(*args, **kwargs)
return testcase_log_console
return testcase_decorator
logger = logging.getLogger('django_test')
class SomeTestCase(TestCase):
#testcase_log_console(logger)
def test_something(self):
logger.info('show something to console.')
I think it will be work
Log configuration on settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': "[%(asctime)s] %(levelname)s %(message)s",
'datefmt': "%d/%b/%Y %H:%M:%S"
}
},
'handlers': {
'file': {
'level': 'DEBUG',
'class': 'logging.FileHandler',
'filename': '/var/log/django_practices.log',
'formatter': 'verbose'
},
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'stream': sys.stdout,
'formatter': 'verbose'
},
},
'loggers': {
'django_test': {
'handlers': ['file', 'console'],
'level': 'DEBUG',
},
'name_your_app': {
'handlers': ['file', 'console'],
'level': 'DEBUG',
}
}
}
In UnitTest file
import logging
logger = logging.getLogger('django_test')
logger.info('test_log')
And Log will be appearance.
I think this is to do with the test runner that you're using - assuming you're using the inbuilt Django tests setup in PyCharm then it should already be setting an environment variable PYTHONUNBUFFERED=1 which I think is what makes the output print directly without being buffered and only showing at the end (which is what I presume is happening). Check that this is set in the test configuration and if it isn't then try that.
See also: Pycharm unit test interactive debug command line doesn't work (particularly if you're using a different test runner)

Django request not logging

I'm very confused. I've searched through many similar threads and tried all of the proposed solutions and nothing works.
I'm trying to setup logging in my Django application. I can get any direct logger calls (logger.error() etc) to write to my file from the application or the console, but the django.request simply won't write. On startup both files are created and have correct permissions. I've tried django-requestlogging and multiple other things, changing disable exisiting loggers, propagate etc and nothing works. This is my logging setup right now:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format' : "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
'datefmt' : "%d/%b/%Y %H:%M:%S"
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
'handlers': {
'file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': '/srv/hawthorn/logs/debug.log',
'formatter': 'verbose'
},
'request_file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': '/srv/hawthorn/logs/request.log',
'formatter': 'verbose'
},
},
'loggers': {
'django.request': {
'handlers':['request_file'],
'level':'INFO',
},
'audio': {
'handlers': ['file'],
'level': 'INFO',
},
}
}
If I even try to change the logfile path to relative, like 'logs/debug.log' the file gets created but stops getting written to (has the correct permissions). My project looks like:
hawthorn/
audio/ (views are here)
admin/
hawthorn/ (settings.py is here)
Interesting note that almost any configuration works when trying from the python console. I could get django-requestlogging from the console (at least calling logger.info() ) but not from hitting my app in a browser (which also calls logger.info() ).
I'm running Django 1.6.1 and python 2.7.6
What am I missing?? All I want is a log of the requests as they come in. I'd really appreciate the help.
Calling it like:
import logging
logger = logging.getLogger(__name__)
then just logger.debug("test") in the view.
EDIT:
So, turns out the requests are being logged to the request.log file, but only warning and errors. I changed all levels of the config to debug, why won't it log all requests?

Can't get celery to do INFO logging the same way Django does it

I have my logging configured like so:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'mail_admins': {
'level': 'INFO',
'class': 'common.handlers.SuperAdminEmailHandler'
},
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler'
}
},
'loggers': {
'': {
'handlers': ['console'],
'level': 'INFO',
'propagate': True,
},
'django.request': {
'handlers': ['console'],
'level': 'ERROR',
'propagate': True,
},
},
}
This is with CELERYD_HIJACK_ROOT_LOGGER = False. I also have this task:
#periodic_task(run_every=crontab())
def test_logging():
import logging
print('running test_logging')
logging.info("Here's an info message", extra = {
'tell_admins': True, 'email_content': 'Additional content'
})
logging.error("Here's an error message")
I have a test URL that calls test_logging directly. When I hit that URL I see this output:
running test_logging
Here's an info message
Here's an error message
[05/Jul/2013 11:07:27] "GET /test/ HTTP/1.1" 200 7
Exactly what I expect. When that same function runs through celery though, I get:
Scheduler: Sending due task scheduler.tasks.test_logging (scheduler.tasks.test_logging)
running test_logging
Here's an error message
Where'd the info message go?! I tried adding another celery-specific logger:
'celery': {
'handlers': ['console'],
'level': 'INFO',
'propagate': True,
},
Now I see this from celery:
running test_logging
running test_logging
Here's an error message
Task scheduler.tasks.test_logging[271b8a4a-0c04-4391-81c5-5b009d70b08d] succeeded in 0.00929498672485s: None
Still no info message. Any thoughts on why and how to get it back?
For some reason, even if Celery is using then handlers you specify in Django's logging settings, it still overrides the levels of the loggers. I'm now using this function to get a logger. With the logger set to the appropriate level, things seem to be working as expected.
import logging
from celery.utils.log import get_task_logger
def get_celery_logger(name):
logger = get_task_logger(name)
logger.level = logging.INFO
return logger
First of all, if you use django-celery you can try it run as so:
./manage.py celeryd --loglevel=info
Secondly, if you use Celery >= 3.0 you must get logger object as:
from celery.utils.log import get_task_logger
logger = get_task_logger(__name__)
# and inside your function use
# logger instead logging
#periodic_task(run_every=crontab())
def test_logging():
print('running test_logging')
logger.info("Here's an info message", extra = {
'tell_admins': True, 'email_content': 'Additional content'
})
logger.error("Here's an error message")
If it would not work you also can try change LOGGIN object

How to write to a specific log file from management command?

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.