OpenTelemetry-Lua is the lua implementation of OpenTelemetry. It provides a set of APIs to directly measure performance and behavior of your software and send this data to observability platforms.
This project currently lives in a alpha status.
git clone this project, then run luarocks make
in project root directory.
This lib is designed for Nginx+LUA/OpenResty ecosystems.
Set the nano time func used by this lib to set span start/end time.
local util = require("opentelemetry.util")
-- default
util.time_nano = util.gettimeofday
-- ngx.now
util.time_nano = util.time_nano
Set/Get the global tracer provider.
local global = require("opentelemetry.global")
-- tp is a tracer provider instance
global.set_tracer_provider(tp)
local tp2 = global.get_tracer_provider()
local tracer = global.tracer(name, opts)
Partially implement of specification: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/context.md.
-- context is stored on ngx.ctx by default, so context is passed between different request phases.
local context = require("opentelemetry.context").new()
-- Associates a Context with the caller's current execution unit, so you can use context.current() to retrieve it. Returns a token you can use to detach.
local token = context:attach()
-- Resets the Context associated with the caller's current execution unit to the value it had before attaching a specified Context. Pass the token returned when attaching context.
context:detach(token)
local cur_context = context.current()
local cur_span = context:span()
local cur_span_context = context:span_context()
local attr = require("opentelemetry.attribute")
-- string attribute
attr.string("service.name", "openresty")
attr.string_array("service.name", {"openresty"})
-- int attribute
attr.int("attr_int", 100)
attr.int_array("attr_ints", {100, 1000})
-- double attribute
attr.double("attr_double", 10.24)
attr.double_array("attr_doubles", {10.24, 20.48})
-- bool attribute
attr.bool("attr_bool", true)
attr.bool_array("attr_bools", {true, false})
Partially implement of specification: https://github.com/open-telemetry/opentelemetry-specification/blob/01e62674c0ac23076736459efd0c05316f84cd6f/specification/resource/sdk.md
local attr = require("opentelemetry.attribute")
local resource_new = require("opentelemetry.resource").new
local resource = resource_new(attr.string("service.name", "openresty"), attr.int("attr_int", 100),
attr.double("attr_double", 10.24), attr.bool("attr_bool", true))
resource:attributes()
Partially implement of specification:
- https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md
- https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md
local attr = require("opentelemetry.attribute")
local resource_new = require("opentelemetry.resource").new
local always_on_sampler = require("opentelemetry.trace.sampling.always_on_sampler").new()
local tp_new = require("opentelemetry.trace.tracer_provider").new
------------------------------------------------------------------
-- create a tracer provider.
--
-- @span_processor [optional] span_processor
-- @opts [optional] config
-- opts.sampler: opentelemetry.trace.sampling.*, default parent_base_sampler
-- opts.resource
-- @return tracer provider factory
------------------------------------------------------------------
local tp = tp_new(span_processor, {
sampler = always_on_sampler,
resource = resource_new(attr.string("service.name", "openresty"), attr.int("attr_int", 100)),
})
------------------------------------------------------------------
-- create a tracer.
--
-- @name tracer name
-- @opts [optional] config
-- opts.version: specifies the version of the instrumentation library
-- opts.schema_url: specifies the Schema URL that should be recorded in the emitted telemetry.
-- @return tracer
------------------------------------------------------------------
local tracer = tp:tracer("opentelemetry-lua", opts)
-- force all processors export spans
tp:force_flush()
-- force all processors export spans and shutdown processors
tp:shutdown()
-- append processor
tp:register_span_processor(span_processor)
-- set processors
tp:set_span_processors(span_processor1, span_processor2)
opentelemetry.trace.batch_span_processor
will store spans in a queue, and start a background timer to export spans.
local batch_span_processor_new = require("opentelemetry.trace.batch_span_processor").new
------------------------------------------------------------------
-- create a batch span processor.
--
-- @exporter opentelemetry.trace.exporter.oltp
-- @opts [optional]
-- opts.drop_on_queue_full: if true, drop span when queue is full, otherwise force process batches, default true
-- opts.max_queue_size: maximum queue size to buffer spans for delayed processing, default 2048
-- opts.batch_timeout: maximum duration for constructing a batch, default 5s
-- opts.inactive_timeout: maximum duration for processing batches, default 2s
-- opts.max_export_batch_size: maximum number of spans to process in a single batch, default 256
-- @return processor
------------------------------------------------------------------
local batch_span_processor = batch_span_processor_new(exporter, {
drop_on_queue_full = false, max_queue_size = 1024, batch_timeout = 3, inactive_timeout = 1, max_export_batch_size = 10
})
Send spans to opentelemetry collector in protobuf format.
local otlp_exporter_new = require("opentelemetry.trace.exporter.otlp").new
local http_client_new = require("opentelemetry.trace.exporter.http_client").new
------------------------------------------------------------------
-- create a http client used by exporter.
--
-- @address opentelemetry collector: host:port
-- @timeout export request timeout
-- @headers export request headers
-- @return http client
------------------------------------------------------------------
local client = http_client_new("127.0.0.1:4317", 3, {header_key = "header_val"})
local exporter = otlp_exporter_new(client)
-- sampling all spans
local always_on_sampler = require("opentelemetry.trace.sampling.always_on_sampler").new()
-- sampling non spans
local always_off_sampler = require("opentelemetry.trace.sampling.always_off_sampler").new()
------------------------------------------------------------------
-- a composite sampler which behaves differently,
-- based on the parent of the span. If the span has no parent,
-- the root(Sampler) is used to make sampling decision. If the span has
-- a parent, depending on whether the parent is sampled.
--
-- @root sampler
-- @return sampler
------------------------------------------------------------------
local parent_base_sampler = require("opentelemetry.trace.sampling.parent_base_sampler").new(always_on_sampler)
------------------------------------------------------------------
-- samples a given fraction of traces. Fractions >= 1 will
-- always sample. Fractions < 0 are treated as zero. To respect the
-- parent trace's sampled_flag, the trace_id_ratio_based sampler should be used
-- as a delegate of a parent base sampler.
--
-- @return sampler
------------------------------------------------------------------
local trace_id_ratio_sampler = require("opentelemetry.trace.sampling.trace_id_ratio_sampler").new(0.5)
local span_kind = require("opentelemetry.trace.span_kind")
local attr = require("opentelemetry.attribute")
------------------------------------------------------------------
-- create a span.
--
-- @span_ctx context with parent span
-- @span_name span name
-- @span_start_config [optional]
-- span_start_config.kind: opentelemetry.trace.span_kind.*
-- span_start_config.attributes: a list of attribute
-- @return
-- context: new context with span
-- span
------------------------------------------------------------------
local context, span = tracer:start(context, name, {kind = span_kind.server, attributes = {attr.string("user", "foo")}})
Implement of specification: https://www.w3.org/TR/trace-context/
local context = require("opentelemetry.context").new()
local trace_context_propagator = require("opentelemetry.trace.propagation.text_map.trace_context_propagator").new()
------------------------------------------------------------------
-- extract span context from upstream request.
--
-- @context current context
-- @carrier get traceparent and tracestate
-- @return new context
------------------------------------------------------------------
local context = trace_context_propagator.extract(context, ngx.req)
local span_kind = require("opentelemetry.trace.span_kind")
local attr = require("opentelemetry.attribute")
local span_status = require("opentelemetry.trace.span_status")
local context, span = tracer:start(context, name, {kind = span_kind.server, attributes = {attr.string("user", "foo")}})
-- get span context
local span_context = span:context()
-- is recording
local ok = span:is_recording()
------------------------------------------------------------------
-- set span status.
--
-- @code see opentelemetry.trace.span_status.*
-- @message error msg
------------------------------------------------------------------
span:set_status(span_status.ERROR, "error msg")
-- set attributes
span:set_attributes(attr.string("comapny", "bar"))
-- add a error event
span:record_error("error msg")
------------------------------------------------------------------
-- add a custom event
--
-- @opts [optional]
-- opts.attributes: a list of attribute
------------------------------------------------------------------
span:add_event(name, {attributes = {attr.string("type", "job")}})
-- get tracer provider
local tp = span:tracer_provider()
To gather metrics about the operation of this library, users can register a metrics reporter with the opentelemetry.global
module. Your metrics reporter should satisfy the interface in lib/opentelemetry/metrics_reporter.lua
. For example:
local otel_global = require("opentelemetry.global")
local my_metrics_reporter = {
add_to_counter = function(self, metric, increment, labels)
print("make a metric call!")
end
record_value = function(self, metric, increment, labels)
print("make a metric call!")
end
observe_value = function(self, metric, increment, labels)
print("make a metric call!")
end
}
otel_global.set_metrics_reporter(metrics_reporter)
You can run benchmarks using make benchmark
.