Packages
snakepit
0.12.0
0.13.0
0.12.0
0.11.1
0.11.0
0.10.1
0.10.0
0.9.1
0.9.0
0.8.9
0.8.8
0.8.7
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.1
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.2
0.1.1
0.1.0
High-performance pooler and session manager for external language integrations. Supports Python, Node.js, Ruby, and more with gRPC streaming, session management, and production-ready process cleanup.
Current section
Files
Jump to
Current section
Files
lib/snakepit/telemetry/span.ex
defmodule Snakepit.Telemetry.Span do
@moduledoc """
Telemetry span helpers for wrapping operations.
Provides convenient helpers for emitting start/stop/exception
telemetry events around function calls.
## Usage
# Automatic span with function
result = Snakepit.Telemetry.Span.span(
[:snakepit, :my_operation],
%{pool: :default},
fn -> do_operation() end
)
# Manual span management
span_ref = Snakepit.Telemetry.Span.start_span([:snakepit, :operation], %{})
# ... do work ...
Snakepit.Telemetry.Span.end_span(span_ref)
"""
@type event :: [atom()]
@type metadata :: map()
@type span_ref :: %{
event: event(),
start_time: integer(),
metadata: metadata()
}
@doc """
Executes a function wrapped in telemetry span events.
Emits `event ++ [:start]` before the function runs,
and `event ++ [:stop]` after it completes successfully.
If the function raises, throws, or exits, emits `event ++ [:exception]`.
## Examples
Span.span([:myapp, :operation], %{user_id: 123}, fn ->
perform_operation()
end)
"""
@spec span(event(), metadata(), (-> result)) :: result when result: any()
def span(event, metadata, fun) when is_function(fun, 0) do
start_time = System.monotonic_time()
:telemetry.execute(
event ++ [:start],
%{system_time: System.system_time()},
metadata
)
try do
result = fun.()
:telemetry.execute(
event ++ [:stop],
%{duration: System.monotonic_time() - start_time},
metadata
)
result
rescue
e ->
:telemetry.execute(
event ++ [:exception],
%{duration: System.monotonic_time() - start_time},
Map.merge(metadata, %{kind: :error, reason: e, stacktrace: __STACKTRACE__})
)
reraise e, __STACKTRACE__
catch
kind, reason ->
:telemetry.execute(
event ++ [:exception],
%{duration: System.monotonic_time() - start_time},
Map.merge(metadata, %{kind: kind, reason: reason, stacktrace: __STACKTRACE__})
)
:erlang.raise(kind, reason, __STACKTRACE__)
end
end
@doc """
Starts a telemetry span.
Returns a span reference that should be passed to `end_span/1` or `end_span/2`.
Emits `event ++ [:start]` immediately.
## Examples
span_ref = Span.start_span([:myapp, :operation], %{user_id: 123})
# ... do work ...
Span.end_span(span_ref)
"""
@spec start_span(event(), metadata()) :: span_ref()
def start_span(event, metadata \\ %{}) do
start_time = System.monotonic_time()
:telemetry.execute(
event ++ [:start],
%{system_time: System.system_time()},
metadata
)
%{
event: event,
start_time: start_time,
metadata: metadata
}
end
@doc """
Ends a telemetry span.
Emits `event ++ [:stop]` with the duration measurement.
## Examples
span_ref = Span.start_span([:myapp, :operation], %{})
# ... do work ...
Span.end_span(span_ref)
"""
@spec end_span(span_ref()) :: :ok
def end_span(span_ref) do
end_span(span_ref, %{})
end
@doc """
Ends a telemetry span with additional metadata.
Merges the additional metadata with the original span metadata
before emitting the stop event.
## Examples
span_ref = Span.start_span([:myapp, :operation], %{})
result = do_work()
Span.end_span(span_ref, %{result: result, items_processed: 100})
"""
@spec end_span(span_ref(), metadata()) :: :ok
def end_span(span_ref, additional_metadata) do
duration = System.monotonic_time() - span_ref.start_time
metadata = Map.merge(span_ref.metadata, additional_metadata)
:telemetry.execute(
span_ref.event ++ [:stop],
%{duration: duration},
metadata
)
:ok
end
@doc """
Ends a span with an exception.
Use this when you catch an exception but want to emit the
exception telemetry event before re-raising or handling it.
## Examples
span_ref = Span.start_span([:myapp, :operation], %{})
try do
do_risky_work()
rescue
e ->
Span.end_span_exception(span_ref, :error, e, __STACKTRACE__)
handle_error(e)
end
"""
@spec end_span_exception(span_ref(), :error | :exit | :throw, term(), Exception.stacktrace()) ::
:ok
def end_span_exception(span_ref, kind, reason, stacktrace) do
duration = System.monotonic_time() - span_ref.start_time
metadata =
Map.merge(span_ref.metadata, %{
kind: kind,
reason: reason,
stacktrace: stacktrace
})
:telemetry.execute(
span_ref.event ++ [:exception],
%{duration: duration},
metadata
)
:ok
end
end