Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change(opentelemetry): make span name and attributes follow the standard spec #10393

Merged
merged 2 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"\}\}/
Loading