clogger.py 4.37 KB
Newer Older
1
#!/usr/bin/env python
2
# -*- mode: python; coding: utf-8 -*-
3 4 5 6 7 8
#
# Basic logger, storing data in /var/log/clogger/*.log
# Author    : Pierre-Elliott Bécue <becue@crans.org>
# License   : GPLv3
# Date      : 27/04/2014

9
import os
10 11
import datetime
import pytz
12 13
import logging

14
TZ = pytz.timezone('Europe/Paris')
15
LDIRPATH = os.getenv('DBG_CLOGGER_PATH', '/var/log/clogger')
16

17 18
class CLogger(logging.Logger):
    """
19
    Crans logger.
20 21
    """

22
    def __init__(self, loggerName, service=None, level="info", debug=False):
23
        """
24
        Initializes logger. The debug variable is useful to have a print to stdout (when debugging)
25 26 27
        """
        super(CLogger, self).__init__(loggerName)

Pierre-Elliott Bécue's avatar
Pierre-Elliott Bécue committed
28 29 30 31 32
        self.c_formatter = None
        self.c_file_handler = None
        self.c_sh = None
        self.c_level = level

33
        # When no service is specified, we don't put the reference in the format.
34
        if service is None:
Pierre-Elliott Bécue's avatar
Pierre-Elliott Bécue committed
35
            self.c_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
36
        else:
Pierre-Elliott Bécue's avatar
Pierre-Elliott Bécue committed
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
            self.c_format = "%%(asctime)s - %%(name)s - %(service)s - %%(levelname)s - %%(message)s" % {'service': service}

        self.create_formatter()
        self.apply_file_handler(loggerName)
        if debug:
            self.apply_stream_handler()

    def get_file_handler_path(self):
        """Returns the file handler path"""
        if self.__file_handler_path is None:
            return ''
        return self.__file_handler_path

    def create_formatter(self):
        """Creates a formatter based on CFormatter class.
        It uses self.format as a source."""

        if self.c_formatter is not None:
            return

        # Creates formatter
        self.c_formatter = CFormatter(self.c_format, "%Y-%m-%dT%H:%M:%S.%f%z")

    def apply_stream_handler(self):
        """Creates a streamhandler that prints to stdout.
        Its level is debug"""
        self.c_sh = logging.StreamHandler()
        self.c_shlevel = logging.DEBUG
        self.c_sh.setLevel(self.c_shlevel)
        self.c_sh.setFormatter(self.c_formatter)
        self.addHandler(self.c_sh)

    def apply_file_handler(self, loggerName):
        """Creates a file handler which level is given by self.c_level"""
        if self.c_file_handler is not None:
            return
73

74
        # Computes the file handler name using service name.
Pierre-Elliott Bécue's avatar
Pierre-Elliott Bécue committed
75
        self.__file_handler_path = os.path.join(LDIRPATH, "%s.log" % (loggerName,))
76

77
        # Creates FileHandler
Pierre-Elliott Bécue's avatar
Pierre-Elliott Bécue committed
78
        self.c_file_handler = logging.FileHandler(self.__file_handler_path)
79

80
        # Catches appropriate level in logging.
Pierre-Elliott Bécue's avatar
Pierre-Elliott Bécue committed
81 82
        self.c_file_handler_level = getattr(logging, self.c_level.upper(), logging.INFO)
        self.c_file_handler.setLevel(self.c_file_handler_level)
83 84

        # Adds formatter to FileHandler
Pierre-Elliott Bécue's avatar
Pierre-Elliott Bécue committed
85
        self.c_file_handler.setFormatter(self.c_formatter)
86

87
        # Adds FileHandler to Handlers
Pierre-Elliott Bécue's avatar
Pierre-Elliott Bécue committed
88
        self.addHandler(self.c_file_handler)
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122

class CFormatter(logging.Formatter):
    """
    This Formatter subclasses the classic formatter to provide a
    timezone-aware logging.
    """

    converter = datetime.datetime.fromtimestamp

    def formatTime(self, record, datefmt=None):
        """
        Return the creation time of the specified LogRecord as formatted text.

        This method should be called from format() by a formatter which
        wants to make use of a formatted time. This method can be overridden
        in formatters to provide for any specific requirement, but the
        basic behaviour is as follows: if datefmt (a string) is specified,
        it is used with time.strftime() to format the creation time of the
        record. Otherwise, the ISO8601 format is used. The resulting
        string is returned. This function uses a user-configurable function
        to convert the creation time to a tuple. By default, time.localtime()
        is used; to change this for a particular formatter instance, set the
        'converter' attribute to a function with the same signature as
        time.localtime() or time.gmtime(). To change it for all formatters,
        for example if you want all logging times to be shown in GMT,
        set the 'converter' attribute in the Formatter class.
        """
        ct = self.converter(record.created, TZ)
        ct = ct.replace(microsecond=int(record.msecs * 1000))
        if datefmt:
            s = ct.strftime(datefmt)
        else:
            s = ct.strftime("%Y-%m-%d %H:%M:%S.%f")
        return s