Ditliti (ditliti v0.1.2)

Copy Markdown View Source

Dependency-free client for the Ditliti ingestion API.

The client struct is immutable, matching normal Elixir data-flow: functions that add context (add_breadcrumb/3, put_user/2, put_session/2) return an updated struct rather than mutating anything in place.

Summary

Functions

Record a breadcrumb, keeping only the most recent 100.

Capture a rescued exception. Pass the __STACKTRACE__ from the rescue clause via stacktrace: to include stack frames.

Reads the trace stashed by put_current_trace/1 (or start_trace/2) for this process, if any.

Record and send a span started via start_span/3.

Build a new client. endpoint is the ingestion API base URL.

Stashes trace in the current process's dictionary, so instrumentation that runs later in the same process without access to the client struct (e.g. an :telemetry handler attached via Ditliti.Db.attach_ecto_telemetry/3, which Ecto/Postgrex invoke in the caller's process) can still correlate to it via current_trace/0. start_trace/2 does this automatically.

Start a child span under the active trace (starting one implicitly if none is active yet). Returns {updated_client, span}, where span is a map you pass to finish_span/3 once the operation completes.

Start (or continue, if an inbound traceparent header is given) the active trace. Downstream calls should read get_traceparent/1 and forward it as the traceparent header of any outgoing HTTP request to propagate the trace into the next service.

Types

t()

@type t() :: %Ditliti{
  api_key: String.t(),
  before_send: (map() -> map() | nil) | nil,
  breadcrumbs: [map()],
  endpoint: String.t(),
  environment: String.t() | nil,
  max_payload_bytes: pos_integer(),
  max_retries: non_neg_integer(),
  project_id: String.t(),
  release: String.t() | nil,
  retry_base_delay_ms: non_neg_integer(),
  session_id: String.t() | nil,
  trace: trace_context() | nil,
  user: map() | nil
}

trace_context()

@type trace_context() :: %{
  trace_id: String.t(),
  span_id: String.t(),
  sampled: boolean()
}

Functions

add_breadcrumb(client, message, opts \\ [])

@spec add_breadcrumb(t(), String.t(), keyword()) :: t()

Record a breadcrumb, keeping only the most recent 100.

capture_exception(client, exception, opts \\ [])

@spec capture_exception(t(), Exception.t(), keyword()) :: :ok | {:error, term()}

Capture a rescued exception. Pass the __STACKTRACE__ from the rescue clause via stacktrace: to include stack frames.

capture_message(client, message, opts \\ [])

@spec capture_message(t(), String.t(), keyword()) :: :ok | {:error, term()}

capture_profile(client, duration_ms, samples, opts \\ [])

@spec capture_profile(t(), non_neg_integer(), list(), keyword()) ::
  :ok | {:error, term()}

capture_replay_segment(client, duration_ms, events, opts \\ [])

@spec capture_replay_segment(t(), non_neg_integer() | float(), list(), keyword()) ::
  :ok | {:error, term()}

current_trace()

@spec current_trace() :: trace_context() | nil

Reads the trace stashed by put_current_trace/1 (or start_trace/2) for this process, if any.

finish_span(client, span, extra_tags \\ %{})

@spec finish_span(t(), map(), map()) :: :ok | {:error, term()}

Record and send a span started via start_span/3.

get_traceparent(ditliti)

@spec get_traceparent(t()) :: String.t() | nil

new(endpoint, api_key, project_id, opts \\ [])

@spec new(String.t(), String.t(), String.t(), keyword()) :: t()

Build a new client. endpoint is the ingestion API base URL.

put_current_trace(trace)

@spec put_current_trace(trace_context() | nil) :: trace_context() | nil

Stashes trace in the current process's dictionary, so instrumentation that runs later in the same process without access to the client struct (e.g. an :telemetry handler attached via Ditliti.Db.attach_ecto_telemetry/3, which Ecto/Postgrex invoke in the caller's process) can still correlate to it via current_trace/0. start_trace/2 does this automatically.

put_session(client, session_id)

@spec put_session(t(), String.t() | nil) :: t()

put_user(client, user)

@spec put_user(t(), map() | nil) :: t()

record_span(client, opts)

@spec record_span(
  t(),
  keyword()
) :: :ok | {:error, term()}

send_envelope(client, envelope)

@spec send_envelope(t(), %{
  optional(:events) => list(),
  optional(:spans) => list(),
  optional(:logs) => list(),
  optional(:replays) => list(),
  optional(:profiles) => list()
}) :: :ok | {:error, term()}

start_span(client, operation_name, opts \\ [])

@spec start_span(t(), String.t(), keyword()) :: {t(), map()}

Start a child span under the active trace (starting one implicitly if none is active yet). Returns {updated_client, span}, where span is a map you pass to finish_span/3 once the operation completes.

start_trace(client, incoming_traceparent \\ nil)

@spec start_trace(t(), String.t() | nil) :: t()

Start (or continue, if an inbound traceparent header is given) the active trace. Downstream calls should read get_traceparent/1 and forward it as the traceparent header of any outgoing HTTP request to propagate the trace into the next service.