From e6711bc4570de27ac1df166520542adda48fd070 Mon Sep 17 00:00:00 2001 From: Rees Dooley Date: Wed, 13 Mar 2024 12:27:31 -0700 Subject: [PATCH] Allow multiple Inst or resource for export * Currently if 2 tracer providers or 2 tracers are exporting, there is a first one in batch wins condition for exporting spans * This change moves encoding the body into its own function, and adds unittests for spans from different tracers and from different trace providers to ensure resource and il parts of the otlp export are set properly --- lib/opentelemetry/trace/exporter/otlp.lua | 51 ++++++++++++++- spec/trace/exporter/otlp_spec.lua | 77 +++++++++++++++++++++++ 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/lib/opentelemetry/trace/exporter/otlp.lua b/lib/opentelemetry/trace/exporter/otlp.lua index 68cac86..fe3b0a0 100644 --- a/lib/opentelemetry/trace/exporter/otlp.lua +++ b/lib/opentelemetry/trace/exporter/otlp.lua @@ -74,7 +74,7 @@ local function call_collector(exporter, pb_encoded_body) return false, res_error or "unknown" end -function _M.export_spans(self, spans) +function _M.encode_spans(self, spans) assert(spans[1]) local body = { @@ -96,12 +96,57 @@ function _M.export_spans(self, spans) } } } + local tracers = {} + local providers = {} + tracers[spans[1].tracer] = 1 + providers[spans[1].tracer.provider] = 1 for _, span in ipairs(spans) do + local rs_idx = providers[span.tracer.provider] + local ils_idx = tracers[span.tracer] + if not rs_idx then + rs_idx = #body.resource_spans + 1 + ils_idx = 1 + providers[span.tracer.provider] = rs_idx + tracers[span.tracer] = ils_idx + table.insert( + body.resource_spans, + { + resource = { + attributes = span.tracer.provider.resource.attrs, + dropped_attributes_count = 0, + }, + instrumentation_library_spans = { + { + instrumentation_library = { + name = span.tracer.il.name, + version = span.tracer.il.version, + }, + spans = {} + }, + }, + }) + elseif not ils_idx then + ils_idx = #body.resource_spans[rs_idx].instrumentation_library_spans + 1 + tracers[span.tracer] = ils_idx + table.insert( + body.resource_spans[rs_idx].instrumentation_library_spans, + { + instrumentation_library = { + name = span.tracer.il.name, + version = span.tracer.il.version, + }, + spans = {} + }) + end table.insert( - body.resource_spans[1].instrumentation_library_spans[1].spans, + body.resource_spans[rs_idx].instrumentation_library_spans[ils_idx].spans, encoder.for_otlp(span)) end - return call_collector(self, pb.encode(body)) + return body +end + +function _M.export_spans(self, spans) + return call_collector(self, pb.encode(self:encode_spans(spans))) end function _M.shutdown(self) diff --git a/spec/trace/exporter/otlp_spec.lua b/spec/trace/exporter/otlp_spec.lua index 7b2f500..20ed8d6 100644 --- a/spec/trace/exporter/otlp_spec.lua +++ b/spec/trace/exporter/otlp_spec.lua @@ -2,8 +2,85 @@ local exporter = require "opentelemetry.trace.exporter.otlp" local client = require "opentelemetry.trace.exporter.http_client" local context = require "opentelemetry.context" local tp = Global.get_tracer_provider() +local tracer_provider_new = require("opentelemetry.trace.tracer_provider").new local tracer = tp:tracer("test") +describe("encode_spans", function() + it("one resource span and one ils for one span", function() + local span + local ctx = context.new() + ctx, span = tracer:start(ctx, "test span") + span:finish() + local cb = exporter.new(nil) + local encoded = cb:encode_spans({span, other_spans}) + -- One resource, one il, one span + assert(#encoded.resource_spans == 1) + local resource = encoded.resource_spans[1] + assert(#resource.instrumentation_library_spans == 1) + assert(#resource.instrumentation_library_spans[1].spans == 1) + end) + + it("one resource span and one ils for multiple span same tracer", function() + local span + local ctx = context.new() + local spans = {} + for i=10,1,-1 do + ctx, span = tracer:start(ctx, "test span" .. i) + span:finish() + table.insert(spans, span) + end + local cb = exporter.new(nil) + local encoded = cb:encode_spans(spans) + -- One resource, one il, 10 spans + assert(#encoded.resource_spans == 1) + local resource = encoded.resource_spans[1] + assert(#resource.instrumentation_library_spans == 1) + assert(#resource.instrumentation_library_spans[1].spans == 10) + end) + + it("one resource span and two ils for spans from distinct tracers", function() + local span + local ctx = context.new() + local spans = {} + ctx, span = tracer:start(ctx, "test span") + span:finish() + table.insert(spans, span) + local other_tracer = tp:tracer("exam") + ctx, other_span = other_tracer:start(ctx, "exam span") + table.insert(spans, other_span) + local cb = exporter.new(nil) + local encoded = cb:encode_spans(spans) + -- One resource, two il, 1 span each + assert(#encoded.resource_spans == 1) + local resource = encoded.resource_spans[1] + assert(#resource.instrumentation_library_spans == 2) + assert(#resource.instrumentation_library_spans[1].spans == 1) + assert(#resource.instrumentation_library_spans[2].spans == 1) + end) + it("distinct trace providers provide distinct resources", function() + local span + local ctx = context.new() + local spans = {} + ctx, span = tracer:start(ctx, "test span") + span:finish() + table.insert(spans, span) + local op = tracer_provider_new(nil, nil) + local other_tracer = op:tracer("exam") + ctx, other_span = other_tracer:start(ctx, "exam span") + table.insert(spans, other_span) + local cb = exporter.new(nil) + local encoded = cb:encode_spans(spans) + -- two resources with one il, 1 span each + assert(#encoded.resource_spans == 2) + local resource = encoded.resource_spans[1] + assert(#resource.instrumentation_library_spans == 1) + assert(#resource.instrumentation_library_spans[1].spans == 1) + resource = encoded.resource_spans[2] + assert(#resource.instrumentation_library_spans == 1) + assert(#resource.instrumentation_library_spans[1].spans == 1) + end) +end) + describe("export_spans", function() it("invokes do_request when there are no failures", function() local span