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

Commit 1d202d3

Browse files
authored
WithSpan: puts a Span into the current Context. (#208)
1 parent 68da304 commit 1d202d3

6 files changed

Lines changed: 350 additions & 0 deletions

File tree

opencensus/context/context.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
#include "opencensus/trace/span.h"
2323

2424
namespace opencensus {
25+
namespace trace {
26+
class WithSpan;
27+
} // namespace trace
2528
namespace context {
2629

2730
// Context holds information specific to an operation, such as a TagMap and
@@ -57,7 +60,9 @@ class Context {
5760
static Context* InternalMutableCurrent();
5861
friend void swap(Context& a, Context& b);
5962

63+
friend class ContextTestPeer;
6064
friend class WithContext;
65+
friend class ::opencensus::trace::WithSpan;
6166

6267
opencensus::tags::TagMap tags_;
6368
opencensus::trace::Span span_;

opencensus/trace/BUILD

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ cc_library(
9292
],
9393
)
9494

95+
cc_library(
96+
name = "with_span",
97+
srcs = ["internal/with_span.cc"],
98+
hdrs = ["with_span.h"],
99+
copts = DEFAULT_COPTS,
100+
visibility = ["//visibility:public"],
101+
deps = [
102+
":trace",
103+
"//opencensus/context",
104+
],
105+
)
106+
95107
# Tests
96108
# ========================================================================= #
97109

@@ -267,6 +279,18 @@ cc_test(
267279
],
268280
)
269281

282+
cc_test(
283+
name = "with_span_test",
284+
srcs = ["internal/with_span_test.cc"],
285+
copts = TEST_COPTS,
286+
deps = [
287+
":trace",
288+
":with_span",
289+
"//opencensus/context",
290+
"@com_google_googletest//:gtest_main",
291+
],
292+
)
293+
270294
# Benchmarks
271295
# ========================================================================= #
272296

@@ -308,3 +332,17 @@ cc_binary(
308332
"@com_github_google_benchmark//:benchmark",
309333
],
310334
)
335+
336+
cc_binary(
337+
name = "with_span_benchmark",
338+
testonly = 1,
339+
srcs = ["internal/with_span_benchmark.cc"],
340+
copts = TEST_COPTS,
341+
linkopts = ["-pthread"], # Required for absl/synchronization bits.
342+
linkstatic = 1,
343+
deps = [
344+
":trace",
345+
":with_span",
346+
"@com_github_google_benchmark//:benchmark",
347+
],
348+
)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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/with_span.h"
16+
17+
#include <cassert>
18+
#include <utility>
19+
20+
#include "opencensus/context/context.h"
21+
#include "opencensus/trace/span.h"
22+
23+
using ::opencensus::context::Context;
24+
using ::opencensus::trace::Span;
25+
26+
namespace opencensus {
27+
namespace trace {
28+
29+
WithSpan::WithSpan(const Span& span, bool cond)
30+
: swapped_span_(span)
31+
#ifndef NDEBUG
32+
,
33+
original_context_(Context::InternalMutableCurrent())
34+
#endif
35+
,
36+
cond_(cond) {
37+
ConditionalSwap();
38+
}
39+
40+
WithSpan::~WithSpan() {
41+
#ifndef NDEBUG
42+
assert(original_context_ == Context::InternalMutableCurrent() &&
43+
"WithSpan must be destructed on the same thread as it was "
44+
"constructed.");
45+
#endif
46+
ConditionalSwap();
47+
}
48+
49+
void WithSpan::ConditionalSwap() {
50+
if (cond_) {
51+
using std::swap;
52+
swap(Context::InternalMutableCurrent()->span_, swapped_span_);
53+
}
54+
}
55+
56+
} // namespace trace
57+
} // namespace opencensus
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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/with_span.h"
16+
17+
#include <cstdlib>
18+
19+
#include "benchmark/benchmark.h"
20+
#include "opencensus/trace/sampler.h"
21+
#include "opencensus/trace/span.h"
22+
23+
namespace opencensus {
24+
namespace context {
25+
class ContextTestPeer {
26+
public:
27+
static const opencensus::trace::Span& CurrentSpan() {
28+
return Context::InternalMutableCurrent()->span_;
29+
}
30+
};
31+
} // namespace context
32+
namespace trace {
33+
namespace {
34+
35+
using opencensus::context::ContextTestPeer;
36+
37+
void BM_WithSpanUnsampled(benchmark::State& state) {
38+
static ::opencensus::trace::NeverSampler sampler;
39+
auto span = Span::StartSpan("MySpan", /*parent=*/nullptr, {&sampler});
40+
if (span.IsRecording()) {
41+
abort();
42+
}
43+
for (auto _ : state) {
44+
WithSpan ws(span);
45+
}
46+
span.End();
47+
}
48+
BENCHMARK(BM_WithSpanUnsampled);
49+
50+
void BM_WithSpanSampled(benchmark::State& state) {
51+
static ::opencensus::trace::AlwaysSampler sampler;
52+
auto span = Span::StartSpan("MySpan", /*parent=*/nullptr, {&sampler});
53+
if (!span.IsRecording()) {
54+
abort();
55+
}
56+
for (auto _ : state) {
57+
WithSpan ws(span);
58+
}
59+
span.End();
60+
}
61+
BENCHMARK(BM_WithSpanSampled);
62+
63+
void BM_WithSpanConditionFalse(benchmark::State& state) {
64+
auto span = Span::StartSpan("MySpan");
65+
for (auto _ : state) {
66+
WithSpan ws(span, false);
67+
}
68+
span.End();
69+
}
70+
BENCHMARK(BM_WithSpanConditionFalse);
71+
72+
} // namespace
73+
} // namespace trace
74+
} // namespace opencensus
75+
76+
BENCHMARK_MAIN();
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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/with_span.h"
16+
17+
#include <iostream>
18+
19+
#include "gtest/gtest.h"
20+
#include "opencensus/context/context.h"
21+
#include "opencensus/trace/span.h"
22+
#include "opencensus/trace/span_context.h"
23+
24+
namespace opencensus {
25+
namespace context {
26+
class ContextTestPeer {
27+
public:
28+
static const opencensus::trace::SpanContext& CurrentCtx() {
29+
return Context::InternalMutableCurrent()->span_.context();
30+
}
31+
};
32+
} // namespace context
33+
} // namespace opencensus
34+
35+
// Not in namespace ::opencensus::context in order to better reflect what user
36+
// code should look like.
37+
38+
namespace {
39+
40+
using opencensus::context::ContextTestPeer;
41+
42+
void LogCurrentContext() {
43+
const std::string s = opencensus::context::Context::Current().DebugString();
44+
std::cout << " current: " << s << "\n";
45+
}
46+
47+
void ExpectNoSpan() {
48+
opencensus::trace::SpanContext zeroed_span_context;
49+
EXPECT_EQ(zeroed_span_context, ContextTestPeer::CurrentCtx());
50+
}
51+
52+
TEST(WithSpanTest, NoSpanInDefaultContext) { ExpectNoSpan(); }
53+
54+
TEST(WithSpanTest, SpanIsAddedAndRemoved) {
55+
auto span = opencensus::trace::Span::StartSpan("MySpan");
56+
ExpectNoSpan();
57+
{
58+
opencensus::trace::WithSpan ws(span);
59+
LogCurrentContext();
60+
EXPECT_EQ(span.context(), ContextTestPeer::CurrentCtx())
61+
<< "Span was installed.";
62+
}
63+
ExpectNoSpan();
64+
span.End();
65+
}
66+
67+
TEST(WithSpanTest, Nesting) {
68+
auto span1 = opencensus::trace::Span::StartSpan("MySpan1");
69+
auto span2 = opencensus::trace::Span::StartSpan("MySpan2");
70+
ExpectNoSpan();
71+
{
72+
opencensus::trace::WithSpan ws1(span1);
73+
EXPECT_EQ(span1.context(), ContextTestPeer::CurrentCtx())
74+
<< "Span1 was installed.";
75+
{
76+
opencensus::trace::WithSpan ws2(span2);
77+
EXPECT_EQ(span2.context(), ContextTestPeer::CurrentCtx())
78+
<< "Span2 was installed.";
79+
}
80+
EXPECT_EQ(span1.context(), ContextTestPeer::CurrentCtx())
81+
<< "Span2 was uninstalled, back to Span1.";
82+
}
83+
ExpectNoSpan();
84+
span2.End();
85+
span1.End();
86+
}
87+
88+
TEST(WithSpanTest, DisabledViaConditional) {
89+
auto span1 = opencensus::trace::Span::StartSpan("MySpan1");
90+
auto span2 = opencensus::trace::Span::StartSpan("MySpan2");
91+
ExpectNoSpan();
92+
{
93+
opencensus::trace::WithSpan ws1(span1);
94+
EXPECT_EQ(span1.context(), ContextTestPeer::CurrentCtx())
95+
<< "Span1 was installed.";
96+
{
97+
opencensus::trace::WithSpan ws2(span2, false);
98+
EXPECT_EQ(span1.context(), ContextTestPeer::CurrentCtx())
99+
<< "Span2 was NOT installed, blocked by condition.";
100+
}
101+
EXPECT_EQ(span1.context(), ContextTestPeer::CurrentCtx())
102+
<< "Still Span1 after WithSpan2 goes out of scope.";
103+
}
104+
ExpectNoSpan();
105+
span2.End();
106+
span1.End();
107+
}
108+
109+
} // namespace

opencensus/trace/with_span.h

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
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_WITH_SPAN_H_
16+
#define OPENCENSUS_TRACE_WITH_SPAN_H_
17+
18+
#include "opencensus/context/context.h"
19+
#include "opencensus/trace/span.h"
20+
21+
namespace opencensus {
22+
namespace trace {
23+
24+
// WithSpan is a scoped object that sets the current Span to the given one,
25+
// until the WithSpan object is destroyed. If the condition is false, it doesn't
26+
// do anything.
27+
//
28+
// Because WithSpan changes the current (thread local) context, NEVER allocate a
29+
// WithSpan in one thread and deallocate in another. A simple way to ensure this
30+
// is to only ever stack-allocate it.
31+
//
32+
// Example usage:
33+
// {
34+
// WithSpan ws(span);
35+
// // Do work.
36+
// }
37+
class WithSpan {
38+
public:
39+
explicit WithSpan(const Span& span, bool cond = true);
40+
~WithSpan();
41+
42+
// No Span&& constructor because it encourages "consuming" the Span with a
43+
// std::move(). It's better to hold on to the Span object because we have to
44+
// call End() on it when the operation is finished.
45+
46+
private:
47+
WithSpan() = delete;
48+
WithSpan(const WithSpan&) = delete;
49+
WithSpan(WithSpan&&) = delete;
50+
WithSpan& operator=(const WithSpan&) = delete;
51+
WithSpan& operator=(WithSpan&&) = delete;
52+
53+
void ConditionalSwap();
54+
55+
Span swapped_span_;
56+
#ifndef NDEBUG
57+
const ::opencensus::context::Context* original_context_;
58+
#endif
59+
const bool cond_;
60+
};
61+
62+
} // namespace trace
63+
} // namespace opencensus
64+
65+
#endif // OPENCENSUS_TRACE_WITH_SPAN_H_

0 commit comments

Comments
 (0)