1 # Copyright (c) 2019 AT&T Intellectual Property.
2 # Copyright (c) 2018-2019 Nokia.
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
16 # This source code is part of the near-RT RIC (RAN Intelligent Controller)
17 # platform project (RICP).
20 """Structured logging library with Mapped Diagnostic Context
22 Outputs the log entries to standard out in structured format, json currently.
23 Severity based filtering.
24 Supports Mapped Diagnostic Context (MDC).
26 Set MDC pairs are automatically added to log entries by the library.
28 from typing import TypeVar
29 from enum import IntEnum
36 """Severity levels of the log messages."""
43 LEVEL_STRINGS = {Level.DEBUG: "DEBUG",
45 Level.WARNING: "WARNING",
49 Value = TypeVar('Value', str, int)
53 """Initialize the mdclogging module.
54 Calling of the function is optional. If not called, the process name
55 (sys.argv[0]) is used by default.
58 name -- name of the component. The name will appear as part of the log
61 def __init__(self, name: str = sys.argv[0], level: Level = Level.DEBUG):
62 """Initialize a Logger instance.
65 name -- name of the component. The name will appear as part of the
69 self.current_level = level
72 def _output_log(self, log: str):
73 """Output the log, currently to stdout."""
76 def log(self, level: Level, message: str):
79 Logs the message with the given severity if it is equal or higher than
80 the current logging level.
83 level -- severity of the log message
84 message -- log message
86 if level >= self.current_level:
88 log_entry["ts"] = int(round(time.time() * 1000))
89 log_entry["crit"] = LEVEL_STRINGS[level]
90 log_entry["id"] = self.procname
91 log_entry["mdc"] = self.mdc
92 log_entry["msg"] = message
93 self._output_log(json.dumps(log_entry))
95 def error(self, message: str):
96 """Log an error message. Equals to log(ERROR, msg)."""
97 self.log(Level.ERROR, message)
99 def warning(self, message: str):
100 """Log a warning message. Equals to log(WARNING, msg)."""
101 self.log(Level.WARNING, message)
103 def info(self, message: str):
104 """Log an info message. Equals to log(INFO, msg)."""
105 self.log(Level.INFO, message)
107 def debug(self, message: str):
108 """Log a debug message. Equals to log(DEBUG, msg)."""
109 self.log(Level.DEBUG, message)
111 def set_level(self, level: Level):
112 """Set current logging level.
115 level -- logging level. Log messages with lower severity will be
119 self.current_level = Level(level)
123 def get_level(self) -> Level:
124 """Return the current logging level."""
125 return self.current_level
127 def add_mdc(self, key: str, value: Value):
128 """Add a logger specific MDC.
130 If an MDC with the given key exists, it is replaced with the new one.
131 An MDC can be removed with remove_mdc() or clean_mdc().
137 self.mdc[key] = value
139 def get_mdc(self, key: str) -> Value:
140 """Return logger's MDC value with the given key or None."""
146 def remove_mdc(self, key: str):
147 """Remove logger's MDC with the given key."""
154 """Remove all MDCs of the logger instance."""