-
Notifications
You must be signed in to change notification settings - Fork 297
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
Add span events and status #794
base: develop
Are you sure you want to change the base?
Changes from all commits
5900cf2
056e86a
1b9d466
d01412d
b2227f5
9dd35c2
beca706
24e8e36
7004622
57ead82
aea058c
c92d19c
84a6697
cd47c49
13b1fcf
d72491d
e27246b
b6704d5
f2d0fd8
146372d
5472f64
ef410e3
1e1c73d
a143bb5
1f61f80
715dc7e
3d7d18a
10cf365
8d796e5
0afe674
a679266
4a761bd
0959e57
6315ede
777ea21
27d80e2
ebd5001
1da9c84
5392321
86a774c
64386d7
9b00bad
84c7ac7
3c9ddfd
7896216
cb5bd17
140eac9
5114a21
a99e283
eaa9ef7
d70c85a
814b22f
9cee7c0
5046d79
4885f86
181bbf2
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 |
---|---|---|
|
@@ -33,6 +33,34 @@ class Span final { | |
public: | ||
class Impl; | ||
|
||
/// @brief Operation status code. | ||
enum class StatusCode : uint8_t { | ||
kUnset, // Default status. | ||
kOk, // Operation has completed successfully. | ||
kError, // The operation contains an error. | ||
}; | ||
|
||
/// @brief Span event. | ||
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.
|
||
/// @see | ||
/// https://github.com/open-telemetry/opentelemetry-proto/blob/main/opentelemetry/proto/trace/v1/trace.proto#L222. | ||
/// @todo Implement attributes. | ||
struct Event final { | ||
/// @brief Constructor. | ||
/// @param name Event name. | ||
explicit Event(std::string_view name); | ||
|
||
/// @brief Constructor. | ||
/// @param name Event name. | ||
/// @param time_unix_nano Event timestamp. | ||
Event(std::string_view name, uint64_t time_unix_nano); | ||
|
||
/// @brief Event name. | ||
std::string name; | ||
|
||
/// @brief Event timestamp. | ||
uint64_t time_unix_nano{}; | ||
}; | ||
|
||
explicit Span( | ||
TracerPtr tracer, | ||
std::string name, | ||
|
@@ -157,6 +185,12 @@ class Span final { | |
/// @overload AddNonInheritableTag | ||
void AddNonInheritableTags(const logging::LogExtra&); | ||
|
||
/// Add an event to Span. | ||
void AddEvent(std::string_view event_name); | ||
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. How do you plan to add attributes to event class? |
||
|
||
/// Set span status. | ||
void SetStatus(StatusCode status, const std::string_view description); | ||
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. We already has method that sets error flag: It is bad to have two methods for making the same result. I think the right solution is to remove and use Maybe there is another better solution? |
||
|
||
/// @brief Sets level for tags logging | ||
void SetLogLevel(logging::Level log_level); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -149,6 +149,7 @@ void Span::Impl::PutIntoLogger(logging::impl::TagWriter writer) && { | |
} | ||
writer.PutLogExtra(log_extra_inheritable_); | ||
|
||
LogEvents(writer); | ||
LogOpenTracing(); | ||
} | ||
|
||
|
@@ -368,6 +369,29 @@ void Span::AddTagFrozen(std::string key, logging::LogExtra::Value value) { | |
pimpl_->log_extra_inheritable_.Extend(std::move(key), std::move(value), logging::LogExtra::ExtendType::kFrozen); | ||
} | ||
|
||
void Span::AddEvent(std::string_view event_name) { pimpl_->events_.emplace_back(event_name); } | ||
|
||
// Opentelemetry implementation: | ||
// https://github.com/open-telemetry/opentelemetry-cpp/blob/v1.18.0/exporters/zipkin/src/recordable.cc#L220 | ||
void Span::SetStatus(StatusCode status, const std::string_view description) { | ||
if (status == StatusCode::kUnset) { | ||
return; | ||
} | ||
|
||
AddTag("otel.status_description", std::string{description}); | ||
|
||
if (status == StatusCode::kError) { | ||
// Error description is required by OpenTelemetry | ||
|
||
// Service data | ||
AddTag("otel.status_code", "ERROR"); | ||
AddTag("error", "true"); | ||
} else { | ||
AddTag("otel.status_code", "OK"); | ||
AddTag("error", "false"); | ||
} | ||
Comment on lines
+377
to
+392
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. place the implementation to anonymous namespace please |
||
} | ||
|
||
void Span::SetLink(std::string link) { AddTagFrozen(kLinkTag, std::move(link)); } | ||
|
||
void Span::SetParentLink(std::string parent_link) { AddTagFrozen(kParentLinkTag, std::move(parent_link)); } | ||
|
@@ -412,6 +436,11 @@ const Span::Impl* GetParentSpanImpl() { | |
return !spans_ptr || spans_ptr->empty() ? nullptr : &spans_ptr->back(); | ||
} | ||
|
||
Span::Event::Event(std::string_view name) | ||
: Span::Event{name, static_cast<uint64_t>(std::chrono::system_clock::now().time_since_epoch().count())} {} | ||
|
||
Span::Event::Event(std::string_view name, uint64_t time_unix_nano) : name{name}, time_unix_nano{time_unix_nano} {} | ||
|
||
namespace impl { | ||
|
||
struct DetachLocalSpansScope::Impl { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,7 @@ class Span::Impl : public boost::intrusive::list_base_hook<boost::intrusive::lin | |
void AttachToCoroStack(); | ||
|
||
private: | ||
void LogEvents(logging::impl::TagWriter& writer) const; | ||
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. Let's remove the method and do all work in DoLogOpenTracing Something like in the end of if (!events_.empty()) {
const auto events_tag = jaeger::MakeTagFromEvents(events_);
writer.PutTag(jaeger::kEvents, events_tag);
} where std::string MakeTagFromEvents(const std::vector<Span::Event>& events) {
formats::json::StringBuilder builder;
{
const formats::json::StringBuilder::ObjectGuard event_guard(builder);
for (const auto& event : events) {
builder.Key(event.name);
builder.WriteUInt64(event.time_unix_nano);
}
}
return builder.GetString();
} |
||
void LogOpenTracing() const; | ||
void DoLogOpenTracing(logging::impl::TagWriter writer) const; | ||
static void AddOpentracingTags(formats::json::StringBuilder& output, const logging::LogExtra& input); | ||
|
@@ -105,6 +106,8 @@ class Span::Impl : public boost::intrusive::list_base_hook<boost::intrusive::lin | |
const ReferenceType reference_type_; | ||
utils::impl::SourceLocation source_location_; | ||
|
||
std::vector<Span::Event> events_; | ||
|
||
friend class Span; | ||
friend class SpanBuilder; | ||
friend class TagScope; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,6 +44,15 @@ LoggerComponent::LoggerComponent(const components::ComponentConfig& config, cons | |
logger_config.extra_attributes = config["extra-attributes"].As<std::unordered_map<std::string, std::string>>({}); | ||
logger_config.attributes_mapping = | ||
config["attributes-mapping"].As<std::unordered_map<std::string, std::string>>({}); | ||
|
||
// Define error mapping | ||
if (logger_config.attributes_mapping.find("otel_status_code") == logger_config.attributes_mapping.end()) { | ||
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 constants for |
||
logger_config.attributes_mapping["otel_status_code"] = "otel.status_code"; | ||
} | ||
if (logger_config.attributes_mapping.find("otel_status_description") == logger_config.attributes_mapping.end()) { | ||
logger_config.attributes_mapping["otel_status_description"] = "otel.status_description"; | ||
} | ||
|
||
logger_config.logs_sink = config["sinks"]["logs"].As<SinkType>(SinkType::kOtlp); | ||
logger_config.tracing_sink = config["sinks"]["tracing"].As<SinkType>(SinkType::kOtlp); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
#include <iostream> | ||
|
||
#include <userver/engine/async.hpp> | ||
#include <userver/formats/json.hpp> | ||
#include <userver/formats/parse/common_containers.hpp> | ||
#include <userver/formats/parse/to.hpp> | ||
#include <userver/logging/impl/tag_writer.hpp> | ||
|
@@ -21,11 +22,40 @@ USERVER_NAMESPACE_BEGIN | |
namespace otlp { | ||
|
||
namespace { | ||
|
||
constexpr std::string_view kTelemetrySdkLanguage = "telemetry.sdk.language"; | ||
constexpr std::string_view kTelemetrySdkName = "telemetry.sdk.name"; | ||
constexpr std::string_view kServiceName = "service.name"; | ||
|
||
const std::string kTimestampFormat = "%Y-%m-%dT%H:%M:%E*S"; | ||
|
||
std::vector<tracing::Span::Event> GetEventsFromValue(const std::string_view value) { | ||
std::vector<tracing::Span::Event> events; | ||
|
||
const auto json_value = formats::json::FromString(value); | ||
|
||
if (!json_value.IsObject()) { | ||
throw std::runtime_error("Expected JSON object in \"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. you can add |
||
for (const auto& [key, value] : formats::common::Items(json_value)) { | ||
events.emplace_back(key, value.As<uint64_t>()); | ||
} | ||
|
||
return events; | ||
} | ||
|
||
void WriteEventsFromValue(::opentelemetry::proto::trace::v1::Span& span, std::string_view value) { | ||
const std::vector<tracing::Span::Event> events = GetEventsFromValue(value); | ||
span.mutable_events()->Reserve(events.size()); | ||
|
||
fdr400 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for (const auto& event : events) { | ||
auto* event_proto = span.add_events(); | ||
event_proto->set_name(event.name); | ||
event_proto->set_time_unix_nano(event.time_unix_nano); | ||
} | ||
} | ||
|
||
} // namespace | ||
|
||
SinkType Parse(const yaml_config::YamlConfig& value, formats::parse::To<SinkType>) { | ||
|
@@ -93,8 +123,9 @@ void Logger::Log(logging::Level level, std::string_view msg) { | |
|
||
++stats_.by_level[static_cast<int>(level)]; | ||
|
||
[[maybe_unused]] auto parse_ok = | ||
utils::encoding::TskvReadRecord(parser, [&](std::string_view key, std::string_view value) { | ||
[[maybe_unused]] auto parse_ok = utils::encoding::TskvReadRecord( | ||
parser, | ||
[this, &log_record, ×tamp](std::string_view key, std::string_view value) { | ||
if (key == "text") { | ||
log_record.mutable_body()->set_string_value(grpc::string(std::string{value})); | ||
return true; | ||
|
@@ -120,7 +151,8 @@ void Logger::Log(logging::Level level, std::string_view msg) { | |
attributes->set_key(std::string{MapAttribute(key)}); | ||
attributes->mutable_value()->set_string_value(std::string{value}); | ||
return true; | ||
}); | ||
} | ||
); | ||
|
||
auto nanoseconds = std::chrono::duration_cast<std::chrono::nanoseconds>(timestamp.time_since_epoch()); | ||
log_record.set_time_unix_nano(nanoseconds.count()); | ||
|
@@ -147,8 +179,9 @@ void Logger::Trace(logging::Level level, std::string_view msg) { | |
std::string start_timestamp; | ||
std::string total_time; | ||
|
||
[[maybe_unused]] auto parse_ok = | ||
utils::encoding::TskvReadRecord(parser, [&](std::string_view key, std::string_view value) { | ||
[[maybe_unused]] auto parse_ok = utils::encoding::TskvReadRecord( | ||
nepridumalnik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
parser, | ||
[this, &span, &start_timestamp, &total_time](std::string_view key, std::string_view value) { | ||
if (key == "trace_id") { | ||
span.set_trace_id(utils::encoding::FromHex(value)); | ||
return true; | ||
|
@@ -176,12 +209,24 @@ void Logger::Trace(logging::Level level, std::string_view msg) { | |
if (key == "timestamp" || key == "text") { | ||
return true; | ||
} | ||
if (key == "events") { | ||
WriteEventsFromValue(span, value); | ||
return true; | ||
} | ||
if (key == "error") { | ||
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. so, if error key is a special keys, maybe add assert that it is not in attributes mapping? |
||
auto attributes = span.add_attributes(); | ||
attributes->set_key(std::string{key}); | ||
attributes->mutable_value()->set_bool_value(value == "true"); | ||
|
||
return true; | ||
} | ||
|
||
auto attributes = span.add_attributes(); | ||
attributes->set_key(std::string{MapAttribute(key)}); | ||
attributes->mutable_value()->set_string_value(std::string{value}); | ||
return true; | ||
}); | ||
} | ||
); | ||
|
||
auto start_timestamp_double = std::stod(start_timestamp); | ||
span.set_start_time_unix_nano(start_timestamp_double * 1'000'000'000); | ||
|
@@ -226,7 +271,8 @@ void Logger::SendingLoop(Queue::Consumer& consumer, LogClient& log_client, Trace | |
[&scope_logs](const opentelemetry::proto::logs::v1::LogRecord& action) { | ||
auto log_records = scope_logs->add_log_records(); | ||
*log_records = action; | ||
}}, | ||
} | ||
}, | ||
action | ||
); | ||
} while (consumer.Pop(action, deadline)); | ||
|
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.