Files
2026-07-13 12:32:21 +08:00

190 lines
5.5 KiB
Lua

local Queue = require "kong.tools.queue"
local propagation = require "kong.observability.tracing.propagation"
local tracing_context = require "kong.observability.tracing.tracing_context"
local otlp = require "kong.observability.otlp"
local otel_utils = require "kong.plugins.opentelemetry.utils"
local clone = require "table.clone"
local to_hex = require "resty.string".to_hex
local bor = require "bit".bor
local ngx = ngx
local kong = kong
local ngx_log = ngx.log
local ngx_ERR = ngx.ERR
local ngx_DEBUG = ngx.DEBUG
local http_export_request = otel_utils.http_export_request
local get_headers = otel_utils.get_headers
local _log_prefix = otel_utils._log_prefix
local encode_traces = otlp.encode_traces
local encode_span = otlp.transform_span
local function get_inject_ctx(extracted_ctx, conf)
local root_span = ngx.ctx.KONG_SPANS and ngx.ctx.KONG_SPANS[1]
-- get the global tracer when available, or instantiate a new one
local tracer = kong.tracing.name == "noop" and kong.tracing.new("otel")
or kong.tracing
-- make propagation work with tracing disabled
if not root_span then
root_span = tracer.start_span("root")
root_span:set_attribute("kong.propagation_only", true)
-- since tracing is disabled, turn off sampling entirely for this trace
kong.ctx.plugin.should_sample = false
end
local injected_parent_span = tracing_context.get_unlinked_span("balancer") or root_span
local trace_id = extracted_ctx.trace_id
local span_id = extracted_ctx.span_id
local parent_id = extracted_ctx.parent_id
local parent_sampled = extracted_ctx.should_sample
local flags = extracted_ctx.w3c_flags or extracted_ctx.flags
-- Overwrite trace ids
-- with the value extracted from incoming tracing headers
if trace_id then
-- to propagate the correct trace ID we have to set it here
-- before passing this span to propagation
injected_parent_span.trace_id = trace_id
-- update the Tracing Context with the trace ID extracted from headers
tracing_context.set_raw_trace_id(trace_id)
end
-- overwrite root span's parent_id
if span_id then
root_span.parent_id = span_id
elseif parent_id then
root_span.parent_id = parent_id
end
-- Configure the sampled flags
local sampled
if kong.ctx.plugin.should_sample == false then
sampled = false
else
-- Sampling decision for the current trace.
local err
-- get_sampling_decision() depends on the value of the trace id: call it
-- after the trace_id is updated
sampled, err = tracer:get_sampling_decision(parent_sampled, conf.sampling_rate)
if err then
ngx_log(ngx_ERR, _log_prefix, "sampler failure: ", err)
end
end
tracer:set_should_sample(sampled)
-- Set the sampled flag for the outgoing header's span
injected_parent_span.should_sample = sampled
extracted_ctx.trace_id = injected_parent_span.trace_id
extracted_ctx.span_id = injected_parent_span.span_id
extracted_ctx.should_sample = injected_parent_span.should_sample
extracted_ctx.parent_id = injected_parent_span.parent_id
flags = flags or 0x00
local sampled_flag = sampled and 1 or 0
local out_flags = bor(flags, sampled_flag)
tracing_context.set_flags(out_flags)
-- return the injected ctx (data to be injected with outgoing tracing headers)
return extracted_ctx
end
local function access(conf)
propagation.propagate(
propagation.get_plugin_params(conf),
get_inject_ctx,
conf
)
end
local function header_filter(conf)
if conf.http_response_header_for_traceid then
local trace_id = tracing_context.get_raw_trace_id()
if not trace_id then
local root_span = ngx.ctx.KONG_SPANS and ngx.ctx.KONG_SPANS[1]
trace_id = root_span and root_span.trace_id
end
if trace_id then
trace_id = to_hex(trace_id)
kong.response.add_header(conf.http_response_header_for_traceid, trace_id)
end
end
end
local function http_export_traces(params, spans)
local conf = params.conf
local resource_attributes = params.options.compiled_resource_attributes
or conf.resource_attributes
local headers = get_headers(conf.headers)
local payload = encode_traces(spans, resource_attributes)
local ok, err = http_export_request({
connect_timeout = conf.connect_timeout,
send_timeout = conf.send_timeout,
read_timeout = conf.read_timeout,
endpoint = conf.traces_endpoint,
}, payload, headers)
if ok then
ngx_log(ngx_DEBUG, _log_prefix, "exporter sent ", #spans,
" spans to ", conf.traces_endpoint)
else
ngx_log(ngx_ERR, _log_prefix, err)
end
return ok, err
end
local function log(conf, options)
ngx_log(ngx_DEBUG, _log_prefix, "total spans in current request: ", ngx.ctx.KONG_SPANS and #ngx.ctx.KONG_SPANS)
kong.tracing.process_span(function (span)
if span.should_sample == false or kong.ctx.plugin.should_sample == false then
-- ignore
return
end
-- overwrite
local trace_id = tracing_context.get_raw_trace_id()
if trace_id then
span.trace_id = trace_id
end
local params = {
conf = conf,
options = options,
}
local queue_conf = clone(Queue.get_plugin_params("opentelemetry", conf))
queue_conf.name = queue_conf.name .. ":traces"
local ok, err = Queue.enqueue(
queue_conf,
http_export_traces,
params,
encode_span(span)
)
if not ok then
kong.log.err("Failed to enqueue span to log server: ", err)
end
end)
end
return {
access = access,
header_filter = header_filter,
log = log,
}