"""Logging. The logging library provides the handler that sends the logs to a regular HTTPS provider (in our case we use Loggly). The message is also formatted in a specific format (json), which includes fields that are specific to the Orchard, such as the service name, the correlation id. """ import datetime import json import logging import logging.handlers import traceback from tornado.httpclient import AsyncHTTPClient from tornado.netutil import Resolver from grass import config Resolver.configure('tornado.netutil.ThreadedResolver', num_threads=config.NUM_THREADS) resolver = Resolver() AsyncHTTPClient.configure(None, max_clients=config.MAX_CLIENTS, resolver=resolver) session = AsyncHTTPClient() def callback(response): """Post callback. When the post has been dispatched to loggly, handle the response (in our case we don't have anything to do). Args: response (object): the response of the request to the service. """ pass class DSNHandler(logging.Handler): """DNSHandler.""" def __init__(self, dsn): """Constructor of HTTPSHandler. Args: dsn (str): the full dsn to send the log to. Returns: HTTPSHandler: the created handler. """ logging.Handler.__init__(self) self.dsn = dsn def get_full_message(self, record): """Get the full message for a record. Some of our systems might still require this data to be sent to loggly, so instead of sending the message of the record, we send the exception information. Args: record (LogRecord): the record to log. Returns: mixed: the string or an object (dictionary). """ if record.exc_info: return '\n'.join(traceback.format_exception(*record.exc_info)) else: return record.msg def emit(self, record): """Emit a record. From the documentation: do whatever it takes to actually log the specified logging record. Here: we send it to the provider. The payload matches Orchard format. Args: record (LogRecord): the record to log. """ try: payload = { 'tag': 'ows1', 'timestamp': datetime.datetime.fromtimestamp( record.created ).isoformat(), 'level': record.levelname, 'correlation_id': record.correlation_id, 'message': self.get_full_message(record), 'service': config.SERVICE_NAME, 'service_version': config.SERVICE_VERSION, 'environment': config.environment, 'meta': { 'file_name': record.filename, 'function_name': record.funcName, 'line': record.lineno, }, } headers = {'Content-Type': 'application/json'} session.fetch( self.dsn, callback=callback, method='POST', body=json.dumps(payload), headers=headers, ) except (KeyboardInterrupt, SystemExit): raise except: self.handleError(record)