|
2 | 2 |
|
3 | 3 | import logging |
4 | 4 | import socket |
| 5 | +import sys |
5 | 6 |
|
6 | 7 | try: |
7 | 8 | import simplejson as json |
|
16 | 17 | from fluent import sender |
17 | 18 |
|
18 | 19 |
|
19 | | -class FluentRecordFormatter(object): |
20 | | - def __init__(self): |
| 20 | +class FluentRecordFormatter(logging.Formatter, object): |
| 21 | + """ A structured formatter for Fluent. |
| 22 | +
|
| 23 | + Best used with server storing data in an ElasticSearch cluster for example. |
| 24 | +
|
| 25 | + :param fmt: a dict with format string as values to map to provided keys. |
| 26 | + """ |
| 27 | + def __init__(self, fmt=None, datefmt=None): |
| 28 | + super(FluentRecordFormatter, self).__init__(None, datefmt) |
| 29 | + |
| 30 | + if not fmt: |
| 31 | + self._fmt_dict = { |
| 32 | + 'sys_host': '%(hostname)s', |
| 33 | + 'sys_name': '%(name)s', |
| 34 | + 'sys_module': '%(module)s', |
| 35 | + } |
| 36 | + else: |
| 37 | + self._fmt_dict = fmt |
| 38 | + |
21 | 39 | self.hostname = socket.gethostname() |
22 | 40 |
|
23 | 41 | def format(self, record): |
24 | | - data = {'sys_host': self.hostname, |
25 | | - 'sys_name': record.name, |
26 | | - 'sys_module': record.module, |
27 | | - # 'sys_lineno': record.lineno, |
28 | | - # 'sys_levelno': record.levelno, |
29 | | - # 'sys_levelname': record.levelname, |
30 | | - # 'sys_filename': record.filename, |
31 | | - # 'sys_funcname': record.funcName, |
32 | | - # 'sys_exc_info': record.exc_info, |
33 | | - } |
34 | | - # if 'sys_exc_info' in data and data['sys_exc_info']: |
35 | | - # data['sys_exc_info'] = self.formatException(data['sys_exc_info']) |
| 42 | + # Only needed for python2.6 |
| 43 | + if sys.version_info[0:2] <= (2, 6) and self.usesTime(): |
| 44 | + record.asctime = self.formatTime(record, self.datefmt) |
| 45 | + |
| 46 | + # Compute attributes handled by parent class. |
| 47 | + super(FluentRecordFormatter, self).format(record) |
| 48 | + # Add ours |
| 49 | + record.hostname = self.hostname |
| 50 | + # Apply format |
| 51 | + data = dict([(key, value % record.__dict__) |
| 52 | + for key, value in self._fmt_dict.items()]) |
36 | 53 |
|
37 | 54 | self._structuring(data, record.msg) |
38 | 55 | return data |
39 | 56 |
|
| 57 | + def usesTime(self): |
| 58 | + return any([value.find('%(asctime)') >= 0 |
| 59 | + for value in self._fmt_dict.values()]) |
| 60 | + |
40 | 61 | def _structuring(self, data, msg): |
| 62 | + """ Melds `msg` into `data`. |
| 63 | +
|
| 64 | + :param data: dictionary to be sent to fluent server |
| 65 | + :param msg: :class:`LogRecord`'s message to add to `data`. |
| 66 | + `msg` can be a simple string for backward compatibility with |
| 67 | + :mod:`logging` framework, a JSON encoded string or a dictionary |
| 68 | + that will be merged into dictionary generated in :meth:`format. |
| 69 | + """ |
41 | 70 | if isinstance(msg, dict): |
42 | 71 | self._add_dic(data, msg) |
43 | | - elif isinstance(msg, str): |
| 72 | + elif isinstance(msg, basestring): |
44 | 73 | try: |
45 | 74 | self._add_dic(data, json.loads(str(msg))) |
46 | 75 | except ValueError: |
47 | | - pass |
| 76 | + self._add_dic(data, {'message': msg}) |
| 77 | + else: |
| 78 | + self._add_dic(data, {'message': msg}) |
48 | 79 |
|
49 | 80 | @staticmethod |
50 | 81 | def _add_dic(data, dic): |
|
0 commit comments