Does python logging.handlers.RotatingFileHandler allow creation of a group writable log file?

Viewed 14876

I'm using the standard python (2.5.2) logging module, specifically the RotatingFileHandler, on a linux system. My application supports both a command-line interface and a web-service interface. I would like to have both write to the same log file. However, when the log file gets rotated, the new file has 644 permissions and is owned by the web server user which prevents the command-line user from writing to it. Can I specify that new log files should be group-writable in the logging configuration or during logging initialization?

I have looked into the mode setting (r/w/a), but it doesn't seem to support any file permissions.

8 Answers

Here is a simple solution worked fine:

import os

class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        os.chmod(self.baseFilename, 0o0777)  # You can change whatever permission you want here.

        # you can also change the group of the file:
        os.chown(self.baseFilename, uid, gid)   # pass the user_id and group_id you want to set 

Here's a complete solution for Django based on rob's solution. in my_module :

import logging
import logging.handlers
import os

The class= that happens during the logging configuration is evaluated in the namespace of the logging module, and by default this does not have a binding to handlers. So we have to put it in explicitly before we can extend it. See this SO article

logging.handlers = logging.handlers

It's this magical incantation that took me forever to find - I couldn't believe it did anything! Finally, Jon's class will load without errors.

class GroupWriteRotatingFileHandler(logging.handlers.RotatingFileHandler):    
    def _open(self):
        prevumask = os.umask(0o002)
        rtv = logging.handlers.RotatingFileHandler._open(self)
        os.umask(prevumask)
        return rtv

To use this for Django add the following this in the settings file

from my_module import GroupWriteRotatingFileHandler
logging.handlers.GroupWriteRotatingFileHandler = GroupWriteRotatingFileHandler

And then in LOGGING['handlers']['file'] you have

'class': 'logging.handlers.GroupWriteRotatingFileHandler'

It looks that def _open(self): worked with umask(0o000) to get all the permissions of -rw-rw-rw-.

os.chmod(self.baseFilename, 0o0777) failed with ValueError: Unable to configure handler 'some_handler': [Errno 1] Operation not permitted: if the log file has ownership of root:root that's different from running process's, such as testuser.

from logging import handlers

import logging
import os

class GroupWriteRotatingFileHandler(handlers.RotatingFileHandler):
    def _open(self):
        prevumask = os.umask(0o000)  # -rw-rw-rw-
        rtv = logging.handlers.RotatingFileHandler._open(self)
        os.umask(prevumask)
        return rtv

LOGGING = {
    'handlers': {
        'db_handler': {
                'level': 'DEBUG',
                'class': 'log.GroupWriteRotatingFileHandler',
                'filename': PATH_TO_LOGS + '/db.log',
                'maxBytes': maxBytes,
                'backupCount': backupCount,
                'formatter': 'standard',
        },

Log files:

logs]# ls -lrt
-rw-rw-rw- 1 root root 71 Apr  1 16:02 db.log

logs]# ls -lrt
total 0
-rw-rw-rw- 1 testuser testuser 0 Apr  1 16:20 db.log

I think what described here is an anti-pattern - different processes should not write data into the same file.

And non of the solutions above worked for me, causing different permissions issues in different scenarios.

As a temp workaround I've added a random suffix into the log filename so each process will get a unique filename on a startup.

The proper way to solve this issue - have a centralized log handler (log server), e.g. rsyslog.

Related