190 lines
5.5 KiB
Lua
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,
|
|
}
|