Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
py-aws-util / logging.py
Size: Mime:
import json
import logging
from collections import Mapping


def json_formatter(obj):
    """Formatter for unserialisable values."""
    return str(obj)


def log_data(*argv, **kwargs):
    extra_fields = []
    _data = {}
    if argv:
        extra_fields.append('data')
        if len(argv) > 1:
            _data = list(map(lambda x: json.dumps(x) if isinstance(x, Mapping) else str(x), argv))
        else:
            _data['data'] = json.dumps(argv[0]) if isinstance(argv[0], Mapping) else str(argv[0])
    for key, value in kwargs.items():
        extra_fields.append(key)
        if isinstance(value, Mapping):
            _data[key] = json.dumps(value)
        else:
            _data[key] = str(value)
    _data['extra_fields'] = extra_fields
    return _data


class JsonFormatter(logging.Formatter):
    """AWS Lambda Logging formatter.

    Formats the log message as a JSON encoded string.  If the message is a
    dict it will be used directly.  If the message can be parsed as JSON, then
    the parse d value is used in the output record.
    """

    def __init__(self, **kwargs):
        """Return a JsonFormatter instance.

        The `json_default` kwarg is used to specify a formatter for otherwise
        unserialisable values.  It must not throw.  Defaults to a function that
        coerces the value to a string.

        Other kwargs are used to specify log field format strings.
        """
        datefmt = kwargs.pop('datefmt', None)

        super(JsonFormatter, self).__init__(datefmt=datefmt)
        self.format_dict = {
            'timestamp': '%(asctime)s',
            'level': '%(levelname)s',
            'location': '%(name)s.%(funcName)s:%(lineno)d',
            'aws_request_id': '%(aws_request_id)s'
        }
        self.format_dict.update(kwargs)
        self.default_json_formatter = kwargs.pop(
            'json_default', json_formatter)
        self.original_format_dict = self.format_dict.copy()

    def format(self, record):
        record_dict = record.__dict__.copy()
        record_dict['asctime'] = self.formatTime(record, self.datefmt)
        record_dict['aws_request_id'] = getattr(record, 'aws_request_id', '00000000-0000-0000-0000-000000000000')
        self.format_dict = self.original_format_dict.copy()
        extra_fields = getattr(record, 'extra_fields', [])
        if extra_fields:
            for field in extra_fields:
                self.format_dict[field] = f'%({field})s'

        log_dict = {
            k: v % record_dict
            for k, v in self.format_dict.items()
            if v
        }

        if isinstance(record_dict['msg'], dict):
            log_dict['message'] = record_dict['msg']
        else:
            log_dict['message'] = record.getMessage()

            # Attempt to decode the message as JSON, if so, merge it with the
            # overall message for clarity.
            try:
                log_dict['message'] = json.loads(log_dict['message'])
            except (TypeError, ValueError):
                pass

        if record.exc_info:
            # Cache the traceback text to avoid converting it multiple times
            # (it's constant anyway)
            # from logging.Formatter:format
            if not record.exc_text:
                record.exc_text = self.formatException(record.exc_info)

        if record.exc_text:
            log_dict['exception'] = record.exc_text

        json_record = json.dumps(log_dict, default=self.default_json_formatter)

        if hasattr(json_record, 'decode'):  # pragma: no cover
            json_record = json_record.decode('utf-8')

        return json_record


def setup_logging(level='DEBUG', formatter_cls=JsonFormatter,
          boto_level=None, **kwargs):
    """Overall Metadata Formatting."""
    if formatter_cls:
        for handler in logging.root.handlers:
            handler.setFormatter(formatter_cls(**kwargs))

    try:
        logging.root.setLevel(level)
    except ValueError:
        logging.root.error('Invalid log level: %s', level)
        level = 'INFO'
        logging.root.setLevel(level)

    if not boto_level:
        boto_level = level

    try:
        logging.getLogger('boto').setLevel(boto_level)
        logging.getLogger('boto3').setLevel(boto_level)
        logging.getLogger('botocore').setLevel(boto_level)
        logging.getLogger('aws_xray_sdk').setLevel(boto_level)
    except ValueError:
        logging.root.error('Invalid log level: %s', boto_level)




# class StructuredMessage(object):
#     def __init__(self, message, **kwargs):
#         self.message = message
#         self.kwargs = kwargs
#
#     def __str__(self):
#         return '%s >>> %s' % (self.message, json.dumps(self.kwargs))
#
# _ = StructuredMessage   # optional, to improve readability
#
# logging.basicConfig(level=logging.INFO, format='%(message)s')
# logging.info(_('message 1', foo='bar', bar='baz', num=123, fnum=123.456))
#
#
# class StructuredLogFormatter(logging.Formatter):
#
#     def format(self, record):
#         record.message = record.getMessage()
#         if self.usesTime():
#             record.asctime = self.formatTime(record, self.datefmt)
#         j = {
#             'levelname': record.levelname,
#             'time': '%(asctime)s.%(msecs)dZ' % dict(asctime=record.asctime, msecs=record.msecs),
#             'aws_request_id': getattr(record, 'aws_request_id', '00000000-0000-0000-0000-000000000000'),
#             'message': record.message,
#             'module': record.module,
#             'extra_data': record.__dict__.get('data', {}),
#         }
#         return json.dumps(j)
#
#
# logger = logging.getLogger()
# logger.setLevel('INFO')
#
# formatter = FormatterJSON(
#     '[%(levelname)s]\t%(asctime)s.%(msecs)dZ\t%(levelno)s\t%(message)s\n',
#     '%Y-%m-%dT%H:%M:%S'
# )
# # Replace the LambdaLoggerHandler formatter :
# logger.handlers[0].setFormatter(formatter)
#
#
# def lambda_handler(event, context):
#     my_input = {
#         'key1': 'value1',
#         'key2': 'value2'
#     }
#     logger.info('Process Info: %s', 'Hello', extra=dict(data=my_input))