How to include a Django logging.FileHanler with Celery on ElasticBeanstalk - django

If I do not include logging I have an Django application with Celery working fine on ElasticBeanstalk using AWS SQS.
When I include logging with a 'logging.FileHandler' celery gets permission denied error because it doesn't have permission rights for my log files. This is my error
ValueError: Unable to configure handler 'celery_file': [Errno 13]
Permission denied: '/opt/python/log/django.log'
This is my logging setup
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler'
},
'file': {
'level': log_level,
'class': 'logging.FileHandler',
#'filters': ['require_debug_false'],
'filename': os.environ.get('LOG_FILE_PATH', LOG_FILE_PATH + '/var/log/django.log')
},
'mail_admins': {
'level': 'ERROR',
#'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
},
'cqc_file' :{
'level': log_level,
'class': 'logging.FileHandler',
#'filters': ['require_debug_false'],
'filename': os.environ.get('LOG_FILE_PATH', LOG_FILE_PATH + '/var/log/cqc.log')
},
'null': {
'class': 'logging.NullHandler',
},
'celery_file': {
'level': 'INFO',
'class': 'logging.FileHandler',
'filename': os.environ.get('LOG_FILE_PATH', LOG_FILE_PATH + '/var/log/celery.log'),
}
},
'loggers': {
'': {
'level': 'WARNING',
'handlers': ['file'],
},
'debug' : {
'level': log_level,
'handlers': ['file'],
},
'django.security.DisallowedHost': {
'handlers': ['null'],
'level' : 'CRITICAL',
'propagate': False,
},
'django.request': {
'handlers': ['file','mail_admins'],
'level': 'DEBUG',
'propagate': True,
},
'cqc_report' : {
'level' : 'INFO',
'handlers' : ['cqc_file']
},
'celery.task' : {
'handlers': ['console', 'celery_file'],
'level': 'INFO',
'propagate': True,
}
}
}
I think I need to give celery access to the django.log, celery.log and cqc.log files through and elastic beanstalk container command. I tired this using:
03_change_permissions:
command: chmod g+s /opt/python/log
04_change_owner:
command: chown root:wsgi /opt/python/log
05_add_celery_to_wsgi:
command: usermod -a -G wsgi celery
But this just gave me an error saying no user celery (or something to that effect).
How do I get the File logging to work?

The error tells you that the user who is running the process does not have the necessary privileges it needs in order to write into the log. Your solution
03_change_permissions:
command: chmod g+s /opt/python/log
04_change_owner:
command: chown root:wsgi /opt/python/log
05_add_celery_to_wsgi:
command: usermod -a -G wsgi celery
seems to be on the right track, because when there are no privileges for a user to access a location, the solution is to provide the necessary privileges. However, your assumption that the user is celery seems to be wrong. A big clue for this is that this user does not exist. So, you need to make sure that
the user to run the process exists
you identify the correct user for the process
the user has the necessary privileges for logging

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

Redirect Django Test Output of Logger to 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)

tornado deploy django and the log repeat output

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

Uwsgi disables django.request logging

If I start application using uwsgi I don't see logs related to django.requests.
But If I start the same code on the same machine using
manage.py runserver 8080
it works perfectly.
Any ideas why it might happen?
I run uwsgi by this command
/home/gs/python-env/bin/uwsgi --ini /etc/uwsgi.d/uwsgi.ini --static-map /static=/home/gs/api/static/
uwsgi.ini
[uwsgi]
http-socket=:8080
home=/home/gs/python-env
chdir=/home/gs/api
module=server.wsgi
env=server.settings
processes=1
enable-threads=true
My logging configuration from settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(process)d %(threadName)s %(module)s %(funcName)s %(message)s'
}
},
'handlers': {
'console': {
'class': 'logging.StreamHandler',
},
'file': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/gs/api.log',
'formatter': 'verbose',
'maxBytes': 1024 * 1024 * 16, # 16Mb
},
'elasticsearch': {
'level': 'DEBUG',
'class': 'api.common.elasticsearch_log_handler.ElasticSearchHandler',
'hosts': [{'host': cluster.ES_HOST, 'port': 443}],
'es_index_name': 'logstash',
'es_additional_fields': {'type': 'api', 'cluser': cluster.CLUSTER_NAME},
'auth_type': ElasticSearchHandler.AuthType.NO_AUTH,
'use_ssl': True,
}
},
'loggers': {
'django': {
'handlers': ['file', 'elasticsearch', 'console'],
'level': 'INFO',
'propagate': True
},
'django.request': {
'handlers': ['file', 'elasticsearch', 'console'],
'level': 'DEBUG',
'propagate':False
}
}
}
If I change info to debug for 'django' I will see my logs from django logger but not from django.request.
UPD: If I write my own middleware I can log requests. But I want to know why django.request doesn't work with uwsgi.
Django's runserver provides the log messages that show up under django.server. When not running under runserver there are still some messages that can be logged to django.request (mostly error messages) but the informational log message for each request only exists in runserver. I verified this by looking at the uWSGI and the Django source.
If you want a similar log message you can use django-request-logging.

Logging all debug info for one particular app?

I setup logging and it's working great. What I want to do now is log all debug info for one particular app called 'my app' in addition to the other logs and am not sure how to achieve this.
Working set up as it is now:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
#
'formatters': {
'default': {
'format': '\n%(levelname)s - %(asctime)s\n%(message)s\n'
},
},
#
'handlers': {
#
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
},
#
'debug_log': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/django/debug.log',
'maxBytes': 1024*1024*5,
'backupCount': 5,
'formatter': 'default',
},
#
'error_log': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/django/error.log',
'maxBytes': 1024*1024*5,
'backupCount': 5,
'formatter': 'default',
},
},
#
'loggers': {
'': {
'handlers': ['mail_admins', 'error_log',],
'level': 'INFO',
'propagate': True,
},
'django': {
'handlers': ['debug_log',],
'level': 'DEBUG',
'propagate': True,
},
}
}
I'd like to set up DEBUG logging that captures everything for the one particular app. I add the following handler to LOGGING:
'app_log': {
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': '/var/log/django/app.log',
'maxBytes': 1024*1024*5,
'backupCount': 5,
'formatter': 'default',
},
And the following logger:
'my_app': {
'handlers': ['app_log',],
'level': 'DEBUG',
'propagate': True,
},
Add the following to my_app.views:
import logging
logger = logging.getLogger(__name__)
And here's where I'm not sure what I need to do next. I can add something like the following to a certain view
...
if blahblahblah:
logger.debug('My Message Here')
...
and things work great. However, what I really want is all debug info for the entire app, not just 'My Message Here'. I want all the query info etc... ie: same info as the standard debug logging but just for that one app.
How do I achieve this?
You haven't explicitly enabled the SQL query logging but I am uncertain if it can be enabled in app scope. AFAIK it can only be enabled or disabled for the entire project.
If you want to do this at the app level, or view level you can try this:
from django.db import connection
logger.debug(connection.queries)
Place this code at the end of each view where you want to see the sql logs.