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