summaryrefslogtreecommitdiff
path: root/logger.py
blob: 4247180a53e664cfa9e070b761c96fce860f64e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
"""Define a logger interface and two concrete loggers: one which prints
everything on stdout, the other using syslog.

:copyright: 2000-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: General Public License version 2 - http://www.gnu.org/licenses

# FIXME use logging from stdlib instead.
"""
__docformat__ = "restructuredtext en"

from warnings import warn
warn('logilab.common.logger module is deprecated and will disappear in a future release. \
use logging module instead.',
     DeprecationWarning, stacklevel=2)

import sys
import traceback
import time


LOG_EMERG   = 0
LOG_ALERT   = 1
LOG_CRIT    = 2
LOG_ERR     = 3
LOG_WARN    = 4
LOG_NOTICE  = 5
LOG_INFO    = 6
LOG_DEBUG   = 7

INDICATORS = ['emergency', 'alert', 'critical', 'error',
              'warning', 'notice', 'info', 'debug']


def make_logger(method='print', threshold=LOG_DEBUG, sid=None, output=None):
    """return a logger for the given method
    
    known methods are 'print', 'eprint' and syslog'
    """
    if method == 'print':
        if output is None:
            output = sys.stdout
        return PrintLogger(threshold, output, sid=sid)
    elif method == 'eprint':
        return PrintLogger(threshold, sys.stderr, sid=sid)
    elif method == 'syslog':
        return SysLogger(threshold, sid)
    elif method == 'file':
        if not output:
            raise ValueError('No logfile specified')
        else:
            logfile = open(output, 'a')
            return PrintLogger(threshold, logfile, sid=sid)
    else:
        raise ValueError('Unknown logger method: %r' % method)


class AbstractLogger:
    """logger interface.
    Priorities allow to filter on the importance of events
    An event gets logged if it's priority is lower than the threshold"""

    def __init__(self, threshold=LOG_DEBUG, priority_indicator=1):
        self.threshold = threshold
        self.priority_indicator = priority_indicator
        
    def log(self, priority=LOG_DEBUG, message='', substs=None):
        """log a message with priority <priority>
        substs are optional substrings
        """
        #print 'LOG', self, priority, self.threshold, message
        if priority <= self.threshold :
            if substs is not None:
                message = message % substs
            if self.priority_indicator:
                message = '[%s] %s' % (INDICATORS[priority], message)
            self._writelog(priority, message)

    def _writelog(self, priority, message):
        """Override this method in concrete class """
        raise NotImplementedError()

    def log_traceback(self, priority=LOG_ERR, tb_info=None):
        """log traceback information with priority <priority>
        """
        assert tb_info is not None
        e_type, value, tbck = tb_info
        stacktb = traceback.extract_tb(tbck)
        l = ['Traceback (most recent call last):']
        for stackentry in stacktb :
            if stackentry[3]:
                plus = '\n    %s' % stackentry[3]
            else:
                plus = ''
            l.append('filename="%s" line_number="%s" function_name="%s"%s' %
                     (stackentry[0], stackentry[1], stackentry[2], plus))
        try:
            l.append(str(e_type) + ': ' + value.__str__())
        except UnicodeError:
            l.append(str(e_type) + ' (message can\'t be displayed)')
            
        self.log(priority, '\n'.join(l))


class PrintLogger(AbstractLogger):
    """logger implementation

    log everything to a file, using the standard output by default
    """
    
    def __init__(self, threshold, output=sys.stdout, sid=None,
                 encoding='UTF-8'):
        AbstractLogger.__init__(self, threshold)
        self.output = output
        self.sid = sid
        self.encoding = encoding
        
    def _writelog(self, priority, message):
        """overridden from AbstractLogger"""
        if isinstance(message, unicode):
            message = message.encode(self.encoding, 'replace')
        if self.sid is not None:
            self.output.write('[%s] [%s] %s\n' % (time.asctime(), self.sid,
                                                  message))
        else:
            self.output.write('[%s] %s\n' % (time.asctime(), message))
        self.output.flush()

class SysLogger(AbstractLogger):
    """ logger implementation

    log everything to syslog daemon
    use the LOCAL_7 facility
    """

    def __init__(self, threshold, sid=None, encoding='UTF-8'):
        import syslog
        AbstractLogger.__init__(self, threshold)
        if sid is None:
            sid = 'syslog'
        self.encoding = encoding
        syslog.openlog(sid, syslog.LOG_PID)
        
    def _writelog(self, priority, message):
        """overridden from AbstractLogger"""
        import syslog
        if isinstance(message, unicode):
            message = message.encode(self.encoding, 'replace')
        syslog.syslog(priority | syslog.LOG_LOCAL7, message)