# `Malla.Tracer`
[🔗](https://github.com/netkubes/malla/blob/main/lib/malla/tracer.ex#L21)

Provides a zero-overhead observability API for Malla services.

`Malla.Tracer` allows you to instrument your code with _spans_, _logs_, and _metrics_
without committing to a specific telemetry backend. The actual tracing implementation
is provided by plugins and can be added later or even swapped with touching your code.

The tracing system works inside a `Malla.Service`. You must include plugin `Malla.Plugins.Tracer` in
your service (or maybe a more specific plugin with a real implementation). Then, on the module
to instrument must do `use Malla.Tracer`.

## Included Macros and Functions

When you use `use Malla.Tracer`, the following macros and functions are imported into your module:

### Macros
- [`span/2`](`Malla.Tracer.span/2`) - Creates a tracing span around the given code block.
- [`span/3`](`Malla.Tracer.span/3`) - Creates a tracing span with options around the given code block.
- [`debug/1`](`Malla.Tracer.debug/1`) - Logs a debug-level message.
- [`debug/2`](`Malla.Tracer.debug/2`) - Logs a debug-level message with metadata.
- [`info/1`](`Malla.Tracer.info/1`) - Logs an info-level message.
- [`info/2`](`Malla.Tracer.info/2`) - Logs an info-level message with metadata.
- [`event/1`](`Malla.Tracer.event/1`) - Records a tracing event.
- [`event/2`](`Malla.Tracer.event/2`) - Records a tracing event with data.
- [`notice/1`](`Malla.Tracer.notice/1`) - Logs a notice-level message.
- [`notice/2`](`Malla.Tracer.notice/2`) - Logs a notice-level message with metadata.
- [`warning/1`](`Malla.Tracer.warning/1`) - Logs a warning-level message.
- [`warning/2`](`Malla.Tracer.warning/2`) - Logs a warning-level message with metadata.
- [`error/1`](`Malla.Tracer.error/1`) - Logs an error-level message.
- [`error/2`](`Malla.Tracer.error/2`) - Logs an error-level message with metadata.

### Functions
- [`span_update/1`](`Malla.Tracer.span_update/1`) - Updates the current tracing span with new data.
- [`metric/2`](`Malla.Tracer.metric/2`) - Records a metric value.
- [`metric/3`](`Malla.Tracer.metric/3`) - Records a metric value with metadata.
- [`labels/1`](`Malla.Tracer.labels/1`) - Sets labels on the current tracing span.
- [`globals/1`](`Malla.Tracer.globals/1`) - Sets global tracing metadata.
- [`get_info/0`](`Malla.Tracer.get_info/0`) - Retrieves current tracing information.
- [`get_info/1`](`Malla.Tracer.get_info/1`) - Retrieves current tracing information with options.
- [`get_base/0`](`Malla.Tracer.get_base/0`) - Gets base tracing configuration.

For comprehensive documentation, see the guide:
- **[Tracing and Instrumentation](guides/09-observability/01-tracing.md)**: For a guide on how to use the tracer.

# `data`

```elixir
@type data() :: keyword() | map()
```

# `id`

```elixir
@type id() :: atom()
```

# `level_code`

```elixir
@type level_code() :: 0..9
```

# `level_name`

```elixir
@type level_name() ::
  :min | :debug | :info | :metric | :event | :notice | :warning | :error | :max
```

# `span_id`

```elixir
@type span_id() :: [atom()]
```

# `debug`
*macro* 

```elixir
@spec debug(String.t(), data()) :: Macro.t()
```

Log a debug-level message.

Debug logs are intended for development and troubleshooting. They can be
completely removed at compile time by setting `:log_min_level` above `:debug`.

Calls callback `c:Malla.Plugins.Tracer.malla_span_log/3`. Default implementation delegates to `Logger`.

## Parameters

- `text` - Log message (supports string interpolation)
- `meta` - Additional metadata (keyword list or map, optional)

## Examples

    debug("User lookup: 123", user_id: 123)
    debug("Cache stats", hits: 10, misses: 2)

## Compile-Time Filtering

    # config/prod.exs
    config :malla, log_min_level: :info  # Removes debug() calls completely

## Auto-Injected Metadata

- `:module` - Current module name
- `:line` - Line number in source file

# `error`
*macro* 

```elixir
@spec error(any()) :: Macro.t()
```

Mark the current span as error or log an error message.

This function is overloaded to support two use cases:

1. **Log an error message**: When called with a string, logs at error level.
2. **Mark span as error**: When called with any other value, records the error
   in the span context and returns the value unchanged (passthrough).

Calls callback `c:Malla.Plugins.Tracer.malla_span_error/2` for non-string values. Default implementation returns the error unchanged.

## Parameters

- `error` - Error value (typically `{:error, reason}`) or string message

## Examples

    # Mark span as error (passthrough pattern)
    span [:payment] do
      case charge_card(amount) do
        {:ok, result} -> result
        {:error, _} = err -> error(err)  # Mark span, return error
      end
    end

    # Pipeline usage
    {:error, :timeout}
    |> error()
    |> handle_payment_error()

    # String variant logs an error
    error("Connection failed")  # Same as error("Connection failed", [])

# `error`
*macro* 

```elixir
@spec error(String.t(), data()) :: Macro.t()
```

Log an error-level message.

Error logs indicate error conditions that require attention.

## Examples

    error("Payment failed", reason: :insufficient_funds, user_id: 123)
    error("Database connection lost", attempts: 3)

See `debug/2` for detailed documentation on behavior and configuration.

# `event`
*macro* 

```elixir
@spec event(id(), data(), data()) :: Macro.t()
```

Record a span event.

Calls callback `c:Malla.Plugins.Tracer.malla_span_event/3`. Default implementation does nothing.

Events represent discrete occurrences within a span (e.g., cache hit, retry attempt,
external API call). They differ from logs in that they're structured data points.

## Parameters

- `type` - Event type identifier (atom)
- `data` - Event data (keyword list or map, optional)
- `meta` - Additional metadata (keyword list or map, optional)

## Examples

    span [:api_request] do
      event(:cache_hit, key: "user:123")
      # ... later ...
      event(:validation_passed, fields: ["email", "name"])
    end

    # Retry event
    event(:retry_attempt, attempt: 3, delay_ms: 1000)

    # External service call
    event(:external_call, service: "payment_gateway", duration_ms: 250)

# `get_base`

```elixir
@spec get_base() :: any()
```

Get the base span context for propagation.

Returns the current span context that can be used to propagate tracing
information to remote services or child processes, enabling distributed tracing.

Calls callback `c:Malla.Plugins.Tracer.malla_span_get_base/0`. Default implementation returns `nil`.

## Returns

Opaque span context value (format depends on telemetry plugin), or `nil` if no span is active.

## Examples

    # Parent process
    span [:parent] do
      base = get_base()

      Task.async(fn ->
        # Child process - would restore context with set_base/1
        # (set_base/1 would be implemented by telemetry plugin)
        do_work()
      end)
    end

    # HTTP client - propagate to remote service
    span [:api_call] do
      base = get_base()
      headers = inject_trace_context(base)  # Plugin-specific
      HTTPClient.get(url, headers: headers)
    end

# `get_info`

```elixir
@spec get_info(keyword()) :: map() | nil
```

Retrieve information about the current span.

Calls callback `c:Malla.Plugins.Tracer.malla_span_info/1`. Default implementation returns `nil`.

Returns metadata about the active span context.

# `globals`

```elixir
@spec globals(data()) :: :ok
```

Set global attributes that apply to all spans in this context.

Calls callback `c:Malla.Plugins.Tracer.malla_span_globals/1`. Default implementation does nothing.

Globals are useful for deployment-wide or service-wide attributes
set once rather than on every span.

## Parameters

- `map` - Keyword list or map of global attributes

## Examples

    # Set once at service startup
    globals(deployment: "production", version: "1.2.3", datacenter: "us-east")

    span [:request] do
      # Globals automatically included in this and all nested spans
      process_request()
    end

# `info`
*macro* 

```elixir
@spec info(String.t(), data()) :: Macro.t()
```

Log an info-level message.

Info logs represent general informational messages about system operation.

## Examples

    info("Payment processed", amount: 100, currency: "USD")
    info("User logged in", user_id: 123, ip: "1.2.3.4")

See `debug/2` for detailed documentation on behavior and configuration.

# `labels`

```elixir
@spec labels(data()) :: :ok
```

Set labels on the current span.

Calls callback `c:Malla.Plugins.Tracer.malla_span_labels/1`. Default implementation does nothing.

Labels are key-value pairs that categorize spans for filtering and aggregation.

## Parameters

- `labels` - Keyword list or map of labels

## Examples

    span [:api_request] do
      labels(user_type: "premium", region: "us-east", version: "v2")
      process_request()
    end

# `level_to_logger`

```elixir
@spec level_to_logger(level_code()) :: level_name()
```

Translates a level code to its corresponding Elixir Logger level.

# `metric`

```elixir
@spec metric(id(), number() | data(), data()) :: :ok
```

Record a metric value.

Calls callback `c:Malla.Plugins.Tracer.malla_span_metric/3`. Default implementation sends the metric to `:telemetry`.

## Examples

    # Simple counter
    metric(:requests_processed, 1)

    # Duration metric with metadata
    metric(:query_duration, %{duration: 150}, %{table: "users"})

    # Multiple values
    metric(:cache_stats, hits: 10, misses: 2)

    # Within a span
    span [:payment] do
      process_payment()
      metric(:payments_successful, 1)
    end

# `name_to_level`

```elixir
@spec name_to_level(level_name() | level_code()) :: level_code()
```

Translates a level name to its corresponding numeric code.

# `notice`
*macro* 

```elixir
@spec notice(String.t(), data()) :: Macro.t()
```

Log a notice-level message.

Notice logs represent significant but normal events that are noteworthy.

## Examples

    notice("Service configuration updated", changes: ["timeout", "retries"])
    notice("Leader election completed", new_leader: node())

See `debug/2` for detailed documentation on behavior and configuration.

# `span`
*macro* 

```elixir
@spec span(
  span_id(),
  keyword()
) :: any()
```

Execute code within a span context.

This is a convenience macro equivalent to `span/3` without options.
Since there are no options, service_id must be present in process dictionary.

Calls callback `c:Malla.Plugins.Tracer.malla_span/3`. Default implementation emits telemetry events for span start and stop.

## Example

    span [:api_request] do
      handle_request(conn)
    end

# `span`
*macro* 

```elixir
@spec span(span_id(), keyword(), keyword()) :: any()
```

Execute code within a span context.

Spans represent units of work in your distributed system. They can be nested
and provide context for all logs, metrics, and events that occur during execution.

## Parameters

- `name` - Span identifier, either an atom or list of atoms (e.g., `[:payment, :process]`)
- `opts` - Keyword list of options (including optional `:service_id`)
- `block` - Code to execute within the span

## Service ID Resolution

Every span must be linked to a specific _service_id_ (see `t:Malla.id/0`),
since it will be used to call the corresponding callbacks. In order to find it,
we will try to extract key `:service_id` from _opts_, or, if not there, from
process dictionary calling `Malla.get_service_id!()`. If not found,
it will raise `Malla.ServiceIdMissing`.

Current service ID is now inserted in process dictionary calling
`Malla.put_service_id/1`.

The macro will call callback `c:Malla.Plugins.Tracer.malla_span/3`.
If not overriden, it will simply execute the code and add some telemetry events.

## Examples

    # Simple span with service_id from process dictionary
    span [:database, :query] do
      Repo.all(User)
    end

    # Span with explicit service_id
    span [:payment, :charge], service_id: PaymentService do
      charge_card(amount)
    end

    # Nested spans
    span [:parent] do
      info("Parent operation")

      span [:child] do
        info("Child operation")
      end
    end

# `span_update`

```elixir
@spec span_update(keyword()) :: :ok
```

Update attributes of the current span.

Adds or updates metadata on the active span. This is useful for adding
contextual information discovered during span execution.

Calls callback `c:Malla.Plugins.Tracer.malla_span_update/1`. Default implementation does nothing.

## Examples

    span [:api_request] do
      span_update(user_id: 123, request_id: uuid)
      process_request()
    end

    span [:database, :query] do
      span_update(table: "users", operation: "select")
      result = execute_query()
      span_update(row_count: length(result))
      result
    end

# `warning`
*macro* 

```elixir
@spec warning(String.t(), data()) :: Macro.t()
```

Log a warning-level message.

Warning logs indicate potentially problematic situations that should be investigated.

## Examples

    warning("Retry attempt 3", max_retries: 5, attempt: 3)
    warning("High memory usage", usage_mb: 1024)

See `debug/2` for detailed documentation on behavior and configuration.

---

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