Skip to content

Commit

Permalink
change(opentelemetry): make span name and attributes follow the stand…
Browse files Browse the repository at this point in the history
…ard spec (#10393)
  • Loading branch information
indrekj authored Nov 3, 2023
1 parent 7edf661 commit 1c3d064
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 12 deletions.
32 changes: 27 additions & 5 deletions apisix/plugins/opentelemetry.lua
Original file line number Diff line number Diff line change
Expand Up @@ -308,19 +308,36 @@ end


function _M.rewrite(conf, api_ctx)
local vars = api_ctx.var

local tracer, err = core.lrucache.plugin_ctx(lrucache, api_ctx, nil, create_tracer_obj, conf)
if not tracer then
core.log.error("failed to fetch tracer object: ", err)
return
end

-- extract trace context from the headers of downstream HTTP request
local upstream_context = trace_context_propagator:extract(context, ngx.req)
local span_name = vars.method

local attributes = {
attr.string("service", api_ctx.service_name),
attr.string("route", api_ctx.route_name),
attr.string("net.host.name", vars.host),
attr.string("http.method", vars.method),
attr.string("http.scheme", vars.scheme),
attr.string("http.target", vars.request_uri),
attr.string("http.user_agent", vars.http_user_agent),
}

if api_ctx.curr_req_matched then
table.insert(attributes, attr.string("apisix.route_id", api_ctx.route_id))
table.insert(attributes, attr.string("apisix.route_name", api_ctx.route_name))
table.insert(attributes, attr.string("http.route", api_ctx.curr_req_matched._path))
span_name = span_name .. " " .. api_ctx.curr_req_matched._path
end

if api_ctx.service_id then
table.insert(attributes, attr.string("apisix.service_id", api_ctx.service_id))
table.insert(attributes, attr.string("apisix.service_name", api_ctx.service_name))
end

if conf.additional_attributes then
inject_attributes(attributes, conf.additional_attributes, api_ctx.var, false)
end
Expand All @@ -334,7 +351,10 @@ function _M.rewrite(conf, api_ctx)
)
end

local ctx = tracer:start(upstream_context, api_ctx.var.request_uri, {
-- extract trace context from the headers of downstream HTTP request
local upstream_context = trace_context_propagator:extract(context, ngx.req)

local ctx = tracer:start(upstream_context, span_name, {
kind = span_kind.server,
attributes = attributes,
})
Expand Down Expand Up @@ -370,6 +390,8 @@ function _M.delayed_body_filter(conf, api_ctx)
"upstream response status: " .. upstream_status)
end

span:set_attributes(attr.int("http.status_code", upstream_status))

span:finish()
end
end
Expand Down
10 changes: 3 additions & 7 deletions t/plugin/opentelemetry4-bugfix-pb-state.t
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,6 @@ passed
local attributes = {}
local span = spans[1]
for _, attribute in ipairs(span.attributes) do
if attribute.key == "hostname" then
-- remove any randomness
goto skip
end
table.insert(attributes_names, attribute.key)
attributes[attribute.key] = attribute.value.string_value or ""
::skip::
Expand Down Expand Up @@ -167,9 +163,9 @@ GET /t
--- no_error_log
type 'opentelemetry.proto.trace.v1.TracesData' does not exists
--- grep_error_log eval
qr/attribute .+?:.[^,]*/
qr/attribute (apisix|x-my).+?:.[^,]*/
--- grep_error_log_out
attribute route: "route_name"
attribute service: ""
attribute apisix.route_id: "1"
attribute apisix.route_name: "route_name"
attribute x-my-header-name: "william"
attribute x-my-header-nick: "bill"
182 changes: 182 additions & 0 deletions t/plugin/opentelemetry5.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

use t::APISIX 'no_plan';
add_block_preprocessor(sub {
my ($block) = @_;

if (!$block->extra_yaml_config) {
my $extra_yaml_config = <<_EOC_;
plugins:
- opentelemetry
- proxy-rewrite
plugin_attr:
opentelemetry:
trace_id_source: x-request-id
batch_span_processor:
max_export_batch_size: 1
inactive_timeout: 0.5
collector:
address: 127.0.0.1:4318
request_timeout: 3
request_headers:
foo: bar
_EOC_
$block->set_value("extra_yaml_config", $extra_yaml_config);
}
if (!defined $block->response_body) {
$block->set_value("response_body", "passed\n");
}
$block;
});
repeat_each(1);
no_long_string();
no_root_location();
log_level("debug");

run_tests;

__DATA__
=== TEST 1: add plugin
--- config
location /t {
content_by_lua_block {
local t = require("lib.test_admin").test
local code, body = t('/apisix/admin/routes/1',
ngx.HTTP_PUT,
[[{
"name": "route-name",
"plugins": {
"opentelemetry": {
"sampler": {
"name": "always_on"
}
},
"proxy-rewrite": {"uri": "/opentracing"}
},
"upstream": {
"nodes": {
"127.0.0.1:1980": 1
},
"type": "roundrobin"
},
"uri": "/articles/*/comments"
}]]
)
if code >= 300 then
ngx.status = code
end
ngx.say(body)
}
}
--- request
GET /t
--- response_body
passed
=== TEST 2: trigger opentelemetry
--- request
GET /articles/12345/comments?foo=bar
--- more_headers
User-Agent: test-client
--- wait: 2
--- response_body
opentracing
=== TEST 3: (resource) check service.name
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/\{"key":"service.name","value":\{"stringValue":"APISIX"\}\}/
=== TEST 4: (span) check name
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/"name":"GET \/articles\/\*\/comments"/
=== TEST 5: (span) check http.status_code
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/\{"key":"http.status_code","value":\{"intValue":"200"\}\}/
=== TEST 6: (span) check http.method
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/\{"key":"http.method","value":\{"stringValue":"GET"\}\}/
=== TEST 7: (span) check http.host
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/\{"key":"net.host.name","value":\{"stringValue":"localhost"\}\}/
=== TEST 8: (span) check http.user_agent
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/\{"key":"http.user_agent","value":\{"stringValue":"test-client"\}\}/
=== TEST 9: (span) check http.target
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/\{"key":"http.target","value":\{"stringValue":"\/articles\/12345\/comments\?foo=bar"\}\}/
=== TEST 10: (span) check http.route
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/\{"key":"http.route","value":\{"stringValue":"\/articles\/\*\/comments"\}\}/
=== TEST 11: (span) check apisix.route_id
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/\{"key":"apisix.route_id","value":\{"stringValue":"1"\}\}/
=== TEST 12: (span) check apisix.route_name
--- exec
tail -n 1 ci/pod/otelcol-contrib/data-otlp.json
--- response_body eval
qr/\{"key":"apisix.route_name","value":\{"stringValue":"route-name"\}\}/

0 comments on commit 1c3d064

Please sign in to comment.