Skip to content
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

[EXPORTER] Elastic Search exporter follow ECS guidelines #3107

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ class ElasticSearchRecordable final : public sdk::logs::Recordable
void WriteValue(const opentelemetry::common::AttributeValue &value, const std::string &name);

public:
ElasticSearchRecordable() noexcept;

/**
* Returns a JSON object contain the log information
*/
Expand Down
50 changes: 44 additions & 6 deletions exporters/elasticsearch/src/es_log_recordable.cc
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ void ElasticSearchRecordable::WriteValue(const opentelemetry::common::AttributeV
}
}

ElasticSearchRecordable::ElasticSearchRecordable() noexcept : sdk::logs::Recordable()
{
json_["ecs"]["version"] = "8.11.0";
}

nlohmann::json ElasticSearchRecordable::GetJSON() noexcept
{
return json_;
Expand All @@ -205,7 +210,38 @@ nlohmann::json ElasticSearchRecordable::GetJSON() noexcept
void ElasticSearchRecordable::SetTimestamp(
opentelemetry::common::SystemTimestamp timestamp) noexcept
{
json_["timestamp"] = timestamp.time_since_epoch().count();
const std::chrono::system_clock::time_point timePoint{timestamp};

// If built with with at least cpp 20 then use std::format
// Otherwise use the old style to format the timestamp in UTC
#if __cplusplus >= 202002L
const std::string dateStr = std::format("{:%FT%T%Ez}", timePoint);
#else
const static int dateToSecondsSize = 19;
const static int millisecondsSize = 8;
const static int timeZoneSize = 1;
const static int dateSize = dateToSecondsSize + millisecondsSize + timeZoneSize;

std::time_t time = std::chrono::system_clock::to_time_t(timePoint);
std::tm tm = *std::gmtime(&time);

char bufferDate[dateSize]; // example: 2024-10-18T07:26:00.123456Z
std::strftime(bufferDate, sizeof(bufferDate), "%Y-%m-%dT%H:%M:%S", &tm);
auto microseconds =
std::chrono::duration_cast<std::chrono::microseconds>(timePoint.time_since_epoch()) %
std::chrono::seconds(1);

char bufferMilliseconds[millisecondsSize];
std::snprintf(bufferMilliseconds, sizeof(bufferMilliseconds), ".%06ld",
static_cast<long>(microseconds.count()));

std::strcat(bufferDate, bufferMilliseconds);
std::strcat(bufferDate, "Z");

const std::string dateStr(bufferDate);
#endif

json_["@timestamp"] = dateStr;
}

void ElasticSearchRecordable::SetObservedTimestamp(
Expand All @@ -216,23 +252,25 @@ void ElasticSearchRecordable::SetObservedTimestamp(

void ElasticSearchRecordable::SetSeverity(opentelemetry::logs::Severity severity) noexcept
{
auto &severityField = json_["log"]["level"];

// Convert the severity enum to a string
std::uint32_t severity_index = static_cast<std::uint32_t>(severity);
if (severity_index >= std::extent<decltype(opentelemetry::logs::SeverityNumToText)>::value)
{
std::stringstream sout;
sout << "Invalid severity(" << severity_index << ")";
json_["severity"] = sout.str();
severityField = sout.str();
}
else
{
json_["severity"] = opentelemetry::logs::SeverityNumToText[severity_index];
severityField = opentelemetry::logs::SeverityNumToText[severity_index];
}
}

void ElasticSearchRecordable::SetBody(const opentelemetry::common::AttributeValue &message) noexcept
{
WriteValue(message, "body");
WriteValue(message, "message");
}

void ElasticSearchRecordable::SetTraceId(const opentelemetry::trace::TraceId &trace_id) noexcept
Expand Down Expand Up @@ -275,7 +313,7 @@ void ElasticSearchRecordable::SetAttribute(
nostd::string_view key,
const opentelemetry::common::AttributeValue &value) noexcept
{
WriteKeyValue(key, value, "attributes");
WriteValue(value, key.data());
}

void ElasticSearchRecordable::SetResource(
Expand All @@ -291,7 +329,7 @@ void ElasticSearchRecordable::SetInstrumentationScope(
const opentelemetry::sdk::instrumentationscope::InstrumentationScope
&instrumentation_scope) noexcept
{
json_["name"] = instrumentation_scope.GetName();
json_["log"]["logger"] = instrumentation_scope.GetName();
}

} // namespace logs
Expand Down
Loading