~rlane/nova/lp773690

« back to all changes in this revision

Viewing changes to nova/log.py

  • Committer: rlane at wikimedia
  • Date: 2011-04-29 22:30:40 UTC
  • mfrom: (382.1.655 nova)
  • Revision ID: rlane@wikimedia.org-20110429223040-i0x3ds9eqwrabyru
MergeĀ fromĀ trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
#    License for the specific language governing permissions and limitations
17
17
#    under the License.
18
18
 
19
 
"""
20
 
Nova logging handler.
 
19
"""Nova logging handler.
21
20
 
22
21
This module adds to logging functionality by adding the option to specify
23
22
a context object when calling the various log methods.  If the context object
24
23
is not specified, default formatting is used.
25
24
 
26
25
It also allows setting of formatting information through flags.
 
26
 
27
27
"""
28
28
 
29
 
 
30
29
import cStringIO
31
30
import inspect
32
31
import json
41
40
 
42
41
 
43
42
FLAGS = flags.FLAGS
44
 
 
45
43
flags.DEFINE_string('logging_context_format_string',
46
44
                    '%(asctime)s %(levelname)s %(name)s '
47
45
                    '[%(request_id)s %(user)s '
48
46
                    '%(project)s] %(message)s',
49
47
                    'format string to use for log messages with context')
50
 
 
51
48
flags.DEFINE_string('logging_default_format_string',
52
49
                    '%(asctime)s %(levelname)s %(name)s [-] '
53
50
                    '%(message)s',
54
51
                    'format string to use for log messages without context')
55
 
 
56
52
flags.DEFINE_string('logging_debug_format_suffix',
57
53
                    'from (pid=%(process)d) %(funcName)s'
58
54
                    ' %(pathname)s:%(lineno)d',
59
55
                    'data to append to log format when level is DEBUG')
60
 
 
61
56
flags.DEFINE_string('logging_exception_prefix',
62
57
                    '(%(name)s): TRACE: ',
63
58
                    'prefix each line of exception output with this format')
64
 
 
65
59
flags.DEFINE_list('default_log_levels',
66
60
                  ['amqplib=WARN',
67
61
                   'sqlalchemy=WARN',
68
62
                   'boto=WARN',
69
63
                   'eventlet.wsgi.server=WARN'],
70
64
                  'list of logger=LEVEL pairs')
71
 
 
72
65
flags.DEFINE_bool('use_syslog', False, 'output to syslog')
73
66
flags.DEFINE_string('logfile', None, 'output to named file')
74
67
 
83
76
INFO = logging.INFO
84
77
DEBUG = logging.DEBUG
85
78
NOTSET = logging.NOTSET
 
79
 
 
80
 
86
81
# methods
87
82
getLogger = logging.getLogger
88
83
debug = logging.debug
93
88
exception = logging.exception
94
89
critical = logging.critical
95
90
log = logging.log
 
91
 
 
92
 
96
93
# handlers
97
94
StreamHandler = logging.StreamHandler
98
95
WatchedFileHandler = logging.handlers.WatchedFileHandler
106
103
 
107
104
 
108
105
def _dictify_context(context):
109
 
    if context == None:
 
106
    if context is None:
110
107
        return None
111
108
    if not isinstance(context, dict) \
112
109
    and getattr(context, 'to_dict', None):
127
124
 
128
125
 
129
126
class NovaLogger(logging.Logger):
130
 
    """
131
 
    NovaLogger manages request context and formatting.
 
127
    """NovaLogger manages request context and formatting.
132
128
 
133
129
    This becomes the class that is instanciated by logging.getLogger.
 
130
 
134
131
    """
 
132
 
135
133
    def __init__(self, name, level=NOTSET):
136
134
        logging.Logger.__init__(self, name, level)
137
135
        self.setup_from_flags()
138
136
 
139
137
    def setup_from_flags(self):
140
 
        """Setup logger from flags"""
 
138
        """Setup logger from flags."""
141
139
        level = NOTSET
142
140
        for pair in FLAGS.default_log_levels:
143
141
            logger, _sep, level_name = pair.partition('=')
148
146
        self.setLevel(level)
