Skip to content

Commit 4aca4c1

Browse files
authored
chore: default variables IDs to 128-bit (#180)
1 parent c948864 commit 4aca4c1

8 files changed

+138
-58
lines changed

doc/API.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -443,24 +443,26 @@ The Datadog nginx module defines additional variables that provide information
443443
about the currently active trace.
444444

445445
### `datadog_trace_id`
446-
`$datadog_trace_id` expands to the decimal representation of the unsigned
447-
64-bit ID of the currently active trace. If there is no currently active
446+
`$datadog_trace_id` expands to the hexadecimal representation of the unsigned
447+
128-bit ID of the currently active trace. If there is no currently active
448448
trace, then the variable expands to a hyphen character (`-`) instead.
449449

450450
### `datadog_span_id`
451-
`$datadog_span_id` expands to the decimal representation of the unsigned 64-bit
451+
`$datadog_span_id` expands to the hexadecimal representation of the unsigned 128-bit
452452
ID of the currently active span.If there is no currently active span, then
453453
the variable expands to a hyphen character (`-`) instead.
454454

455455
Note that if `datadog_trace_locations` is `on`, then `$datadog_span_id` will
456456
refer to the span associated with the location (outbound request), not its
457457
parent (inbound request).
458458

459-
### `datadog_trace_id_hex`
460-
Same as `$datadog_trace_id`, but is the hexadecimal representation of the 128-bit ID.
459+
### `datadog_trace_id_64bits_base10`
460+
This reflects the legacy behavior prior to `v1.6.0`, where `$datadog_trace_id` evaluated to the decimal representation of the 64-bit trace ID.
461+
If there is no currently active span, then the variable expands to a hyphen character (`-`) instead.
461462

462-
### `datadog_span_id_hex`
463-
Same as `$datadog_span_id`, but is the hexadecimal representation.
463+
### `datadog_span_id_64bits_base10`
464+
This reflects the legacy behavior prior to `v1.6.0`, where `$datadog_span_id` evaluated to the decimal representation of the 64-bit span ID.
465+
If there is no currently active span, then the variable expands to a hyphen character (`-`) instead.
464466

465467
### `datadog_location`
466468
`$datadog_location` expands to the name or pattern of the `location` block that

src/datadog_variable.cpp

-3
Original file line numberDiff line numberDiff line change
@@ -274,9 +274,6 @@ ngx_int_t add_variables(ngx_conf_t* cf) noexcept {
274274
variable->get_handler = expand_location_variable;
275275
variable->data = 0;
276276

277-
ngx_log_error(NGX_LOG_WARN, cf->log, 0,
278-
"In the next release, $datadog_trace_id and $datadog_span_id "
279-
"will return their values in hexadecimal format.");
280277
return NGX_OK;
281278
}
282279
} // namespace nginx

