Current section
Files
Jump to
Current section
Files
lib/core/tracing.ex
defmodule Polyn.Tracing do
# Functions to enable distributed tracing across services
# Attempts to follow OpenTelemetry conventions
# https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/messaging/
@moduledoc false
# Ensures calling module can access Tracer library
defmacro __using__(_opts) do
quote do
require OpenTelemetry.Tracer
end
end
@doc """
Start a span for publishing an event
"""
defmacro publish_span(type, do: block) do
block = record_exceptions(block)
quote do
OpenTelemetry.Tracer.with_span("#{unquote(type)} send", %{kind: "PRODUCER"},
do: unquote(block)
)
end
end
@doc """
Start a span for handling a received message for an event
"""
defmacro subscribe_span(type, headers, do: block) do
block = record_exceptions(block)
quote do
# Extract a `traceparent` header from a message so it can connect the current span to a remote span
# https://www.w3.org/TR/trace-context/#traceparent-header
if unquote(headers) do
:otel_propagator_text_map.extract(unquote(headers))
end
OpenTelemetry.Tracer.with_span("#{unquote(type)} receive", %{kind: "CONSUMER"},
do: unquote(block)
)
end
end
@doc """
Start a span to handle processing of batch messages
"""
defmacro processing_span(type, do: block) do
block = record_exceptions(block)
quote do
OpenTelemetry.Tracer.with_span("#{unquote(type)} process", %{kind: "CONSUMER"},
do: unquote(block)
)
end
end
@doc """
Common attributes to add to a span involving an individual message
https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/messaging/#messaging-attributesADd common at
"""
defmacro span_attributes(conn: conn, type: type, event: event, payload: payload) do
quote do
OpenTelemetry.Tracer.set_attributes(%{
"messaging.system" => "NATS",
"messaging.destination" => unquote(type),
"messaging.protocol" => "Polyn",
"messaging.url" => Gnat.server_info(unquote(conn)).client_ip,
"messaging.message_id" => unquote(event).id,
"messaging.message_payload_size_bytes" => byte_size(unquote(payload))
})
end
end
@doc """
Add a `traceparent` header to the headers for a message so that the
subscribers can be connected with it
https://www.w3.org/TR/trace-context/#traceparent-header
"""
def add_trace_header(headers) do
:otel_propagator_text_map.inject(headers)
end
@doc """
Link one span to another span
"""
def link_to_context(ctx) do
OpenTelemetry.link(ctx)
end
@doc """
Get the current span context
"""
def current_context do
OpenTelemetry.Ctx.get_current()
end
# Any errors that happen, expecially validation errors, we want the span to record so observability tools
# will show the error
defp record_exceptions(block) do
quote do
try do
unquote(block)
rescue
e ->
OpenTelemetry.Tracer.record_exception(e, __STACKTRACE__)
reraise e, __STACKTRACE__
end
end
end
@doc """
Add non-raised timeout exceptions to the current span
"""
def record_timeout_exception(event_type, json) do
OpenTelemetry.Tracer.record_exception(%RuntimeError{
message: "request for #{event_type} timeout. payload: \n#{json}"
})
end
end