|
| 1 | +import contextlib |
| 2 | +import datetime |
| 3 | +import inspect |
| 4 | +import json |
| 5 | +import logging |
| 6 | +import numbers |
| 7 | +import uuid |
| 8 | +try: |
| 9 | + from time import monotonic |
| 10 | +except ImportError: |
| 11 | + from monotonic import monotonic |
| 12 | + |
| 13 | +import six |
| 14 | + |
| 15 | +from obsender import exceptions |
| 16 | + |
| 17 | + |
| 18 | +class DurationMixin(object): |
| 19 | + """Mixin to meter timedelta and send its metric for wrapped code""" |
| 20 | + |
| 21 | + @contextlib.contextmanager |
| 22 | + def send_duration(self, metric_id, method_name='send_metric', **kwargs): |
| 23 | + if not hasattr(self, method_name): |
| 24 | + raise exceptions.SendMetricException( |
| 25 | + 'Method %s not found' % method_name |
| 26 | + ) |
| 27 | + method = getattr(self, method_name) |
| 28 | + start_time = monotonic() |
| 29 | + try: |
| 30 | + yield |
| 31 | + finally: |
| 32 | + method( |
| 33 | + metric_id, |
| 34 | + (monotonic() - start_time), |
| 35 | + **kwargs |
| 36 | + ) |
| 37 | + |
| 38 | + |
| 39 | +class SenderLoggerAdapter(logging.LoggerAdapter): |
| 40 | + """Writes Sender info for each log entry""" |
| 41 | + |
| 42 | + def __init__(self, logger, sender): |
| 43 | + super(SenderLoggerAdapter, self).__init__(logger, {}) |
| 44 | + self._sender = sender |
| 45 | + |
| 46 | + def process(self, msg, kwargs): |
| 47 | + _msg = ("[obsender_sender=%s host=%s, app_name=%s] %s" |
| 48 | + % (self._sender.__class__.__name__, |
| 49 | + self._sender.host, |
| 50 | + self._sender.app_name, |
| 51 | + msg)) |
| 52 | + return _msg, kwargs |
| 53 | + |
| 54 | + |
| 55 | +def jsonify(obj): |
| 56 | + if isinstance(obj, uuid.UUID): |
| 57 | + return str(obj) |
| 58 | + elif isinstance(obj, datetime.datetime) and obj.tzinfo is None: |
| 59 | + # TODO(d.burmistrov): make TZ conversion |
| 60 | + return obj.isoformat(sep='T') + 'Z' |
| 61 | + elif isinstance(obj, datetime.timedelta): |
| 62 | + return str(obj.total_seconds()) |
| 63 | + else: |
| 64 | + raise TypeError(obj) |
| 65 | + |
| 66 | + |
| 67 | +def stringify(obj): |
| 68 | + if isinstance(obj, six.string_types): |
| 69 | + return obj |
| 70 | + elif obj is None: |
| 71 | + raise TypeError("can't convert ambiguous value %r" % obj) |
| 72 | + elif isinstance(obj, bool): |
| 73 | + return str(obj).lower() |
| 74 | + elif isinstance(obj, numbers.Number): |
| 75 | + return str(obj) |
| 76 | + elif isinstance(obj, uuid.UUID): |
| 77 | + return jsonify(obj) |
| 78 | + elif isinstance(obj, (datetime.datetime, datetime.timedelta)): |
| 79 | + return jsonify(obj) |
| 80 | + elif isinstance(obj, (list, tuple, dict)): |
| 81 | + return json.dumps(obj, default=jsonify) |
| 82 | + elif inspect.isclass(obj): |
| 83 | + return '%s.%s' % (obj.__module__, obj.__name__) |
| 84 | + elif isinstance(obj, BaseException): |
| 85 | + return repr(obj) |
| 86 | + else: |
| 87 | + raise TypeError("can't convert unsupported value %r" % obj) |
| 88 | + |
| 89 | + |
| 90 | +def floatify(obj): |
| 91 | + if isinstance(obj, numbers.Number): |
| 92 | + return float(obj) |
| 93 | + else: |
| 94 | + raise TypeError("Can't convert unsupported value %r" % obj) |
0 commit comments