blob: 0cdb4003a6e7a6f2217033d8a8052e2e757c25b7 [file] [log] [blame]
alshabib6ca05622017-01-31 14:08:36 -08001#
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
19import logging
20import logging.config
21from collections import OrderedDict
22
23import structlog
24from structlog.stdlib import BoundLogger, INFO
25
26try:
27 from thread import get_ident as _get_ident
28except ImportError:
29 from dummy_thread import get_ident as _get_ident
30
31
32class FluentRenderer(object):
33 def __call__(self, logger, name, event_dict):
34 # in order to keep structured log data in event_dict to be forwarded as
35 # is to the fluent logger, we need to pass it into the logger framework
36 # as the first positional argument.
37 args = (event_dict, )
38 kwargs = {}
39 return args, kwargs
40
41
42class PlainRenderedOrderedDict(OrderedDict):
43 """Our special version of OrderedDict that renders into string as a dict,
44 to make the log stream output cleaner.
45 """
Zack Williams7eb36d02019-03-19 07:16:12 -070046
alshabib6ca05622017-01-31 14:08:36 -080047 def __repr__(self, _repr_running={}):
48 'od.__repr__() <==> repr(od)'
49 call_key = id(self), _get_ident()
50 if call_key in _repr_running:
51 return '...'
52 _repr_running[call_key] = 1
53 try:
54 if not self:
55 return '{}'
56 return '{%s}' % ", ".join("%s: %s" % (k, v)
57 for k, v in self.items())
58 finally:
59 del _repr_running[call_key]
60
61
62def setup_logging(log_config, instance_id, verbosity_adjust=0, fluentd=None):
63 """
64 Set up logging such that:
65 - The primary logging entry method is structlog
66 (see http://structlog.readthedocs.io/en/stable/index.html)
67 - By default, the logging backend is Python standard lib logger
68 - Alternatively, fluentd can be configured with to be the backend,
69 providing direct bridge to a fluent logging agent.
70 """
71
72 def add_exc_info_flag_for_exception(_, name, event_dict):
73 if name == 'exception':
74 event_dict['exc_info'] = True
75 return event_dict
76
77 def add_instance_id(_, __, event_dict):
78 event_dict['instance_id'] = instance_id
79 return event_dict
80
81 # if fluentd is specified, we need to override the config data with
82 # its host and port info
83 if fluentd is not None:
84 fluentd_host = fluentd.split(':')[0].strip()
85 fluentd_port = int(fluentd.split(':')[1].strip())
86
87 handlers = log_config.get('handlers', None)
88 if isinstance(handlers, dict):
89 for _, defs in handlers.iteritems():
90 if isinstance(defs, dict):
91 if defs.get('class', '').endswith('FluentHandler'):
92 defs['host'] = fluentd_host
93 defs['port'] = fluentd_port
94
95 # Configure standard logging
96 logging.config.dictConfig(log_config)
97 logging.root.level -= 10 * verbosity_adjust
98
99 processors = [
100 add_exc_info_flag_for_exception,
101 structlog.processors.StackInfoRenderer(),
102 structlog.processors.format_exc_info,
103 add_instance_id,
104 FluentRenderer(),
105 ]
106 structlog.configure(logger_factory=structlog.stdlib.LoggerFactory(),
107 context_class=PlainRenderedOrderedDict,
108 wrapper_class=BoundLogger,
109 processors=processors)
110
111 # Mark first line of log
112 log = structlog.get_logger()
113 log.info("first-line")
114 return log