From ec12fe974a13fd79847fd05f4d6efe57ff5d1b9d 2016-10-19 08:40:39 From: Marcin Lulek Date: 2016-10-19 08:40:39 Subject: [PATCH] formatters: added json formatter --- diff --git a/backend/src/appenlight/lib/logging.py b/backend/src/appenlight/lib/logging.py new file mode 100644 index 0000000..275e50e --- /dev/null +++ b/backend/src/appenlight/lib/logging.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2010-2016 RhodeCode GmbH +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License, version 3 +# (only), as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# This program is dual-licensed. If you wish to learn more about the +# AppEnlight Enterprise Edition, including its added features, Support +# services, and proprietary license terms, please see +# https://rhodecode.com/licenses/ + +import json +import logging + +ignored_keys = ['args', 'asctime', 'created', 'exc_info', 'exc_text', + 'filename', 'funcName', 'levelname', 'levelno', 'lineno', + 'message', 'module', 'msecs', 'msg', 'name', 'pathname', + 'process', 'processName', 'relativeCreated', 'stack_info', + 'thread', 'threadName'] + + +class JSONFormatter(logging.Formatter): + def format(self, record): + """ + Format the specified record as text. + + The record's attribute dictionary is used as the operand to a + string formatting operation which yields the returned string. + Before formatting the dictionary, a couple of preparatory steps + are carried out. The message attribute of the record is computed + using LogRecord.getMessage(). If the formatting string uses the + time (as determined by a call to usesTime(), formatTime() is + called to format the event time. If there is exception information, + it is formatted using formatException() and appended to the message. + """ + record.message = record.getMessage() + log_dict = vars(record) + keys = [k for k in log_dict.keys() if k not in ignored_keys] + payload = {'message': record.message} + payload.update({k: log_dict[k] for k in keys}) + record.message = json.dumps(payload, default=lambda x: str(x)) + + if self.usesTime(): + record.asctime = self.formatTime(record, self.datefmt) + s = self.formatMessage(record) + if record.exc_info: + # Cache the traceback text to avoid converting it multiple times + # (it's constant anyway) + if not record.exc_text: + record.exc_text = self.formatException(record.exc_info) + if record.exc_text: + if s[-1:] != "\n": + s = s + "\n" + s = s + record.exc_text + if record.stack_info: + if s[-1:] != "\n": + s = s + "\n" + s = s + self.formatStack(record.stack_info) + return s diff --git a/backend/src/appenlight/templates/ini/production.ini.jinja2 b/backend/src/appenlight/templates/ini/production.ini.jinja2 index 6187bbb..7f67ee5 100644 --- a/backend/src/appenlight/templates/ini/production.ini.jinja2 +++ b/backend/src/appenlight/templates/ini/production.ini.jinja2 @@ -153,7 +153,7 @@ keys = root, appenlight, sqlalchemy keys = console [formatters] -keys = generic +keys = generic, json [logger_root] level = WARN @@ -176,9 +176,14 @@ qualname = sqlalchemy.engine class = StreamHandler args = (sys.stderr,) level = NOTSET -formatter = generic +formatter = json [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +# json string will land as "message" key of format string +[formatter_json] +class=appenlight.lib.logging.JSONFormatter +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s + # End logging configuration diff --git a/development.ini b/development.ini index 71cad4a..7c1faef 100644 --- a/development.ini +++ b/development.ini @@ -160,7 +160,7 @@ keys = root, appenlight, sqlalchemy, elasticsearch keys = console [formatters] -keys = generic +keys = generic, json [logger_root] level = INFO @@ -188,9 +188,14 @@ qualname = sqlalchemy.engine class = StreamHandler args = (sys.stderr,) level = NOTSET -formatter = generic +formatter = json [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +# json string will land as "message" key of format string +[formatter_json] +class=appenlight.lib.logging.JSONFormatter +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s + # End logging configuration diff --git a/testing.ini b/testing.ini index cfdac98..ea8cee5 100644 --- a/testing.ini +++ b/testing.ini @@ -146,7 +146,7 @@ keys = root, appenlight, sqlalchemy, elasticsearch keys = console [formatters] -keys = generic +keys = generic, json [logger_root] level = INFO @@ -174,9 +174,14 @@ qualname = sqlalchemy.engine class = StreamHandler args = (sys.stderr,) level = NOTSET -formatter = generic +formatter = json [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s +# json string will land as "message" key of format string +[formatter_json] +class=appenlight.lib.logging.JSONFormatter +format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s + # End logging configuration