项目文件夹

文件
2026-07-13 12:32:21 +08:00

906 行
27 KiB
Lua

local helpers = require "spec.helpers"
local cjson = require "cjson"
local pretty = require "pl.pretty"
local to_hex = require "resty.string".to_hex
local get_rand_bytes = require("kong.tools.rand").get_rand_bytes
local fmt = string.format
local TCP_PORT = 35001
local http_route_host = "http-route"
local http_route_ignore_host = "http-route-ignore"
local http_route_w3c_host = "http-route-w3c"
local http_route_dd_host = "http-route-dd"
local http_route_b3_single_host = "http-route-b3-single"
local http_route_ins_host = "http-route-ins"
local http_route_clear_host = "http-clear-route"
local http_route_no_preserve_host = "http-no-preserve-route"
local function gen_trace_id()
return to_hex(get_rand_bytes(16))
end
local function gen_span_id()
return to_hex(get_rand_bytes(8))
end
local function get_span(name, spans)
for _, span in ipairs(spans) do
if span.name == name then
return span
end
end
end
local function assert_has_span(name, spans)
local span = get_span(name, spans)
assert.is_truthy(span, fmt("\nExpected to find %q span in:\n%s\n",
name, pretty.write(spans)))
return span
end
local function get_span_by_id(spans, id)
for _, span in ipairs(spans) do
if span.span_id == id then
return span
end
end
end
local function assert_correct_trace_hierarchy(spans, incoming_span_id)
for _, span in ipairs(spans) do
if span.name == "kong" then
-- if there is an incoming span id, it should be the parent of the root span
if incoming_span_id then
assert.equals(incoming_span_id, span.parent_id)
end
else
-- all other spans in this trace should have a local span as parent
assert.not_equals(incoming_span_id, span.parent_id)
assert.is_truthy(get_span_by_id(spans, span.parent_id))
end
end
end
local function setup_otel_old_propagation(bp, service)
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_host },
}).id},
config = {
-- fake endpoint, request to backend will sliently fail
traces_endpoint = "http://localhost:8080/v1/traces",
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_ignore_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
header_type = "ignore",
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_w3c_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
header_type = "w3c",
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_dd_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
header_type = "datadog",
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_b3_single_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
header_type = "b3-single",
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_ins_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
header_type = "instana",
}
})
end
-- same configurations as "setup_otel_old_propagation", using the new
-- propagation configuration fields
local function setup_otel_new_propagation(bp, service)
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
propagation = {
extract = { "b3", "w3c", "jaeger", "ot", "datadog", "aws", "gcp", "instana" },
inject = { "preserve" },
default_format = "w3c",
}
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_ignore_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
propagation = {
extract = { },
inject = { "preserve" },
default_format = "w3c",
}
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_w3c_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
propagation = {
extract = { "b3", "w3c", "jaeger", "ot", "datadog", "aws", "gcp", "instana" },
inject = { "preserve", "w3c" },
default_format = "w3c",
}
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_dd_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
propagation = {
extract = { "b3", "w3c", "jaeger", "ot", "datadog", "aws", "gcp", "instana" },
inject = { "preserve", "datadog" },
default_format = "datadog",
}
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_b3_single_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
propagation = {
extract = { "b3", "w3c", "jaeger", "ot", "datadog", "aws", "gcp", "instana" },
inject = { "preserve", "b3-single" },
default_format = "w3c",
}
}
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_ins_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
propagation = {
extract = { "b3", "w3c", "jaeger", "ot", "datadog", "aws", "gcp", "instana" },
inject = { "preserve", "instana" },
default_format = "instana",
}
}
})
-- available with new configuration only:
-- no preserve
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_no_preserve_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
-- old configuration ignored when new propagation configuration is provided
header_type = "preserve",
propagation = {
extract = { "b3" },
inject = { "w3c" },
default_format = "w3c",
}
}
})
-- clear
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { http_route_clear_host },
}).id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
propagation = {
extract = { "w3c", "ot" },
inject = { "preserve" },
clear = {
"ot-tracer-traceid",
"ot-tracer-spanid",
"ot-tracer-sampled",
},
default_format = "b3",
}
}
})
end
for _, strategy in helpers.each_strategy() do
for _, instrumentations in ipairs({"all", "off"}) do
for _, sampling_rate in ipairs({1, 0}) do
for _, propagation_config in ipairs({"old", "new"}) do
describe("propagation tests #" .. strategy ..
" instrumentations: #" .. instrumentations ..
" sampling_rate: " .. sampling_rate ..
" propagation config: #" .. propagation_config, function()
local service
local proxy_client
local sampled_flag_w3c
local sampled_flag_b3
if instrumentations == "all" and sampling_rate == 1 then
sampled_flag_w3c = "01"
sampled_flag_b3 = "1"
else
sampled_flag_w3c = "00"
sampled_flag_b3 = "0"
end
lazy_setup(function()
local bp = helpers.get_db_utils(strategy, { "services", "routes", "plugins" })
service = bp.services:insert()
if propagation_config == "old" then
setup_otel_old_propagation(bp, service)
else
setup_otel_new_propagation(bp, service)
end
helpers.start_kong({
database = strategy,
plugins = "bundled",
nginx_conf = "spec/fixtures/custom_nginx.template",
tracing_instrumentations = instrumentations,
tracing_sampling_rate = sampling_rate,
})
proxy_client = helpers.proxy_client()
end)
teardown(function()
helpers.stop_kong()
end)
it("default propagation headers (w3c)", function()
local r = proxy_client:get("/", {
headers = {
host = http_route_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.matches("00%-%x+-%x+-" .. sampled_flag_w3c, json.headers.traceparent)
end)
it("propagates tracing headers (b3 request)", function()
local trace_id = gen_trace_id()
local r = proxy_client:get("/", {
headers = {
["x-b3-sampled"] = "1",
["x-b3-traceid"] = trace_id,
host = http_route_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.equals(trace_id, json.headers["x-b3-traceid"])
end)
describe("propagates tracing headers (b3-single request)", function()
it("with parent_id", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
b3 = fmt("%s-%s-%s-%s", trace_id, span_id, "1", parent_id),
host = http_route_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.matches(trace_id .. "%-%x+%-" .. sampled_flag_b3 .. "%-%x+", json.headers.b3)
end)
it("without parent_id", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
b3 = fmt("%s-%s-1", trace_id, span_id),
host = http_route_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.matches(trace_id .. "%-%x+%-" .. sampled_flag_b3, json.headers.b3)
end)
it("reflects the disabled sampled flag of the incoming tracing header", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
b3 = fmt("%s-%s-0", trace_id, span_id),
host = http_route_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
-- incoming header has sampled=0: always disabled by
-- parent-based sampler
assert.matches(trace_id .. "%-%x+%-0", json.headers.b3)
end)
end)
it("propagates w3c tracing headers", function()
local trace_id = gen_trace_id() -- w3c only admits 16-byte trace_ids
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
traceparent = fmt("00-%s-%s-01", trace_id, parent_id),
host = http_route_host
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.matches("00%-" .. trace_id .. "%-%x+-" .. sampled_flag_w3c, json.headers.traceparent)
end)
it("defaults to w3c without propagating when header_type set to ignore and w3c headers sent", function()
local trace_id = gen_trace_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
traceparent = fmt("00-%s-%s-01", trace_id, parent_id),
host = http_route_ignore_host
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.is_not_nil(json.headers.traceparent)
-- incoming trace id is ignored
assert.not_matches("00%-" .. trace_id .. "%-%x+-" .. sampled_flag_w3c, json.headers.traceparent)
end)
it("defaults to w3c without propagating when header_type set to ignore and b3 headers sent", function()
local trace_id = gen_trace_id()
local r = proxy_client:get("/", {
headers = {
["x-b3-sampled"] = "1",
["x-b3-traceid"] = trace_id,
host = http_route_ignore_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.is_not_nil(json.headers.traceparent)
-- incoming trace id is ignored
assert.not_matches("00%-" .. trace_id .. "%-%x+-" .. sampled_flag_w3c, json.headers.traceparent)
end)
it("propagates w3c tracing headers when header_type set to w3c", function()
local trace_id = gen_trace_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
traceparent = fmt("00-%s-%s-01", trace_id, parent_id),
host = http_route_w3c_host
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.matches("00%-" .. trace_id .. "%-%x+-" .. sampled_flag_w3c, json.headers.traceparent)
end)
it("propagates w3c tracing headers + incoming format (preserve + w3c)", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
b3 = fmt("%s-%s-%s-%s", trace_id, span_id, sampled_flag_b3, parent_id),
host = http_route_w3c_host
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.matches("00%-" .. trace_id .. "%-%x+-" .. sampled_flag_w3c, json.headers.traceparent)
assert.not_equals(fmt("%s-%s-%s-%s", trace_id, span_id, sampled_flag_b3, parent_id), json.headers.b3)
assert.matches(trace_id .. "%-%x+%-" .. sampled_flag_b3 .. "%-%x+", json.headers.b3)
-- if no instrumentation is enabled no new spans are created so the
-- incoming span is the parent of the outgoing span
if instrumentations == "off" then
assert.matches(trace_id .. "%-%x+%-" .. sampled_flag_b3 .. "%-" .. span_id, json.headers.b3)
end
end)
it("propagates b3-single tracing headers when header_type set to b3-single", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
b3 = fmt("%s-%s-%s-%s", trace_id, span_id, sampled_flag_b3, parent_id),
host = http_route_b3_single_host
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.not_equals(fmt("%s-%s-%s-%s", trace_id, span_id, sampled_flag_b3, parent_id), json.headers.b3)
assert.matches(trace_id .. "%-%x+%-" .. sampled_flag_b3 .. "%-%x+", json.headers.b3)
end)
it("propagates jaeger tracing headers", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
["uber-trace-id"] = fmt("%s:%s:%s:%s", trace_id, span_id, parent_id, "1"),
host = http_route_host
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
-- Trace ID is left padded with 0 for assert
assert.matches( ('0'):rep(32-#trace_id) .. trace_id .. ":%x+:%x+:" .. sampled_flag_w3c, json.headers["uber-trace-id"])
end)
it("propagates ot headers", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
["ot-tracer-traceid"] = trace_id,
["ot-tracer-spanid"] = span_id,
["ot-tracer-sampled"] = "1",
host = http_route_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.equals(trace_id, json.headers["ot-tracer-traceid"])
end)
it("propagates instana headers", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
["x-instana-t"] = trace_id,
["x-instana-s"] = span_id,
host = http_route_ins_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.equals(trace_id, json.headers["x-instana-t"])
end)
describe("propagates datadog tracing headers", function()
it("with datadog headers in client request", function()
local trace_id = "1234567890"
local r = proxy_client:get("/", {
headers = {
["x-datadog-trace-id"] = trace_id,
host = http_route_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.equals(trace_id, json.headers["x-datadog-trace-id"])
assert.is_not_nil(tonumber(json.headers["x-datadog-parent-id"]))
end)
it("without datadog headers in client request", function()
local r = proxy_client:get("/", {
headers = { host = http_route_dd_host },
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.is_not_nil(tonumber(json.headers["x-datadog-trace-id"]))
assert.is_not_nil(tonumber(json.headers["x-datadog-parent-id"]))
end)
end)
it("propagate spwaned span with ot headers", function()
local r = proxy_client:get("/", {
headers = {
host = http_route_host,
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
local traceparent = json.headers["traceparent"]
local m = assert(ngx.re.match(traceparent, [[00\-([0-9a-f]+)\-([0-9a-f]+)\-([0-9a-f]+)]]))
assert.same(32, #m[1])
assert.same(16, #m[2])
assert.same(sampled_flag_w3c, m[3])
end)
if propagation_config == "new" then
it("clears non-propagated headers when configured to do so", function()
local trace_id = gen_trace_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
traceparent = fmt("00-%s-%s-01", trace_id, parent_id),
["ot-tracer-traceid"] = trace_id,
["ot-tracer-spanid"] = parent_id,
["ot-tracer-sampled"] = "1",
host = http_route_clear_host
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.matches("00%-" .. trace_id .. "%-%x+-" .. sampled_flag_w3c, json.headers.traceparent)
assert.is_nil(json.headers["ot-tracer-traceid"])
assert.is_nil(json.headers["ot-tracer-spanid"])
assert.is_nil(json.headers["ot-tracer-sampled"])
end)
it("does not preserve incoming header type if preserve is not specified", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
b3 = fmt("%s-%s-%s-%s", trace_id, span_id, sampled_flag_b3, parent_id),
host = http_route_no_preserve_host
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
-- b3 was not injected, only preserved as incoming
assert.equals(fmt("%s-%s-%s-%s", trace_id, span_id, sampled_flag_b3, parent_id), json.headers.b3)
-- w3c was injected
assert.matches("00%-" .. trace_id .. "%-%x+-" .. sampled_flag_w3c, json.headers.traceparent)
end)
end
end)
end
for _, sampling_rate in ipairs({1, 0, 0.5}) do
describe("propagation tests #" .. strategy .. " instrumentations: " .. instrumentations .. " dynamic sampling_rate: " .. sampling_rate, function()
local service
local proxy_client
lazy_setup(function()
local bp = helpers.get_db_utils(strategy, { "services", "routes", "plugins" })
service = bp.services:insert()
bp.plugins:insert({
name = "opentelemetry",
route = {id = bp.routes:insert({
service = service,
hosts = { "http-route" },
}).id},
config = {
-- fake endpoint, request to backend will sliently fail
traces_endpoint = "http://localhost:8080/v1/traces",
sampling_rate = sampling_rate,
}
})
helpers.start_kong({
database = strategy,
plugins = "bundled",
nginx_conf = "spec/fixtures/custom_nginx.template",
tracing_instrumentations = instrumentations,
})
proxy_client = helpers.proxy_client()
end)
teardown(function()
helpers.stop_kong()
end)
it("propagates tracing headers (b3 request)", function()
local trace_id = gen_trace_id()
local r = proxy_client:get("/", {
headers = {
["x-b3-sampled"] = "1",
["x-b3-traceid"] = trace_id,
host = "http-route",
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.equals(trace_id, json.headers["x-b3-traceid"])
end)
describe("propagates tracing headers (b3-single request)", function()
it("with parent_id", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
b3 = fmt("%s-%s-%s-%s", trace_id, span_id, "1", parent_id),
host = "http-route",
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.matches(trace_id .. "%-%x+%-%x+%-%x+", json.headers.b3)
end)
end)
it("propagates w3c tracing headers", function()
local trace_id = gen_trace_id() -- w3c only admits 16-byte trace_ids
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
traceparent = fmt("00-%s-%s-01", trace_id, parent_id),
host = "http-route"
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.matches("00%-" .. trace_id .. "%-%x+%-%x+", json.headers.traceparent)
end)
it("propagates jaeger tracing headers", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local parent_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
["uber-trace-id"] = fmt("%s:%s:%s:%s", trace_id, span_id, parent_id, "1"),
host = "http-route"
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
-- Trace ID is left padded with 0 for assert
assert.matches( ('0'):rep(32-#trace_id) .. trace_id .. ":%x+:%x+:%x+", json.headers["uber-trace-id"])
end)
it("propagates ot headers", function()
local trace_id = gen_trace_id()
local span_id = gen_span_id()
local r = proxy_client:get("/", {
headers = {
["ot-tracer-traceid"] = trace_id,
["ot-tracer-spanid"] = span_id,
["ot-tracer-sampled"] = "1",
host = "http-route",
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.equals(trace_id, json.headers["ot-tracer-traceid"])
end)
describe("propagates datadog tracing headers", function()
it("with datadog headers in client request", function()
local trace_id = "7532726115487256575"
local r = proxy_client:get("/", {
headers = {
["x-datadog-trace-id"] = trace_id,
host = "http-route",
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.equals(trace_id, json.headers["x-datadog-trace-id"])
assert.is_not_nil(tonumber(json.headers["x-datadog-parent-id"]))
end)
it("with a shorter-than-64b trace_id", function()
local trace_id = "1234567890"
local r = proxy_client:get("/", {
headers = {
["x-datadog-trace-id"] = trace_id,
host = "http-route",
},
})
local body = assert.response(r).has.status(200)
local json = cjson.decode(body)
assert.equals(trace_id, json.headers["x-datadog-trace-id"])
assert.is_not_nil(tonumber(json.headers["x-datadog-parent-id"]))
end)
end)
end)
end
end
end
for _, instrumentation in ipairs({ "request", "request,balancer", "all" }) do
describe("propagation tests with enabled " .. instrumentation .. " instrumentation #" .. strategy, function()
local service, route
local proxy_client
lazy_setup(function()
local bp = helpers.get_db_utils(strategy, { "services", "routes", "plugins" }, { "tcp-trace-exporter" })
service = bp.services:insert()
route = bp.routes:insert({
service = service,
hosts = { "http-route" },
})
bp.plugins:insert({
name = "opentelemetry",
route = {id = route.id},
config = {
traces_endpoint = "http://localhost:8080/v1/traces",
}
})
bp.plugins:insert({
name = "tcp-trace-exporter",
config = {
host = "127.0.0.1",
port = TCP_PORT,
custom_spans = false,
}
})
helpers.start_kong({
database = strategy,
plugins = "bundled,tcp-trace-exporter",
nginx_conf = "spec/fixtures/custom_nginx.template",
tracing_instrumentations = instrumentation,
tracing_sampling_rate = 1,
})
proxy_client = helpers.proxy_client()
end)
teardown(function()
helpers.stop_kong()
end)
it("sets the outgoint parent span's ID correctly (issue #11294)", function()
local trace_id = gen_trace_id()
local incoming_span_id = gen_span_id()
local thread = helpers.tcp_server(TCP_PORT)
local r = proxy_client:get("/", {
headers = {
traceparent = fmt("00-%s-%s-01", trace_id, incoming_span_id),
host = "http-route"
},
})
local body = assert.response(r).has.status(200)
local _, res = thread:join()
assert.is_string(res)
local spans = cjson.decode(res)
local parent_span
if instrumentation == "request" then
-- balancer instrumentation is not enabled,
-- the outgoing parent span is the root span
parent_span = assert_has_span("kong", spans)
else
-- balancer instrumentation is enabled,
-- the outgoing parent span is the balancer span
parent_span = assert_has_span("kong.balancer", spans)
end
local json = cjson.decode(body)
assert.matches("00%-" .. trace_id .. "%-" .. parent_span.span_id .. "%-01", json.headers.traceparent)
assert_correct_trace_hierarchy(spans, incoming_span_id)
end)
it("disables sampling when incoming header has sampled disabled", function()
local trace_id = gen_trace_id()
local incoming_span_id = gen_span_id()
local thread = helpers.tcp_server(TCP_PORT)
local r = proxy_client:get("/", {
headers = {
traceparent = fmt("00-%s-%s-00", trace_id, incoming_span_id),
host = "http-route"
},
})
assert.response(r).has.status(200)
local _, res = thread:join()
assert.is_string(res)
local spans = cjson.decode(res)
assert.equals(0, #spans, res)
end)
end)
end
end