src/tracing_library.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -147,17 +147,17 @@ class SpanContextJSONWriter : public dd::DictWriter {
147147
std::string span_property(std::string_view key, const dd::Span &span) {
148148
const auto not_found = "-";
149149

150-
if (key == "trace_id_hex") {
150+
if (key == "trace_id_hex" || key == "trace_id") {
151151
return span.trace_id().hex_padded();
152-
} else if (key == "span_id_hex") {
152+
} else if (key == "span_id_hex" || key == "span_id") {
153153
char buffer[17];
154154
int written =
155155
std::snprintf(buffer, sizeof(buffer), "%016" PRIx64, span.id());
156156
assert(written == 16);
157157
return {buffer, static_cast<size_t>(written)};
158-
} else if (key == "trace_id") {
158+
} else if (key == "trace_id_64bits_base10") {
159159
return std::to_string(span.trace_id().low);
160-
} else if (key == "span_id") {
160+
} else if (key == "span_id_64bits_base10") {
161161
return std::to_string(span.id());
162162
} else if (key == "json") {
163163
SpanContextJSONWriter writer;

test/cases/variables/conf/in_access_log_format.conf

+7-2
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,17 @@ events {
1010
http {
1111
datadog_agent_url http://agent:8126;
1212

13-
log_format wild_and_crazy escape=none "here is your access record: [$datadog_trace_id, $datadog_span_id, $datadog_json, \"$datadog_location\"]";
13+
# Enforce tracecontext propagation style to facilitate 128-bit IDs comparison.
14+
# Since, Datadog propagation context set the higher bytes in `x-datadog-trace-id` and the lower part
15+
# in `_dd.p.tid`.
16+
datadog_propagation_styles "tracecontext";
17+
18+
log_format wild_and_crazy_128bits escape=none "here is your access record: [\"$datadog_trace_id\", \"$datadog_span_id\", $datadog_json, \"$datadog_location\"]";
1419

1520
server {
1621
listen 80;
1722

18-
access_log /dev/stdout wild_and_crazy;
23+
access_log /dev/stdout wild_and_crazy_128bits;
1924

2025
location ~ /https?[0-9]* {
2126
proxy_pass http://http:8080;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# "/datadog-tests" is a directory created by the docker build
2+
# of the nginx test image. It contains the module, the
3+
# nginx config, and "index.html".
4+
load_module /datadog-tests/ngx_http_datadog_module.so;
5+
6+
events {
7+
worker_connections 1024;
8+
}
9+
10+
http {
11+
datadog_agent_url http://agent:8126;
12+
13+
log_format wild_and_crazy_64bits escape=none "here is your access record: [\"$datadog_trace_id_64bits_base10\", \"$datadog_span_id_64bits_base10\", $datadog_json, \"$datadog_location\"]";
14+
15+
server {
16+
listen 80;
17+
18+
access_log /dev/stdout wild_and_crazy_64bits;
19+
20+
location ~ /https?[0-9]* {
21+
proxy_pass http://http:8080;
22+
}
23+
24+
location /sync {
25+
# access log in the default format (so we can use it to sync)
26+
access_log /dev/stdout;
27+
return 200;
28+
}
29+
}
30+
}

test/cases/variables/conf/in_request_headers.conf

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,15 @@ events {
1010
http {
1111
datadog_agent_url http://agent:8126;
1212

13+
# Enforce tracecontext propagation style
14+
# because Datadog 128-bit trace ID is the composition of the header + `_dd.p.tid`
15+
datadog_propagation_styles "tracecontext";
16+
1317
server {
1418
listen 80;
1519

1620
location ~ /https?[0-9]* {
17-
proxy_set_header x-datadog-test-thingy "[$datadog_trace_id, $datadog_span_id, $datadog_json, \"$datadog_location\"]";
21+
proxy_set_header x-datadog-test-thingy "[\"$datadog_trace_id\", \"$datadog_span_id\", $datadog_json, \"$datadog_location\"]";
1822
proxy_pass http://http:8080;
1923
}
2024
}

test/cases/variables/conf/which_span_id_in_access_log_format.conf

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ events {
1010
http {
1111
datadog_agent_url http://agent:8126;
1212

13-
log_format wild_and_crazy escape=none "here is your span ID: $datadog_span_id";
13+
log_format wild_and_crazy escape=none "here is your span ID: \"$datadog_span_id\"";
1414

1515
server {
1616
listen 80;

test/cases/variables/test_variables.py

+82-40
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
class TestVariables(case.TestCase):
99

1010
def test_in_access_log_format(self):
11-
conf_path = Path(__file__).parent / './conf/in_access_log_format.conf'
11+
conf_path = Path(__file__).parent / "./conf/in_access_log_format.conf"
1212
conf_text = conf_path.read_text()
1313

1414
status, log_lines = self.orch.nginx_replace_config(
@@ -18,16 +18,17 @@ def test_in_access_log_format(self):
1818
# Drain any old nginx log lines.
1919
self.orch.sync_nginx_access_log()
2020

21-
status, _, body = self.orch.send_nginx_http_request('/http')
21+
status, _, body = self.orch.send_nginx_http_request("/http")
2222
self.assertEqual(200, status, body)
2323
response = json.loads(body)
24-
headers = response['headers']
25-
trace_id, span_id = int(headers['x-datadog-trace-id']), int(
26-
headers['x-datadog-parent-id'])
24+
headers = response["headers"]
25+
traceparent = headers["traceparent"]
26+
assert traceparent
27+
_, trace_id, span_id, _ = traceparent.split("-")
2728

2829
log_lines = self.orch.sync_nginx_access_log()
2930
num_matching_lines = 0
30-
prefix = 'here is your access record: '
31+
prefix = "here is your access record: "
3132
for line in log_lines:
3233
if not line.startswith(prefix):
3334
continue
@@ -37,36 +38,73 @@ def test_in_access_log_format(self):
3738
self.assertEqual(trace_id, log_trace_id, line)
3839
self.assertEqual(span_id, log_span_id, line)
3940
self.assertEqual(dict, type(propagation))
40-
self.assertEqual('/https?[0-9]*', location)
41+
self.assertEqual("/https?[0-9]*", location)
42+
43+
self.assertEqual(1, num_matching_lines)
44+
45+
def test_in_access_log_format_legacy(self):
46+
"""Using the legacy 64bits trace ID and span ID"""
47+
conf_path = Path(
48+
__file__).parent / "./conf/in_access_log_format_legacy.conf"
49+
conf_text = conf_path.read_text()
50+
51+
status, log_lines = self.orch.nginx_replace_config(
52+
conf_text, conf_path.name)
53+
self.assertEqual(0, status, log_lines)
54+
55+
# Drain any old nginx log lines.
56+
self.orch.sync_nginx_access_log()
57+
58+
status, _, body = self.orch.send_nginx_http_request("/http")
59+
self.assertEqual(200, status, body)
60+
response = json.loads(body)
61+
headers = response["headers"]
62+
trace_id = headers["x-datadog-trace-id"]
63+
span_id = headers["x-datadog-parent-id"]
64+
65+
log_lines = self.orch.sync_nginx_access_log()
66+
num_matching_lines = 0
67+
prefix = "here is your access record: "
68+
for line in log_lines:
69+
if not line.startswith(prefix):
70+
continue
71+
num_matching_lines += 1
72+
log_trace_id, log_span_id, propagation, location = json.loads(
73+
line[len(prefix):])
74+
self.assertEqual(trace_id, log_trace_id, line)
75+
self.assertEqual(span_id, log_span_id, line)
76+
self.assertEqual(dict, type(propagation))
77+
self.assertEqual("/https?[0-9]*", location)
4178

4279
self.assertEqual(1, num_matching_lines)
4380

4481
def test_in_request_headers(self):
45-
conf_path = Path(__file__).parent / './conf/in_request_headers.conf'
82+
conf_path = Path(__file__).parent / "./conf/in_request_headers.conf"
4683
conf_text = conf_path.read_text()
4784

4885
status, log_lines = self.orch.nginx_replace_config(
4986
conf_text, conf_path.name)
5087
self.assertEqual(0, status, log_lines)
5188

52-
status, _, body = self.orch.send_nginx_http_request('/http')
89+
status, _, body = self.orch.send_nginx_http_request("/http")
5390
self.assertEqual(200, status)
5491
response = json.loads(body)
55-
headers = response['headers']
56-
trace_id, span_id = int(headers['x-datadog-trace-id']), int(
57-
headers['x-datadog-parent-id'])
92+
headers = response["headers"]
93+
traceparent = headers["traceparent"]
94+
assert traceparent
95+
_, trace_id, span_id, _ = traceparent.split("-")
5896

5997
# The service being reverse proxied by nginx returns a JSON response
6098
# containing the request headers. By instructing nginx to add headers
6199
# whose values depend on the variables, we can extract the values of
62100
# the variables from the response.
63-
self.assertIn('x-datadog-test-thingy', headers)
101+
self.assertIn("x-datadog-test-thingy", headers)
64102
header_trace_id, header_span_id, propagation, location = json.loads(
65-
headers['x-datadog-test-thingy'])
103+
headers["x-datadog-test-thingy"])
66104
self.assertEqual(trace_id, header_trace_id)
67105
self.assertEqual(span_id, header_span_id)
68106
self.assertEqual(dict, type(propagation))
69-
self.assertEqual('/https?[0-9]*', location)
107+
self.assertEqual("/https?[0-9]*", location)
70108

71109
def test_which_span_id_in_headers(self):
72110
"""Verify that when `datadog_trace_locations` is `on`, the span
@@ -83,31 +121,31 @@ def test_which_span_id_in_headers(self):
83121
nginx.
84122
"""
85123
conf_path = Path(
86-
__file__).parent / './conf/which_span_id_in_headers.conf'
124+
__file__).parent / "./conf/which_span_id_in_headers.conf"
87125
conf_text = conf_path.read_text()
88126

89127
status, log_lines = self.orch.nginx_replace_config(
90128
conf_text, conf_path.name)
91129
self.assertEqual(0, status, log_lines)
92130

93-
self.orch.sync_service('agent')
131+
self.orch.sync_service("agent")
94132

95133
headers = {
96-
'X-Datadog-Trace-ID': 123,
97-
'X-Datadog-Parent-ID': 456,
98-
'X-Datadog-Sampling-Priority': 2 # manual keep
134+
"X-Datadog-Trace-ID": 123,
135+
"X-Datadog-Parent-ID": 456,
136+
"X-Datadog-Sampling-Priority": 2, # manual keep
99137
}
100-
status, _, body = self.orch.send_nginx_http_request('/http',
138+
status, _, body = self.orch.send_nginx_http_request("/http",
101139
headers=headers)
102140
self.assertEqual(200, status)
103141
response = json.loads(body)
104-
headers = response['headers']
142+
headers = response["headers"]
105143

106-
self.assertIn('x-datadog-test-thingy', headers)
107-
variable_span_id = int(json.loads(headers['x-datadog-test-thingy']))
144+
self.assertIn("x-datadog-test-thingy", headers)
145+
variable_span_id = headers["x-datadog-test-thingy"]
108146

109147
self.orch.reload_nginx()
110-
log_lines = self.orch.sync_service('agent')
148+
log_lines = self.orch.sync_service("agent")
111149

112150
# Map {span ID -> parent ID} for each span sent to the agent by nginx.
113151
# This will allow us to check that $datadog_span_id is the _location_
@@ -122,10 +160,12 @@ def test_which_span_id_in_headers(self):
122160
continue
123161
for chunk in trace:
124162
first, *rest = chunk
125-
if first['service'] == 'nginx':
163+
if first["service"] == "nginx":
126164
nginx_sent_a_trace = True
127165
for span in chunk:
128-
parent_by_span_id[span['span_id']] = span['parent_id']
166+
parent_by_span_id[format(span["span_id"],
167+
"016x")] = format(
168+
span["parent_id"], "016x")
129169

130170
self.assertTrue(nginx_sent_a_trace)
131171
self.assertIn(variable_span_id, parent_by_span_id)
@@ -138,45 +178,45 @@ def test_which_span_id_in_access_log_format(self):
138178
referred to by the `$datadog_span_id` variable in an access log format
139179
string is the location span, not the request span.
140180
"""
141-
conf_path = Path(
142-
__file__).parent / './conf/which_span_id_in_access_log_format.conf'
181+
conf_path = (Path(__file__).parent /
182+
"./conf/which_span_id_in_access_log_format.conf")
143183
conf_text = conf_path.read_text()
144184

145185
status, log_lines = self.orch.nginx_replace_config(
146186
conf_text, conf_path.name)
147187
self.assertEqual(0, status, log_lines)
148188

149-
self.orch.sync_service('agent')
189+
self.orch.sync_service("agent")
150190

151191
# Drain any old nginx log lines.
152192
self.orch.sync_nginx_access_log()
153193

154194
headers = {
155-
'X-Datadog-Trace-ID': 123,
156-
'X-Datadog-Parent-ID': 456,
157-
'X-Datadog-Sampling-Priority': 2 # manual keep
195+
"X-Datadog-Trace-ID": 123,
196+
"X-Datadog-Parent-ID": 456,
197+
"X-Datadog-Sampling-Priority": 2, # manual keep
158198
}
159-
status, _, body = self.orch.send_nginx_http_request('/http',
199+
status, _, body = self.orch.send_nginx_http_request("/http",
160200
headers=headers)
161201
self.assertEqual(200, status, body)
162202
response = json.loads(body)
163-
headers = response['headers']
203+
headers = response["headers"]
164204

165205
log_lines = self.orch.sync_nginx_access_log()
166-
prefix = 'here is your span ID: '
206+
prefix = "here is your span ID: "
167207
logged_span_id = None
168208
for line in log_lines:
169209
if not line.startswith(prefix):
170210
continue
171-
logged_span_id = int(line[len(prefix):])
211+
logged_span_id = line[len(prefix) + 1:-1]
172212

173213
self.assertNotEqual(None, logged_span_id)
174214

175215
# Now look at the agent's logs to see the spans actually sent, and
176216
# verify that `logged_span_id` is the _location_ span, not the request
177217
# span.
178218
self.orch.reload_nginx()
179-
log_lines = self.orch.sync_service('agent')
219+
log_lines = self.orch.sync_service("agent")
180220

181221
parent_by_span_id = {}
182222
nginx_sent_a_trace = False
@@ -187,10 +227,12 @@ def test_which_span_id_in_access_log_format(self):
187227
continue
188228
for chunk in trace:
189229
first, *rest = chunk
190-
if first['service'] == 'nginx':
230+
if first["service"] == "nginx":
191231
nginx_sent_a_trace = True
192232
for span in chunk:
193-
parent_by_span_id[span['span_id']] = span['parent_id']
233+
parent_by_span_id[format(span["span_id"],
234+
"016x")] = format(
235+
span["parent_id"], "016x")
194236

195237
self.assertTrue(nginx_sent_a_trace)
196238
self.assertIn(logged_span_id, parent_by_span_id)

0 commit comments

Comments
 (0)