Tracing a Notification

Copy Markdown View Source

Chimeway emits standard :telemetry events at every stage of the notification lifecycle: triggering, policy evaluation, delivery planning, and actual delivery attempts. You can hook into these events to emit logs, record metrics, or debug why a notification wasn't sent.

Core Principles of Chimeway Telemetry

  1. Safety First: Chimeway intentionally does not include the notification payload in telemetry metadata. This prevents accidentally leaking PII, passwords, or sensitive financial data into logs or APM systems.
  2. Correlation IDs: To trace a notification through its lifecycle, rely on safe correlation IDs instead of payload data. Every step of the pipeline includes a consistent correlation_id and the original notification_key.

Available Telemetry Events

Chimeway emits the following key events (each has a :start, :stop, and :exception variant):

  • [:chimeway, :deliveries, :plan, *] - Emitted when a notification is triggered and a delivery plan is created.
  • [:chimeway, :policy, :evaluate, *] - Emitted when policy rules (quiet hours, frequency caps) are evaluated.
  • [:chimeway, :delivery, :attempt, *] - Emitted for each adapter delivery attempt (e.g., sending the actual email).
  • [:chimeway, :oban, :worker, *] - Emitted by the Oban worker during async dispatch.

Attaching to Telemetry Events

To trace deliveries, you can create a module that attaches to these events. Here is an example of a simple logger that listens to the [:chimeway, :delivery, :attempt, :stop] event to see whether an attempt succeeded or failed.

defmodule MyApp.ChimewayLogger do
  require Logger

  def setup do
    :telemetry.attach(
      "chimeway-logger-delivery-stop",
      [:chimeway, :delivery, :attempt, :stop],
      &__MODULE__.handle_event/4,
      nil
    )
  end

  def handle_event(_event, measurements, metadata, _config) do
    # ⚠️ Notice that `metadata` only contains safe correlation IDs and state.
    # The actual payload is intentionally omitted.
    delivery_id = metadata[:delivery_id]
    correlation_id = metadata[:correlation_id]
    notification_key = metadata[:notification_key]
    status = metadata[:status]
    
    Logger.info(
      "Delivery Attempt Finished [#{status}] " <>
      "delivery_id=#{delivery_id} " <>
      "correlation_id=#{correlation_id} " <>
      "key=#{notification_key} " <>
      "duration=#{measurements.duration}"
    )
  end
end

To enable this, call MyApp.ChimewayLogger.setup() in your application's start function (e.g., in application.ex).

Diagnosing "Why wasn't this sent?"

If you're using IEx and need to know why a specific notification was suppressed, you can use the built-in Chimeway.Traces module. The traces provide an explainable view of the delivery's policy evaluations.

# In IEx
alias Chimeway.Traces

# If you have the delivery_id:
Traces.explain_delivery(delivery_id)

# If you only have the correlation_id (useful if you found it in your logs!):
Traces.find_traces_by_correlation_id(correlation_id)

The trace will explicitly tell you if a notification was blocked by a frequency cap, suppressed during quiet hours, or rejected due to opt-out preferences.