def parse_exc_info(exc_info): """Parse exc_info and return commonly used strings.""" _, value, _ = exc_info module = value.__class__.__module__ name = value.__class__.__name__ if module: fullnames = ("%s:%s" % (module, name), "%s.%s" % (module, name)) else: fullnames = (name, ) try: # Favor unicode in exception messages. message = six.text_type(value) except Exception: try: # If exception cannot be represented in unicode, this means # that it is a byte string encoded with an encoding # that is not compatible with the default system encoding. # So, just pass this byte string along. message = str(value) except Exception: message = "<unprintable %s object>" % type(value).__name__ return (module, name, fullnames, message)
def format_exc_info(exc_info): _, value, tb = exc_info module = value.__class__.__module__ name = value.__class__.__name__ if module: fullname = "{}.{}".format(module, name) else: fullname = name try: # Favor unicode in exception messages. message = six.text_type(value) except Exception: try: # If exception cannot be represented in unicode, this means # that it is a byte string encoded with an encoding # that is not compatible with the default system encoding. # So, just pass this byte string along. message = str(value) except Exception: message = "<unprintable %s object>" % type(value).__name__ return { "error.class": fullname, "error.message": message, }
def deobfuscate(name, key): if not (name and key): return '' # Always pass name and key as str to _encode() return ''.join(_encode(six.text_type(base64.b64decode(six.b(name)), encoding='Latin-1'), key))
def deobfuscate(name, key): if not (name and key): return '' # Always pass name and key as str to _encode() return ''.join( _encode( six.text_type(base64.b64decode(six.b(name)), encoding='Latin-1'), key))
def record_exception(self, exc=None, value=None, tb=None, params={}, ignore_errors=[]): # Bail out if the transaction is not active or # collection of errors not enabled. if not self._settings: return settings = self._settings error_collector = settings.error_collector if not error_collector.enabled or not settings.collect_errors: return # If no exception details provided, use current exception. if exc is None and value is None and tb is None: exc, value, tb = sys.exc_info() # Has to be an error to be logged. if exc is None or value is None or tb is None: return # 'should_ignore' is a tri-state variable with the following behavior. # 'True' - ignore the error. # 'False'- record the error. # 'None' - Use the default ignore rules. should_ignore = None if callable(ignore_errors): should_ignore = ignore_errors(exc, value, tb) if should_ignore: return if should_ignore is None: # We need to check for module.name and module:name. # Originally we used module.class but that was # inconsistent with everything else which used # module:name. So changed to use ':' as separator, but # for backward compatability need to support '.' as # separator for time being. Check that with the ':' # last as we will use that name as the exception type. module = value.__class__.__module__ name = value.__class__.__name__ if module: fullname = '%s.%s' % (module, name) else: fullname = name if (not callable(ignore_errors)) and (fullname in ignore_errors): return if fullname in error_collector.ignore_errors: return if module: fullname = '%s:%s' % (module, name) else: fullname = name if (not callable(ignore_errors)) and (fullname in ignore_errors): return if fullname in error_collector.ignore_errors: return # Only remember up to limit of what can be caught for a # single transaction. This could be trimmed further # later if there are already recorded errors and would # go over the harvest limit. if len(self._errors) >= settings.agent_limits.errors_per_transaction: return if params: custom_params = dict(self._custom_params) custom_params.update(params) else: custom_params = self._custom_params exc_type = exc.__name__ try: message = str(value) except Exception: try: # Assume JSON encoding can handle unicode. message = six.text_type(value) except Exception: message = '<unprintable %s object>' % type(value).__name__ # Check that we have not recorded this exception # previously for this transaction due to multiple # error traces triggering. This is not going to be # exact but the UI hides exceptions of same type # anyway. Better that we under count exceptions of # same type and message rather than count same one # multiple times. for error in self._errors: if error.type == exc_type and error.message == message: return stack_trace = traceback.format_exception(exc, value, tb) node = newrelic.core.error_node.ErrorNode( timestamp=time.time(), type=fullname, message=message, stack_trace=stack_trace, custom_params=custom_params, file_name=None, line_number=None, source=None) # TODO Errors are recorded in time order. If # there are two exceptions of same type and # different message, the UI displays the first # one. In the PHP agent it was recording the # errors in reverse time order and so the UI # displayed the last one. What is the the # official order in which they should be sent. self._errors.append(node)
def record_exception(self, exc=None, value=None, tb=None, params={}, ignore_errors=[]): # Bail out if the transaction is not active or # collection of errors not enabled. if not self._settings: return settings = self._settings error_collector = settings.error_collector if not error_collector.enabled or not settings.collect_errors: return # If no exception details provided, use current exception. if exc is None and value is None and tb is None: exc, value, tb = sys.exc_info() # Has to be an error to be logged. if exc is None or value is None or tb is None: return # Where ignore_errors is a callable it should return a # tri-state variable with the following behavior. # # True - Ignore the error. # False- Record the error. # None - Use the default ignore rules. should_ignore = None if callable(ignore_errors): should_ignore = ignore_errors(exc, value, tb) if should_ignore: return module = value.__class__.__module__ name = value.__class__.__name__ if should_ignore is None: # We need to check for module.name and module:name. # Originally we used module.class but that was # inconsistent with everything else which used # module:name. So changed to use ':' as separator, but # for backward compatability need to support '.' as # separator for time being. Check that with the ':' # last as we will use that name as the exception type. if module: fullname = '%s.%s' % (module, name) else: fullname = name if not callable(ignore_errors) and fullname in ignore_errors: return if fullname in error_collector.ignore_errors: return if module: fullname = '%s:%s' % (module, name) else: fullname = name if not callable(ignore_errors) and fullname in ignore_errors: return if fullname in error_collector.ignore_errors: return else: if module: fullname = '%s:%s' % (module, name) else: fullname = name # Only remember up to limit of what can be caught for a # single transaction. This could be trimmed further # later if there are already recorded errors and would # go over the harvest limit. if len(self._errors) >= settings.agent_limits.errors_per_transaction: return # Only add params if High Security Mode is off. if settings.high_security: custom_params = {} else: if params: custom_params = dict(self._custom_params) custom_params.update(params) else: custom_params = self._custom_params exc_type = exc.__name__ try: message = str(value) except Exception: try: # Assume JSON encoding can handle unicode. message = six.text_type(value) except Exception: message = '<unprintable %s object>' % type(value).__name__ # Check that we have not recorded this exception # previously for this transaction due to multiple # error traces triggering. This is not going to be # exact but the UI hides exceptions of same type # anyway. Better that we under count exceptions of # same type and message rather than count same one # multiple times. for error in self._errors: if error.type == fullname and error.message == message: return node = newrelic.core.error_node.ErrorNode( timestamp=time.time(), type=fullname, message=message, stack_trace=exception_stack(tb), custom_params=custom_params, file_name=None, line_number=None, source=None) # TODO Errors are recorded in time order. If # there are two exceptions of same type and # different message, the UI displays the first # one. In the PHP agent it was recording the # errors in reverse time order and so the UI # displayed the last one. What is the the # official order in which they should be sent. self._errors.append(node)
def record_exception(self, exc=None, value=None, tb=None, params={}, ignore_errors=[]): # Bail out if the transaction is not active or # collection of errors not enabled. if not self._settings: return settings = self._settings error_collector = settings.error_collector if not error_collector.enabled: return if not settings.collect_errors and not settings.collect_error_events: return # If no exception details provided, use current exception. if exc is None and value is None and tb is None: exc, value, tb = sys.exc_info() # Has to be an error to be logged. if exc is None or value is None or tb is None: return # Where ignore_errors is a callable it should return a # tri-state variable with the following behavior. # # True - Ignore the error. # False- Record the error. # None - Use the default ignore rules. should_ignore = None if callable(ignore_errors): should_ignore = ignore_errors(exc, value, tb) if should_ignore: return module = value.__class__.__module__ name = value.__class__.__name__ if should_ignore is None: # We need to check for module.name and module:name. # Originally we used module.class but that was # inconsistent with everything else which used # module:name. So changed to use ':' as separator, but # for backward compatibility need to support '.' as # separator for time being. Check that with the ':' # last as we will use that name as the exception type. if module: fullname = '%s.%s' % (module, name) else: fullname = name if not callable(ignore_errors) and fullname in ignore_errors: return if fullname in error_collector.ignore_errors: return if module: fullname = '%s:%s' % (module, name) else: fullname = name if not callable(ignore_errors) and fullname in ignore_errors: return if fullname in error_collector.ignore_errors: return else: if module: fullname = '%s:%s' % (module, name) else: fullname = name # Only remember up to limit of what can be caught for a # single transaction. This could be trimmed further # later if there are already recorded errors and would # go over the harvest limit. if len(self._errors) >= settings.agent_limits.errors_per_transaction: return # Only add params if High Security Mode is off. custom_params = {} if settings.high_security: if params: _logger.debug('Cannot add custom parameters in ' 'High Security Mode.') else: try: for k, v in params.items(): name, val = process_user_attribute(k, v) if name: custom_params[name] = val except Exception: _logger.debug( 'Parameters failed to validate for unknown ' 'reason. Dropping parameters for error: %r. Check ' 'traceback for clues.', fullname, exc_info=True) custom_params = {} # Check to see if we need to strip the message before recording it. if (settings.strip_exception_messages.enabled and fullname not in settings.strip_exception_messages.whitelist): message = STRIP_EXCEPTION_MESSAGE else: try: # Favor unicode in exception messages. message = six.text_type(value) except Exception: try: # If exception cannot be represented in unicode, this means # that it is a byte string encoded with an encoding # that is not compatible with the default system encoding. # So, just pass this byte string along. message = str(value) except Exception: message = '<unprintable %s object>' % type(value).__name__ # Check that we have not recorded this exception # previously for this transaction due to multiple # error traces triggering. This is not going to be # exact but the UI hides exceptions of same type # anyway. Better that we under count exceptions of # same type and message rather than count same one # multiple times. for error in self._errors: if error.type == fullname and error.message == message: return node = newrelic.core.error_node.ErrorNode( timestamp=time.time(), type=fullname, message=message, stack_trace=exception_stack(tb), custom_params=custom_params, file_name=None, line_number=None, source=None) # TODO Errors are recorded in time order. If # there are two exceptions of same type and # different message, the UI displays the first # one. In the PHP agent it was recording the # errors in reverse time order and so the UI # displayed the last one. What is the the # official order in which they should be sent. self._errors.append(node)
def record_exception(self, exc=None, value=None, tb=None, params={}, ignore_errors=[]): # Bail out if the transaction is not active or # collection of errors not enabled. if not self._settings: return settings = self._settings error_collector = settings.error_collector if not error_collector.enabled or not settings.collect_errors: return # If no exception details provided, use current exception. if exc is None and value is None and tb is None: exc, value, tb = sys.exc_info() # Has to be an error to be logged. if exc is None or value is None or tb is None: return # Where ignore_errors is a callable it should return a # tri-state variable with the following behavior. # # True - Ignore the error. # False- Record the error. # None - Use the default ignore rules. should_ignore = None if callable(ignore_errors): should_ignore = ignore_errors(exc, value, tb) if should_ignore: return module = value.__class__.__module__ name = value.__class__.__name__ if should_ignore is None: # We need to check for module.name and module:name. # Originally we used module.class but that was # inconsistent with everything else which used # module:name. So changed to use ':' as separator, but # for backward compatability need to support '.' as # separator for time being. Check that with the ':' # last as we will use that name as the exception type. if module: fullname = "%s.%s" % (module, name) else: fullname = name if not callable(ignore_errors) and fullname in ignore_errors: return if fullname in error_collector.ignore_errors: return if module: fullname = "%s:%s" % (module, name) else: fullname = name if not callable(ignore_errors) and fullname in ignore_errors: return if fullname in error_collector.ignore_errors: return else: if module: fullname = "%s:%s" % (module, name) else: fullname = name # Only remember up to limit of what can be caught for a # single transaction. This could be trimmed further # later if there are already recorded errors and would # go over the harvest limit. if len(self._errors) >= settings.agent_limits.errors_per_transaction: return # Only add params if High Security Mode is off. custom_params = {} if settings.high_security: if params: _logger.debug("Cannot add custom parameters in " "High Security Mode.") else: for k, v in params.items(): name, value = process_user_attribute(k, v) if name: custom_params[name] = value # Check to see if we need to strip the message before recording it. if settings.strip_exception_messages.enabled and fullname not in settings.strip_exception_messages.whitelist: message = STRIP_EXCEPTION_MESSAGE else: try: message = str(value) except Exception: try: # Assume JSON encoding can handle unicode. message = six.text_type(value) except Exception: message = "<unprintable %s object>" % type(value).__name__ # Check that we have not recorded this exception # previously for this transaction due to multiple # error traces triggering. This is not going to be # exact but the UI hides exceptions of same type # anyway. Better that we under count exceptions of # same type and message rather than count same one # multiple times. for error in self._errors: if error.type == fullname and error.message == message: return node = newrelic.core.error_node.ErrorNode( timestamp=time.time(), type=fullname, message=message, stack_trace=exception_stack(tb), custom_params=custom_params, file_name=None, line_number=None, source=None, ) # TODO Errors are recorded in time order. If # there are two exceptions of same type and # different message, the UI displays the first # one. In the PHP agent it was recording the # errors in reverse time order and so the UI # displayed the last one. What is the the # official order in which they should be sent. self._errors.append(node)
def _observe_exception(self, exc_info=None, ignore_errors=[]): # Bail out if the transaction is not active or # collection of errors not enabled. transaction = self.transaction settings = transaction and transaction.settings if not settings: return if not settings.error_collector.enabled: return # At least one destination for error events must be enabled if not (settings.collect_traces or settings.collect_span_events or settings.collect_errors or settings.collect_error_events): return # If no exception details provided, use current exception. if exc_info and None not in exc_info: exc, value, tb = exc_info else: exc, value, tb = sys.exc_info() # Has to be an error to be logged. if exc is None or value is None or tb is None: return # Where ignore_errors is a callable it should return a # tri-state variable with the following behavior. # # True - Ignore the error. # False- Record the error. # None - Use the default ignore rules. should_ignore = None if hasattr(transaction, '_ignore_errors'): should_ignore = transaction._ignore_errors(exc, value, tb) if should_ignore: return if callable(ignore_errors): should_ignore = ignore_errors(exc, value, tb) if should_ignore: return module = value.__class__.__module__ name = value.__class__.__name__ if should_ignore is None: # We need to check for module.name and module:name. # Originally we used module.class but that was # inconsistent with everything else which used # module:name. So changed to use ':' as separator, but # for backward compatibility need to support '.' as # separator for time being. Check that with the ':' # last as we will use that name as the exception type. if module: names = ('%s:%s' % (module, name), '%s.%s' % (module, name)) else: names = (name) for fullname in names: if not callable(ignore_errors) and fullname in ignore_errors: return if fullname in settings.error_collector.ignore_errors: return fullname = names[0] else: if module: fullname = '%s:%s' % (module, name) else: fullname = name # Check to see if we need to strip the message before recording it. if (settings.strip_exception_messages.enabled and fullname not in settings.strip_exception_messages.whitelist): message = STRIP_EXCEPTION_MESSAGE else: try: # Favor unicode in exception messages. message = six.text_type(value) except Exception: try: # If exception cannot be represented in unicode, this means # that it is a byte string encoded with an encoding # that is not compatible with the default system encoding. # So, just pass this byte string along. message = str(value) except Exception: message = '<unprintable %s object>' % type(value).__name__ # Record a supportability metric if error attributes are being # overiden. if 'error.class' in self.agent_attributes: transaction._record_supportability('Supportability/' 'SpanEvent/Errors/Dropped') # Add error details as agent attributes to span event. self._add_agent_attribute('error.class', fullname) self._add_agent_attribute('error.message', message) return fullname, message, tb
def record_exception(self, exc_info=None, params={}, ignore_errors=[]): # Bail out if the transaction is not active or # collection of errors not enabled. # 记录异常,调用的是事务对象Transaction的接口,很好理解 transaction = self.transaction settings = transaction and transaction.settings if not settings: return # If no exception details provided, use current exception. if exc_info and None not in exc_info: exc, value, tb = exc_info else: exc, value, tb = sys.exc_info() # Has to be an error to be logged. if exc is None or value is None or tb is None: return # Where ignore_errors is a callable it should return a # tri-state variable with the following behavior. # # True - Ignore the error. # False- Record the error. # None - Use the default ignore rules. should_ignore = None if hasattr(transaction, '_ignore_errors'): should_ignore = transaction._ignore_errors(exc, value, tb) if should_ignore: return if callable(ignore_errors): should_ignore = ignore_errors(exc, value, tb) if should_ignore: return module = value.__class__.__module__ name = value.__class__.__name__ if should_ignore is None: # We need to check for module.name and module:name. # Originally we used module.class but that was # inconsistent with everything else which used # module:name. So changed to use ':' as separator, but # for backward compatibility need to support '.' as # separator for time being. Check that with the ':' # last as we will use that name as the exception type. # 组装异常错误信息名称 if module: names = ('%s:%s' % (module, name), '%s.%s' % (module, name)) else: names = (name) for fullname in names: if not callable(ignore_errors) and fullname in ignore_errors: return if fullname in settings.error_collector.ignore_errors: return fullname = names[0] else: if module: fullname = '%s:%s' % (module, name) else: fullname = name # Only add params if High Security Mode is off. custom_params = {} if settings.high_security: if params: _logger.debug('Cannot add custom parameters in ' 'High Security Mode.') else: try: for k, v in params.items(): name, val = process_user_attribute(k, v) if name: custom_params[name] = val except Exception: _logger.debug( 'Parameters failed to validate for unknown ' 'reason. Dropping parameters for error: %r. Check ' 'traceback for clues.', fullname, exc_info=True) custom_params = {} # Check to see if we need to strip the message before recording it. if (settings.strip_exception_messages.enabled and fullname not in settings.strip_exception_messages.whitelist): message = STRIP_EXCEPTION_MESSAGE else: try: # Favor unicode in exception messages. message = six.text_type(value) except Exception: try: # If exception cannot be represented in unicode, this means # that it is a byte string encoded with an encoding # that is not compatible with the default system encoding. # So, just pass this byte string along. message = str(value) except Exception: message = '<unprintable %s object>' % type(value).__name__ # Record a supportability metric if error attributes are being # overiden. if 'error.class' in self.agent_attributes: transaction._record_supportability('Supportability/' 'SpanEvent/Errors/Dropped') # Add error details as agent attributes to span event. # 添加一些代理的参数 self._add_agent_attribute('error.class', fullname) self._add_agent_attribute('error.message', message) transaction._create_error_node(settings, fullname, message, custom_params, self.guid, tb)