Current section

Files

Jump to
snakebridge lib snakebridge telemetry.ex
Raw

lib/snakebridge/telemetry.ex

defmodule SnakeBridge.Telemetry do
@moduledoc """
Telemetry event definitions for SnakeBridge.
This module provides instrumentation for compile-time operations including:
- Source scanning
- Python introspection
- Code generation
- Lock file verification
## Event List
| Event | Measurements | Metadata |
|-------|-------------|----------|
| `[:snakebridge, :compile, :start]` | `system_time` | `libraries`, `strict` |
| `[:snakebridge, :compile, :stop]` | `duration`, `symbols_generated`, `files_written` | `libraries`, `mode` |
| `[:snakebridge, :compile, :exception]` | `duration` | `reason`, `stacktrace` |
| `[:snakebridge, :compile, :scan, :stop]` | `duration`, `files_scanned`, `symbols_found` | `library`, `phase`, `details` |
| `[:snakebridge, :compile, :introspect, :start]` | `system_time` | `library`, `phase`, `details` |
| `[:snakebridge, :compile, :introspect, :stop]` | `duration`, `symbols_introspected`, `cache_hits` | `library`, `phase`, `details` |
| `[:snakebridge, :compile, :generate, :stop]` | `duration`, `bytes_written`, `functions_generated`, `classes_generated` | `library`, `phase`, `details` |
| `[:snakebridge, :docs, :fetch]` | `duration` | `module`, `function`, `source` |
| `[:snakebridge, :lock, :verify]` | `duration` | `result`, `warnings` |
## Usage
# Attach handlers in your application
SnakeBridge.Telemetry.Handlers.Logger.attach()
# Compile-time events are automatically emitted during mix compile
"""
# ============================================================
# COMPILATION EVENTS
# ============================================================
@doc """
Emits compile start event.
## Measurements
- `system_time` - System time when compilation started
## Metadata
- `library` - `:all`
- `phase` - `:compile`
- `details` - `%{libraries: [...], strict: boolean()}`
"""
@spec compile_start([atom()], boolean()) :: :ok
def compile_start(libraries, strict) do
emit(
[:snakebridge, :compile, :start],
%{system_time: System.system_time()},
%{library: :all, phase: :compile, details: %{libraries: libraries, strict: strict}}
)
end
@doc """
Emits compile stop event.
## Measurements
- `duration` - Time in native units
- `symbols_generated` - Number of symbols generated
- `files_written` - Number of files written
## Metadata
- `library` - `:all`
- `phase` - `:compile`
- `details` - `%{libraries: [...], mode: :normal | :strict}`
"""
@spec compile_stop(integer(), non_neg_integer(), non_neg_integer(), [atom()], :normal | :strict) ::
:ok
def compile_stop(start_time, symbols, files, libraries, mode) do
emit(
[:snakebridge, :compile, :stop],
%{
duration: System.monotonic_time() - start_time,
symbols_generated: symbols,
files_written: files
},
%{library: :all, phase: :compile, details: %{libraries: libraries, mode: mode}}
)
end
@doc """
Emits compile exception event.
## Measurements
- `duration` - Time in native units
## Metadata
- `library` - `:all`
- `phase` - `:compile`
- `details` - `%{reason: term(), stacktrace: list()}`
"""
@spec compile_exception(integer(), term(), list()) :: :ok
def compile_exception(start_time, reason, stacktrace) do
emit(
[:snakebridge, :compile, :exception],
%{duration: System.monotonic_time() - start_time},
%{library: :all, phase: :compile, details: %{reason: reason, stacktrace: stacktrace}}
)
end
# ============================================================
# SCANNING EVENTS
# ============================================================
@doc """
Emits scan stop event.
## Measurements
- `duration` - Time in native units
- `files_scanned` - Number of files scanned
- `symbols_found` - Number of symbols found
## Metadata
- `library` - `:all`
- `phase` - `:scan`
- `details` - `%{paths: [String.t()]}`
"""
@spec scan_stop(integer(), non_neg_integer(), non_neg_integer(), [String.t()]) :: :ok
def scan_stop(start_time, files, symbols, paths) do
emit(
[:snakebridge, :compile, :scan, :stop],
%{
duration: System.monotonic_time() - start_time,
files_scanned: files,
symbols_found: symbols
},
%{library: :all, phase: :scan, details: %{paths: paths}}
)
end
# ============================================================
# INTROSPECTION EVENTS
# ============================================================
@doc """
Emits introspect start event.
## Measurements
- `system_time` - System time when introspection started
## Metadata
- `library` - Library atom being introspected
- `phase` - `:introspect`
- `details` - `%{batch_size: non_neg_integer()}`
"""
@spec introspect_start(atom(), non_neg_integer()) :: :ok
def introspect_start(library, batch_size) do
emit(
[:snakebridge, :compile, :introspect, :start],
%{system_time: System.system_time()},
%{library: library, phase: :introspect, details: %{batch_size: batch_size}}
)
end
@doc """
Emits introspect stop event.
## Measurements
- `duration` - Time in native units
- `symbols_introspected` - Number of symbols introspected
- `cache_hits` - Number of cache hits
## Metadata
- `library` - Library atom introspected
- `phase` - `:introspect`
- `details` - `%{python_time: integer()}`
"""
@spec introspect_stop(integer(), atom(), non_neg_integer(), non_neg_integer(), integer()) :: :ok
def introspect_stop(start_time, library, symbols, cache_hits, python_time) do
emit(
[:snakebridge, :compile, :introspect, :stop],
%{
duration: System.monotonic_time() - start_time,
symbols_introspected: symbols,
cache_hits: cache_hits
},
%{library: library, phase: :introspect, details: %{python_time: python_time}}
)
end
# ============================================================
# GENERATION EVENTS
# ============================================================
@doc """
Emits generate stop event.
## Measurements
- `duration` - Time in native units
- `bytes_written` - Number of bytes written
- `functions_generated` - Number of functions generated
- `classes_generated` - Number of classes generated
## Metadata
- `library` - Library atom generated
- `phase` - `:generate`
- `details` - `%{file: String.t()}`
"""
@spec generate_stop(
integer(),
atom(),
String.t(),
non_neg_integer(),
non_neg_integer(),
non_neg_integer()
) :: :ok
def generate_stop(start_time, library, file, bytes, functions, classes) do
emit(
[:snakebridge, :compile, :generate, :stop],
%{
duration: System.monotonic_time() - start_time,
bytes_written: bytes,
functions_generated: functions,
classes_generated: classes
},
%{library: library, phase: :generate, details: %{file: file}}
)
end
# ============================================================
# DOCUMENTATION EVENTS
# ============================================================
@doc """
Emits docs fetch event.
## Measurements
- `duration` - Time in native units
## Metadata
- `module` - Module fetched
- `function` - Function name
- `source` - `:cache`, `:python`, or `:metadata`
"""
@spec docs_fetch(integer(), module(), atom(), :cache | :python | :metadata) :: :ok
def docs_fetch(start_time, module, function, source) do
emit(
[:snakebridge, :docs, :fetch],
%{duration: System.monotonic_time() - start_time},
%{module: module, function: function, source: source}
)
end
# ============================================================
# LOCK FILE EVENTS
# ============================================================
@doc """
Emits lock verify event.
## Measurements
- `duration` - Time in native units
## Metadata
- `result` - `:ok`, `:warning`, or `:error`
- `warnings` - List of warning strings
"""
@spec lock_verify(integer(), :ok | :warning | :error, [String.t()]) :: :ok
def lock_verify(start_time, result, warnings \\ []) do
emit(
[:snakebridge, :lock, :verify],
%{duration: System.monotonic_time() - start_time},
%{result: result, warnings: warnings}
)
end
@doc """
Returns the expected metadata fields for an event.
"""
@spec event_metadata_schema([atom()]) :: [atom()]
def event_metadata_schema([:snakebridge, :compile | _]) do
[:library, :phase, :details]
end
def event_metadata_schema([:snakebridge, :runtime | _]) do
[:library, :function, :call_type]
end
def event_metadata_schema(_event), do: []
defp emit(event, measurements, metadata) do
case Application.ensure_all_started(:telemetry) do
{:ok, _} -> :telemetry.execute(event, measurements, metadata)
{:error, _} -> :ok
end
end
end