0xck / jsonlogfmt

JSON formatter

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

JSON formatter for logging.Logger

Allows format standard logging messasges to JSON

Usage

import JSONMapFormatter and use one for logging.Handler obj.

Terms

Look at logger argumets for logger.info('MESSAGE', {'1st': {'2nd': {'3rd': {'value': 3}}, 'value': 2}, 'value': 1}, 'more value', 'one more value', {'4th': 4}):

  • 1st is always message obj, in this example: 'MESSAGE'
  • dict-like objs that may be described by JSONMap or just stored in one of JSON path, in this example: {'1st': {'2nd': {'3rd': {'value': 3}}, 'value': 2}, 'value': 1} and {'4th': 4}
  • non dict-like objs or args that just stored in one of JSON path, in this example: 'more value' and 'one more value'

Simple example:

test.py:

import logging
from jsonlogfmt import JSONMapFormatter

fmt = JSONMapFormatter()

console = logging.StreamHandler()
console.setFormatter(fmt)
console.setLevel(logging.INFO)

logger = logging.getLogger()
logger.addHandler(console)
logger.setLevel(logging.INFO)


if __name__ == '__main__':

    logger.info('MESSAGE', {'1st': {'2nd': {'3rd': {'value': 3}}, 'value': 2}, 'value': 1}, 'more value', 'one more value', {'4th': 4})

Output:

(output was formatted, original is ordinary flat string)

{'extra': {'data': {'1st': {'2nd': {'3rd': {'value': 3}}, 'value': 2},
                    '4th': 4,
                    'args': ['more value', 'one more value']},
           'exception': {'type': '', 'value': 1},
           'funcName': '<module>',
           'lineno': 18,
           'pathname': './test.py'},
 'levelname': 'INFO',
 'msg': 'MESSAGE',
 'name': 'root',
 'time': '2018-02-05 21:06:25,618'}

Default logging format example:

Default logging formatting is also supported. That may be useful for some purposes e.g. for creating standard syslog entry with extra JSON message. Changing JSONMapFormatter() to JSONMapFormatter(fmt='%(name)s / %(asctime)s / %(message)s') gives:

Output:

(output was formatted, original is ordinary flat string)

root / 2018-02-05 21:08:20,876 / 
{'extra': {'data': {'1st': {'2nd': {'3rd': {'value': 3}}, 'value': 2},
                    '4th': 4,
                    'args': ['more value', 'one more value']},
           'exception': {'type': '', 'value': 1},
           'funcName': '<module>',
           'lineno': 18,
           'pathname': './test.py'},
 'levelname': 'INFO',
 'msg': 'MESSAGE',
 'name': 'root',
 'time': '2018-02-05 21:08:20,876'}

Custom JSONMap

By default JSONMapFormatter uses JSONMAP dict-like obj, one can be changed. Use dict-like obj or JSON.

newJSONMap = {'created': '',
    'extra': OrderedDict([('funcName', '')]),
    'levelname': '',
    'msg': '',
    'time': ''}

fmt = JSONMapFormatter(jsonmap=newJSONMap)

Output:

(output was formatted, original is ordinary flat string)

{'created': 1517855408.837364,
 'extra': {'data': {'1st': {'2nd': {'3rd': {'value': 3}}, 'value': 2},
                    '4th': 4,
                    'args': ['more value', 'one more value'],
                    'value': 1},
           'funcName': '<module>'},
 'levelname': 'INFO',
 'msg': 'MESSAGE',
 'time': '2018-02-05 21:30:08,837'}

Note.

Be careful, entry values of dict-like obj if they have similar keys on one level will be rewritten on value of latest obj.

Also in addition to Logger object attribetes there are several keys that define some values:

  • time is for formatted time
  • exctype is for name of class of exception
  • excvalue is for exception args attribute (usualy one contains exception message)
  • exctrace is for formatted exception traceback

All of them use default formatting from Formatter class

Remap default Logger keys

JSON map requires to set keys defined in Logger class, like levelname or msg for retriving appropriate value from Logger object. But those keys can be remapped by using remap dict-like obj so that JSON message will contain defined in remap keys instead any keys in JSON map. Use dict-like obj or JSON. E.g.:

REMAP = {
  'time': 'Time of issue',
  'levelname': 'LEVEL',
  'funcName': 'function'}

fmt = JSONMapFormatter(jsonmap=newJSONMap, remap=REMAP)

Output:

(output was formatted, original is ordinary flat string)

