-
Notifications
You must be signed in to change notification settings - Fork 6
/
logger.py
169 lines (151 loc) · 6.43 KB
/
logger.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/local/bin/python3.4
"""
Copyright (c) 2018 SONATA-NFV, 5GTANGO, Paderborn University
ALL RIGHTS RESERVED.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Neither the name of the SONATA-NFV, 5GTANGO, Paderborn University
nor the names of its contributors may be used to endorse or promote
products derived from this software without specific prior written
permission.
This work has been performed in the framework of the SONATA project,
funded by the European Commission under Grant number 671517 through
the Horizon 2020 and 5G-PPP programmes. The authors would like to
acknowledge the contributions of their colleagues of the SONATA
partner consortium (www.sonata-nfv.eu).
This work has also been performed in the framework of the 5GTANGO project,
funded by the European Commission under Grant number 761493 through
the Horizon 2020 and 5G-PPP programmes. The authors would like to
acknowledge the contributions of their colleagues of the SONATA
partner consortium (www.5gtango.eu).
"""
import logging
import coloredlogs
import datetime
import json
import sys
import traceback
class TangoLogger(object):
"""
5GTAGNO logger that allows to switch to "JSON mode" to create JSON log messages in the 5GTANGO format.
Two modes:
- log_json = False: Normal colored logging in text format
- log_json = True: 5GTANGO logging (flat JSON objects and metadata)
Example:
LOG = TangoLogger.getLogger("logger_name", log_level=logging.INFO, log_json=True)
LOG.warning("this is a test message", extra={"start_stop": "START", "status": "201"})
Turns into (printed to a single line):
{
"type":"W",
"time_elapsed":"",
"operation":"setup_logging",
"status":"201",
"message":"this is a test message",
"component":"tango.tngsdk.package",
"processName":"MainProcess",
"threadName":"MainThread",
"start_stop":"START",
"lineno":73,
"timestamp":"2018-11-15 19:25:49.348161 UTC"
}
"""
@staticmethod
def reconfigure_all_tango_loggers(
log_level=logging.INFO, log_json=False):
"""
Configure all active TangoLoggers (identified by 'tango.' prfix).
Two modes:
- log_json = False: Normal colored logging in text format
- log_json = True: 5GTANGO logging (flat JSON objects and metadata)
"""
# reconfigure all our TangoLoggers
for n, l in logging.Logger.manager.loggerDict.items():
# use prefix to only get TangoLoggers
if n.startswith("tango.") and isinstance(l, logging.Logger):
TangoLogger._reconfigure_logger(l, log_level, log_json)
@staticmethod
def _reconfigure_logger(logger, log_level, log_json):
"""
Reconfigure specific logger (switch between normal logging and 5GTANGO JsonLogging).
Switch is done by muting the unwanted handlers.
"""
# apply log_level
logger.setLevel(log_level)
for h in logger.handlers:
# show messages in all handlers
h.setLevel(log_level)
# disable handler depending on log_json
if isinstance(h, TangoJsonLogHandler):
if not log_json:
h.setLevel(999) # disable (hide all)
else:
if log_json:
h.setLevel(999) # disable (hide all)
@staticmethod
def getLogger(name, log_level=logging.INFO, log_json=False):
"""
Create a TangoLogger logger.
"""
# all TangoLoggers are prefixed for global setup
logger = logging.getLogger("tango.{}".format(name))
logger.propagate = False # important to not emit logs twice
coloredlogs.install(
logger=logger,
fmt="%(asctime)s %(hostname)s %(name)s:l%(lineno)d"
+ " %(levelname)s %(message)s")
th = TangoJsonLogHandler()
logger.addHandler(th)
# configure logger
TangoLogger._reconfigure_logger(logger, log_level, log_json)
return logger
class TangoJsonLogHandler(logging.StreamHandler):
"""
Custom log handler to create JSON-based log messages as required by the 5GTANGO SP.
https://github.com/sonata-nfv/tng-gtk-utils
It uses the normal Python logging interface and utilizes the "extra" parameter of the logging methods
to add additional fields (optionally) for the JSON output.
"""
def _to_tango_dict(self, record):
"""
Creates a dict in 5GTANGO format from the given record.
Sets defaults of not given.
"""
# fetch exception info and stack trace if available
exc_info_str = None
if record.exc_info:
exc_info_str = str(
traceback.format_exception(
record.exc_info[0],
record.exc_info[1],
record.exc_info[2]))
d = {
# TANGO default fields
"type": record.levelname[0],
"timestamp": "{} UTC".format(datetime.datetime.utcnow()),
"start_stop": record.__dict__.get("start_stop", ""),
"component": record.name,
"operation": record.__dict__.get("operation", record.funcName),
"message": str(record.msg),
"status": record.__dict__.get("status", ""),
"time_elapsed": record.__dict__.get("time_elapsed", ""),
# some additional fields (because we can ;-))
"lineno": record.lineno,
"threadName": record.threadName,
"processName": record.processName,
"stack_info": str(record.stack_info),
"exc_info": exc_info_str
}
return d
def emit(self, record):
"""
We go the simple way here: Just print the JSON :-)
"""
print(json.dumps(self._to_tango_dict(record)))
sys.stdout.flush()