-
Notifications
You must be signed in to change notification settings - Fork 423
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 exporter #471
Zipkin exporter #471
Changes from 14 commits
8fd81b0
a532b72
861da22
586d5c9
babbc5d
5570d18
1039f4c
fd5a5b6
d4f22e3
0fbe3af
6d3e445
cdf5dc4
19d789f
84b0458
93beff6
81049c7
791e2a1
aff76e2
dcc758b
b3e96c7
4c97dc7
3806ee2
0125752
ae0a1a0
8c351c1
b2cfa8e
803eaa3
7283a85
67d6793
5764899
18390cf
590bbcf
b54304d
ad1506e
7450085
11ffb1e
e6e333f
d4c7217
653d084
e0057f3
6c89e4f
0eb9ce4
4633712
359a385
051fccf
acb1040
f97e6ca
fa6826a
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,31 @@ | ||
# Copyright 2020, OpenTelemetry Authors | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
include_directories(include) | ||
find_package(CURL REQUIRED) | ||
if(CURL_FOUND) | ||
add_compile_definitions(WITH_CURL) | ||
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. Seems 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. Thank. using |
||
endif() | ||
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. If this 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. Thank you. Have removed the |
||
add_library(zipkin_trace_exporter src/zipkin_exporter.cc src/recordable.cc) | ||
if(BUILD_TESTING) | ||
add_executable(zipkin_recordable_test test/zipkin_recordable_test.cc) | ||
|
||
target_link_libraries(zipkin_recordable_test ${GTEST_BOTH_LIBRARIES} | ||
${CMAKE_THREAD_LIBS_INIT} zipkin_trace_exporter) | ||
|
||
gtest_add_tests( | ||
TARGET zipkin_recordable_test | ||
TEST_PREFIX exporter. | ||
TEST_LIST zipkin_recordable_test) | ||
endif() # BUILD_TESTING |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#pragma once | ||
|
||
#include "nlohmann/json.hpp" | ||
#include "opentelemetry/sdk/trace/recordable.h" | ||
#include "opentelemetry/version.h" | ||
|
||
OPENTELEMETRY_BEGIN_NAMESPACE | ||
namespace exporter | ||
{ | ||
namespace zipkin | ||
{ | ||
using ZipkinSpan = nlohmann::json; | ||
|
||
enum class TransportFormat | ||
{ | ||
JSON, | ||
PROTOBUF | ||
}; | ||
|
||
class Recordable final : public sdk::trace::Recordable | ||
{ | ||
public: | ||
const ZipkinSpan &span() const noexcept { return span_; } | ||
|
||
void SetIds(trace::TraceId trace_id, | ||
trace::SpanId span_id, | ||
trace::SpanId parent_span_id) noexcept override; | ||
|
||
void SetAttribute(nostd::string_view key, | ||
const opentelemetry::common::AttributeValue &value) noexcept override; | ||
|
||
void AddEvent(nostd::string_view name, | ||
core::SystemTimestamp timestamp, | ||
const common::KeyValueIterable &attributes) noexcept override; | ||
|
||
void AddLink(const opentelemetry::trace::SpanContext &span_context, | ||
const common::KeyValueIterable &attributes) noexcept override; | ||
|
||
void SetStatus(trace::CanonicalCode code, nostd::string_view description) noexcept override; | ||
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. Update 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. Yes, plan to do it based on which PR is merged first :) |
||
|
||
void SetName(nostd::string_view name) noexcept override; | ||
|
||
void SetStartTime(opentelemetry::core::SystemTimestamp start_time) noexcept override; | ||
|
||
virtual void SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept override; | ||
|
||
void SetDuration(std::chrono::nanoseconds duration) noexcept override; | ||
|
||
private: | ||
ZipkinSpan span_; | ||
}; | ||
} // namespace zipkin | ||
} // namespace exporter | ||
OPENTELEMETRY_END_NAMESPACE |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
#pragma once | ||
|
||
#include "opentelemetry/exporters/zipkin/recordable.h" | ||
#include "opentelemetry/ext/http/client/http_client_factory.h" | ||
#include "opentelemetry/ext/http/common/url_parser.h" | ||
#include "opentelemetry/sdk/trace/exporter.h" | ||
#include "opentelemetry/sdk/trace/span_data.h" | ||
|
||
#include "nlohmann/json.hpp" | ||
|
||
OPENTELEMETRY_BEGIN_NAMESPACE | ||
namespace exporter | ||
{ | ||
namespace zipkin | ||
{ | ||
|
||
std::string ZIPKIN_ENDPOINT_DEFAULT = "http://localhost:9411/api/v2/spans"; | ||
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. Make this constant, and also change the naming to 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. done. |
||
|
||
/** | ||
* Struct to hold Zipkin exporter options. | ||
*/ | ||
struct ZipkinExporterOptions | ||
{ | ||
// The endpoint to export to. By default the OpenTelemetry Collector's default endpoint. | ||
std::string endpoint = ZIPKIN_ENDPOINT_DEFAULT; | ||
TransportFormat format = TransportFormat::JSON; | ||
std::string service_name = "default-service"; | ||
std::string ipv4; | ||
std::string ipv6; | ||
}; | ||
|
||
namespace trace_sdk = opentelemetry::sdk::trace; | ||
namespace http_client = opentelemetry::ext::http::client; | ||
|
||
/** | ||
* The Zipkin exporter exports span data in JSON format as expected by Zipkin | ||
*/ | ||
class ZipkinExporter final : public trace_sdk::SpanExporter, public http_client::EventHandler | ||
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. Why do you derive from 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. Good catch. It is not needed, and so removed. |
||
{ | ||
public: | ||
/** | ||
* Create an ZipkinExporter using all default options. | ||
lalitb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
ZipkinExporter(); | ||
|
||
/** | ||
* Create an ZipkinExporter using the given options. | ||
lalitb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
ZipkinExporter(const ZipkinExporterOptions &options); | ||
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. Use /cc @maxgolov @lalitb @g-easy @jsuereth I see that in other places you do not use 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. Agree. Will fix it. We follow google c++ style guide: Also, would be good to add 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. This is fixed for zipkin. Will have separate PR for adding |
||
|
||
/** | ||
* Create a span recordable. | ||
* @return a newly initialized Recordable object | ||
*/ | ||
std::unique_ptr<trace_sdk::Recordable> MakeRecordable() noexcept override; | ||
|
||
/** | ||
* Export a batch of span recordables in JSON format. | ||
* @param spans a span of unique pointers to span recordables | ||
*/ | ||
trace_sdk::ExportResult Export( | ||
const nostd::span<std::unique_ptr<trace_sdk::Recordable>> &spans) noexcept override; | ||
|
||
/** | ||
* Shut down the exporter. | ||
* @param timeout an optional timeout, default to max. | ||
*/ | ||
bool Shutdown( | ||
std::chrono::microseconds timeout = std::chrono::microseconds::max()) noexcept override | ||
{ | ||
return true; | ||
}; | ||
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. The extra 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. removed. |
||
|
||
void OnResponse(http_client::Response &response) noexcept override | ||
{ /*Not required */ | ||
} | ||
|
||
virtual void OnEvent(http_client::SessionState state, | ||
nostd::string_view msg) noexcept override{/* Not required */}; | ||
|
||
virtual void OnConnecting(const http_client::SSLCertificate &) noexcept override{ | ||
/* Not required */}; | ||
|
||
private: | ||
void InitializeLocalEndpoint(); | ||
|
||
private: | ||
// The configuration options associated with this exporter. | ||
bool isShutdown_ = false; | ||
std::shared_ptr<http_client::SessionManager> http_session_manager_; | ||
nlohmann::json local_end_point_; | ||
ZipkinExporterOptions options_; | ||
ext::http::common::UrlParser url_parser_; | ||
}; | ||
} // namespace zipkin | ||
} // namespace exporter | ||
OPENTELEMETRY_END_NAMESPACE |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
#include "opentelemetry/exporters/zipkin/recordable.h" | ||
|
||
#include <iostream> | ||
|
||
OPENTELEMETRY_BEGIN_NAMESPACE | ||
namespace exporter | ||
{ | ||
namespace zipkin | ||
{ | ||
|
||
const int kAttributeValueSize = 14; | ||
|
||
void Recordable::SetIds(trace::TraceId trace_id, | ||
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. Is this function reusable by other exporters, would they use the same field names? @mishal23 - you need to borrow this same code for ETW exporter, to populate the actual IDs in your PR. 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. @maxgolov - Not sure if we can move it to common. As different exporters expect these fields in different formats. E.g, 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. The implementation of |
||
trace::SpanId span_id, | ||
trace::SpanId parent_span_id) noexcept | ||
{ | ||
char traceid[trace::TraceId::kSize * 2] = {0}; | ||
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. Inconsistent naming for 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. Good catch. Have changed for all three id's. |
||
trace_id.ToLowerBase16(traceid); | ||
char spanid[trace::SpanId::kSize * 2] = {0}; | ||
span_id.ToLowerBase16(spanid); | ||
char parentid[trace::SpanId::kSize * 2] = {0}; | ||
parent_span_id.ToLowerBase16(parentid); | ||
span_["id"] = std::string(spanid, 16); | ||
span_["parentId"] = std::string(parentid, 16); | ||
span_["traceId"] = std::string(traceid, 32); | ||
} | ||
|
||
void PopulateAttribute(nlohmann::json &attribute, | ||
nostd::string_view key, | ||
const opentelemetry::common::AttributeValue &value) | ||
{ | ||
// Assert size of variant to ensure that this method gets updated if the variant | ||
// definition changes | ||
static_assert( | ||
nostd::variant_size<opentelemetry::common::AttributeValue>::value == kAttributeValueSize, | ||
"AttributeValue contains unknown type"); | ||
|
||
// attribute->set_key(key.data(), key.size()); | ||
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. Is this still needed? 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. Not needed. Removed it. |
||
|
||
if (nostd::holds_alternative<bool>(value)) | ||
{ | ||
attribute[key.data()] = nostd::get<bool>(value); | ||
} | ||
else if (nostd::holds_alternative<int>(value)) | ||
{ | ||
attribute[key.data()] = nostd::get<int>(value); | ||
} | ||
else if (nostd::holds_alternative<int64_t>(value)) | ||
{ | ||
|
||
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. Remove this blank line. 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. done :) |
||
attribute[key.data()] = nostd::get<int64_t>(value); | ||
} | ||
else if (nostd::holds_alternative<unsigned int>(value)) | ||
{ | ||
attribute[key.data()] = nostd::get<unsigned int>(value); | ||
} | ||
else if (nostd::holds_alternative<uint64_t>(value)) | ||
{ | ||
attribute[key.data()] = nostd::get<uint64_t>(value); | ||
} | ||
else if (nostd::holds_alternative<double>(value)) | ||
{ | ||
attribute[key.data()] = nostd::get<double>(value); | ||
} | ||
else if (nostd::holds_alternative<nostd::string_view>(value)) | ||
{ | ||
attribute[key.data()] = nostd::string_view(nostd::get<nostd::string_view>(value).data(), | ||
nostd::get<nostd::string_view>(value).size()); | ||
} | ||
else if (nostd::holds_alternative<nostd::span<const bool>>(value)) | ||
{ | ||
attribute[key.data()] = {}; | ||
for (const auto &val : nostd::get<nostd::span<const bool>>(value)) | ||
{ | ||
attribute[key.data()].push_back(val); | ||
} | ||
} | ||
else if (nostd::holds_alternative<nostd::span<const int>>(value)) | ||
{ | ||
attribute[key.data()] = {}; | ||
for (const auto &val : nostd::get<nostd::span<const int>>(value)) | ||
{ | ||
attribute[key.data()].push_back(val); | ||
} | ||
} | ||
else if (nostd::holds_alternative<nostd::span<const int64_t>>(value)) | ||
{ | ||
attribute[key.data()] = {}; | ||
for (const auto &val : nostd::get<nostd::span<const int64_t>>(value)) | ||
{ | ||
attribute[key.data()].push_back(val); | ||
} | ||
} | ||
else if (nostd::holds_alternative<nostd::span<const unsigned int>>(value)) | ||
{ | ||
attribute[key.data()] = {}; | ||
for (const auto &val : nostd::get<nostd::span<const unsigned int>>(value)) | ||
{ | ||
attribute[key.data()].push_back(val); | ||
} | ||
} | ||
else if (nostd::holds_alternative<nostd::span<const uint64_t>>(value)) | ||
{ | ||
attribute[key.data()] = {}; | ||
for (const auto &val : nostd::get<nostd::span<const uint64_t>>(value)) | ||
{ | ||
attribute[key.data()].push_back(val); | ||
} | ||
} | ||
else if (nostd::holds_alternative<nostd::span<const double>>(value)) | ||
{ | ||
attribute[key.data()] = {}; | ||
for (const auto &val : nostd::get<nostd::span<const double>>(value)) | ||
{ | ||
attribute[key.data()].push_back(val); | ||
} | ||
} | ||
else if (nostd::holds_alternative<nostd::span<const nostd::string_view>>(value)) | ||
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. Just curious about the perf implication of these if-else checks, not sure if a small jump table would make it faster. Not a blocker for this PR though. 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. Yes, we are doing these variant checks at multiple places, would be better to use dispatch table throughout. Will add a ticket for same. |
||
{ | ||
attribute[key.data()] = {}; | ||
for (const auto &val : nostd::get<nostd::span<const nostd::string_view>>(value)) | ||
{ | ||
attribute[key.data()].push_back(std::string(val.data(), val.size())); | ||
} | ||
} | ||
} | ||
|
||
void Recordable::SetAttribute(nostd::string_view key, | ||
const opentelemetry::common::AttributeValue &value) noexcept | ||
{ | ||
if (!span_.contains("tags")) | ||
{ | ||
span_["tags"] = nlohmann::json::object(); | ||
} | ||
PopulateAttribute(span_["tags"], key, value); | ||
} | ||
|
||
void Recordable::AddEvent(nostd::string_view name, | ||
core::SystemTimestamp timestamp, | ||
const common::KeyValueIterable &attributes) noexcept | ||
{ | ||
nlohmann::json annotations = nlohmann::json::object(); // empty object | ||
attributes.ForEachKeyValue([&](nostd::string_view key, common::AttributeValue value) noexcept { | ||
PopulateAttribute(annotations, key, value); | ||
return true; | ||
}); | ||
span_["annotations"][name.data()]["value"] = annotations; | ||
span_["annotations"]["timestamp"] = | ||
std::chrono::duration_cast<std::chrono::milliseconds>(timestamp.time_since_epoch()).count(); | ||
} | ||
|
||
void Recordable::AddLink(const opentelemetry::trace::SpanContext &span_context, | ||
const common::KeyValueIterable &attributes) noexcept | ||
{ | ||
// TODO: Currently not supported by specs: | ||
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/sdk_exporters/zipkin.md | ||
} | ||
|
||
void Recordable::SetStatus(trace::CanonicalCode code, nostd::string_view description) noexcept | ||
{ | ||
span_["tags"]["otel.status_code"] = code; | ||
if (description.size()) | ||
span_["tags"]["otel.status_description"] = description; | ||
} | ||
|
||
void Recordable::SetName(nostd::string_view name) noexcept | ||
{ | ||
span_["name"] = name.data(); | ||
} | ||
|
||
void Recordable::SetStartTime(opentelemetry::core::SystemTimestamp start_time) noexcept | ||
{ | ||
span_["timestamp"] = start_time.time_since_epoch().count(); | ||
} | ||
|
||
void Recordable::SetDuration(std::chrono::nanoseconds duration) noexcept | ||
{ | ||
span_["duration"] = duration.count(); | ||
} | ||
|
||
void Recordable::SetSpanKind(opentelemetry::trace::SpanKind span_kind) noexcept {} | ||
} // namespace zipkin | ||
} // namespace exporter | ||
OPENTELEMETRY_END_NAMESPACE |
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.
Do we need update this to
Copyright 2021
?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.
fixed