Malla.Tracer (malla v0.0.1-rc.1)

Copy Markdown View Source

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 - Creates a tracing span around the given code block.
  • span/3 - Creates a tracing span with options around the given code block.
  • debug/1 - Logs a debug-level message.
  • debug/2 - Logs a debug-level message with metadata.
  • info/1 - Logs an info-level message.
  • info/2 - Logs an info-level message with metadata.
  • event/1 - Records a tracing event.
  • event/2 - Records a tracing event with data.
  • notice/1 - Logs a notice-level message.
  • notice/2 - Logs a notice-level message with metadata.
  • warning/1 - Logs a warning-level message.
  • warning/2 - Logs a warning-level message with metadata.
  • error/1 - Logs an error-level message.
  • error/2 - Logs an error-level message with metadata.

Functions

  • span_update/1 - Updates the current tracing span with new data.
  • metric/2 - Records a metric value.
  • metric/3 - Records a metric value with metadata.
  • labels/1 - Sets labels on the current tracing span.
  • globals/1 - Sets global tracing metadata.
  • get_info/0 - Retrieves current tracing information.
  • get_info/1 - Retrieves current tracing information with options.
  • get_base/0 - Gets base tracing configuration.

For comprehensive documentation, see the guide:

Summary

Functions

Log a debug-level message.

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

Log an error-level message.

Get the base span context for propagation.

Retrieve information about the current span.

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

Log an info-level message.

Set labels on the current span.

Translates a level code to its corresponding Elixir Logger level.

Record a metric value.

Translates a level name to its corresponding numeric code.

Log a notice-level message.

Execute code within a span context.

Execute code within a span context.

Update attributes of the current span.

Log a warning-level message.

Types

data()

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

id()

@type id() :: atom()

level_code()

@type level_code() :: 0..9

level_name()

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

span_id()

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

Functions

debug(text, meta \\ [])

(macro)
@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 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(text)

(macro)
@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 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(text, meta)

(macro)
@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(type, data \\ [], meta \\ [])

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

Record a span event.

Calls callback 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()

@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 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(opts \\ [])

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

Retrieve information about the current span.

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

Returns metadata about the active span context.

globals(map)

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

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

Calls callback 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(text, meta \\ [])

(macro)
@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(labels)

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

Set labels on the current span.

Calls callback 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(name)

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

Translates a level code to its corresponding Elixir Logger level.

metric(id, data, meta \\ [])

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

Record a metric value.

Calls callback 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(level)

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

Translates a level name to its corresponding numeric code.

notice(text, meta \\ [])

(macro)
@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(name, list)

(macro)
@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 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(name, opts, list)

(macro)
@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 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 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(update)

@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 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(text, meta \\ [])

(macro)
@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.