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