def span_event(self, *args, **kwargs): attrs = super(DatastoreNodeMixin, self).span_event(*args, **kwargs) i_attrs = attrs[0] i_attrs['category'] = 'datastore' i_attrs['component'] = self.product i_attrs['span.kind'] = 'client' if self.database_name: _, i_attrs['db.instance'] = process_user_attribute( 'db.instance', self.database_name) else: i_attrs['db.instance'] = 'Unknown' if self.instance_hostname: _, i_attrs['peer.hostname'] = process_user_attribute( 'peer.hostname', self.instance_hostname) else: i_attrs['peer.hostname'] = 'Unknown' peer_address = '%s:%s' % (self.instance_hostname or 'Unknown', self.port_path_or_id or 'Unknown') _, i_attrs['peer.address'] = process_user_attribute( 'peer.address', peer_address) return attrs
def span_event(self, *args, **kwargs): self.agent_attributes['http.url'] = self.http_url attrs = super(ExternalNode, self).span_event(*args, **kwargs) i_attrs = attrs[0] i_attrs['category'] = 'http' i_attrs['span.kind'] = 'client' _, i_attrs['component'] = attribute.process_user_attribute( 'component', self.library) if self.method: _, i_attrs['http.method'] = attribute.process_user_attribute( 'http.method', self.method) return attrs
def span_event(self, *args, **kwargs): attrs = super(ExternalNodeMixin, self).span_event(*args, **kwargs) i_attrs = attrs[0] i_attrs['category'] = 'http' i_attrs['span.kind'] = 'client' _, i_attrs['http.url'] = process_user_attribute( 'http.url', self.url_with_path) _, i_attrs['component'] = process_user_attribute( 'component', self.library) if self.method: _, i_attrs['http.method'] = process_user_attribute( 'http.method', self.method) return attrs
def request_parameters_attributes(self): # Request parameters are a special case of agent attributes, so they # must be added on to agent_attributes separately # # Filter request parameters through the AttributeFilter, but set the # destinations to NONE. # # That means by default, request parameters won't get included in any # destination. But, it will allow user added include/exclude attribute # filtering rules to be applied to the request parameters. attributes_request = [] if self._request_params: r_attrs = {} for k, v in self._request_params.items(): new_key = 'request.parameters.%s' % k new_val = ",".join(v) final_key, final_val = process_user_attribute(new_key, new_val) if final_key: r_attrs[final_key] = final_val attributes_request = create_attributes( r_attrs, DST_NONE, self._settings.attribute_filter) return attributes_request
def record_exception(self, exc_info=None, params={}, ignore_errors=[]): recorded = self._observe_exception(exc_info, ignore_errors) if recorded: fullname, message, tb = recorded transaction = self.transaction settings = transaction and transaction.settings # 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 = {} transaction._create_error_node(settings, fullname, message, custom_params, self.guid, tb)
def notice_error(self, error=None, attributes=None, expected=None, ignore=None, status_code=None): attributes = attributes if attributes is not None else {} recorded = self._observe_exception( error, ignore=ignore, expected=expected, status_code=status_code, ) if recorded: fullname, message, tb, is_expected = recorded transaction = self.transaction settings = transaction and transaction.settings # Only add params if High Security Mode is off. custom_params = {} if settings.high_security: if attributes: _logger.debug( "Cannot add custom parameters in High Security Mode.") else: try: for k, v in attributes.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 = {} if settings and settings.code_level_metrics and settings.code_level_metrics.enabled: source = extract_code_from_traceback(tb) else: source = None transaction._create_error_node( settings, fullname, message, is_expected, custom_params, self.guid, tb, source=source, )
def http_url(self): if hasattr(self, '_http_url'): return self._http_url _, url_attr = attribute.process_user_attribute('http.url', self.url_with_path) self._http_url = url_attr return url_attr
def request_parameters_attributes(self): # Request parameters are a special case of agent attributes, so # they must be added on to agent_attributes separately # There are 3 cases we need to handle: # # 1. LEGACY: capture_params = False # # Don't add request parameters at all, which means they will not # go through the AttributeFilter. # # 2. LEGACY: capture_params = True # # Filter request parameters through the AttributeFilter, but # set the destinations to `TRANSACTION_TRACER | ERROR_COLLECTOR`. # # If the user does not add any additional attribute filtering # rules, this will result in the same outcome as the old # capture_params = True behavior. They will be added to transaction # traces and error traces. # # 3. CURRENT: capture_params is None # # Filter request parameters through the AttributeFilter, but set # the destinations to NONE. # # That means by default, request parameters won't get included in # any destination. But, it will allow user added include/exclude # attribute filtering rules to be applied to the request parameters. attributes_request = [] if (self.capture_params is None) or self.capture_params: if self._request_params: r_attrs = {} for k, v in self._request_params.items(): new_key = 'request.parameters.%s' % k new_val = ",".join(v) final_key, final_val = process_user_attribute(new_key, new_val) if final_key: r_attrs[final_key] = final_val if self.capture_params is None: attributes_request = create_attributes(r_attrs, DST_NONE, self.attribute_filter) elif self.capture_params: attributes_request = create_attributes(r_attrs, DST_ERROR_COLLECTOR | DST_TRANSACTION_TRACER, self.attribute_filter) return attributes_request
def processed_user_attributes(self): if hasattr(self, '_processed_user_attributes'): return self._processed_user_attributes self._processed_user_attributes = u_attrs = {} for k, v in self.user_attributes.items(): k, v = attribute.process_user_attribute(k, v) u_attrs[k] = v return u_attrs
def span_event(self, *args, **kwargs): sql = self.formatted if sql: # Truncate to 2000 bytes and append ... _, sql = attribute.process_user_attribute( 'db.statement', sql, max_length=2000, ending='...') self.agent_attributes['db.statement'] = sql return super(DatabaseNode, self).span_event(*args, **kwargs)
def db_instance(self): if hasattr(self, '_db_instance'): return self._db_instance db_instance_attr = None if self.database_name: _, db_instance_attr = attribute.process_user_attribute( 'db.instance', self.database_name) self._db_instance = db_instance_attr return db_instance_attr
def create_custom_event(event_type, params): """Creates a valid custom event. Ensures that the custom event has a valid name, and also checks the format and number of attributes. No event is created, if the name is invalid. An event is created, if any of the attributes are invalid, but the invalid attributes are dropped. Args: event_type (str): The type (name) of the custom event. params (dict): Attributes to add to the event. Returns: Custom event (list of 2 dicts), if successful. None, if not successful. """ # TODO 创建一个有效的自定义事件 # TODO event_type 自定义事件类型 # TODO params 自定义事件参数 name = process_event_type(event_type) if name is None: return None attributes = {} try: for k, v in params.items(): key, value = process_user_attribute(k, v) if key: if len(attributes) >= MAX_NUM_USER_ATTRIBUTES: _logger.debug( 'Maximum number of attributes already ' 'added to event %r. Dropping attribute: %r=%r', name, key, value) else: attributes[key] = value except Exception: _logger.debug( 'Attributes failed to validate for unknown reason. ' 'Check traceback for clues. Dropping event: %r.', name, exc_info=True) return None intrinsics = { 'type': name, # TODO 自定义事件访问类型 'timestamp': int(1000.0 * time.time()), # TODO 访问时间戳 } event = [intrinsics, attributes] return event
def agent_attributes(self): # LEGACY: capture_params = True # # Filter request parameters as a normal agent attribute. # # If the user does not add any additional attribute filtering # rules, this will result in the same outcome as the old # capture_params = True behavior. They will be added to transaction # traces and error traces. if self.capture_params is True: for k, v in self._request_params.items(): new_key = 'request.parameters.%s' % k new_val = ",".join(v) final_key, final_val = process_user_attribute(new_key, new_val) if final_key: self._add_agent_attribute(final_key, final_val) self._request_params.clear() # Add WSGI agent attributes if self.read_duration != 0: self._add_agent_attribute('wsgi.input.seconds', self.read_duration) if self._bytes_read != 0: self._add_agent_attribute('wsgi.input.bytes', self._bytes_read) if self._calls_read != 0: self._add_agent_attribute('wsgi.input.calls.read', self._calls_read) if self._calls_readline != 0: self._add_agent_attribute('wsgi.input.calls.readline', self._calls_readline) if self._calls_readlines != 0: self._add_agent_attribute('wsgi.input.calls.readlines', self._calls_readlines) if self.sent_duration != 0: self._add_agent_attribute('wsgi.output.seconds', self.sent_duration) if self._bytes_sent != 0: self._add_agent_attribute('wsgi.output.bytes', self._bytes_sent) if self._calls_write != 0: self._add_agent_attribute('wsgi.output.calls.write', self._calls_write) if self._calls_yield != 0: self._add_agent_attribute('wsgi.output.calls.yield', self._calls_yield) return super(WSGIWebTransaction, self).agent_attributes
def span_event(self, *args, **kwargs): attrs = super(DatabaseNode, self).span_event(*args, **kwargs) i_attrs = attrs[0] sql = self.formatted # Truncate to 2000 bytes and append ... _, sql = process_user_attribute('db.statement', sql, max_length=2000, ending='...') i_attrs['db.statement'] = sql return attrs
def create_custom_event(event_type, params): """Creates a valid custom event. Ensures that the custom event has a valid name, and also checks the format and number of attributes. No event is created, if the name is invalid. An event is created, if any of the attributes are invalid, but the invalid attributes are dropped. Args: event_type (str): The type (name) of the custom event. params (dict): Attributes to add to the event. Returns: Custom event (list of 2 dicts), if successful. None, if not successful. """ name = process_event_type(event_type) if name is None: return None attributes = {} try: for k, v in params.items(): key, value = process_user_attribute(k, v) if key: if len(attributes) >= MAX_NUM_USER_ATTRIBUTES: _logger.debug('Maximum number of attributes already ' 'added to event %r. Dropping attribute: %r=%r', name, key, value) else: attributes[key] = value except Exception: _logger.debug('Attributes failed to validate for unknown reason. ' 'Check traceback for clues. Dropping event: %r.', name, exc_info=True) return None intrinsics = { 'type': name, 'timestamp': time.time(), } event = [intrinsics, attributes] return event
def add_custom_parameter(self, name, value): if not self._settings: return False if self._settings.high_security: _logger.debug('Cannot add custom parameter in High Security Mode.') return False if len(self._custom_params) >= MAX_NUM_USER_ATTRIBUTES: _logger.debug('Maximum number of custom attributes already ' 'added. Dropping attribute: %r=%r', name, value) return False key, val = process_user_attribute(name, value) if key is None: return False else: self._custom_params[key] = val return True
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_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)