JustBash.Telemetry (JustBash v0.4.0)

View Source

Telemetry instrumentation for the JustBash interpreter.

This module emits telemetry events for script execution, allowing you to monitor performance, track usage, and integrate with observability tools.

All events use :telemetry.span/3, which automatically includes a telemetry_span_context in metadata for distributed tracing correlation.

Available Events

All events follow the span pattern with :start, :stop, and :exception suffixes.

Session Execution

  • [:just_bash, :session, :run, :start] - Emitted when JustBash.exec/2 begins execution

    • Measurement: %{system_time: integer, monotonic_time: integer}
    • Metadata: %{session: pid(), telemetry_span_context: reference()}
  • [:just_bash, :session, :run, :stop] - Emitted when JustBash.exec/2 completes

    • Measurement: %{duration: native_time, monotonic_time: integer}
    • Metadata: %{session: pid(), status: :ok | :error, exit_code: integer, bytes_in: integer, bytes_out: integer, telemetry_span_context: reference()}

  • [:just_bash, :session, :run, :exception] - Emitted when JustBash.exec/2 raises

    • Measurement: %{duration: native_time, monotonic_time: integer}
    • Metadata: %{session: pid(), kind: :error | :exit | :throw, reason: term, stacktrace: list, telemetry_span_context: reference()}

    • Rare by design: the interpreter contains a crashed command and a raise from the statement loop, so those produce a :stop with a non-zero exit_code — plus a [:just_bash, :command, :exception] when a command was the cause — rather than an exception here.

Command Execution

  • [:just_bash, :command, :start] - Emitted before a command executes

    • Measurement: %{system_time: integer, monotonic_time: integer}
    • Metadata: %{command: String.t(), args: list(String.t()), telemetry_span_context: reference()}
  • [:just_bash, :command, :stop] - Emitted after a command completes

    • Measurement: %{duration: native_time, monotonic_time: integer}
    • Metadata: %{command: String.t(), args: list(String.t()), exit_code: integer, bytes_in: integer, bytes_out: integer, telemetry_span_context: reference()}
    • When the command is a JustBash.CLI tool, metadata also includes subcommand: list(String.t()) — the resolved subcommand path (e.g. ["pr", "review"]) — so observability doesn't collapse an entire CLI into a single command bucket.
  • [:just_bash, :command, :exception] - Emitted when a command raises

    • Measurement: %{duration: native_time, monotonic_time: integer}
    • Metadata: %{command: String.t(), args: list(String.t()), kind: atom, reason: term, stacktrace: list, telemetry_span_context: reference()}
    • A command that raises is contained — JustBash.exec/2 still returns a shell result — but the containment sits outside this span deliberately, so a crash stays distinguishable from a script that legitimately fails. The :stop event does not fire for the same command.

For Loop Execution

  • [:just_bash, :for_loop, :start] - Emitted before a for loop begins

    • Measurement: %{system_time: integer, monotonic_time: integer}
    • Metadata: %{variable: String.t(), item_count: integer, telemetry_span_context: reference()}
  • [:just_bash, :for_loop, :stop] - Emitted after a for loop completes

    • Measurement: %{duration: native_time, monotonic_time: integer}
    • Metadata: %{variable: String.t(), item_count: integer, iteration_count: integer, exit_code: integer | nil, telemetry_span_context: reference()}
  • [:just_bash, :for_loop, :exception] - Emitted when a for loop raises

    • Measurement: %{duration: native_time, monotonic_time: integer}
    • Metadata: %{variable: String.t(), item_count: integer, kind: atom, reason: term, stacktrace: list, telemetry_span_context: reference()}

While/Until Loop Execution

  • [:just_bash, :while_loop, :start] - Emitted before a while/until loop begins

    • Measurement: %{system_time: integer, monotonic_time: integer}
    • Metadata: %{until: boolean, telemetry_span_context: reference()}
  • [:just_bash, :while_loop, :stop] - Emitted after a while/until loop completes

    • Measurement: %{duration: native_time, monotonic_time: integer}
    • Metadata: %{until: boolean, iteration_count: integer, exit_code: integer | nil, telemetry_span_context: reference()}
  • [:just_bash, :while_loop, :exception] - Emitted when a while/until loop raises

    • Measurement: %{duration: native_time, monotonic_time: integer}
    • Metadata: %{until: boolean, kind: atom, reason: term, stacktrace: list, telemetry_span_context: reference()}

Usage Example

Attach handlers to receive telemetry events:

:telemetry.attach_many(
  "just-bash-handler",
  [
    [:just_bash, :session, :run, :start],
    [:just_bash, :session, :run, :stop],
    [:just_bash, :session, :run, :exception],
    [:just_bash, :command, :start],
    [:just_bash, :command, :stop],
    [:just_bash, :command, :exception],
    [:just_bash, :for_loop, :start],
    [:just_bash, :for_loop, :stop],
    [:just_bash, :for_loop, :exception],
    [:just_bash, :while_loop, :start],
    [:just_bash, :while_loop, :stop],
    [:just_bash, :while_loop, :exception]
  ],
  &MyApp.TelemetryHandler.handle_event/4,
  nil
)

Note on Output

Output (stdout/stderr) is intentionally NOT included in telemetry metadata to avoid memory issues with large outputs. Byte counts (bytes_in, bytes_out) are provided instead. Use output collectors or sinks if you need to capture command output.