Help us improve
Share bugs, ideas, or general feedback.
Observability for Elixir/Phoenix: structured (JSON) logging, :telemetry events/handlers, OpenTelemetry tracing, error reporting (e.g. Sentry), and metrics/LiveDashboard.
npx claudepluginhub ariesclark/skills --plugin elixir-phoenixHow this skill is triggered — by the user, by Claude, or both
Slash command
/elixir-phoenix:observabilityWhen to use
Use when adding or reviewing logging, instrumentation, traces, or metrics (structured/JSON logs (`logger_json`), `:telemetry`, OpenTelemetry, error reporting (Sentry), and metrics/LiveDashboard), or judging how an app reports what it does in production.
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Three distinct pillars; don't conflate them: **logs** (what happened), **traces** (how a request flowed), **metrics** (aggregate rates/latencies). Pairs with `elixir-conventions`.
Runs automated patient safety test suites for healthcare deployments, blocking on CRITICAL failures in CDSS accuracy, PHI exposure, and data integrity.
Share bugs, ideas, or general feedback.
Three distinct pillars; don't conflate them: logs (what happened), traces (how a request flowed), metrics (aggregate rates/latencies). Pairs with elixir-conventions.
Logger.error("charge failed", order_id: id, reason: inspect(reason)). Pass metadata, don't bake values into the message string (so logs stay queryable and group well).logger_json) so a log pipeline can index fields. No ANSI color codes in prod: they corrupt JSON and log parsers (colors are for dev consoles only).:telemetry handlers in Application.start/2, once, not per-request. Handler functions must be fast and must not crash (a raising handler gets detached).elixir-conventions: raise the unexpected so it's captured with a trace).trace_id/span_id into logger metadata.phoenix-security); filter params and scrub headers.:info+ in prod, :debug in dev) and rate-limit noisy error reporting.# Don't: values trapped in the message; ANSI colors in prod
Logger.info("user #{id} did #{action}")
# Do: message is the event, data is metadata
Logger.info("user.action", user_id: id, action: action)
# prod: JSON formatter, metadata allowlist, no colors
config :logger, :default_handler,
formatter: {LoggerJSON.Formatters.Basic, metadata: [:request_id, :trace_id, :span_id, :user_id]}
# Application.start/2
:telemetry.attach_many(
"obs-handlers",
[[:my_app, :repo, :query], [:phoenix, :endpoint, :stop]],
&MyApp.Telemetry.handle_event/4,
nil
)
def handle_event([:my_app, :repo, :query], %{total_time: t}, meta, _) do
if System.convert_time_unit(t, :native, :millisecond) > 500,
do: Logger.warning("slow query", source: meta.source, ms: ...)
end
Use Telemetry.Metrics + a reporter (Prometheus, or Phoenix.LiveDashboard for a live view) to turn events into metrics. Don't hand-roll counters.
opentelemetry_phoenix, opentelemetry_ecto, opentelemetry_bandit/cowboy, opentelemetry_oban).trace_id/span_id.Errors and logs are different pipelines. Crashes/exceptions should reach an error reporter (Sentry et al.) with a stacktrace; structured logs are for expected, queryable events. If something interesting is also a bug, raise it; don't just log a string.