update link to upper-constraints.txt
[optf/osdf.git] / osdf / logging / osdf_logging.py
1 # -------------------------------------------------------------------------
2 #   Copyright (c) 2015-2017 AT&T Intellectual Property
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 #
18 import logging
19 from logging import config
20 import os
21 import traceback
22
23 import yaml
24
25 from osdf.logging import monkey
26 from osdf.utils.programming_utils import MetaSingleton
27
28 BASE_DIR = os.path.dirname(__file__)
29 LOGGING_FILE = os.path.join(BASE_DIR, '..', '..', 'config', 'log.yml')
30
31
32 def format_exception(err, prefix=None):
33     """Format operation for use with ecomp logging
34
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 def create_log_dirs():
45     with open(LOGGING_FILE, 'r') as fid:
46         yaml_config = yaml.full_load(fid)
47     for key in yaml_config['handlers']:
48         a = yaml_config['handlers'][key]
49         if a.get('filename'):
50             os.makedirs(os.path.dirname(a['filename']), exist_ok=True)
51
52
53 class OOFOSDFLogMessageHelper(metaclass=MetaSingleton):
54     """Provides loggers as a singleton (otherwise, we end up with duplicate messages).
55
56     Provides error_log, metric_log, audit_log, and debug_log (in that order)
57
58     Additionally can provide specific log handlers too
59     """
60     log_handlers = None
61     default_levels = ["error", "metrics", "audit", "debug"]
62
63     def get_handlers(self, levels=None):
64         """Return ONAP-compliant log handlers for different levels. Each "level" ends up in a different log file
65
66         with a prefix of that level.
67
68         For example: error_log, metrics_log, audit_log, debug_log in that order
69
70         :param levels: None or list of levels subset of self.default_levels (["error", "metrics", "audit", "debug"])
71
72         :return: list of log_handlers in the order of levels requested.
73               if levels is None: we return handlers for self.default_levels
74               if levels is ["error", "audit"], we return log handlers for that.
75         """
76         create_log_dirs()
77         monkey.patch_all()
78         config.yamlConfig(filepath=LOGGING_FILE, watchDog=False)
79         wanted_levels = self.default_levels if levels is None else levels
80         return [logging.getLogger(x) for x in wanted_levels]
81
82
83 class OOFOSDFLogMessageFormatter(object):
84
85     @staticmethod
86     def accepted_valid_request(req_id, request):
87         """valid request message formatter
88
89         """
90         return "Accepted valid request for ID: {} for endpoint: {}".format(
91             req_id, request.url)
92
93     @staticmethod
94     def invalid_request(req_id, err):
95         """invalid request message formatter
96
97         """
98         return "Invalid request for request ID: {}; cause: {}".format(
99             req_id, format_exception(err))
100
101     @staticmethod
102     def invalid_response(req_id, err):
103         """invalid response message formatter
104
105         """
106         return "Invalid response for request ID: {}; cause: {}".format(
107             req_id, format_exception(err))
108
109     @staticmethod
110     def malformed_request(request, err):
111         """Malformed request message formatter
112
113         """
114         return "Malformed request for URL {}, from {}; cause: {}".format(
115             request.url, request.remote_address, format_exception(err))
116
117     @staticmethod
118     def malformed_response(response, client, err):
119         """Malformed response message formatter
120
121         """
122         return "Malformed response {} for client {}; cause: {}".format(
123             response, client, format_exception(err))
124
125     @staticmethod
126     def need_policies(req_id):
127         """Policies needed message formatter
128
129         """
130         return "Policies required but found no policies for request ID: {}".format(req_id)
131
132     @staticmethod
133     def policy_service_error(url, req_id, err):
134         """policy service error message formatter
135
136         """
137         return "Unable to call policy for {} for request ID: {}; cause: {}".format(
138             url, req_id, format_exception(err))
139
140     @staticmethod
141     def requesting_url(url, req_id):
142         """Message formatter for requesting url
143
144         """
145         return "Making a call to URL {} for request ID: {}".format(url, req_id)
146
147     @staticmethod
148     def requesting(service_name, req_id):
149         """Message formatter for requesting a service
150
151         """
152         return "Making a call to service {} for request ID: {}".format(service_name, req_id)
153
154     @staticmethod
155     def error_requesting(service_name, req_id, err):
156         """Message formatter on error requesting a service
157
158         """
159         return "Error while requesting service {} for request ID: {}; cause: {}".format(
160             service_name, req_id, format_exception(err))
161
162     @staticmethod
163     def calling_back(req_id, callback_url):
164         """Message formatter when a callback url is invoked
165
166         """
167         return "Posting result to callback URL for request ID: {}; callback URL={}".format(
168             req_id, callback_url)
169
170     @staticmethod
171     def calling_back_with_body(req_id, callback_url, body):
172         """Message formatter when a callback url with body is invoked
173
174         """
175         return "Posting result to callback URL for request ID: {}; callback URL={} body={}".format(
176             req_id, callback_url, body)
177
178     @staticmethod
179     def error_calling_back(req_id, callback_url, err):
180         """Message formatter on an error while posting result to callback url
181
182         """
183         return "Error while posting result to callback URL {} for request ID: {}; cause: {}".format(
184             req_id, callback_url, format_exception(err))
185
186     @staticmethod
187     def received_request(url, remote_addr, json_body):
188         """Message when a call is received
189
190         """
191         return "Received a call to {} from {} {}".format(url, remote_addr, json_body)
192
193     @staticmethod
194     def new_worker_thread(req_id, extra_msg=""):
195         """Message on invoking a new worker thread
196
197         """
198         res = "Initiating new worker thread for request ID: {}".format(req_id)
199         return res + extra_msg
200
201     @staticmethod
202     def inside_worker_thread(req_id, extra_msg=""):
203         """Message when inside a worker thread
204
205         """
206         res = "Inside worker thread for request ID: {}".format(req_id)
207         return res + extra_msg
208
209     @staticmethod
210     def processing(req_id, desc):
211         """Processing a request
212
213         """
214         return "Processing request ID: {} -- {}".format(req_id, desc)
215
216     @staticmethod
217     def processed(req_id, desc):
218         """Processed the request
219
220         """
221         return "Processed request ID: {} -- {}".format(req_id, desc)
222
223     @staticmethod
224     def error_while_processing(req_id, desc, err):
225         """Error while processing the request
226
227         """
228         return "Error while processing request ID: {} -- {}; cause: {}".format(
229             req_id, desc, format_exception(err))
230
231     @staticmethod
232     def creating_local_env(req_id):
233         """Creating a local environment
234
235         """
236         return "Creating local environment request ID: {}".format(
237             req_id)
238
239     @staticmethod
240     def error_local_env(req_id, desc, err):
241         """Error creating a local env
242
243         """
244         return "Error while creating local environment for request ID: {} -- {}; cause: {}".format(
245             req_id, desc, err.__traceback__)
246
247     @staticmethod
248     def inside_new_thread(req_id, extra_msg=""):
249         """Inside a new thread
250
251         """
252         res = "Spinning up a new thread for request ID: {}".format(req_id)
253         return res + " " + extra_msg
254
255     @staticmethod
256     def error_response_posting(req_id, desc, err):
257         """Error while posting a response
258
259         """
260         return "Error while posting a response for a request ID: {} -- {}; cause: {}".format(
261             req_id, desc, err.__traceback__)
262
263     @staticmethod
264     def received_http_response(resp):
265         """Received a http response
266
267         """
268         return "Received response [code: {}, headers: {}, data: {}]".format(
269             resp.status_code, resp.headers, resp.__dict__)
270
271     @staticmethod
272     def sending_response(req_id, desc):
273         """sending a response
274
275         """
276         return "Response is sent for request ID: {} -- {}".format(
277             req_id, desc)
278
279     @staticmethod
280     def listening_response(req_id, desc):
281         """Resposne is sent for a request ID
282
283         """
284         return "Response is sent for request ID: {} -- {}".format(
285             req_id, desc)
286
287     @staticmethod
288     def items_received(item_num, item_type, desc="Received"):
289         """Items received
290
291         """
292         return "{} {} {}".format(desc, item_num, item_type)
293
294     @staticmethod
295     def items_sent(item_num, item_type, desc="Published"):
296         """items published
297
298         """
299         return "{} {} {}".format(desc, item_num, item_type)
300
301
302 MH = OOFOSDFLogMessageFormatter
303
304 error_log, metrics_log, audit_log, debug_log = OOFOSDFLogMessageHelper().get_handlers()
305
306
307 def warn_audit_error(msg):
308     """Log the message to error_log.warn and audit_log.warn
309
310     """
311     log_message_multi(msg, audit_log.warn, error_log.warn)
312
313
314 def log_message_multi(msg, *logger_methods):
315     """Log the msg to multiple loggers
316
317     :param msg: message to log
318     :param logger_methods: e.g. error_log.warn, audit_log.warn, etc.
319     """
320     for log_method in logger_methods:
321         log_method(msg)