{'LEVEL': 'INFO',
 'Time of issue': '2018-02-10 21:25:15,482',
 'created': 1518287115.4829462,
 'extra': {'data': {'1st': {'2nd': {'3rd': {'value': 3}}, 'value': 2},
                    '4th': 4,
                    'args': ['more value', 'one more value'],
                    'value': 1},
           'function': '<module>'},
 'msg': 'MESSAGE'}

Note.

Maybe you have a question: why would you not use JSON map for structure of JSON message and remap for defining Logger keys that has to be changed? E.g.:

JSONMAP = {
  'Time':'',
  'LEVEL':'',
  'extra':
    'Message':''}

REMAP = {
  'Time': 'time',
  'LEVEL': 'levelname',
  'Message': 'msg'}

Answer is pretty simple this is more complicated.

Custom extrakeys and argskey

By default for storing extra data extra: {data: {}} path is used (extra: {data: {args: {}}} for non dict-like args) that behavior can be changed. Use extrakeys for dict-like obj and argskey for non dict-like args). Use list-like obj or JSON. E.g. fmt = JSONMapFormatter(jsonmap=newJSONMap, extrakeys=['newExtrapath', 'subpath'], argskey=['newArgspath']) gives:

Output:

(output was formatted, original is ordinary flat string)

{'created': 1517856642.977184,
 'extra': {'funcName': '<module>'},
 'levelname': 'INFO',
 'msg': 'MESSAGE',
 'newExtrapath': {'subpath': {'1st': {'2nd': {'3rd': {'value': 3}}, 'value': 2},
                              '4th': 4,
                              'newArgspath': ['more value', 'one more value'],
                              'value': 1}},
 'time': '2018-02-05 21:50:42,977'}

Note.

extrakeys is parent path for argskey that means non dict-like args stores in common extra path.

Remap time and exception keys:

For some reason additional keys for time and exception defined in separate dict-like obj. By default those keys and values are:

  • time for formatted time (default formatting from Formatter class)
  • exctype for exception class
  • excvalue exception arguments, usually message
  • exctrace exception traceback

All of them or just part can be changed. Also JSONMap has to changed for new keys. Use dict-like obj or JSON. E.g.:

AUXMAP = {
    'exctype': 'type',
    'excvalue': 'value',
    'exctrace': 'traceback'
}


JSONMAP = OrderedDict({
    'time': '',
    'levelname': '',
    'name': '',
    'msg': '',
    'extra': OrderedDict({
            'exception': OrderedDict({
                'type': '',
                'value': '',
                'traceback': ''
            })
    })
})

fmt = JSONMapFormatter(jsonmap=JSONMAP, auxmap=AUXMAP)

Output:

(output was formatted, original is ordinary flat string)

{'extra': {'exception': {'traceback': 'Traceback (most recent call last):\n'
                                      '  File "./test.py", line 57, in '
                                      '<module>\n'
                                      '    1/0\n'
                                      'ZeroDivisionError: division by zero',
                         'type': 'ZeroDivisionError',
                         'value': ['division by zero']}},
 'levelname': 'ERROR',
 'msg': 'division by zero',
 'name': 'root',
 'time': '2018-02-19 15:35:41,990'}

Stripping empty values

By default all values defined in JSON map will be added even some of them are empty, to change this behavior set strip variable to True. That prevents appearance of empty values in message. But it does not prevent empty values if they are added via args.

newJSONMap = OrderedDict([('time', ''),
   ('levelname', ''),
   ('EMPTY', OrderedDict()),
   ('msg', ''),
   ('extra', OrderedDict([('funcName', '')])),
   ('created', '')])

fmt = JSONMapFormatter(jsonmap=newJSONMap, extrakeys=['newExtrapath', 'subpath'], argskey=['newArgspath'], strip=True)

Output:

(output was formatted, original is ordinary flat string)

{'created': 1517906994.2913642,
 'extra': {'funcName': '<module>'},
 'levelname': 'INFO',
 'msg': 'MESSAGE',
 'newExtrapath': {'subpath': {'1st': {'2nd': {'3rd': {'value': 3}}, 'value': 2},
                              '4th': 4,
                              'newArgspath': ['more value', 'one more value'],
                              'value': 1}},
 'time': '2018-02-06 11:49:54,291'}

As you can see there is not EMPTY key in output above.

About

JSON formatter

License:Mozilla Public License 2.0


Languages

Language:Python 100.0%