-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Zipkin library #692
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
Zipkin library #692
Changes from all commits
e7ad40a
3f1225c
05bf311
01838f8
40e945e
7e7bf38
9cdf72f
d264083
aeb15f5
7e9fc97
4a5d901
9f0e96e
4ae612c
6e2e7b6
eaedc32
f899947
6d740b4
6f1b778
0c79766
ea4e8f4
fdd3933
63f9183
c5daf35
2874f88
2c8884a
7bb9fe2
b994a8c
9b001e5
04e373e
dfda5be
4cd118a
41c0f57
a54f433
e0ecb77
c1e8841
7ace62b
d5bcc2f
8ad37c3
dc1e357
c53be46
759c6bc
6cb2ac9
e14d861
9560f8e
50322ee
4628898
a06e89b
be09c20
cff6194
d236ceb
7c5dfae
f0fda03
4d424c9
8b0d96a
11318bb
e27d597
55e8dbf
a157ad0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| licenses(["notice"]) # Apache 2 | ||
|
|
||
| package(default_visibility = ["//visibility:public"]) | ||
|
|
||
| load("//bazel:envoy_build_system.bzl", "envoy_cc_library") | ||
|
|
||
| envoy_cc_library( | ||
| name = "zipkin_lib", | ||
| srcs = [ | ||
| "span_buffer.cc", | ||
| "span_context.cc", | ||
| "tracer.cc", | ||
| "util.cc", | ||
| "zipkin_core_types.cc", | ||
| ], | ||
| hdrs = [ | ||
| "span_buffer.h", | ||
| "span_context.h", | ||
| "tracer.h", | ||
| "tracer_interface.h", | ||
| "util.h", | ||
| "zipkin_core_constants.h", | ||
| "zipkin_core_types.h", | ||
| "zipkin_json_field_names.h", | ||
| ], | ||
| external_deps = ["rapidjson"], | ||
| deps = [ | ||
| "//include/envoy/common:optional", | ||
| "//include/envoy/common:time_interface", | ||
| "//include/envoy/runtime:runtime_interface", | ||
| "//source/common/common:hex_lib", | ||
| "//source/common/common:singleton", | ||
| "//source/common/common:utility_lib", | ||
| "//source/common/network:address_lib", | ||
| ], | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| #include "common/tracing/zipkin/span_buffer.h" | ||
|
|
||
| namespace Zipkin { | ||
|
|
||
| bool SpanBuffer::addSpan(SpanPtr&& span) { | ||
| if (span_buffer_.size() == span_buffer_.capacity()) { | ||
| // Buffer full | ||
| return false; | ||
| } | ||
| span_buffer_.push_back(std::move(span)); | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| std::string SpanBuffer::toStringifiedJsonArray() { | ||
| std::string stringified_json_array = "["; | ||
|
|
||
| if (pendingSpans()) { | ||
| stringified_json_array += span_buffer_[0]->toJson(); | ||
| const uint64_t size = span_buffer_.size(); | ||
| for (uint64_t i = 1; i < size; i++) { | ||
| stringified_json_array += ","; | ||
| stringified_json_array += span_buffer_[i]->toJson(); | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could consider using https://github.com/lyft/envoy/blob/master/source/common/json/json_loader.h#L22, which does basically what's being done here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, Json::Factory::listAsJsonString() does not do exactly the same thing. As a result of that method, the elements of the stringified json array are stringified as well, which means that an extra level of double-quote escaping would be present. That was not my intention. |
||
| } | ||
| stringified_json_array += "]"; | ||
|
|
||
| return stringified_json_array; | ||
| } | ||
| } // Zipkin | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| #pragma once | ||
|
|
||
| #include "common/tracing/zipkin/zipkin_core_types.h" | ||
|
|
||
| namespace Zipkin { | ||
|
|
||
| /** | ||
| * This class implements a simple buffer to store Zipkin tracing spans | ||
| * prior to flushing them. | ||
| */ | ||
| class SpanBuffer { | ||
| public: | ||
| /** | ||
| * Constructor that creates an empty buffer. Space needs to be allocated by invoking | ||
| * the method allocateBuffer(size). | ||
| */ | ||
| SpanBuffer() {} | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does anyone use the default constructor? If not I'd suggest removing it.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| /** | ||
| * Constructor that initializes a buffer with the given size. | ||
| * | ||
| * @param size The desired buffer size. | ||
| */ | ||
| SpanBuffer(uint64_t size) { allocateBuffer(size); } | ||
|
|
||
| /** | ||
| * Allocates space for an empty buffer or resizes a previously-allocated one. | ||
| * | ||
| * @param size The desired buffer size. | ||
| */ | ||
| void allocateBuffer(uint64_t size) { span_buffer_.reserve(size); } | ||
|
|
||
| /** | ||
| * Adds the given Zipkin span to the buffer. | ||
| * | ||
| * @param span The span to be added to the buffer. | ||
| * | ||
| * @return true if the span was successfully added, or false if the buffer was full. | ||
| */ | ||
| bool addSpan(SpanPtr&& span); | ||
|
|
||
| /** | ||
| * Empties the buffer. This method is supposed to be called when all buffered spans | ||
| * have been sent to to the Zipkin service. | ||
| */ | ||
| void clear() { span_buffer_.clear(); } | ||
|
|
||
| /** | ||
| * @return the number of spans currently buffered. | ||
| */ | ||
| uint64_t pendingSpans() { return span_buffer_.size(); } | ||
|
|
||
| /** | ||
| * @return the contents of the buffer as a stringified array of JSONs, where | ||
| * each JSON in the array corresponds to one Zipkin span. | ||
| */ | ||
| std::string toStringifiedJsonArray(); | ||
|
|
||
| private: | ||
| // We use a pre-allocated vector to improve performance | ||
| std::vector<SpanPtr> span_buffer_; | ||
| }; | ||
| } // Zipkin | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,152 @@ | ||
| #include "common/tracing/zipkin/span_context.h" | ||
|
|
||
| #include "common/common/utility.h" | ||
| #include "common/tracing/zipkin/zipkin_core_constants.h" | ||
|
|
||
| namespace Zipkin { | ||
|
|
||
| namespace { | ||
| // The functions below are needed due to the "C++ static initialization order fiasco." | ||
|
|
||
| /** | ||
| * @return String that separates the span-context fields in its string-serialized form. | ||
| */ | ||
| static const std::string& fieldSeparator() { | ||
| static const std::string* field_separator = new std::string(";"); | ||
|
|
||
| return *field_separator; | ||
| } | ||
|
|
||
| /** | ||
| * @return String value corresponding to an empty span context. | ||
| */ | ||
| static const std::string& unitializedSpanContext() { | ||
| static const std::string* unitialized_span_context = | ||
| new std::string("0000000000000000" + fieldSeparator() + "0000000000000000" + | ||
| fieldSeparator() + "0000000000000000"); | ||
|
|
||
| return *unitialized_span_context; | ||
| } | ||
|
|
||
| /** | ||
| * @return String with regular expression to match a 16-digit hexadecimal number. | ||
| */ | ||
| static const std::string& hexDigitGroupRegexStr() { | ||
| static const std::string* hex_digit_group_regex_str = new std::string("([0-9,a-z]{16})"); | ||
|
|
||
| return *hex_digit_group_regex_str; | ||
| } | ||
|
|
||
| /** | ||
| * @return a string with a regular expression to match a valid string-serialized span context. | ||
| * | ||
| * Note that a function is needed because we cannot concatenate static strings from | ||
| * different compilation units at initialization time (the initialization order is not | ||
| * guaranteed). In this case, the compilation units are ZipkinCoreConstants and SpanContext. | ||
| */ | ||
| static const std::string& spanContextRegexStr() { | ||
| // ^([0-9,a-z]{16});([0-9,a-z]{16});([0-9,a-z]{16})((;(cs|sr|cr|ss))*)$ | ||
| static const std::string* span_context_regex_str = new std::string( | ||
| "^" + hexDigitGroupRegexStr() + fieldSeparator() + hexDigitGroupRegexStr() + | ||
| fieldSeparator() + hexDigitGroupRegexStr() + "((" + fieldSeparator() + "(" + | ||
| ZipkinCoreConstants::get().CLIENT_SEND + "|" + ZipkinCoreConstants::get().SERVER_RECV + "|" + | ||
| ZipkinCoreConstants::get().CLIENT_RECV + "|" + ZipkinCoreConstants::get().SERVER_SEND + | ||
| "))*)$"); | ||
|
|
||
| return *span_context_regex_str; | ||
| } | ||
|
|
||
| /** | ||
| * @return a regex to match a valid string-serialization of a span context. | ||
| * | ||
| * Note that a function is needed because the string used to build the regex | ||
| * cannot be initialized statically. | ||
| */ | ||
| static const std::regex& spanContextRegex() { | ||
| static const std::regex* span_context_regex = new std::regex(spanContextRegexStr()); | ||
|
|
||
| return *span_context_regex; | ||
| } | ||
| } // namespace | ||
|
|
||
| SpanContext::SpanContext(const Span& span) { | ||
| trace_id_ = span.traceId(); | ||
| id_ = span.id(); | ||
| parent_id_ = span.isSetParentId() ? span.parentId() : 0; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you initialize these via the constructor's member initializer list? It might allows you to make these fields
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These fields are not supposed to be |
||
|
|
||
| for (const Annotation& annotation : span.annotations()) { | ||
| if (annotation.value() == ZipkinCoreConstants::get().CLIENT_RECV) { | ||
| annotation_values_.cr_ = true; | ||
| } else if (annotation.value() == ZipkinCoreConstants::get().CLIENT_SEND) { | ||
| annotation_values_.cs_ = true; | ||
| } else if (annotation.value() == ZipkinCoreConstants::get().SERVER_RECV) { | ||
| annotation_values_.sr_ = true; | ||
| } else if (annotation.value() == ZipkinCoreConstants::get().SERVER_SEND) { | ||
| annotation_values_.ss_ = true; | ||
| } | ||
| } | ||
|
|
||
| is_initialized_ = true; | ||
| } | ||
|
|
||
| const std::string SpanContext::serializeToString() { | ||
| if (!is_initialized_) { | ||
| return unitializedSpanContext(); | ||
| } | ||
|
|
||
| std::string result; | ||
| result = traceIdAsHexString() + fieldSeparator() + idAsHexString() + fieldSeparator() + | ||
| parentIdAsHexString(); | ||
|
|
||
| if (annotation_values_.cr_) { | ||
| result += fieldSeparator() + ZipkinCoreConstants::get().CLIENT_RECV; | ||
| } | ||
| if (annotation_values_.cs_) { | ||
| result += fieldSeparator() + ZipkinCoreConstants::get().CLIENT_SEND; | ||
| } | ||
| if (annotation_values_.sr_) { | ||
| result += fieldSeparator() + ZipkinCoreConstants::get().SERVER_RECV; | ||
| } | ||
| if (annotation_values_.ss_) { | ||
| result += fieldSeparator() + ZipkinCoreConstants::get().SERVER_SEND; | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| void SpanContext::populateFromString(const std::string& span_context_str) { | ||
| std::smatch match; | ||
|
|
||
| trace_id_ = parent_id_ = id_ = 0; | ||
| annotation_values_.cs_ = annotation_values_.cr_ = annotation_values_.ss_ = | ||
| annotation_values_.sr_ = false; | ||
|
|
||
| if (std::regex_search(span_context_str, match, spanContextRegex())) { | ||
| // This is a valid string encoding of the context | ||
| trace_id_ = std::stoull(match.str(1), nullptr, 16); | ||
| id_ = std::stoull(match.str(2), nullptr, 16); | ||
| parent_id_ = std::stoull(match.str(3), nullptr, 16); | ||
|
|
||
| std::string matched_annotations = match.str(4); | ||
| if (matched_annotations.size() > 0) { | ||
| std::vector<std::string> annotation_value_strings = | ||
| StringUtil::split(matched_annotations, fieldSeparator()); | ||
| for (const std::string& annotation_value : annotation_value_strings) { | ||
| if (annotation_value == ZipkinCoreConstants::get().CLIENT_RECV) { | ||
| annotation_values_.cr_ = true; | ||
| } else if (annotation_value == ZipkinCoreConstants::get().CLIENT_SEND) { | ||
| annotation_values_.cs_ = true; | ||
| } else if (annotation_value == ZipkinCoreConstants::get().SERVER_RECV) { | ||
| annotation_values_.sr_ = true; | ||
| } else if (annotation_value == ZipkinCoreConstants::get().SERVER_SEND) { | ||
| annotation_values_.ss_ = true; | ||
| } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is some commonality in this if-else block with the constructor block that does the same. Please consider refactoring in the future.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @htuch, @mattklein123, @RomanDzhabarov, @adriancole : Thanks for your reviews! |
||
| } | ||
| } | ||
|
|
||
| is_initialized_ = true; | ||
| } else { | ||
| is_initialized_ = false; | ||
| } | ||
| } | ||
| } // Zipkin | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could also be done with
std::hexandstd::stringstreamin 2-3 lines of code. Is there a reason you opted to do the manual unrolling? If so, please add a comment to the code.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason is performance.