log.py 9.32 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Copyright (C) 2010  Internet Systems Consortium.
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

Jerry's avatar
Jerry committed
16
"""This module is to convert python logging module over
17
18
19
20
to log4python.
Copyright (C) 2010  Internet Systems Consortium.
To use, simply 'import isc.log.log' and log away!
"""
21
22
23
24
25
import os
import syslog
import logging
import logging.handlers

Jerry's avatar
Jerry committed
26
"""LEVELS: logging levels mapping
27
"""
28
29
30
31
32
33
LEVELS = {'debug' : logging.DEBUG,
       	  'info' : logging.INFO,
          'warning' : logging.WARNING,
          'error' : logging.ERROR,
          'critical' : logging.CRITICAL}

Jerry's avatar
Jerry committed
34

Jerry's avatar
Jerry committed
35
FORMATTER = logging.Formatter("%(name)s: %(levelname)s: %(message)s")
Jerry's avatar
Jerry committed
36
37
TIME_FORMATTER = logging.Formatter("%(asctime)s.%(msecs)03d %(name)s: %(levelname)s: %(message)s",
                                   "%d-%b-%Y %H:%M:%S")
38

Jerry's avatar
Jerry committed
39
40
class NSFileLogHandler(logging.handlers.RotatingFileHandler):
    """RotatingFileHandler: replace RotatingFileHandler with a custom handler"""
Jerry's avatar
Jerry committed
41
42

    def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0):
Jerry's avatar
Jerry committed
43
44
45
        dir = os.path.split(filename)
        if not (os.path.exists(dir[0])):
            os.makedirs(dir[0])
Jerry's avatar
Jerry committed
46
47
48
        super(NSFileLogHandler, self).__init__(filename, mode, maxBytes,
                                                backupCount, encoding, delay)

49
    def shouldRollover(self, record):
Jerry's avatar
Jerry committed
50
        """Rewrite RotatingFileHandler.shouldRollover. 
51
       
Jerry's avatar
Jerry committed
52
        If the log file is deleted at runtime, a new file will be created.
53
54
        """
        dfn = self.baseFilename                 
55
        if (self.stream) and (not os.path.exists(dfn)): #Does log file exist?
56
            self.stream.close()
57
            dir = os.path.split(dfn)
58
            if not (os.path.exists(dir[0])): #Does log subdirectory exist?
59
                os.makedirs(dir[0])
60
            self.stream = self._open()
Jerry's avatar
Jerry committed
61
        return super(NSFileLogHandler, self).shouldRollover(record)
Jerry's avatar
Jerry committed
62
    
63
    def update_config(self, file_name, backup_count, max_bytes):
Jerry's avatar
Jerry committed
64
        """Update RotatingFileHandler configuration.
65

66
        If the file path does not exist, we will use the old log file.
67
68
69
70
71
        input:
            log file name
            max backup count
            predetermined log file size
        """
Jerry's avatar
Jerry committed
72
73
74
        dir = os.path.split(file_name)
        if(os.path.exists(dir[0])):
            self.baseFilename = file_name
Jerry's avatar
Jerry committed
75
76
        self.maxBytes = max_bytes
        self.backupCount = backup_count
77

78

Jerry's avatar
Jerry committed
79
80
class NSSysLogHandler(logging.Handler):    
    """Replace SysLogHandler with a custom handler 
81
82

    A handler class which sends formatted logging records to a syslog
Jerry's avatar
Jerry committed
83
    server.
84
    """
85
    def __init__(self, ident, logopt=0, facility=syslog.LOG_USER):        
Jerry's avatar
Jerry committed
86
        """Initialize a handler.
87
88
89
    
        If facility is not specified, LOG_USER is used.
        """
Jerry's avatar
Jerry committed
90
91
92
93
94
        super(NSSysLogHandler, self).__init__()
        self._ident = ident        
        self._logopt = logopt        
        self._facility = facility        
        self._mappings = {            
95
96
97
98
99
100
101
                logging.DEBUG: syslog.LOG_DEBUG,            
                logging.INFO: syslog.LOG_INFO,            
                logging.WARNING: syslog.LOG_WARNING,            
                logging.ERROR: syslog.LOG_ERR,            
                logging.CRITICAL: syslog.LOG_CRIT,            
                }   
        
Jerry's avatar
Jerry committed
102
103
104
    def _encodeLevel(self, level):        
        """Encoding the priority."""
        return self._mappings.get(level, syslog.LOG_INFO)    
105
   
106
    def emit(self, record):   
Jerry's avatar
Jerry committed
107
        """Emit a record.
108
109
110
111
     
        The record is formatted, and then sent to the syslog server. If
        exception information is present, it is NOT sent to the server.
        """
Jerry's avatar
Jerry committed
112
        syslog.openlog(self._ident, self._logopt, self._facility)        
113
        msg = self.format(record)        
Jerry's avatar
Jerry committed
114
        prio = self._encodeLevel(record.levelno)        
115
116
117
118
        syslog.syslog(prio, msg)        
        syslog.closelog()


Jerry's avatar
Jerry committed
119
120
121
122
123
class NSLogger(logging.getLoggerClass()):
    """Override logging.logger behaviour."""
    def __init__(self, log_name, log_file, severity='debug', versions=0,
                 max_bytes=0, log_to_console=True):
        """Initializes the logger with some specific parameters
Jerry's avatar
Jerry committed
124

Jerry's avatar
Jerry committed
125
        If log_to_console is True, stream handler will be used;
Jerry's avatar
Jerry committed
126
        else syslog handler will be used.
Jerry's avatar
Jerry committed
127
128

        To disable file handler, set log_file = ''.
129
        """
