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:
- Tracing and Instrumentation: For a guide on how to use the tracer.
Summary
Functions
Log a debug-level message.
Mark the current span as error or log an error message.
Log an error-level message.
Record a span event.
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
Functions
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 completelyAuto-Injected Metadata
:module- Current module name:line- Line number in source file
Mark the current span as error or log an error message.
This function is overloaded to support two use cases:
- Log an error message: When called with a string, logs at error level.
- 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", [])
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.
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)
@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
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.
@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
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.
@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
@spec level_to_logger(level_code()) :: level_name()
Translates a level code to its corresponding Elixir Logger level.
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
@spec name_to_level(level_name() | level_code()) :: level_code()
Translates a level name to its corresponding numeric code.
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.
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
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
@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
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.