View Source Sentry.LoggerHandler (Sentry v13.3.0)
A highly-configurable :logger handler
that reports logged messages and crashes to Sentry.
This module is available since v9.0.0 of this library.
This handler can do two distinct things with the messages it receives:
Captured Sentry events — report crashes and (optionally)
Loggermessages such asLogger.error("oops")to Sentry as events, the same waySentry.capture_exception/2andSentry.capture_message/2do. This is always active when the handler is attached.Structured logs — forward log entries to Sentry's Logs UI as structured log events. This is active when
:enable_logsistruein your Sentry configuration.
The two are independent: a single log can become a captured Sentry event, a structured log, both, or neither, depending on configuration.
You usually don't add this handler manually
Setting
config :sentry, enable_logs: truemakes the SDK automatically attach this handler at startup — you do not need to call:logger.add_handler/3orLogger.add_handlers/1yourself. Configure it through the:logsoption of your Sentry config (see the Sentry configuration and the "Sending logs to Sentry" section below). Add the handler manually only when you want full control over the options documented under "Configuration", or when you want error reporting without structured logs.
When to Use the Handler vs the Backend?
Sentry's Elixir SDK also ships with
Sentry.LoggerBackend, an ElixirLoggerbackend. The backend has similar functionality to this handler. The main functional difference is thatSentry.LoggerBackendruns in its own process, whileSentry.LoggerHandlerruns in the process that logs. The latter is generally preferable.The reason both exist is that
:loggerhandlers are a relatively-new feature in Erlang/OTP, andSentry.LoggerBackendwas created before:loggerhandlers were introduced.In general, use
Sentry.LoggerHandlerwhenever possible. In future Elixir releases,Loggerbackends may become deprecated and henceSentry.LoggerBackendmay be eventually removed.
Features
This logger handler provides the features listed here.
Crash Reports
The reason you'll want to add this handler to your application is so that you can
report crashes in your system to Sentry. Sometimes, you're able to catch exceptions
and handle them (such as reporting them to Sentry), which is what you can do with
Sentry.PlugCapture for example.
However, Erlang/OTP systems are made of processes running concurrently, and
sometimes those processes crash and exit. If you're not explicitly catching
exceptions in those processes to report them to Sentry, then you won't see those
crash reports in Sentry. That's where this handler comes in. This handler hooks
into :logger and reports nicely-formatted crash reports to Sentry.
Overload Protection
This handler has built-in overload protection via the :sync_threshold
configuration option. Under normal circumstances, this handler sends events to
Sentry asynchronously, without blocking the logging process. However, if the
number of queued up events exceeds the :sync_threshold, then this handler
starts blocking the logging process until the event is sent.
Overload protection is available since v10.6.0.
Rate Limiting
You can configure this handler to rate-limit the number of messages it sends to
Sentry. This can help avoid "spamming" Sentry. See the :rate_limiting configuration
option.
Rate limiting is available since v10.5.0.
Usage
To add this handler to your system, see the documentation for handlers in Elixir.
You can configure this handler in the :logger key under your application's configuration,
potentially alongside other :logger handlers:
config :my_app, :logger, [
{:handler, :my_sentry_handler, Sentry.LoggerHandler, %{
config: %{capture_metadata: [:file, :line]}
}}
]If you do this, then you'll want to add this to your application's Application.start/2
callback, similarly to what you would do with Sentry.LoggerBackend and the
call to Logger.add_backend/1:
def start(_type, _args) do
Logger.add_handlers(:my_app)
# ...
endAlternatively, you can skip the :logger configuration and add the handler directly
to your application's Application.start/2 callback:
def start(_type, _args) do
:logger.add_handler(:my_sentry_handler, Sentry.LoggerHandler, %{
config: %{capture_metadata: [:file, :line]}
})
# ...
endSending logs to Sentry
To send structured logs to Sentry's logs feature,
enable logs in your Sentry configuration. This auto-attaches the handler — there is
no need to configure :logger or call :logger.add_handler/3:
config :sentry,
# ...
enable_logs: true,
logs: [level: :info, metadata: [:request_id]]With this configuration, every Logger call at :info or above becomes a structured log
event in Sentry, and crashes are still reported as captured Sentry events (just like the manual
setup above). The :logs options are documented in the
Sentry configuration.
Also capturing Logger messages as Sentry events
By default the auto-attached handler reports crashes as Sentry events but leaves
standalone messages (such as Logger.error("oops")) as structured logs only. To also
report those messages as Sentry events — for example, to turn Logger.error/1 calls into
Sentry issues while keeping Logger.info/1 out of your issues stream — use the :capture_*
keys under :logs:
config :sentry,
enable_logs: true,
logs: [
level: :info, # structured logs at :info and above
capture_log_messages: true, # also report messages as Sentry events...
capture_level: :error, # ...but only at :error and above
capture_metadata: :all, # include Logger metadata in those events
capture_excluded_domains: [:cowboy] # domains to exclude from those events
]The :capture_* keys configure captured Sentry events and are independent from the
matching logs feature keys (:metadata/:capture_metadata,
:excluded_domains/:capture_excluded_domains).
Including
LoggermetadataFor the auto-attached handler, metadata for the two destinations is configured separately. The
:metadatakey under:logslists theLoggermetadata attached as attributes on structured logs. The:capture_metadatakey lists the metadata attached under:extra(aslogger_metadata) on captured Sentry events. Both accept a list of keys or:all, and both default to[]. So if your custom metadata is missing from a captured event, setconfig :sentry, logs: [capture_metadata: [...]](or:all). When you add the handler manually instead, use this module's own:capture_metadataconfiguration option.
Configuration
This handler supports the following configuration options:
:capture_level(Logger.level/0) - The minimumLoggerlevel to send Sentry events for. Available since v13.3.0. The default value is:error.:capture_excluded_domains(list ofatom/0) - Any messages with a domain in the configured list will not be sent as Sentry events. The default avoids double-reporting events fromSentry.PlugCapture. Available since v13.3.0. The default value is[:cowboy, :bandit].:capture_metadata(list ofatom/0, or:all) - Use this to include non-Sentry logger metadata in captured Sentry events. If it's a list of keys, metadata in those keys will be added in the:extracontext (seeSentry.Context.set_extra_context/1) under the:logger_metadatakey. If set to:all, all metadata will be included. Available since v13.3.0. The default value is[].:tags_from_metadata(list ofatom/0) - Use this to include logger metadata as tags in reports. Metadata under the specified keys in those keys will be added as tags to the event. Available since v10.9.0. The default value is[].:capture_log_messages(boolean/0) - Whentrue, this module will report all logged messages to Sentry (provided they're not filtered by:capture_excluded_domainsand:capture_level). The default offalsemeans that the handler will only send crash reports, which are messages with metadata that has the shape of an exit reason and a stacktrace. The default value isfalse.:rate_limiting(keyword/0ornil) - since v10.4.0 - If present, enables rate limiting of reported messages. This can help avoid "spamming" Sentry with repeated log messages. To disable rate limiting, set this tonilor don't pass it altogether.:max_events(non_neg_integer/0) - Required. The maximum number of events to send to Sentry in the:intervalperiod.:interval(non_neg_integer/0) - Required. The interval (in milliseconds) to send:max_eventsevents.
The default value is
nil.:sync_threshold- (since v10.6.0) The number of queued events after which this handler switches to sync mode. Generally, this handler sends messages to Sentry asynchronously, equivalent to usingresult: :noneinSentry.send_event/2. However, if the number of queued events exceeds this threshold, the handler will switch to sync mode, where it starts usingresult: :syncto block until the event is sent. If you always want to use sync mode, set this option to0. This option effectively implements overload protection.If you would rather drop events to shed load instead, use the
:discard_thresholdoption.:sync_thresholdand:discard_thresholdcannot be used together. The default behavior of the handler is to switch to sync mode, so to disable this option and discard events instead set:sync_thresholdtoniland set:discard_thresholdinstead.The default value is
100.:discard_threshold- (since v10.9.0) The number of queued events after which this handler will start to discard events. This option effectively implements load shedding.:discard_thresholdand:sync_thresholdcannot be used together. If you set this option, set:sync_thresholdtonil.The default value is
nil.
The previous handler option names :level, :excluded_domains, and :metadata are
still accepted as aliases for :capture_level, :capture_excluded_domains, and
:capture_metadata, respectively.
Examples
To log all messages with level :error and above to Sentry, set :capture_log_messages
to true:
config :my_app, :logger, [
{:handler, :my_sentry_handler, Sentry.LoggerHandler, %{
config: %{
capture_metadata: [:file, :line],
capture_log_messages: true,
capture_level: :error
}
}}
]Now, logs like this will be reported as messages to Sentry:
Logger.error("Something went wrong")If you want to customize options for the reported message, use the :sentry metadata
key in the Logger call. For example, to add a tag to the Sentry event:
Logger.error("Something went wrong", sentry: [tags: %{my_tag: "my_value"}])Sentry context (in :sentry) is also read from the logger metadata, so you can configure
it for a whole process (with Logger.metadata/1). Last but not least, context is also read
from the ancestor chain of the process (:"$callers"), so if you set :sentry context
in a process and then spawn something like a task or a GenServer from that process,
the context will be included in the reported messages.