Skip to content
This repository was archived by the owner on Jul 31, 2023. It is now read-only.

Commit 33f3826

Browse files
authored
Add propagation helpers for the "traceparent:" header. (#260)
1 parent a7568a6 commit 33f3826

File tree

6 files changed

+335
-0
lines changed

6 files changed

+335
-0
lines changed

opencensus/trace/BUILD

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ cc_library(
7575
deps = [
7676
":cloud_trace_context",
7777
":span_context",
78+
":trace_context",
7879
"//opencensus/common/internal:random_lib",
7980
"@com_google_absl//absl/base:core_headers",
8081
"@com_google_absl//absl/base:endian",
@@ -150,6 +151,23 @@ cc_library(
150151
],
151152
)
152153

154+
cc_library(
155+
name = "trace_context",
156+
srcs = [
157+
"internal/trace_context.cc",
158+
],
159+
hdrs = [
160+
"propagation/trace_context.h",
161+
],
162+
copts = DEFAULT_COPTS,
163+
visibility = ["//visibility:public"],
164+
deps = [
165+
":span_context",
166+
"@com_google_absl//absl/base:endian",
167+
"@com_google_absl//absl/strings",
168+
],
169+
)
170+
153171
cc_library(
154172
name = "with_span",
155173
srcs = ["internal/with_span.cc"],
@@ -358,6 +376,17 @@ cc_test(
358376
],
359377
)
360378

379+
cc_test(
380+
name = "trace_context_test",
381+
srcs = ["internal/trace_context_test.cc"],
382+
copts = TEST_COPTS,
383+
deps = [
384+
":span_context",
385+
":trace_context",
386+
"@com_google_googletest//:gtest_main",
387+
],
388+
)
389+
361390
cc_test(
362391
name = "trace_options_test",
363392
srcs = ["internal/trace_options_test.cc"],
@@ -445,6 +474,18 @@ cc_binary(
445474
],
446475
)
447476

477+
cc_binary(
478+
name = "trace_context_benchmark",
479+
testonly = 1,
480+
srcs = ["internal/trace_context_benchmark.cc"],
481+
copts = TEST_COPTS,
482+
linkstatic = 1,
483+
deps = [
484+
":trace_context",
485+
"@com_github_google_benchmark//:benchmark",
486+
],
487+
)
488+
448489
cc_binary(
449490
name = "with_span_benchmark",
450491
testonly = 1,

opencensus/trace/CMakeLists.txt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ opencensus_lib(trace
4040
common_random
4141
trace_cloud_trace_context
4242
trace_span_context
43+
trace_trace_context
4344
absl::strings
4445
absl::base
4546
absl::memory
@@ -74,6 +75,15 @@ opencensus_lib(trace_span_context
7475
DEPS
7576
absl::strings)
7677

78+
opencensus_lib(trace_trace_context
79+
PUBLIC
80+
SRCS
81+
internal/trace_context.cc
82+
DEPS
83+
trace_span_context
84+
absl::base
85+
absl::strings)
86+
7787
opencensus_lib(trace_with_span
7888
PUBLIC
7989
SRCS
@@ -159,6 +169,9 @@ opencensus_test(trace_trace_config_test
159169

160170
opencensus_test(trace_trace_options_test internal/trace_options_test.cc trace)
161171

172+
opencensus_test(trace_trace_context_test internal/trace_context_test.cc
173+
trace_trace_context)
174+
162175
opencensus_test(trace_with_span_test
163176
internal/with_span_test.cc
164177
trace
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "opencensus/trace/propagation/trace_context.h"
16+
17+
#include "opencensus/trace/span_context.h"
18+
#include "opencensus/trace/span_id.h"
19+
#include "opencensus/trace/trace_id.h"
20+
#include "opencensus/trace/trace_options.h"
21+
22+
#include "absl/strings/ascii.h"
23+
#include "absl/strings/escaping.h"
24+
#include "absl/strings/str_cat.h"
25+
26+
namespace opencensus {
27+
namespace trace {
28+
namespace propagation {
29+
30+
namespace {
31+
32+
// Returns true if the string only contains valid lowercase hex digits.
33+
bool IsLowercaseHexDigits(absl::string_view s) {
34+
for (int i = 0; i < s.length(); ++i) {
35+
if (!((s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'f'))) {
36+
return false;
37+
}
38+
}
39+
return true;
40+
}
41+
42+
// Returns true if the converted string was valid hex.
43+
bool FromHex(absl::string_view hex, std::string* bin) {
44+
if (!IsLowercaseHexDigits(hex)) return false;
45+
*bin = absl::HexStringToBytes(hex);
46+
return true;
47+
}
48+
49+
} // namespace
50+
51+
SpanContext FromTraceParentHeader(absl::string_view header) {
52+
constexpr int kDelimiterLen = 1;
53+
constexpr char kDelimiter = '-';
54+
constexpr int kVersionLen = 1;
55+
constexpr int kTraceIdLen = 16;
56+
constexpr int kSpanIdLen = 8;
57+
constexpr int kTraceOptionsLen = 1;
58+
constexpr int kVersionLenHex = 2 * kVersionLen;
59+
constexpr int kTraceIdLenHex = 2 * kTraceIdLen;
60+
constexpr int kSpanIdLenHex = 2 * kSpanIdLen;
61+
constexpr int kTraceOptionsLenHex = 2 * kTraceOptionsLen;
62+
constexpr int kTotalLenInHexDigits = kVersionLenHex + kTraceIdLenHex +
63+
kSpanIdLenHex + kTraceOptionsLenHex +
64+
3 * kDelimiterLen;
65+
constexpr int kVersionOfs = 0;
66+
constexpr int kTraceIdOfs = kVersionOfs + kVersionLenHex + kDelimiterLen;
67+
constexpr int kSpanIdOfs = kTraceIdOfs + kTraceIdLenHex + kDelimiterLen;
68+
constexpr int kOptionsOfs = kSpanIdOfs + kSpanIdLenHex + kDelimiterLen;
69+
static_assert(kOptionsOfs + kTraceOptionsLenHex == kTotalLenInHexDigits,
70+
"bad offsets");
71+
static SpanContext invalid;
72+
if (header.size() != kTotalLenInHexDigits || header[kVersionOfs] != '0' ||
73+
header[kVersionOfs + 1] != '0' ||
74+
header[kTraceIdOfs - kDelimiterLen] != kDelimiter ||
75+
header[kSpanIdOfs - kDelimiterLen] != kDelimiter ||
76+
header[kOptionsOfs - kDelimiterLen] != kDelimiter) {
77+
return invalid; // Invalid length, version or format.
78+
}
79+
std::string trace_id_bin, span_id_bin, options_bin;
80+
if (!FromHex(header.substr(kTraceIdOfs, kTraceIdLenHex), &trace_id_bin) ||
81+
!FromHex(header.substr(kSpanIdOfs, kSpanIdLenHex), &span_id_bin) ||
82+
!FromHex(header.substr(kOptionsOfs, kTraceOptionsLenHex), &options_bin)) {
83+
return invalid; // Invalid hex.
84+
}
85+
return SpanContext(
86+
TraceId(reinterpret_cast<const uint8_t*>(trace_id_bin.data())),
87+
SpanId(reinterpret_cast<const uint8_t*>(span_id_bin.data())),
88+
TraceOptions(reinterpret_cast<const uint8_t*>(options_bin.data())));
89+
}
90+
91+
std::string ToTraceParentHeader(const SpanContext& ctx) {
92+
return absl::StrCat("00-", ctx.ToString());
93+
}
94+
95+
} // namespace propagation
96+
} // namespace trace
97+
} // namespace opencensus
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "opencensus/trace/propagation/trace_context.h"
16+
17+
#include "benchmark/benchmark.h"
18+
19+
namespace opencensus {
20+
namespace trace {
21+
namespace propagation {
22+
namespace {
23+
24+
constexpr char kHeader[] =
25+
"00-404142434445464748494a4b4c4d4e4f-6162636465666768-01";
26+
27+
void BM_FromTraceParentHeader(benchmark::State& state) {
28+
while (state.KeepRunning()) {
29+
FromTraceParentHeader(kHeader);
30+
}
31+
}
32+
BENCHMARK(BM_FromTraceParentHeader);
33+
34+
void BM_ToTraceParentHeader(benchmark::State& state) {
35+
auto ctx = FromTraceParentHeader(kHeader);
36+
while (state.KeepRunning()) {
37+
ToTraceParentHeader(ctx);
38+
}
39+
}
40+
BENCHMARK(BM_ToTraceParentHeader);
41+
42+
} // namespace
43+
} // namespace propagation
44+
} // namespace trace
45+
} // namespace opencensus
46+
47+
BENCHMARK_MAIN();
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "opencensus/trace/propagation/trace_context.h"
16+
17+
#include "gmock/gmock.h"
18+
#include "gtest/gtest.h"
19+
#include "opencensus/trace/span_context.h"
20+
#include "opencensus/trace/span_id.h"
21+
#include "opencensus/trace/trace_id.h"
22+
23+
namespace opencensus {
24+
namespace trace {
25+
namespace propagation {
26+
namespace {
27+
28+
MATCHER(IsValid, "is a valid SpanContext") { return arg.IsValid(); }
29+
MATCHER(IsInvalid, "is an invalid SpanContext") { return !arg.IsValid(); }
30+
31+
TEST(TraceParentTest, ParseFull) {
32+
constexpr char header[] =
33+
"00-404142434445464748494a4b4c4d4e4f-6162636465666768-01";
34+
SpanContext ctx = FromTraceParentHeader(header);
35+
EXPECT_THAT(ctx, IsValid());
36+
EXPECT_EQ("404142434445464748494a4b4c4d4e4f-6162636465666768-01",
37+
ctx.ToString());
38+
EXPECT_EQ(header, ToTraceParentHeader(ctx));
39+
}
40+
41+
TEST(TraceParentTest, ParseTraceDisabled) {
42+
constexpr char header[] =
43+
"00-404142434445464748494a4b4c4d4e4f-6162636465666768-00";
44+
SpanContext ctx = FromTraceParentHeader(header);
45+
EXPECT_THAT(ctx, IsValid());
46+
EXPECT_EQ("404142434445464748494a4b4c4d4e4f-6162636465666768-00",
47+
ctx.ToString());
48+
EXPECT_EQ(header, ToTraceParentHeader(ctx));
49+
}
50+
51+
TEST(TraceParentTest, ExpectedFailures) {
52+
#define INVALID(str) EXPECT_THAT(FromTraceParentHeader(str), IsInvalid())
53+
INVALID("");
54+
INVALID("12-404142434445464748494a4b4c4d4e4f-6162636465666768-00")
55+
<< "unknown version.";
56+
INVALID("00-404142434445464748494a4b4c4d4e4f-6162636465666768-0")
57+
<< "too short.";
58+
INVALID("00-404142434445464748494a4b4c4d4e4f-6162636465666768-011")
59+
<< "too long.";
60+
INVALID("xy-404142434445464748494a4b4c4d4e4f-6162636465666768-01")
61+
<< "version not hex.";
62+
INVALID("00-4x4y42434445464748494a4b4c4d4e4f-6162636465666768-01")
63+
<< "trace_id not hex.";
64+
INVALID("00-404142434445464748494a4b4c4d4e4f-6x6y636465666768-01")
65+
<< "span_id not hex.";
66+
INVALID("00-404142434445464748494a4b4c4d4e4f-6162636465666768-xy")
67+
<< "options not hex.";
68+
INVALID("00-00000000000000000000000000000000-6162636465666768-01")
69+
<< "trace_id can't be zero.";
70+
INVALID("00-404142434445464748494a4b4c4d4e4f-0000000000000000-01")
71+
<< "span_id can't be zero.";
72+
INVALID("00_404142434445464748494a4b4c4d4e4f-6162636465666768-01")
73+
<< "invalid delimiter.";
74+
INVALID("00-404142434445464748494a4b4c4d4e4f_6162636465666768-01")
75+
<< "invalid delimiter.";
76+
INVALID("00-404142434445464748494a4b4c4d4e4f-6162636465666768_01")
77+
<< "invalid delimiter.";
78+
INVALID("00-404142434445464748494A4B4C4D4E4F-6162636465666768-01")
79+
<< "uppercase is invalid.";
80+
#undef INVALID
81+
}
82+
83+
} // namespace
84+
} // namespace propagation
85+
} // namespace trace
86+
} // namespace opencensus
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef OPENCENSUS_TRACE_PROPAGATION_TRACE_CONTEXT_H_
16+
#define OPENCENSUS_TRACE_PROPAGATION_TRACE_CONTEXT_H_
17+
18+
#include <string>
19+
20+
#include "absl/strings/string_view.h"
21+
#include "opencensus/trace/span_context.h"
22+
23+
namespace opencensus {
24+
namespace trace {
25+
namespace propagation {
26+
27+
// Implementation of the TraceContext propagation protocol:
28+
// https://github.com/w3c/distributed-tracing
29+
30+
// Parses the value of the "traceparent: ..." header, returning a
31+
// SpanContext. If parsing fails, IsValid will be false.
32+
//
33+
// The input format is a lowercase hex string:
34+
// - version_id: 1 byte, currently must be zero - hex encoded (2 characters)
35+
// - trace_id: 16 bytes (opaque blob) - hex encoded (32 characters)
36+
// - span_id: 8 bytes (opaque blob) - hex encoded (16 characters)
37+
// - trace_options: 1 byte (LSB means tracing enabled) - hex encoded (2 ch)
38+
//
39+
// Example: "00-404142434445464748494a4b4c4d4e4f-6162636465666768-01"
40+
// v trace_id span_id options
41+
//
42+
SpanContext FromTraceParentHeader(absl::string_view header);
43+
44+
// Returns a value for the traceparent header.
45+
std::string ToTraceParentHeader(const SpanContext& ctx);
46+
47+
} // namespace propagation
48+
} // namespace trace
49+
} // namespace opencensus
50+
51+
#endif // OPENCENSUS_TRACE_PROPAGATION_TRACE_CONTEXT_H_

0 commit comments

Comments
 (0)