blob: e3b8ae58c3a077e9d0eb3a09f43ed2a6e4377d2d [file] [log] [blame]
Chip Boling67b674a2019-02-08 11:42:18 -06001#
2# Copyright 2017 the original author or authors.
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17"""Setting up proper logging for Voltha"""
18
Zack Williams84a71e92019-11-15 09:00:19 -070019from __future__ import absolute_import
20import builtins # bring in python 2 to 3 compat imports: https://python-future.org/imports.html
Chip Bolinga9febbf2020-07-29 12:34:12 -050021import json
Chip Boling67b674a2019-02-08 11:42:18 -060022import logging
23import logging.config
24from collections import OrderedDict
25
26import structlog
27from structlog.stdlib import BoundLogger, INFO
28
29try:
Zack Williams84a71e92019-11-15 09:00:19 -070030 from _thread import get_ident as _get_ident
Chip Boling67b674a2019-02-08 11:42:18 -060031except ImportError:
Zack Williams84a71e92019-11-15 09:00:19 -070032 from _dummy_thread import get_ident as _get_ident
Chip Boling67b674a2019-02-08 11:42:18 -060033
34
35class StructuredLogRenderer(object):
36 def __call__(self, logger, name, event_dict):
37 # in order to keep structured log data in event_dict to be forwarded as
38 # is, we need to pass it into the logger framework as the first
39 # positional argument.
40 args = (event_dict,)
41 kwargs = {}
42 return args, kwargs
43
44
Chip Bolinga9febbf2020-07-29 12:34:12 -050045class EncoderFix(json.JSONEncoder):
46 def default(self, obj):
47 return str(obj)
48
49
50class JsonRenderedOrderedDict(OrderedDict):
51 """Our special version of OrderedDict that renders into string as a dict,
52 to make the log stream output cleaner.
53 """
54 def __repr__(self, _repr_running=None):
55 # od.__repr__() <==> repr(od)
56 call_key = id(self), _get_ident()
57 _repr_running = _repr_running or dict()
58
59 if call_key in _repr_running:
60 return '...'
61
62 _repr_running[call_key] = 1
63 try:
64 if not self:
65 return ''
66
67 # Convert to JSON but strip off outside '{}'
68 msg = '"msg":"{}",'.format(self.pop('event'))
69 json_msg = json.dumps(self, indent=None,
70 cls=EncoderFix,
71 separators=(', ', ': '),
72 sort_keys=True)[1:-1]
73 return msg + json_msg
74
75 finally:
76 del _repr_running[call_key]
77
78
Chip Boling67b674a2019-02-08 11:42:18 -060079class PlainRenderedOrderedDict(OrderedDict):
80 """Our special version of OrderedDict that renders into string as a dict,
81 to make the log stream output cleaner.
82 """
83 def __repr__(self, _repr_running={}):
84 'od.__repr__() <==> repr(od)'
85 call_key = id(self), _get_ident()
86 if call_key in _repr_running:
87 return '...'
88 _repr_running[call_key] = 1
89 try:
90 if not self:
91 return '{}'
92 return '{%s}' % ", ".join("%s: %s" % (k, v)
93 for k, v in self.items())
94 finally:
95 del _repr_running[call_key]
96
97
98def setup_logging(log_config, instance_id, verbosity_adjust=0):
99 """
100 Set up logging such that:
101 - The primary logging entry method is structlog
102 (see http://structlog.readthedocs.io/en/stable/index.html)
103 - By default, the logging backend is Python standard lib logger
104 """
105
106 def add_exc_info_flag_for_exception(_, name, event_dict):
107 if name == 'exception':
108 event_dict['exc_info'] = True
109 return event_dict
110
111 def add_instance_id(_, __, event_dict):
112 event_dict['instance_id'] = instance_id
113 return event_dict
114
115 # Configure standard logging
116 logging.config.dictConfig(log_config)
Rohan Agrawalc1ee2962020-03-25 20:33:11 +0000117 logging.root.level = verbosity_adjust
Chip Boling67b674a2019-02-08 11:42:18 -0600118
119 processors = [
120 add_exc_info_flag_for_exception,
121 structlog.processors.StackInfoRenderer(),
122 structlog.processors.format_exc_info,
123 add_instance_id,
Chip Bolinga9febbf2020-07-29 12:34:12 -0500124 structlog.processors.UnicodeDecoder(),
125 structlog.processors.JSONRenderer()
Chip Boling67b674a2019-02-08 11:42:18 -0600126 ]
127 structlog.configure(logger_factory=structlog.stdlib.LoggerFactory(),
Chip Bolinga9febbf2020-07-29 12:34:12 -0500128 context_class=JsonRenderedOrderedDict,
Chip Boling67b674a2019-02-08 11:42:18 -0600129 wrapper_class=BoundLogger,
130 processors=processors)
131
132 # Mark first line of log
133 log = structlog.get_logger()
Matteo Scandolo63efb062019-11-26 12:14:48 -0700134 log.info("first-line", log_level=logging.root.level, verbosity_adjust=verbosity_adjust)
Chip Boling67b674a2019-02-08 11:42:18 -0600135 return log
136
137
Rohan Agrawalc1ee2962020-03-25 20:33:11 +0000138def string_to_int(loglevel):
139 l = loglevel.upper()
140 if l == "DEBUG": return 10
141 elif l == "INFO": return 20
142 elif l == "WARN": return 30
143 elif l == "ERROR": return 40
144 elif l == "FATAL": return 50
145 else: return 0
146
147
Chip Bolinga9febbf2020-07-29 12:34:12 -0500148def update_logging(instance_id, _vcore_id, verbosity_adjust=0):
Chip Boling67b674a2019-02-08 11:42:18 -0600149 """
150 Add the vcore id to the structured logger
151 :param vcore_id: The assigned vcore id
152 :return: structure logger
153 """
154 def add_exc_info_flag_for_exception(_, name, event_dict):
155 if name == 'exception':
156 event_dict['exc_info'] = True
157 return event_dict
158
159 def add_instance_id(_, __, event_dict):
160 event_dict['instance_id'] = instance_id
161 return event_dict
162
Rohan Agrawalc1ee2962020-03-25 20:33:11 +0000163 logging.root.level = verbosity_adjust
164
Chip Boling67b674a2019-02-08 11:42:18 -0600165 processors = [
166 add_exc_info_flag_for_exception,
167 structlog.processors.StackInfoRenderer(),
168 structlog.processors.format_exc_info,
169 add_instance_id,
Chip Boling67b674a2019-02-08 11:42:18 -0600170 StructuredLogRenderer(),
171 ]
172 structlog.configure(processors=processors)
173
174 # Mark first line of log
175 log = structlog.get_logger()
176 log.info("updated-logger")
177 return log