149
147
 
150
148
    def _log(self, level, msg, args, exc_info=None, extra=None, context=None):
151
 
        """Extract context from any log call"""
 
149
        """Extract context from any log call."""
152
150
        if not extra:
153
151
            extra = {}
154
152
        if context:
157
155
        return logging.Logger._log(self, level, msg, args, exc_info, extra)
158
156
 
159
157
    def addHandler(self, handler):
160
 
        """Each handler gets our custom formatter"""
 
158
        """Each handler gets our custom formatter."""
161
159
        handler.setFormatter(_formatter)
162
160
        return logging.Logger.addHandler(self, handler)
163
161
 
164
162
    def audit(self, msg, *args, **kwargs):
165
 
        """Shortcut for our AUDIT level"""
 
163
        """Shortcut for our AUDIT level."""
166
164
        if self.isEnabledFor(AUDIT):
167
165
            self._log(AUDIT, msg, args, **kwargs)
168
166
 
169
167
    def exception(self, msg, *args, **kwargs):
170
 
        """Logging.exception doesn't handle kwargs, so breaks context"""
 
168
        """Logging.exception doesn't handle kwargs, so breaks context."""
171
169
        if not kwargs.get('exc_info'):
172
170
            kwargs['exc_info'] = 1
173
171
        self.error(msg, *args, **kwargs)
181
179
            for k in env.keys():
182
180
                if not isinstance(env[k], str):
183
181
                    env.pop(k)
184
 
            message = "Environment: %s" % json.dumps(env)
 
182
            message = 'Environment: %s' % json.dumps(env)
185
183
            kwargs.pop('exc_info')
186
184
            self.error(message, **kwargs)
187
185
 
188
186
 
189
187
class NovaFormatter(logging.Formatter):
190
 
    """
191
 
    A nova.context.RequestContext aware formatter configured through flags.
 
188
    """A nova.context.RequestContext aware formatter configured through flags.
192
189
 
193
190
    The flags used to set format strings are: logging_context_foramt_string
194
191
    and logging_default_format_string.  You can also specify
197
194
 
198
195
    For information about what variables are available for the formatter see:
199
196
    http://docs.python.org/library/logging.html#formatter
 
197
 
200
198
    """
201
199
 
202
200
    def format(self, record):
203
 
        """Uses contextstring if request_id is set, otherwise default"""
 
201
        """Uses contextstring if request_id is set, otherwise default."""
204
202
        if record.__dict__.get('request_id', None):
205
203
            self._fmt = FLAGS.logging_context_format_string
206
204
        else:
214
212
        return logging.Formatter.format(self, record)
215
213
 
216
214
    def formatException(self, exc_info, record=None):
217
 
        """Format exception output with FLAGS.logging_exception_prefix"""
 
215
        """Format exception output with FLAGS.logging_exception_prefix."""
218
216
        if not record:
219
217
            return logging.Formatter.formatException(self, exc_info)
220
218
        stringbuffer = cStringIO.StringIO()
221
219
        traceback.print_exception(exc_info[0], exc_info[1], exc_info[2],
222
220
                                  None, stringbuffer)
223
 
        lines = stringbuffer.getvalue().split("\n")
 
221
        lines = stringbuffer.getvalue().split('\n')
224
222
        stringbuffer.close()
225
223
        formatted_lines = []
226
224
        for line in lines:
227
225
            pl = FLAGS.logging_exception_prefix % record.__dict__
228
 
            fl = "%s%s" % (pl, line)
 
226
            fl = '%s%s' % (pl, line)
229
227
            formatted_lines.append(fl)
230
 
        return "\n".join(formatted_lines)
 
228
        return '\n'.join(formatted_lines)
 
229
 
231
230
 
232
231
_formatter = NovaFormatter()
233
232
 
241
240
        NovaLogger.__init__(self, name, level)
242
241
 
243
242
    def setup_from_flags(self):
244
 
        """Setup logger from flags"""
 
243
        """Setup logger from flags."""
245
244
        global _filelog
246
245
        if FLAGS.use_syslog:
247
246
            self.syslog = SysLogHandler(address='/dev/log')