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-:apnsor: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-:apnsor: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-:apnsor: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-:apnsor: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-:apnsor: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
endMetrics 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
@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, byproviderpushx.push.error.count— failed sends, byproviderandstatus(:invalid_token,:rate_limited,:server_error, ...)pushx.push.exception.count— sends that raised, byproviderandkindpushx.push.duration— latency distribution of successful sends in milliseconds, byprovider; 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), byproviderandstatus; same bucketspushx.retry.attempt.count— retry attempts, byproviderand thestatusthat triggered thempushx.retry.delay— backoff delay distribution in milliseconds, byprovider
Metric names are the events' names with the measurement appended, so they
line up with PushX.Telemetry's documented events.
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"