13

I want to configure my Python logger in such a way so that each instance of logger should log in a file having the same name as the name of the logger itself.

e.g.:

log_hm = logging.getLogger('healthmonitor') log_hm.info("Testing Log") # Should log to /some/path/healthmonitor.log log_sc = logging.getLogger('scripts') log_sc.debug("Testing Scripts") # Should log to /some/path/scripts.log log_cr = logging.getLogger('cron') log_cr.info("Testing cron") # Should log to /some/path/cron.log 

I want to keep it generic and dont want to hardcode all kind of logger names I can have. Is that possible?

4 Answers 4

22

How about simply wrap the handler code in a function:

import os def myLogger(name): logger = logging.getLogger(name) logger.setLevel(logging.DEBUG) handler = logging.FileHandler(os.path.join('/some/path/', name + '.log'), 'w') logger.addHandler(handler) return logger log_hm = myLogger('healthmonitor') log_hm.info("Testing Log") # Should log to /some/path/healthmonitor.log 

To prevent creating duplicate handlers, care needs to be taken to ensure that myLogger(name) is only called once per name. Usually that means putting myLogger(name) inside

if __name__ == '__main__': log_hm = myLogger('healthmonitor') 

of the main script.

Sign up to request clarification or add additional context in comments.

4 Comments

This is a workable solution but I'm looking for something more Pythonic. Could there be a way to exploit the existing Python logging framework rather than adding a wrapper on top of it?
There is an issue with this approach. It adds duplicate handlers when called more than once with the same name. And opening the file is write mode going to wipe out the earlier data.
is there any way to prevent add duplicate handler each time call mylogger methods?
in my case, the file is not created at /some/path/ its in root of the project directory.
10
import os import logging class MyFileHandler(object): def __init__(self, dir, logger, handlerFactory, **kw): kw['filename'] = os.path.join(dir, logger.name) self._handler = handlerFactory(**kw) def __getattr__(self, n): if hasattr(self._handler, n): return getattr(self._handler, n) raise AttributeError, n logger = logging.getLogger('test') logger.setLevel(logging.INFO) handler = MyFileHandler(os.curdir, logger, logging.FileHandler) logger.addHandler(handler) logger.info('hello mylogger') 

3 Comments

Could it be possible to add the handler before the "getLogger" call so that I don't have to add it up everytime I call getLogger?
@sharjeel: Set the handler during initialization and all future calls to getLogger() don't need to add an handler. The solution is similar to the ~unutbu's one. In that example myLogger should be called only once for file, otherwise you will have strange side effects.
I like this approach. You can also keep a track of the files opened and for a new instance, check if the file is already opened then don't re-open. Though, such kind of science is not required in my case :-)
2

The approach used in the above solution is correct, but that has issue of adding duplicate handlers when called more than once. Here is the improved version.

import os def getLogger(name): # logger.getLogger returns the cached logger when called multiple times # logger.Logger created a new one every time and that avoids adding # duplicate handlers logger = logging.Logger(name) logger.setLevel(logging.DEBUG) handler = logging.FileHandler(os.path.join('/some/path/', name + '.log'), 'a') logger.addHandler(handler) return logger def test(i): log_hm = getLogger('healthmonitor') log_hm.info("Testing Log %s", i) # Should log to /some/path/healthmonitor.log test(1) test(2) 

Comments

0

I'm trying to implement this solution with both dynamic path and file name but nothing is written in the file.

class PaymentViewSet(viewsets.ModelViewSet): serializer_class = PaymentSerializer queryset = Payment.objects.all() permission_classes = [IsAuthenticated] def paymentLog(self, paymentSerializer): # file : logs/${terminalName}/${%Y-%m}-payments.log terminalName = TerminalSerializer(Terminal.objects.get(pk=paymentSerializer.data.get("terminal"))).data.get("name") filePath = os.path.join(settings.LOG_PATH, terminalName) if not os.path.exists(filePath): os.makedirs(filePath) fileName = filePath + "/" + datetime.now().strftime("%Y-%m") +'-payments.log' handler = logging.FileHandler(fileName) handler.setFormatter('%(asctime)s [PAYMENT]- %(message)s') logger = logging.Logger("payment") logger.setLevel(logging.INFO) logger.addHandler(handler) # logger.propagate = False logging.info(paymentSerializer.data) # printout() def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) self.perform_create(serializer) # log here self.paymentLog(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) 

The path and file are created like intended but the log never writes.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.