Telemetry events emitted by this library, plus a small logger handler.
Every HTTP call is wrapped in a :telemetry.span/3, which yields:
[:typesafe, :request, :start]with measurements%{system_time: integer}[:typesafe, :request, :stop]with measurements%{duration: integer}[:typesafe, :request, :exception]with%{duration: integer}and the usualkind,reason,stacktracemetadata
Durations are in native time units; convert with System.convert_time_unit/3.
Metadata
The start event carries method, path, model, and question_count.
The stop event adds:
status- HTTP status, ornilwhen no response arrivedretry_count- retries performed byTypeSafe.Retryinput_tokens/output_tokens- from the responseusage, ornilerror- aTypeSafe.Errorwhen the call failed, otherwisenil
question_count and model describe the request as sent, so a raw
TypeSafe.HTTP.post/4 call still reports them when the body has them.
Failures are stop events, not exception events
Every failure this library knows how to name is a normal outcome of the
span, so it arrives as a :stop event with metadata.error set to a
%TypeSafe.Error{}. That covers :auth, :rate_limited, :overloaded, :timeout,
:connection, :unexpected and :validation alike: an expired key, a 429
that outlived its retries, a socket that never opened, and a body the
library could not decode all look the same to a handler, and all of them
come with a duration. The :exception event fires only when code raises,
which in practice means a bug in a handler, in a Req step, or in this
library. A handler that watches only :exception will see none of the
failures that matter operationally.
Attaching a handler
:telemetry.attach("typesafe-watch", [:typesafe, :request, :stop], fn _event, _m, meta, tokens ->
if meta.error, do: Logger.warning("typesafe #{meta.error.type}: #{Exception.message(meta.error)}")
:counters.add(tokens, 1, meta[:input_tokens] || 0)
:counters.add(tokens, 2, meta[:output_tokens] || 0)
end, :counters.new(2, [:write_concurrency]))Read the running totals back with :counters.get(tokens, 1) and
:counters.get(tokens, 2). The duration in the measurements map is in
native time units; turn it into milliseconds with
System.convert_time_unit(duration, :native, :millisecond).
Logging
attach_logger/1 attaches a handler that logs one line per request at the
level given (or TYPESAFE_LOG_LEVEL, default :info).
Summary
Functions
Attaches a Logger handler for request stop and exception events.
Detaches the handler attached by attach_logger/1.
The telemetry event prefix, [:typesafe, :request].
Runs fun inside a [:typesafe, :request] span.
Functions
@spec attach_logger(keyword()) :: :ok | {:error, :already_exists}
Attaches a Logger handler for request stop and exception events.
Options
:level- log level, defaults toTYPESAFE_LOG_LEVELor:info
@spec detach_logger() :: :ok | {:error, :not_found}
Detaches the handler attached by attach_logger/1.
@spec prefix() :: [atom()]
The telemetry event prefix, [:typesafe, :request].
Runs fun inside a [:typesafe, :request] span.
fun must return {result, extra_metadata}; the extra metadata is merged
into the stop event.