defmodule OpentelemetryFunction do
@moduledoc """
Documentation for `OpentelemetryFunction`.
This package provides functions to help propagating OpenTelemetry context
across functions that are executed asynchronously.
"""
require OpenTelemetry.Tracer
@doc """
Accepts a function and wraps it in a function which propagates OpenTelemetry
context.
This function supports functions with arity up to 9.
## Example
# Before
task = Task.async(func)
Task.await(task, timeout)
# With explicit context propagation
ctx = OpenTelemetry.Ctx.get_current()
task = Task.async(fn ->
OpenTelemetry.Ctx.attach(ctx)
func.()
end)
Task.await(task, timeout)
# With this helper function
task = Task.async(OpentelemetryFunction.wrap(func))
Task.await(task, timeout)
## It is also possible to use this with MFA:
# Before
:jobs.enqueue(:tasks_queue, {mod, fun, args})
# After
wrapped_fun = OpentelemetryFunction.wrap({mod, fun, args})
:jobs.enqueue(:tasks_queue, wrapped_fun)
"""
def wrap(fun_or_mfa, span_name \\ "Function.wrap")
@spec wrap(fun, binary) :: fun
Enum.each(0..9, fn arity ->
args = for _ <- 1..arity//1, arity > 0, do: Macro.unique_var(:arg, __MODULE__)
def wrap(original_fun, span_name) when is_function(original_fun, unquote(arity)) do
outer_ctx = OpenTelemetry.Ctx.get_current()
fn unquote_splicing(args) ->
OpenTelemetry.Tracer.with_span outer_ctx, span_name, %{} do
try do
original_fun.(unquote_splicing(args))
rescue
exception ->
OpenTelemetry.Tracer.record_exception(exception, __STACKTRACE__)
OpenTelemetry.Tracer.set_status(
OpenTelemetry.status(:error, Exception.message(exception))
)
reraise(exception, __STACKTRACE__)
end
end
end
end
end)
@spec wrap({module, atom, [term]}, binary()) :: fun
def wrap({mod, fun, args}, span_name) do
outer_ctx = OpenTelemetry.Ctx.get_current()
fn ->
OpenTelemetry.Tracer.with_span outer_ctx, span_name, %{} do
try do
apply(mod, fun, args)
rescue
exception ->
OpenTelemetry.Tracer.record_exception(exception, __STACKTRACE__)
OpenTelemetry.Tracer.set_status(
OpenTelemetry.status(:error, Exception.message(exception))
)
reraise(exception, __STACKTRACE__)
end
end
end
end
end