View Source Sentry.LoggerHandler (Sentry v10.8.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.

When to Use the Handler vs the Backend?

Sentry's Elixir SDK also ships with Sentry.LoggerBackend, an Elixir Logger backend. The backend has similar functionality to this handler. The main functional difference is that Sentry.LoggerBackend runs in its own process, while Sentry.LoggerHandler runs in the process that logs. The latter is generally preferable.

The reason both exist is that :logger handlers are a relatively-new feature in Erlang/OTP, and Sentry.LoggerBackend was created before :logger handlers were introduced.

In general, use Sentry.LoggerHandler whenever possible. In future Elixir releases, Logger backends may become deprecated and hence Sentry.LoggerBackend may 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: %{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)

  # ...
end

Alternatively, 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: %{metadata: [:file, :line]}
  })

  # ...
end

Configuration

This handler supports the following configuration options:

  • :level (Logger.level/0) - The minimum Logger level to send events for. The default value is :error.

  • :excluded_domains (list of atom/0) - Any messages with a domain in the configured list will not be sent. The default is so as to avoid double-reporting events from Sentry.PlugCapture. The default value is [:cowboy, :bandit].

  • :metadata (list of atom/0, or :all) - Use this to include non-Sentry logger metadata in reports. If it's a list of keys, metadata in those keys will be added in the :extra context (see Sentry.Context.set_extra_context/1) under the :logger_metadata key. If set to :all, all metadata will be included. The default value is [].

  • :capture_log_messages (boolean/0) - When true, this module will report all logged messages to Sentry (provided they're not filtered by :excluded_domains and :level). The default of false means 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 is false.

  • :rate_limiting (keyword/0 or nil) - 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 to nil or don't pass it altogether.

    • :max_events (non_neg_integer/0) - Required. The maximum number of events to send to Sentry in the :interval period.

    • :interval (non_neg_integer/0) - Required. The interval (in milliseconds) to send :max_events events.

    The default value is nil.

  • :sync_threshold (non_neg_integer/0) - 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 using result: :none in Sentry.send_event/2. However, if the number of queued events exceeds this threshold, the handler will switch to sync mode, where it starts using result: :sync to block until the event is sent. If you always want to use sync mode, set this option to 0. This option effectively implements overload protection. The default value is 100.

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: %{metadata: [:file, :line], capture_log_messages: true, 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.