# `HTTP.Telemetry`
[🔗](https://github.com/gsmlg-dev/http_fetch/blob/v0.21.17/apps/http_fetch/lib/http/telemetry.ex#L1)

Telemetry integration for comprehensive HTTP request and response monitoring.

This module provides automatic telemetry event emission for all HTTP operations,
enabling observability, metrics collection, and performance monitoring. All
events use the `[:http_fetch, ...]` prefix.

## Automatic Events

All `HTTP.fetch/2` operations automatically emit telemetry events. No
configuration is required - simply attach handlers to receive events.

Request metadata contains only finite method, scheme, protocol and error
categories plus numeric status. Headers and all URI components (including
host, path, userinfo, query and fragment) are omitted. Arbitrary exception
details are replaced with `:request_failed`.

Pass `telemetry: false` to `HTTP.fetch/2` to suppress request events and its
response streams and internally created upload streams/bridges. A producer
stream created independently before the request has its own telemetry setting.
Shared HTTP/2 connection/pool events contain only safe aggregate measurements
and finite categories and remain independent of a single request's option.
For a strict global opt-out, configure `config :http_fetch, telemetry: false`;
this suppresses both `[:http_fetch, ...]` and the shared runtime's
`[:http_runtime, ...]` events, including other clients using that runtime.
`config :http_runtime, telemetry: false` independently suppresses all shared
runtime events. The bundled TLS and QUIC engines emit no telemetry events.

## Event Types

### Request Events

**`[:http_fetch, :request, :start]`** - Emitted when a request begins

- Measurements: `%{start_time: integer}` (microseconds)
- Metadata: `%{method: atom, scheme: :http | :https | :other}`

**`[:http_fetch, :request, :stop]`** - Emitted when a request completes successfully

- Measurements: `%{duration: integer, status: integer, response_size: integer}`
  - `duration` - Request duration in microseconds
  - `status` - HTTP status code
  - `response_size` - Response body size in bytes
- Metadata: `%{scheme: atom, status: integer, http_version: atom}`

**`[:http_fetch, :request, :exception]`** - Emitted when a request fails

- Measurements: `%{duration: integer}` (microseconds)
- Metadata: `%{scheme: atom, error: atom}`

### Streaming Events

**`[:http_fetch, :streaming, :start]`** - Emitted when response streaming begins

- Measurements: `%{content_length: integer}` (0 if unknown)
- Metadata: `%{}`

**`[:http_fetch, :streaming, :chunk]`** - Emitted for each stream chunk received

- Measurements: `%{bytes_received: integer, total_bytes: integer}`
- Metadata: `%{}`

**`[:http_fetch, :streaming, :stop]`** - Emitted when streaming completes

- Measurements: `%{total_bytes: integer, duration: integer}` (duration in microseconds)
- Metadata: `%{}`

### Response Body Events

**`[:http_fetch, :response, :body_read_start]`** - Emitted when reading response body

- Measurements: `%{content_length: integer}`
- Metadata: `%{}`

**`[:http_fetch, :response, :body_read_stop]`** - Emitted when body read completes

- Measurements: `%{bytes_read: integer, duration: integer}`
- Metadata: `%{}`

### HTTP/2 Runtime Events

`[:http_fetch, :http2, :connection]` reports `active_streams`, `protocol_streams`,
`buffered_receive_bytes`, `pending_upload_bytes`, `receive_budget_bytes`,
and `writer_batch_peak_bytes`. Metadata contains only `event` and
`lifecycle`; it excludes headers, bodies, URLs, scope and TLS identity.
Events are emitted on stream admission/release, receive admission/consumption,
and connection termination. Receive bytes include padding and deliveries
awaiting application acknowledgement; upload bytes count pending owner slices.
BodyBridge separately exposes its bounded source-chunk budget through status/1.

`[:http_fetch, :http2, :pool]` reports aggregate `reservations`, `waiters`,
`connecting`, `connections`, `draining`, and `queue_wait_us` measurements.
Metadata contains only finite `event` and `outcome` atoms. Pool keys, URLs,
request tokens, owner PIDs, and connector identities are excluded.

`[:http_fetch, :http2, :runtime]` reports connection close, peer reset,
GOAWAY, and upload flow-control stall events. Metadata contains only fixed
`event` and `outcome` atoms. Peer error codes and stall durations are numeric
measurements, never tags. `[:http_fetch, :http2, :body_bridge]` reports a
bridge's final byte count, peak buffer size, and lifetime in microseconds;
its `outcome` is a fixed atom, with no source or owner identity.

## Usage Example

    # Attach a simple logger handler
    :telemetry.attach_many(
      "http-logger",
      [
        [:http_fetch, :request, :start],
        [:http_fetch, :request, :stop],
        [:http_fetch, :request, :exception]
      ],
      fn event_name, measurements, metadata, _config ->
        case event_name do
          [:http_fetch, :request, :start] ->
            IO.puts("Request started: " <> Atom.to_string(metadata.scheme))

          [:http_fetch, :request, :stop] ->
            duration_ms = measurements.duration / 1000
            IO.puts("Request completed in " <> Float.to_string(duration_ms) <> "ms")

          [:http_fetch, :request, :exception] ->
            IO.puts("Request failed: " <> inspect(metadata.error))
        end
      end,
      nil
    )

## Metrics Collection

    # Collect request duration metrics
    :telemetry.attach(
      "http-metrics",
      [:http_fetch, :request, :stop],
      fn _event, measurements, metadata, _config ->
        # Send to your metrics system
        MyMetrics.record_http_request(
          scheme: metadata.scheme,
          status: metadata.status,
          duration_us: measurements.duration
        )
      end,
      nil
    )

## Integration with Telemetry.Metrics

    # Define metrics for visualization
    import Telemetry.Metrics

    [
      # Request duration histogram
      distribution("http_fetch.request.duration",
        unit: {:native, :millisecond},
        tags: [:status]
      ),

      # Request count by status
      counter("http_fetch.request.count",
        tags: [:status]
      ),

      # Response size summary
      summary("http_fetch.request.response_size",
        unit: :byte
      ),

      # Streaming throughput
      distribution("http_fetch.streaming.chunk.bytes_received",
        unit: :byte
      )
    ]

# `request_exception`

```elixir
@spec request_exception(URI.t(), term(), integer()) :: :ok
```

Emits a telemetry event for request failure.

## Examples
    iex> HTTP.Telemetry.request_exception(URI.parse("https://example.com"), :timeout, 5000)
    :ok

# `request_start`

```elixir
@spec request_start(atom() | String.t(), URI.t(), HTTP.Headers.t()) :: :ok
```

Emits a telemetry event for request start.

## Examples
    iex> HTTP.Telemetry.request_start("GET", URI.parse("https://example.com"), %HTTP.Headers{})
    :ok

# `request_stop`

```elixir
@spec request_stop(integer(), URI.t(), integer(), integer(), atom() | nil) :: :ok
```

Emits a telemetry event for request completion. Network requests include the
actual protocol as `metadata.http_version` when supplied.

## Examples
    iex> HTTP.Telemetry.request_stop(200, URI.parse("https://example.com"), 1024, 1500)
    :ok

# `response_body_read_start`

```elixir
@spec response_body_read_start(integer()) :: :ok
```

Emits a telemetry event for response body reading start.

## Examples
    iex> HTTP.Telemetry.response_body_read_start(1024)
    :ok

# `response_body_read_stop`

```elixir
@spec response_body_read_stop(integer(), integer()) :: :ok
```

Emits a telemetry event for response body reading completion.

## Examples
    iex> HTTP.Telemetry.response_body_read_stop(1024, 500)
    :ok

# `streaming_chunk`

```elixir
@spec streaming_chunk(integer(), integer()) :: :ok
```

Emits a telemetry event for streaming chunk received.

## Examples
    iex> HTTP.Telemetry.streaming_chunk(8192, 16384)
    :ok

# `streaming_start`

```elixir
@spec streaming_start(integer()) :: :ok
```

Emits a telemetry event for streaming start.

## Examples
    iex> HTTP.Telemetry.streaming_start(5242880)
    :ok

# `streaming_stop`

```elixir
@spec streaming_stop(integer(), integer()) :: :ok
```

Emits a telemetry event for streaming completion.

## Examples
    iex> HTTP.Telemetry.streaming_stop(5242880, 10000)
    :ok

---

*Consult [api-reference.md](api-reference.md) for complete listing*
