def transform(value, stack=None, context=None): # TODO: make this extendable if context is None: context = {} if stack is None: stack = [] objid = id(value) if objid in context: return '<...>' context[objid] = 1 transform_rec = lambda o: transform(o, stack + [value], context) if any(value is s for s in stack): ret = 'cycle' elif isinstance(value, (tuple, list, set, frozenset)): try: ret = type(value)(transform_rec(o) for o in value) except Exception: # We may be dealing with a namedtuple class value_type(list): __name__ = type(value).__name__ ret = value_type(transform_rec(o) for o in value) elif isinstance(value, uuid.UUID): ret = repr(value) elif isinstance(value, dict): ret = dict( (to_unicode(k), transform_rec(v)) for k, v in six.iteritems(value)) elif isinstance(value, six.text_type): ret = to_unicode(value) elif isinstance(value, six.binary_type): ret = to_string(value) elif not isinstance(value, six.class_types) and \ _has_elasticapm_metadata(value): ret = transform_rec(value.__elasticapm__()) elif isinstance(value, bool): ret = bool(value) elif isinstance(value, float): ret = float(value) elif isinstance(value, int): ret = int(value) elif six.PY2 and isinstance(value, long): # noqa F821 ret = long(value) # noqa F821 elif value is not None: try: ret = transform(repr(value)) except: # It's common case that a model's __unicode__ definition may try to query the database # which if it was not cleaned up correctly, would hit a transaction aborted exception ret = u'<BadRepr: %s>' % type(value) else: ret = None del context[objid] return ret
def get_headers(environ): """ Returns only proper HTTP headers. """ for key, value in six.iteritems(environ): key = str(key) if key.startswith('HTTP_') and key not in \ ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'): yield key[5:].replace('_', '-').lower(), value elif key in ('CONTENT_TYPE', 'CONTENT_LENGTH'): yield key.replace('_', '-').lower(), value
def varmap(func, var, context=None, name=None): """ Executes ``func(key_name, value)`` on all values recurisively discovering dict and list scoped values. """ if context is None: context = {} objid = id(var) if objid in context: return func(name, '<...>') context[objid] = 1 if isinstance(var, dict): ret = dict((k, varmap(func, v, context, k)) for k, v in six.iteritems(var)) elif isinstance(var, (list, tuple)): ret = [varmap(func, f, context, name) for f in var] else: ret = func(name, var) del context[objid] return ret
def build_msg_for_logging(self, event_type, data=None, date=None, extra=None, stack=None, **kwargs): """ Captures, processes and serializes an event into a dict object """ if data is None: data = {} if extra is None: extra = {} if not date: date = datetime.datetime.utcnow() if stack is None: stack = self.auto_log_stacks if 'context' not in data: data['context'] = context = {} else: context = data['context'] # if '.' not in event_type: # Assume it's a builtin event_type = 'elasticapm.events.%s' % event_type handler = self.get_handler(event_type) result = handler.capture(self, data=data, **kwargs) if self._filter_exception_type(result): return # data (explicit) culprit takes over auto event detection culprit = result.pop('culprit', None) if data.get('culprit'): culprit = data['culprit'] for k, v in six.iteritems(result): if k not in data: data[k] = v log = data.get('log', {}) if stack and 'stacktrace' not in log: if stack is True: frames = iter_stack_frames() else: frames = stack frames = varmap( lambda k, v: shorten(v, string_length=self.string_max_length, list_length=self.list_max_length), stacks.get_stack_info(frames)) log['stacktrace'] = frames if 'stacktrace' in log and not culprit: culprit = get_culprit(log['stacktrace'], self.include_paths, self.exclude_paths) if 'level' in log and isinstance(log['level'], six.integer_types): log['level'] = logging.getLevelName(log['level']).lower() if log: data['log'] = log if culprit: data['culprit'] = culprit context['custom'] = extra # Run the data through processors for processor in self.processors: data = processor(self, data) # Make sure all data is coerced data = transform(data) data.update({ 'timestamp': date.strftime(defaults.TIMESTAMP_FORMAT), }) return self.build_msg({'errors': [data]})
def _emit(self, record, **kwargs): data = {} for k, v in six.iteritems(record.__dict__): if '.' not in k and k not in ('culprit', ): continue data[k] = v stack = getattr(record, 'stack', None) if stack is True: stack = iter_stack_frames() if stack: frames = [] started = False last_mod = '' for item in stack: if isinstance(item, (list, tuple)): frame, lineno = item else: frame, lineno = item, item.f_lineno if not started: f_globals = getattr(frame, 'f_globals', {}) module_name = f_globals.get('__name__', '') if last_mod.startswith( 'logging' ) and not module_name.startswith('logging'): started = True else: last_mod = module_name continue frames.append((frame, lineno)) stack = frames extra = getattr(record, 'data', {}) # Add in all of the data from the record that we aren't already capturing for k in record.__dict__.keys(): if k in ('stack', 'name', 'args', 'msg', 'levelno', 'exc_text', 'exc_info', 'data', 'created', 'levelname', 'msecs', 'relativeCreated'): continue if k.startswith('_'): continue extra[k] = record.__dict__[k] date = datetime.datetime.utcfromtimestamp(record.created) # If there's no exception being processed, # exc_info may be a 3-tuple of None # http://docs.python.org/library/sys.html#sys.exc_info if record.exc_info and all(record.exc_info): handler = self.client.get_handler('elasticapm.events.Exception') data.update(handler.capture(self.client, exc_info=record.exc_info)) # data['checksum'] = handler.get_hash(data) data['level'] = record.levelno data['logger'] = record.name return self.client.capture('Message', param_message={ 'message': record.msg, 'params': record.args }, stack=stack, data=data, extra=extra, date=date, **kwargs)