Jerry's avatar
Jerry committed
130
131
132
133
134
135
136
        self._log_name = log_name
        self._log_file = log_file
        self._severity = severity
        self._versions = versions
        self._max_bytes = max_bytes

        super(NSLogger, self).__init__(self._log_name)
137
138

        # Set up a specific logger with our desired output level
Jerry's avatar
Jerry committed
139
        logLevel = LEVELS.get(self._severity, logging.NOTSET)
140
        self.setLevel(logLevel)
141

Jerry's avatar
Jerry committed
142
143
144
145
146
        self._file_handler = None
        self._stream_handler = None
        self._syslog_handler = None

        self._add_rotate_handler(self._log_file, self._versions, self._max_bytes)
147
        if log_to_console:
Jerry's avatar
Jerry committed
148
            self._add_stream_handler()
149
        else:
Jerry's avatar
Jerry committed
150
            self._add_syslog_handler()
151

Jerry's avatar
Jerry committed
152
153
    def _add_rotate_handler(self, log_file, backup_count, max_bytes):
        """Add a rotate file handler.
154
155
   
        input:
156
            log_file : the location of log file. Handler will not be created 
Jerry's avatar
Jerry committed
157
                       if log_file=''
158
159
160
            max_bytes : limit log growth
            backup_count : max backup count
        """
161
162
        if(log_file != 0  and log_file != ''):
            try:
Jerry's avatar
Jerry committed
163
164
                self._file_handler = NSFileLogHandler(filename = log_file,
                                          maxBytes = max_bytes, backupCount = backup_count)
165
            except IOError:
Jerry's avatar
Jerry committed
166
                self._file_handler = None
167
                return
Jerry's avatar
Jerry committed
168
169
            self._file_handler.setFormatter(TIME_FORMATTER)
            self.addHandler(self._file_handler)
170

Jerry's avatar
Jerry committed
171
172
    def _add_stream_handler(self):
        """Add a stream handler.
173
174
175
    
        sys.stderr will be used for logging output.
        """
Jerry's avatar
Jerry committed
176
177
178
        self._stream_handler = logging.StreamHandler()
        self._stream_handler.setFormatter(TIME_FORMATTER)
        self.addHandler(self._stream_handler)
179

Jerry's avatar
Jerry committed
180
181
    def _add_syslog_handler(self, facility=syslog.LOG_USER):
        """Add a syslog handler.
182
183
184
185
   
        If facility is not specified, LOG_USER is used.
        The default severity level is INFO.
        """
Jerry's avatar
Jerry committed
186
187
        self._syslog_handler = NSSysLogHandler('BIND10', facility)
        self._syslog_handler.setFormatter(FORMATTER)
Jerry's avatar
Jerry committed
188
        #set syslog handler severity level INFO
Jerry's avatar
Jerry committed
189
190
        self._syslog_handler.setLevel(logging.INFO)
        self.addHandler(self._syslog_handler)
191

Jerry's avatar
Jerry committed
192
193
    def _update_rotate_handler(self, log_file, backup_count, max_bytes):
        """If the rotate file handler has been added to the logger, update its 
Jerry's avatar
Jerry committed
194
        configuration, or add it to the logger.
195
        """
Jerry's avatar
Jerry committed
196
        if (self._file_handler in self.handlers):
197
            if(log_file != 0 and log_file != ''):
Jerry's avatar
Jerry committed
198
                self._file_handler.update_config(log_file, backup_count, max_bytes)
199
            else:
Jerry's avatar
Jerry committed
200
201
202
203
                """If log file is empty, the handler will be removed."""
                self._file_handler.flush()
                self._file_handler.close()
                self.removeHandler(self._file_handler)
204
        else:
Jerry's avatar
Jerry committed
205
206
207
            self._add_rotate_handler(log_file, backup_count, max_bytes)

    def _get_config(self, config_data):
208
         """Get config data from module configuration"""
Jerry's avatar
Jerry committed
209
210
211
212
213
         
         log_file_str = config_data.get('log_file')
         if(log_file_str):
            self._log_file = log_file_str
         
Jerry's avatar
Jerry committed
214
         severity_str = config_data.get('log_severity')
Jerry's avatar
Jerry committed
215
216
217
         if(severity_str):
             self._severity = severity_str

Jerry's avatar
Jerry committed
218
         versions_str = config_data.get('log_versions')
Jerry's avatar
Jerry committed
219
220
221
         if(versions_str):
             self._versions = int(versions_str)

Jerry's avatar
Jerry committed
222
         max_bytes_str = config_data.get('log_max_bytes')
Jerry's avatar
Jerry committed
223
224
225
226
227
         if(max_bytes_str):
             self._max_bytes = int(max_bytes_str)

    def update_config(self, config_data):
        """Update logger's configuration.
228
229
230

        We can update logger's log level and its rotate file handler's configuration.
        """
Jerry's avatar
Jerry committed
231
232
233
        self._get_config(config_data)

        logLevel = LEVELS.get(self._severity, logging.NOTSET)
234
235
        if(logLevel != self.getEffectiveLevel()):
            self.setLevel(logLevel)
Jerry's avatar
Jerry committed
236
        self._update_rotate_handler(self._log_file, self._versions, self._max_bytes)
237
238

    def log_message(self, level, msg, *args, **kwargs):
Jerry's avatar
Jerry committed
239
        """Log 'msg % args' with the integer severity 'level'.
240
241
242
243
     
        To pass exception information, use the keyword argument exc_info with
        a true value, e.g.
  
Jerry's avatar
Jerry committed
244
        logger.log_message('info', "We have a %s", "mysterious problem").
245
        """
246
247
248
249
        logLevel = LEVELS.get(level, logging.NOTSET)
        self.log(logLevel, msg, *args, **kwargs)