def load(dsn, scope=None, transport_registry=None): """ Parses a Sentry compatible DSN and loads it into the given scope. >>> import raven >>> dsn = 'https://*****:*****@sentry.local/project_id' >>> # Apply configuration to local scope >>> raven.load(dsn, locals()) >>> # Return DSN configuration >>> options = raven.load(dsn) """ if not transport_registry: from raven.transport import TransportRegistry, default_transports transport_registry = TransportRegistry(default_transports) url = urlparse(dsn) if not transport_registry.supported_scheme(url.scheme): raise ValueError('Unsupported Sentry DSN scheme: %r' % url.scheme) if scope is None: scope = {} scope_extras = transport_registry.compute_scope(url, scope) scope.update(scope_extras) return scope
def from_string(cls, value, transport=None, transport_registry=None): if PY2: value = to_string(value) url = urlparse(value) if url.scheme not in ('http', 'https'): warnings.warn( 'Transport selection via DSN is deprecated. You should explicitly pass the transport class to Client() instead.' ) if transport is None: if not transport_registry: from raven.transport import TransportRegistry, default_transports transport_registry = TransportRegistry(default_transports) if not transport_registry.supported_scheme(url.scheme): raise InvalidDsn(ERR_UNKNOWN_SCHEME.format(url.scheme, value)) transport = transport_registry.get_transport_cls(url.scheme) netloc = url.hostname if url.port: netloc += ':%s' % url.port path_bits = url.path.rsplit('/', 1) if len(path_bits) > 1: path = path_bits[0] else: path = '' project = path_bits[-1] if not all([netloc, project, url.username, url.password]): raise InvalidDsn('Invalid Sentry DSN: %r' % url.geturl()) base_url = '%s://%s%s' % (url.scheme.rsplit('+', 1)[-1], netloc, path) return cls(base_url=base_url, project=project, public_key=url.username, secret_key=url.password, options=dict(parse_qsl(url.query)), transport=transport)
def from_string(cls, value, transport=None, transport_registry=None): # in Python 2.x sending the DSN as a unicode value will eventually # cause issues in httplib if PY2: value = to_string(value) url = urlparse(value.strip()) if url.scheme not in ("http", "https"): warnings.warn( "Transport selection via DSN is deprecated. You should explicitly pass the transport class to Client() instead." ) if transport is None: if not transport_registry: from raven.transport import TransportRegistry, default_transports transport_registry = TransportRegistry(default_transports) if not transport_registry.supported_scheme(url.scheme): raise InvalidDsn(ERR_UNKNOWN_SCHEME.format(url.scheme, value)) transport = transport_registry.get_transport_cls(url.scheme) netloc = url.hostname if url.port: netloc += ":%s" % url.port path_bits = url.path.rsplit("/", 1) if len(path_bits) > 1: path = path_bits[0] else: path = "" project = path_bits[-1] if not all([netloc, project, url.username, url.password]): raise InvalidDsn("Invalid Sentry DSN: %r" % url.geturl()) base_url = "%s://%s%s" % (url.scheme.rsplit("+", 1)[-1], netloc, path) return cls( base_url=base_url, project=project, public_key=url.username, secret_key=url.password, options=dict(parse_qsl(url.query)), transport=transport, )
def from_string(cls, value, transport=None, transport_registry=None): url = urlparse(value) if url.scheme not in ('http', 'https'): warnings.warn('Transport selection via DSN is deprecated. You should explicitly pass the transport class to Client() instead.') if transport is None: if not transport_registry: from raven.transport import TransportRegistry, default_transports transport_registry = TransportRegistry(default_transports) if not transport_registry.supported_scheme(url.scheme): raise InvalidDsn(ERR_UNKNOWN_SCHEME.format(url.scheme)) transport = transport_registry.get_transport_cls(url.scheme) netloc = url.hostname if url.port: netloc += ':%s' % url.port path_bits = url.path.rsplit('/', 1) if len(path_bits) > 1: path = path_bits[0] else: path = '' project = path_bits[-1] if not all([netloc, project, url.username, url.password]): raise InvalidDsn('Invalid Sentry DSN: %r' % url.geturl()) base_url = '%s://%s%s' % (url.scheme.rsplit('+', 1)[-1], netloc, path) return cls( base_url=base_url, project=project, public_key=url.username, secret_key=url.password, options=dict(parse_qsl(url.query)), transport=transport, )
class Client(object): """ The base Raven client, which handles both local direct communication with Sentry (through the GroupedMessage API), as well as communicating over the HTTP API to multiple servers. Will read default configuration from the environment variable ``SENTRY_DSN`` if available. >>> from raven import Client >>> # Read configuration from ``os.environ['SENTRY_DSN']`` >>> client = Client() >>> # Specify a DSN explicitly >>> client = >>> Client(dsn='https://*****:*****@sentry.local/project_id') >>> # Configure the client manually >>> client = Client( >>> servers=['http://sentry.local/api/store/'], >>> include_paths=['my.package'], >>> project='project_id', >>> public_key='public_key', >>> secret_key='secret_key', >>> ) >>> # Record an exception >>> try: >>> 1/0 >>> except ZeroDivisionError: >>> ident = client.get_ident(client.captureException()) >>> print "Exception caught; reference is %%s" %% ident """ logger = logging.getLogger('raven') protocol_version = '2.0' _registry = TransportRegistry(transports=default_transports) def __init__(self, servers=None, include_paths=None, exclude_paths=None, timeout=None, name=None, auto_log_stacks=None, key=None, string_max_length=None, list_max_length=None, site=None, public_key=None, secret_key=None, processors=None, project=None, dsn=None, **kwargs): # configure loggers first cls = self.__class__ self.state = ClientState() self.logger = logging.getLogger('%s.%s' % (cls.__module__, cls.__name__)) self.error_logger = logging.getLogger('sentry.errors') if isinstance(servers, basestring): # must be a DSN: if dsn: # TODO: this should indicate what the caller can do to correct # the constructor msg = "You seem to be incorrectly instantiating the " + \ "raven Client class" raise ValueError(msg) dsn = servers servers = None if dsn is None and os.environ.get('SENTRY_DSN'): msg = "Configuring Raven from environment variable 'SENTRY_DSN'" self.logger.info(msg) dsn = os.environ['SENTRY_DSN'] if dsn: # TODO: should we validate other options werent sent? urlparts = urlparse(dsn) msg = "Configuring Raven for host: %s://%s:%s" % ( urlparts.scheme, urlparts.netloc, urlparts.path) self.logger.info(msg) options = raven.load(dsn, transport_registry=self._registry) servers = options['SENTRY_SERVERS'] project = options['SENTRY_PROJECT'] public_key = options['SENTRY_PUBLIC_KEY'] secret_key = options['SENTRY_SECRET_KEY'] # servers may be set to a NoneType (for Django) if servers and not (key or (secret_key and public_key)): msg = 'Missing configuration for client. Please see documentation.' raise TypeError(msg) self.servers = servers self.include_paths = set(include_paths or defaults.INCLUDE_PATHS) self.exclude_paths = set(exclude_paths or defaults.EXCLUDE_PATHS) self.timeout = int(timeout or defaults.TIMEOUT) self.name = unicode(name or defaults.NAME) self.auto_log_stacks = bool(auto_log_stacks or defaults.AUTO_LOG_STACKS) self.key = str(key or defaults.KEY) self.string_max_length = int(string_max_length or defaults.MAX_LENGTH_STRING) self.list_max_length = int(list_max_length or defaults.MAX_LENGTH_LIST) if (site or defaults.SITE): self.site = unicode(site or defaults.SITE) else: self.site = None self.public_key = public_key self.secret_key = secret_key self.project = project or defaults.PROJECT self.processors = processors or defaults.PROCESSORS self.module_cache = ModuleProxyCache() @classmethod def register_scheme(cls, scheme, transport_class): cls._registry.register_scheme(scheme, transport_class) def get_processors(self): for processor in self.processors: yield self.module_cache[processor](self) def get_ident(self, result): """ Returns a searchable string representing a message. >>> result = client.process(**kwargs) >>> ident = client.get_ident(result) """ return '$'.join(result) def get_handler(self, name): return self.module_cache[name](self) def build_msg(self, event_type, data=None, date=None, time_spent=None, extra=None, stack=None, public_key=None, **kwargs): """ Captures, processes and serializes an event into a dict object """ # create ID client-side so that it can be passed to application event_id = uuid.uuid4().hex 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 '.' not in event_type: # Assume it's a builtin event_type = 'raven.events.%s' % event_type handler = self.get_handler(event_type) result = handler.capture(**kwargs) # data (explicit) culprit takes over auto event detection culprit = result.pop('culprit', None) if data.get('culprit'): culprit = data['culprit'] for k, v in result.iteritems(): if k not in data: data[k] = v if stack and 'sentry.interfaces.Stacktrace' not in data: if stack is True: frames = iter_stack_frames() else: frames = stack data.update({ 'sentry.interfaces.Stacktrace': { 'frames': varmap( lambda k, v: shorten(v, string_length=self. string_max_length, list_length=self.list_max_length), get_stack_info(frames)) }, }) if 'sentry.interfaces.Stacktrace' in data and not culprit: culprit = get_culprit( data['sentry.interfaces.Stacktrace']['frames'], self.include_paths, self.exclude_paths) if not data.get('level'): data['level'] = logging.ERROR data['modules'] = get_versions(self.include_paths) data['server_name'] = self.name data.setdefault('extra', {}) data.setdefault('level', logging.ERROR) # Shorten lists/strings for k, v in extra.iteritems(): data['extra'][k] = shorten(v, string_length=self.string_max_length, list_length=self.list_max_length) if culprit: data['culprit'] = culprit if 'checksum' not in data: checksum_bits = handler.get_hash(data) else: checksum_bits = data['checksum'] if isinstance(checksum_bits, (list, tuple)): checksum = hashlib.md5() for bit in checksum_bits: checksum.update(to_string(bit)) checksum = checksum.hexdigest() else: checksum = checksum_bits data['checksum'] = checksum # Run the data through processors for processor in self.get_processors(): data.update(processor.process(data)) # Make sure all data is coerced data = transform(data) if 'message' not in data: data['message'] = handler.to_string(data) data.update({ 'timestamp': date, 'time_spent': time_spent, 'event_id': event_id, }) data.setdefault('project', self.project) data.setdefault('site', self.site) data.setdefault('public_key', self.public_key) return data def capture(self, event_type, data=None, date=None, time_spent=None, extra=None, stack=None, public_key=None, **kwargs): """ Captures and processes an event and pipes it off to SentryClient.send. To use structured data (interfaces) with capture: >>> capture('Message', message='foo', data={ >>> 'sentry.interfaces.Http': { >>> 'url': '...', >>> 'data': {}, >>> 'query_string': '...', >>> 'method': 'POST', >>> }, >>> 'logger': 'logger.name', >>> 'site': 'site.name', >>> }, extra={ >>> 'key': 'value', >>> }) The finalized ``data`` structure contains the following (some optional) builtin values: >>> { >>> # the culprit and version information >>> 'culprit': 'full.module.name', # or /arbitrary/path >>> >>> # all detectable installed modules >>> 'modules': { >>> 'full.module.name': 'version string', >>> }, >>> >>> # arbitrary data provided by user >>> 'extra': { >>> 'key': 'value', >>> } >>> } :param event_type: the module path to the Event class. Builtins can use shorthand class notation and exclude the full module path. :param data: the data base, useful for specifying structured data interfaces. Any key which contains a '.' will be assumed to be a data interface. :param date: the datetime of this event :param time_spent: a float value representing the duration of the event :param event_id: a 32-length unique string identifying this event :param extra: a dictionary of additional standard metadata :param culprit: a string representing the cause of this event (generally a path to a function) :param public_key: the public key to use for this message (defaults to ``Client.public_key``) :return: a 32-length string identifying this event """ data = self.build_msg(event_type, data, date, time_spent, extra, stack, public_key=public_key, **kwargs) self.send(**data) return (data['event_id'], data['checksum']) def _send_remote(self, url, data, headers={}): parsed = urlparse(url) transport = self._registry.get_transport(parsed) # Push sending of errors to the background so they're async deferred.defer(transport.send, data, headers) def _get_log_message(self, data): # decode message so we can show the actual event try: data = self.decode(data) except: message = '<failed decoding data>' else: message = data.pop('message', '<no message value>') return message def send_remote(self, url, data, headers={}): if not self.state.should_try(): message = self._get_log_message(data) self.error_logger.error(message) return try: self._send_remote(url=url, data=data, headers=headers) except Exception, e: if isinstance(e, urllib2.HTTPError): body = e.read() self.error_logger.error( 'Unable to reach Sentry log server: %s (url: %%s, body: %%s)' % (e, ), url, body, exc_info=True, extra={'data': { 'body': body, 'remote_url': url }}) else: tmpl = 'Unable to reach Sentry log server: %s (url: %%s)' self.error_logger.error(tmpl % (e, ), url, exc_info=True, extra={'data': { 'remote_url': url }}) message = self._get_log_message(data) self.error_logger.error('Failed to submit message: %r', message) self.state.set_fail() else:
def setup_handlers(): if 'sentry_handler' not in __opts__: log.debug('No \'sentry_handler\' key was found in the configuration') return False options = {} dsn = get_config_value('dsn') if dsn is not None: try: # support raven ver 5.5.0 from raven.transport import TransportRegistry, default_transports from raven.utils.urlparse import urlparse transport_registry = TransportRegistry(default_transports) url = urlparse(dsn) if not transport_registry.supported_scheme(url.scheme): raise ValueError('Unsupported Sentry DSN scheme: {0}'.format( url.scheme)) dsn_config = {} conf_extras = transport_registry.compute_scope(url, dsn_config) dsn_config.update(conf_extras) options.update({ 'project': dsn_config['SENTRY_PROJECT'], 'servers': dsn_config['SENTRY_SERVERS'], 'public_key': dsn_config['SENTRY_PUBLIC_KEY'], 'secret_key': dsn_config['SENTRY_SECRET_KEY'] }) except ValueError as exc: log.info('Raven failed to parse the configuration provided ' 'DSN: {0}'.format(exc)) # Allow options to be overridden if previously parsed, or define them for key in ('project', 'servers', 'public_key', 'secret_key'): config_value = get_config_value(key) if config_value is None and key not in options: log.debug('The required \'sentry_handler\' configuration key, ' '\'{0}\', is not properly configured. Not configuring ' 'the sentry logging handler.'.format(key)) return elif config_value is None: continue options[key] = config_value # site: An optional, arbitrary string to identify this client installation. options.update({ # site: An optional, arbitrary string to identify this client # installation 'site': get_config_value('site'), # name: This will override the server_name value for this installation. # Defaults to socket.gethostname() 'name': get_config_value('name'), # exclude_paths: Extending this allow you to ignore module prefixes # when sentry attempts to discover which function an error comes from 'exclude_paths': get_config_value('exclude_paths', ()), # include_paths: For example, in Django this defaults to your list of # INSTALLED_APPS, and is used for drilling down where an exception is # located 'include_paths': get_config_value('include_paths', ()), # list_max_length: The maximum number of items a list-like container # should store. 'list_max_length': get_config_value('list_max_length'), # string_max_length: The maximum characters of a string that should be # stored. 'string_max_length': get_config_value('string_max_length'), # auto_log_stacks: Should Raven automatically log frame stacks # (including locals) all calls as it would for exceptions. 'auto_log_stacks': get_config_value('auto_log_stacks'), # timeout: If supported, the timeout value for sending messages to # remote. 'timeout': get_config_value('timeout', 1), # processors: A list of processors to apply to events before sending # them to the Sentry server. Useful for sending additional global state # data or sanitizing data that you want to keep off of the server. 'processors': get_config_value('processors'), # dsn: Ensure the DSN is passed into the client 'dsn': dsn }) client = raven.Client(**options) context = get_config_value('context') context_dict = {} if context is not None: for tag in context: tag_value = __salt__['grains.get'](tag) if len(tag_value) > 0: context_dict[tag] = tag_value if len(context_dict) > 0: client.context.merge({'tags': context_dict}) try: handler = SentryHandler(client) handler.setLevel(LOG_LEVELS[get_config_value('log_level', 'error')]) return handler except ValueError as exc: log.debug( 'Failed to setup the sentry logging handler: {0}'.format(exc), exc_info=exc)
def setup_handlers(): """ sets up the sentry handler """ if not __opts__.get("sentry_handler"): log.debug("'sentry_handler' config is empty or not defined") return False # Regenerating dunders can be expensive, so only do it if the user enables # `sentry_handler` as checked above __grains__ = salt.loader.grains(__opts__) __salt__ = salt.loader.minion_mods(__opts__) options = {} dsn = get_config_value("dsn") if dsn is not None: try: # support raven ver 5.5.0 from raven.transport import TransportRegistry, default_transports from raven.utils.urlparse import urlparse transport_registry = TransportRegistry(default_transports) url = urlparse(dsn) if not transport_registry.supported_scheme(url.scheme): raise ValueError("Unsupported Sentry DSN scheme: {}".format( url.scheme)) except ValueError as exc: log.info( "Raven failed to parse the configuration provided DSN: %s", exc) if not dsn: for key in ("project", "servers", "public_key", "secret_key"): config_value = get_config_value(key) if config_value is None and key not in options: log.debug( "The required 'sentry_handler' configuration key, " "'%s', is not properly configured. Not configuring " "the sentry logging handler.", key, ) return elif config_value is None: continue options[key] = config_value # site: An optional, arbitrary string to identify this client installation. options.update({ # site: An optional, arbitrary string to identify this client # installation "site": get_config_value("site"), # name: This will override the server_name value for this installation. # Defaults to socket.gethostname() "name": get_config_value("name"), # exclude_paths: Extending this allow you to ignore module prefixes # when sentry attempts to discover which function an error comes from "exclude_paths": get_config_value("exclude_paths", ()), # include_paths: For example, in Django this defaults to your list of # INSTALLED_APPS, and is used for drilling down where an exception is # located "include_paths": get_config_value("include_paths", ()), # list_max_length: The maximum number of items a list-like container # should store. "list_max_length": get_config_value("list_max_length"), # string_max_length: The maximum characters of a string that should be # stored. "string_max_length": get_config_value("string_max_length"), # auto_log_stacks: Should Raven automatically log frame stacks # (including locals) all calls as it would for exceptions. "auto_log_stacks": get_config_value("auto_log_stacks"), # timeout: If supported, the timeout value for sending messages to # remote. "timeout": get_config_value("timeout", 1), # processors: A list of processors to apply to events before sending # them to the Sentry server. Useful for sending additional global state # data or sanitizing data that you want to keep off of the server. "processors": get_config_value("processors"), # dsn: Ensure the DSN is passed into the client "dsn": dsn, }) client = raven.Client(**options) context = get_config_value("context") context_dict = {} if context is not None: for tag in context: try: tag_value = __grains__[tag] except KeyError: log.debug("Sentry tag '%s' not found in grains.", tag) continue if tag_value: context_dict[tag] = tag_value if context_dict: client.context.merge({"tags": context_dict}) try: handler = SentryHandler(client) exclude_patterns = get_config_value("exclude_patterns", None) if exclude_patterns: filter_regexes = [ re.compile(pattern) for pattern in exclude_patterns ] class FilterExcludedMessages: @staticmethod def filter(record): m = record.getMessage() return not any(regex.search(m) for regex in filter_regexes) handler.addFilter(FilterExcludedMessages()) handler.setLevel(LOG_LEVELS[get_config_value("log_level", "error")]) return handler except ValueError as exc: log.debug("Failed to setup the sentry logging handler", exc_info=True)
def setup_handlers(): ''' sets up the sentry handler ''' __grains__ = salt.loader.grains(__opts__) __salt__ = salt.loader.minion_mods(__opts__) if 'sentry_handler' not in __opts__: log.debug('No \'sentry_handler\' key was found in the configuration') return False options = {} dsn = get_config_value('dsn') if dsn is not None: try: # support raven ver 5.5.0 from raven.transport import TransportRegistry, default_transports from raven.utils.urlparse import urlparse transport_registry = TransportRegistry(default_transports) url = urlparse(dsn) if not transport_registry.supported_scheme(url.scheme): raise ValueError('Unsupported Sentry DSN scheme: {0}'.format( url.scheme)) except ValueError as exc: log.info( 'Raven failed to parse the configuration provided DSN: %s', exc) if not dsn: for key in ('project', 'servers', 'public_key', 'secret_key'): config_value = get_config_value(key) if config_value is None and key not in options: log.debug( 'The required \'sentry_handler\' configuration key, ' '\'%s\', is not properly configured. Not configuring ' 'the sentry logging handler.', key) return elif config_value is None: continue options[key] = config_value # site: An optional, arbitrary string to identify this client installation. options.update({ # site: An optional, arbitrary string to identify this client # installation 'site': get_config_value('site'), # name: This will override the server_name value for this installation. # Defaults to socket.gethostname() 'name': get_config_value('name'), # exclude_paths: Extending this allow you to ignore module prefixes # when sentry attempts to discover which function an error comes from 'exclude_paths': get_config_value('exclude_paths', ()), # include_paths: For example, in Django this defaults to your list of # INSTALLED_APPS, and is used for drilling down where an exception is # located 'include_paths': get_config_value('include_paths', ()), # list_max_length: The maximum number of items a list-like container # should store. 'list_max_length': get_config_value('list_max_length'), # string_max_length: The maximum characters of a string that should be # stored. 'string_max_length': get_config_value('string_max_length'), # auto_log_stacks: Should Raven automatically log frame stacks # (including locals) all calls as it would for exceptions. 'auto_log_stacks': get_config_value('auto_log_stacks'), # timeout: If supported, the timeout value for sending messages to # remote. 'timeout': get_config_value('timeout', 1), # processors: A list of processors to apply to events before sending # them to the Sentry server. Useful for sending additional global state # data or sanitizing data that you want to keep off of the server. 'processors': get_config_value('processors'), # dsn: Ensure the DSN is passed into the client 'dsn': dsn }) client = raven.Client(**options) context = get_config_value('context') context_dict = {} if context is not None: for tag in context: try: tag_value = __grains__[tag] except KeyError: log.debug('Sentry tag \'%s\' not found in grains.', tag) continue if len(tag_value) > 0: context_dict[tag] = tag_value if len(context_dict) > 0: client.context.merge({'tags': context_dict}) try: handler = SentryHandler(client) exclude_patterns = get_config_value('exclude_patterns', None) if exclude_patterns: filter_regexes = [ re.compile(pattern) for pattern in exclude_patterns ] class FilterExcludedMessages(object): @staticmethod def filter(record): m = record.getMessage() return not any(regex.search(m) for regex in filter_regexes) handler.addFilter(FilterExcludedMessages()) handler.setLevel(LOG_LEVELS[get_config_value('log_level', 'error')]) return handler except ValueError as exc: log.debug('Failed to setup the sentry logging handler', exc_info=True)
def setup_handlers(): if 'sentry_handler' not in __opts__: log.debug('No \'sentry_handler\' key was found in the configuration') return False options = {} dsn = get_config_value('dsn') if dsn is not None: try: # support raven ver 5.5.0 from raven.transport import TransportRegistry, default_transports from raven.utils.urlparse import urlparse transport_registry = TransportRegistry(default_transports) url = urlparse(dsn) if not transport_registry.supported_scheme(url.scheme): raise ValueError('Unsupported Sentry DSN scheme: {0}'.format(url.scheme)) dsn_config = {} conf_extras = transport_registry.compute_scope(url, dsn_config) dsn_config.update(conf_extras) options.update({ 'project': dsn_config['SENTRY_PROJECT'], 'servers': dsn_config['SENTRY_SERVERS'], 'public_key': dsn_config['SENTRY_PUBLIC_KEY'], 'secret_key': dsn_config['SENTRY_SECRET_KEY'] }) except ValueError as exc: log.info( 'Raven failed to parse the configuration provided ' 'DSN: {0}'.format(exc) ) # Allow options to be overridden if previously parsed, or define them for key in ('project', 'servers', 'public_key', 'secret_key'): config_value = get_config_value(key) if config_value is None and key not in options: log.debug( 'The required \'sentry_handler\' configuration key, ' '\'{0}\', is not properly configured. Not configuring ' 'the sentry logging handler.'.format(key) ) return elif config_value is None: continue options[key] = config_value # site: An optional, arbitrary string to identify this client installation. options.update({ # site: An optional, arbitrary string to identify this client # installation 'site': get_config_value('site'), # name: This will override the server_name value for this installation. # Defaults to socket.gethostname() 'name': get_config_value('name'), # exclude_paths: Extending this allow you to ignore module prefixes # when sentry attempts to discover which function an error comes from 'exclude_paths': get_config_value('exclude_paths', ()), # include_paths: For example, in Django this defaults to your list of # INSTALLED_APPS, and is used for drilling down where an exception is # located 'include_paths': get_config_value('include_paths', ()), # list_max_length: The maximum number of items a list-like container # should store. 'list_max_length': get_config_value('list_max_length'), # string_max_length: The maximum characters of a string that should be # stored. 'string_max_length': get_config_value('string_max_length'), # auto_log_stacks: Should Raven automatically log frame stacks # (including locals) all calls as it would for exceptions. 'auto_log_stacks': get_config_value('auto_log_stacks'), # timeout: If supported, the timeout value for sending messages to # remote. 'timeout': get_config_value('timeout', 1), # processors: A list of processors to apply to events before sending # them to the Sentry server. Useful for sending additional global state # data or sanitizing data that you want to keep off of the server. 'processors': get_config_value('processors'), # dsn: Ensure the DSN is passed into the client 'dsn': dsn }) client = raven.Client(**options) context = get_config_value('context') context_dict = {} if context is not None: for tag in context: tag_value = __salt__['grains.get'](tag) if len(tag_value) > 0: context_dict[tag] = tag_value if len(context_dict) > 0: client.context.merge({'tags': context_dict}) try: handler = SentryHandler(client) handler.setLevel(LOG_LEVELS[get_config_value('log_level', 'error')]) return handler except ValueError as exc: log.debug( 'Failed to setup the sentry logging handler: {0}'.format(exc), exc_info=exc )