PushX.Telemetry (PushX v0.15.0)

Copy Markdown View Source

Telemetry integration for PushX.

PushX emits the following telemetry events:

Events

[:pushx, :push, :start]

Emitted when a push notification request starts.

Measurements: %{system_time: integer} Metadata:

  • :provider - :apns or :fcm
  • :token - Device token (truncated for privacy)

[:pushx, :push, :stop]

Emitted when a push notification request completes successfully.

Measurements: %{duration: integer} (in native time units) Metadata:

  • :provider - :apns or :fcm
  • :token - Device token (truncated)
  • :status - :sent
  • :id - Provider message ID (if available)

[:pushx, :push, :exception]

Emitted when a push notification request raises an exception.

Measurements: %{duration: integer} Metadata:

  • :provider - :apns or :fcm
  • :token - Device token (truncated)
  • :kind - Exception kind (:error, :exit, :throw)
  • :reason - Exception reason
  • :stacktrace - Exception stacktrace

[:pushx, :push, :error]

Emitted when a push notification request returns an error response.

Measurements: %{duration: integer} Metadata:

  • :provider - :apns or :fcm
  • :token - Device token (truncated)
  • :status - Error status (e.g., :invalid_token, :rate_limited)
  • :reason - Error reason string

[:pushx, :retry, :attempt]

Emitted when a retry attempt is made.

Measurements: %{delay_ms: integer, attempt: integer} Metadata:

  • :provider - :apns or :fcm
  • :status - The error status that triggered the retry

Example Usage

Attach a handler in your application startup:

:telemetry.attach_many(
  "pushx-logger",
  [
    [:pushx, :push, :start],
    [:pushx, :push, :stop],
    [:pushx, :push, :error],
    [:pushx, :push, :exception]
  ],
  &MyApp.PushXTelemetry.handle_event/4,
  nil
)

Example handler:

defmodule MyApp.PushXTelemetry do
  require Logger

  def handle_event([:pushx, :push, :stop], %{duration: duration}, metadata, _config) do
    duration_ms = System.convert_time_unit(duration, :native, :millisecond)
    Logger.info("Push sent to #{metadata.provider} in #{duration_ms}ms")
  end

  def handle_event([:pushx, :push, :error], _measurements, metadata, _config) do
    Logger.warning("Push failed: #{metadata.status} - #{metadata.reason}")
  end

  def handle_event(_event, _measurements, _metadata, _config), do: :ok
end

Metrics with Telemetry.Metrics

With the optional telemetry_metrics dependency, metrics/0 returns a ready-made, low-cardinality metric list you can hand to any reporter (Telemetry.Metrics.ConsoleReporter, TelemetryMetricsPrometheus, PromEx, Phoenix.LiveDashboard):

# mix.exs
{:telemetry_metrics, "~> 1.0"}

# your telemetry supervisor / LiveDashboard metrics module
def metrics, do: MyApp.metrics() ++ PushX.Telemetry.metrics()

See metrics/0 for the list; roll your own from the events above if you need different tags or buckets.

Summary

Functions

A ready-made Telemetry.Metrics list for PushX (requires the optional telemetry_metrics dependency).

Truncates a device token for privacy-safe logging.

Functions

metrics()

(since 0.14.0)
@spec metrics() :: [Telemetry.Metrics.t()]

A ready-made Telemetry.Metrics list for PushX (requires the optional telemetry_metrics dependency).

Deliberately low-cardinality — device tokens are never a tag:

  • pushx.push.sent.count — successful sends, by provider
  • pushx.push.error.count — failed sends, by provider and status (:invalid_token, :rate_limited, :server_error, ...)
  • pushx.push.exception.count — sends that raised, by provider and kind
  • pushx.push.duration — latency distribution of successful sends in milliseconds, by provider; buckets tuned for provider round-trips (5 ms .. 10 s)
  • pushx.push.error.duration — latency distribution of failed sends (timeouts and 5xx land here — alert on this one for provider slowdowns), by provider and status; same buckets
  • pushx.retry.attempt.count — retry attempts, by provider and the status that triggered them
  • pushx.retry.delay — backoff delay distribution in milliseconds, by provider

Metric names are the events' names with the measurement appended, so they line up with PushX.Telemetry's documented events.

truncate_token(token)

@spec truncate_token(String.t()) :: String.t()

Truncates a device token for privacy-safe logging.

Shows first 8 and last 4 characters, replacing the middle with .... Returns the token unchanged if it is 16 characters or shorter.

Examples

iex> PushX.Telemetry.truncate_token("abcdefgh12345678ijklmnop")
"abcdefgh...mnop"

iex> PushX.Telemetry.truncate_token("short")
"short"