Re-org folders, onboard test folder, test config
[optf/osdf.git] / osdf / logging / osdf_logging.py
1 import logging
2 import traceback
3 import uuid
4
5 import logging
6 from logging.handlers import RotatingFileHandler
7 from osdf.utils.programming_utils import MetaSingleton
8
9
10 def log_handlers_pre_onap(config_file="config/pre_onap_logging_common_v1.config",
11                           service_name="OOF_OSDF"):
12     """
13     Convenience handlers for logging to different log files
14
15     :param config_file: configuration file (properties file) that specifies log location, rotation, etc.
16     :param service_name: name for this service
17     :return: dictionary of log objects: "error", "metrics", "audit", "debug"
18
19     We can use the returned values as follows:
20     X["error"].fatal("a FATAL message for the error log")
21     X["error"].error("an ERROR message for the error log")
22     X["error"].warn("a WARN message for the error log")
23     X["audit"].info("an INFO message for the audit log")
24     X["metrics"].info("an INFO message for the metrics log")
25     X["debug"].debug("a DEBUG message for the debug log")
26     """
27     # Keeping main_params as a place-holder for ONAP related logging needs
28     # main_params = dict(instanceUUID=uuid.uuid1(), serviceName=service_name, configFile=config_file)
29     return dict((x, logging.getLogger(x))   # keep **main_params as a placeholder for ONAP fields
30                 for x in ["error", "metrics", "audit", "debug"])
31
32
33 def format_exception(err, prefix=None):
34     """Format operation for use with ecomp logging
35     :param err: exception object
36     :param prefix: prefix string message
37     :return: formatted exception (via repr(traceback.format_tb(err.__traceback__))
38     """
39     exception_lines = traceback.format_exception(err.__class__, err, err.__traceback__)
40     exception_desc = "".join(exception_lines)
41     return exception_desc if not prefix else prefix + ": " + exception_desc
42
43
44 class OOF_OSDFLogMessageHelper(metaclass=MetaSingleton):
45     """Provides loggers as a singleton (otherwise, we end up with duplicate messages).
46     Provides error_log, metric_log, audit_log, and debug_log (in that order)
47     Additionally can provide specific log handlers too
48     """
49     log_handlers = None
50     default_levels = ["error", "metrics", "audit", "debug"]
51
52     def _setup_handlers(self, log_version="pre_onap", config_file=None, service_name=None):
53         """return error_log, metrics_log, audit_log, debug_log"""
54         if self.log_handlers is None:
55             params = {}
56             params.update({"config_file": config_file} if config_file else {})
57             params.update({"service_name": service_name} if service_name else {})
58
59             if log_version == "pre_onap":
60                 self.log_handlers = log_handlers_pre_onap(**params)
61
62     def get_handlers(self, levels=None, log_version="pre_onap", config_file=None, service_name=None):
63         """Return ONAP-compliant log handlers for different levels. Each "level" ends up in a different log file
64         with a prefix of that level.
65
66         For example: error_log, metrics_log, audit_log, debug_log in that order
67         :param levels: None or list of levels subset of self.default_levels (["error", "metrics", "audit", "debug"])
68         :param log_version: Currently only pre_onap is supported
69         :param config_file: Logging configuration file for ONAP compliant logging
70         :param service_name: Name of the service
71         :return: list of log_handlers in the order of levels requested.
72               if levels is None: we return handlers for self.default_levels
73               if levels is ["error", "audit"], we return log handlers for that.
74         """
75         self._setup_handlers(log_version="pre_onap", config_file=config_file, service_name=service_name)
76         wanted_levels = self.default_levels if levels is None else levels
77         return [self.log_handlers.get(x) for x in wanted_levels]
78
79
80 class OOF_OSDFLogMessageFormatter(object):
81
82     @staticmethod
83     def accepted_valid_request(req_id, request):
84         return "Accepted valid request for ID: {} for endpoint: {}".format(
85             req_id, request.url)
86
87     @staticmethod
88     def invalid_request(req_id, err):
89         return "Invalid request for request ID: {}; cause: {}".format(
90             req_id, format_exception(err))
91
92     @staticmethod
93     def invalid_response(req_id, err):
94         return "Invalid response for request ID: {}; cause: {}".format(
95             req_id, format_exception(err))
96
97     @staticmethod
98     def malformed_request(request, err):
99         return "Malformed request for URL {}, from {}; cause: {}".format(
100             request.url, request.remote_address, format_exception(err))
101
102     @staticmethod
103     def malformed_response(response, client, err):
104         return "Malformed response {} for client {}; cause: {}".format(
105             response, client, format_exception(err))
106
107     @staticmethod
108     def need_policies(req_id):
109         return "Policies required but found no policies for request ID: {}".format(req_id)
110
111     @staticmethod
112     def policy_service_error(url, req_id, err):
113         return "Unable to call policy for {} for request ID: {}; cause: {}".format(
114             url, req_id, format_exception(err))
115
116     @staticmethod
117     def requesting_url(url, req_id):
118         return "Making a call to URL {} for request ID: {}".format(url, req_id)
119
120     @staticmethod
121     def requesting(service_name, req_id):
122         return "Making a call to service {} for request ID: {}".format(service_name, req_id)
123
124     @staticmethod
125     def error_requesting(service_name, req_id, err):
126         return "Error while requesting service {} for request ID: {}; cause: {}".format(
127             service_name, req_id, format_exception(err))
128
129     @staticmethod
130     def calling_back(req_id, callback_url):
131         return "Posting result to callback URL for request ID: {}; callback URL={}".format(
132             req_id, callback_url)
133
134     @staticmethod
135     def calling_back_with_body(req_id, callback_url, body):
136         return "Posting result to callback URL for request ID: {}; callback URL={} body={}".format(
137             req_id, callback_url, body)
138
139     @staticmethod
140     def error_calling_back(req_id, callback_url, err):
141         return "Error while posting result to callback URL {} for request ID: {}; cause: {}".format(
142             req_id, callback_url, format_exception(err))
143
144     @staticmethod
145     def received_request(url, remote_addr, json_body):
146         return "Received a call to {} from {} {}".format(url, remote_addr, json_body)
147
148     @staticmethod
149     def new_worker_thread(req_id, extra_msg=""):
150         res = "Initiating new worker thread for request ID: {}".format(req_id)
151         return res + extra_msg
152
153     @staticmethod
154     def inside_worker_thread(req_id, extra_msg=""):
155         res = "Inside worker thread for request ID: {}".format(req_id)
156         return res + extra_msg
157
158     @staticmethod
159     def processing(req_id, desc):
160         return "Processing request ID: {} -- {}".format(req_id, desc)
161
162     @staticmethod
163     def processed(req_id, desc):
164         return "Processed request ID: {} -- {}".format(req_id, desc)
165
166     @staticmethod
167     def error_while_processing(req_id, desc, err):
168         return "Error while processing request ID: {} -- {}; cause: {}".format(
169             req_id, desc, format_exception(err))
170
171     @staticmethod
172     def creating_local_env(req_id):
173         return "Creating local environment request ID: {}".format(
174             req_id)
175
176     @staticmethod
177     def error_local_env(req_id, desc, err):
178         return "Error while creating local environment for request ID: {} -- {}; cause: {}".format(
179             req_id, desc, err.__traceback__)
180
181     @staticmethod
182     def inside_new_thread(req_id, extra_msg=""):
183         res = "Spinning up a new thread for request ID: {}".format(req_id)
184         return res + " " + extra_msg
185
186     @staticmethod
187     def error_response_posting(req_id, desc, err):
188         return "Error while posting a response for a request ID: {} -- {}; cause: {}".format(
189             req_id, desc, err.__traceback__)
190
191     @staticmethod
192     def received_http_response(resp):
193         return "Received response [code: {}, headers: {}, data: {}]".format(
194             resp.status_code, resp.headers, resp.__dict__)
195
196     @staticmethod
197     def sending_response(req_id, desc):
198         return "Response is sent for request ID: {} -- {}".format(
199             req_id, desc)
200
201     @staticmethod
202     def listening_response(req_id, desc):
203         return "Response is sent for request ID: {} -- {}".format(
204             req_id, desc)
205
206     @staticmethod
207     def items_received(item_num, item_type, desc="Received"):
208         return "{} {} {}".format(desc, item_num, item_type)
209
210     @staticmethod
211     def items_sent(item_num, item_type, desc="Published"):
212         return "{} {} {}".format(desc, item_num, item_type)
213
214
215 MH = OOF_OSDFLogMessageFormatter
216 error_log, metrics_log, audit_log, debug_log = OOF_OSDFLogMessageHelper().get_handlers()
217
218 def warn_audit_error(msg):
219     """Log the message to error_log.warn and audit_log.warn"""
220     log_message_multi(msg, audit_log.warn, error_log.warn)
221
222
223 def log_message_multi(msg, *logger_methods):
224     """Log the msg to multiple loggers
225     :param msg: message to log
226     :param logger_methods: e.g. error_log.warn, audit_log.warn, etc.
227     """
228     for log_method in logger_methods:
229         log_method(msg)