Skip to content

Commit 00e1b5a

Browse files
authored
implement RAII based span acquisition (#57)
* implement RAII based span acquisition * format
1 parent e56be8b commit 00e1b5a

File tree

9 files changed

+204
-140
lines changed

9 files changed

+204
-140
lines changed

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,14 +88,27 @@ SegmentContextPtr current_segment = createSegmentContext(seg_config);
8888
First, you must create root span to trace current workload.
8989
9090
```cpp
91-
CurrentSegmentSpanPtr current_span = current_segment->createCurrentSegmentRootSpan();
91+
CurrentSegmentSpanPtr current_span = current_segment->createEntrySpan();
9292
```
9393

9494
After that, you can create another span to trace another workload, such as RPC to other services.
9595
Note that you must have parent span to create secondary span. It will construct parent-child relation when analysis.
9696

9797
```cpp
98-
CurrentSegmentSpanPtr current_span = current_segment->createCurrentSegmentSpan(current_span);
98+
CurrentSegmentSpanPtr current_span = current_segment->createExitSpan(current_span);
99+
```
100+
101+
Alternative approach is RAII based one. It is used like below,
102+
103+
```cpp
104+
{
105+
StartEntrySpan entry_span(current_segment, "sample_op1");
106+
{
107+
StartExitSpan exit_span(current_segment, entry_span.get(), "sample_op2");
108+
109+
// something...
110+
}
111+
}
99112
```
100113

101114
#### Send segment to OAP
@@ -105,7 +118,7 @@ to avoid undefined behavior.
105118

106119
```cpp
107120
SegmentContextPtr current_segment = createSegmentContext(config);
108-
CurrentSegmentSpanPtr current_span = current_segment->createCurrentSegmentRootSpan();
121+
CurrentSegmentSpanPtr current_span = current_segment->createEntrySpan();
109122

110123
current_span->startSpan("sample_workload");
111124
current_span->endSpan();

cpp2sky/segment_context.h

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,13 @@ class SegmentContext {
225225
* Generate a segment span related with this segment context.
226226
* @param parent_span Parent span which is extracted from caller.
227227
*/
228-
virtual CurrentSegmentSpanPtr createCurrentSegmentSpan(
228+
virtual CurrentSegmentSpanPtr createExitSpan(
229229
CurrentSegmentSpanPtr parent_span) = 0;
230230

231231
/**
232232
* Generate root segment span, called once per workload.
233233
*/
234-
virtual CurrentSegmentSpanPtr createCurrentSegmentRootSpan() = 0;
234+
virtual CurrentSegmentSpanPtr createEntrySpan() = 0;
235235

236236
/**
237237
* Generate sw8 value to send SegmentRef.
@@ -268,5 +268,47 @@ class SegmentContext {
268268
};
269269

270270
using SegmentContextPtr = std::shared_ptr<SegmentContext>;
271+
/**
272+
* RAII based span creation. It acquired then create new span with required
273+
* properties. The span wiil be closed and set end time when called destructor.
274+
*/
275+
class StartEntrySpan {
276+
public:
277+
StartEntrySpan(SegmentContextPtr segment_context,
278+
std::string_view operation_name)
279+
: span_(segment_context->createEntrySpan()) {
280+
span_->startSpan(operation_name.data());
281+
}
282+
283+
~StartEntrySpan() {
284+
// Span won't be released because the entity is holded by SegmentContext.
285+
span_->endSpan();
286+
}
287+
288+
CurrentSegmentSpanPtr get() { return span_; }
289+
290+
private:
291+
CurrentSegmentSpanPtr span_;
292+
};
293+
294+
class StartExitSpan {
295+
public:
296+
StartExitSpan(SegmentContextPtr segment_context,
297+
CurrentSegmentSpanPtr parent_span,
298+
std::string_view operation_name)
299+
: span_(segment_context->createExitSpan(parent_span)) {
300+
span_->startSpan(operation_name.data());
301+
}
302+
303+
~StartExitSpan() {
304+
// Span won't be released because the entity is holded by SegmentContext.
305+
span_->endSpan();
306+
}
307+
308+
CurrentSegmentSpanPtr get() { return span_; }
309+
310+
private:
311+
CurrentSegmentSpanPtr span_;
312+
};
271313

272314
} // namespace cpp2sky

example/sample.cc

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "cpp2sky/propagation.h"
1919
#include "cpp2sky/segment_context.h"
2020
#include "cpp2sky/tracer.h"
21+
#include "cpp2sky/well_known_names.h"
2122
#include "httplib.h"
2223

2324
using namespace cpp2sky;
@@ -38,18 +39,27 @@ int main() {
3839
auto tracer = createInsecureGrpcTracer(config);
3940

4041
svr.Get("/ping", [&](const httplib::Request& req, httplib::Response& res) {
41-
// 2. Create segment context
42-
auto current_segment = tracer->newSegment();
42+
std::string context = req.get_header_value(kPropagationHeader.data());
4343

44-
// 3. Initialize span data to track root workload on current service.
45-
auto current_span = current_segment->createCurrentSegmentRootSpan();
44+
SegmentContextPtr segment_context;
45+
if (!context.empty()) {
46+
// 2. Create segment context with propagated information.
47+
segment_context = tracer->newSegment(createSpanContext(context));
48+
}
4649

47-
// 4. Set info
48-
current_span->startSpan("/ping");
49-
current_span->endSpan();
50+
{
51+
// 3. Create entry span.
52+
StartEntrySpan current_span(segment_context, "sample_op3");
5053

51-
// 5. Send span data
52-
tracer->sendSegment(std::move(current_segment));
54+
/**
55+
* something....
56+
*/
57+
}
58+
59+
// 4. Send span data
60+
if (segment_context != nullptr) {
61+
tracer->sendSegment(std::move(segment_context));
62+
}
5363
});
5464

5565
svr.listen("0.0.0.0", 8081);

example/sample_client.cc

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,26 +40,47 @@ int main() {
4040
// 2. Create segment context
4141
auto current_segment = tracer->newSegment();
4242

43-
// 3. Initialize span data to track root workload on current service.
44-
auto current_span = current_segment->createCurrentSegmentRootSpan();
45-
46-
// 4. Set info
47-
current_span->startSpan("/ping");
48-
49-
httplib::Client cli("remote", 8082);
50-
51-
auto context =
52-
current_segment->createSW8HeaderValue(current_span, "remote:8082");
53-
54-
httplib::Headers headers;
55-
if (context.has_value()) {
56-
headers = {{kPropagationHeader.data(), *context}};
43+
/**
44+
* 3. Create entry span it traces RPC call.
45+
* Span lifetime is managed by RAII. So user don't have to call startSpan and
46+
* endSpan explicitly. But it provides basic approach that doesn't use RAII.
47+
*
48+
* example:
49+
*
50+
* auto current_span = current_segment->createEntrySpan();
51+
* current_span->startSpan("sample_op1");
52+
*
53+
* auto current_span2 = current_segment->createExitSpan();
54+
* current_span2->startSpan("sample_op2");
55+
*
56+
* httplib::Client cli("remote", 8082);
57+
* httplib::Headers headers = {
58+
* {kPropagationHeader.data(),
59+
* current_segment->createSW8HeaderValue(current_span, "remote:8082")}};
60+
*
61+
* auto res = cli.Get("/ping", headers);
62+
*
63+
* current_span2->endSpan();
64+
* current_span->endSpan();
65+
*
66+
*/
67+
{
68+
StartEntrySpan entry_span(current_segment, "sample_op1");
69+
70+
{
71+
std::string target_address = "remote:8082";
72+
StartExitSpan exit_span(current_segment, entry_span.get(), "sample_op2");
73+
exit_span.get()->setPeer(target_address);
74+
75+
httplib::Client cli("remote", 8082);
76+
httplib::Headers headers = {
77+
{kPropagationHeader.data(), *current_segment->createSW8HeaderValue(
78+
exit_span.get(), target_address)}};
79+
80+
auto res = cli.Get("/ping", headers);
81+
}
5782
}
58-
auto res = cli.Get("/ping", headers);
59-
60-
current_span->endSpan();
6183

62-
// 5. Send span data
6384
tracer->sendSegment(std::move(current_segment));
6485
return 0;
6586
}

source/segment_context_impl.cc

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -172,30 +172,23 @@ SegmentContextImpl::SegmentContextImpl(const std::string& service_name,
172172
service_(service_name),
173173
service_instance_(instance_name) {}
174174

175-
CurrentSegmentSpanPtr SegmentContextImpl::createCurrentSegmentSpan(
175+
CurrentSegmentSpanPtr SegmentContextImpl::createExitSpan(
176176
CurrentSegmentSpanPtr parent_span) {
177-
auto current_span =
178-
std::make_shared<CurrentSegmentSpanImpl>(spans_.size(), *this);
179-
if (parent_span != nullptr) {
180-
current_span->setParentSpanId(parent_span->spanId());
181-
current_span->setSpanType(SpanType::Exit);
182-
} else {
183-
current_span->setParentSpanId(-1);
184-
current_span->setSpanType(SpanType::Entry);
185-
}
186-
// It supports only HTTP request tracing.
187-
current_span->setSpanLayer(SpanLayer::Http);
188-
if (should_skip_analysis_) {
189-
current_span->setSkipAnalysis();
190-
}
191-
192-
spans_.push_back(current_span);
177+
auto current_span = createSpan();
178+
current_span->setParentSpanId(parent_span->spanId());
179+
current_span->setSpanType(SpanType::Exit);
193180
return current_span;
194181
}
195182

196-
CurrentSegmentSpanPtr SegmentContextImpl::createCurrentSegmentRootSpan() {
197-
assert(spans_.empty());
198-
return createCurrentSegmentSpan(nullptr);
183+
CurrentSegmentSpanPtr SegmentContextImpl::createEntrySpan() {
184+
if (!spans_.empty()) {
185+
return nullptr;
186+
}
187+
188+
auto current_span = createSpan();
189+
current_span->setParentSpanId(-1);
190+
current_span->setSpanType(SpanType::Entry);
191+
return current_span;
199192
}
200193

201194
std::optional<std::string> SegmentContextImpl::createSW8HeaderValue(
@@ -215,8 +208,8 @@ std::optional<std::string> SegmentContextImpl::createSW8HeaderValue(
215208
return encodeSpan(target_span, target_address);
216209
}
217210

218-
std::string SegmentContextImpl::encodeSpan(CurrentSegmentSpanPtr parent_span,
219-
const std::string_view target_address) {
211+
std::string SegmentContextImpl::encodeSpan(
212+
CurrentSegmentSpanPtr parent_span, const std::string_view target_address) {
220213
assert(parent_span);
221214
std::string header_value;
222215

@@ -236,6 +229,20 @@ std::string SegmentContextImpl::encodeSpan(CurrentSegmentSpanPtr parent_span,
236229
return header_value;
237230
}
238231

232+
CurrentSegmentSpanPtr SegmentContextImpl::createSpan() {
233+
auto current_span =
234+
std::make_shared<CurrentSegmentSpanImpl>(spans_.size(), *this);
235+
236+
// It supports only HTTP request tracing.
237+
current_span->setSpanLayer(SpanLayer::Http);
238+
if (should_skip_analysis_) {
239+
current_span->setSkipAnalysis();
240+
}
241+
242+
spans_.push_back(current_span);
243+
return current_span;
244+
}
245+
239246
SegmentObject SegmentContextImpl::createSegmentObject() {
240247
SegmentObject obj;
241248
obj.set_traceid(trace_id_);

source/segment_context_impl.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@ class SegmentContextImpl : public SegmentContext {
144144
}
145145
#pragma endregion
146146

147-
CurrentSegmentSpanPtr createCurrentSegmentSpan(
147+
CurrentSegmentSpanPtr createExitSpan(
148148
CurrentSegmentSpanPtr parent_span) override;
149149

150-
CurrentSegmentSpanPtr createCurrentSegmentRootSpan() override;
150+
CurrentSegmentSpanPtr createEntrySpan() override;
151151
std::optional<std::string> createSW8HeaderValue(
152152
const std::string_view target_address) override {
153153
return createSW8HeaderValue(nullptr, target_address);
@@ -163,6 +163,7 @@ class SegmentContextImpl : public SegmentContext {
163163
private:
164164
std::string encodeSpan(CurrentSegmentSpanPtr parent_span,
165165
const std::string_view target_address);
166+
CurrentSegmentSpanPtr createSpan();
166167

167168
SpanContextPtr parent_span_context_;
168169
SpanContextExtensionPtr parent_ext_span_context_;

test/e2e/consumer.cc

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,28 +31,19 @@ void init() {
3131
config.set_address("collector:19876");
3232
}
3333

34-
void handlePong(Tracer* tracer, SegmentContext* scp,
35-
const httplib::Request& req, httplib::Response& response) {
36-
auto span = scp->createCurrentSegmentRootSpan();
37-
span->startSpan("/pong");
38-
span->endSpan();
39-
}
40-
4134
int main() {
4235
init();
4336

4437
httplib::Server svr;
4538
auto tracer = createInsecureGrpcTracer(config);
4639

4740
svr.Get("/pong", [&](const httplib::Request& req, httplib::Response& res) {
48-
if (req.has_header(kPropagationHeader.data())) {
49-
auto parent = req.get_header_value(kPropagationHeader.data());
50-
auto parent_span = createSpanContext(parent);
51-
52-
auto current_segment = tracer->newSegment(parent_span);
53-
handlePong(tracer.get(), current_segment.get(), req, res);
54-
tracer->sendSegment(std::move(current_segment));
55-
}
41+
auto parent = req.get_header_value(kPropagationHeader.data());
42+
auto segment_context = tracer->newSegment(createSpanContext(parent));
43+
44+
{ StartEntrySpan entry_span(segment_context, "/pong"); }
45+
46+
tracer->sendSegment(std::move(segment_context));
5647
});
5748

5849
svr.listen("0.0.0.0", 8080);

0 commit comments

Comments
 (0)