5#include <spdlog/spdlog.h>
6#include <spdlog/sinks/stdout_sinks.h>
7#include <rapidjson/stringbuffer.h>
8#include <rapidjson/writer.h>
21inline std::string normalized(std::string value) {
22 const auto first = value.find_first_not_of(
" \t\r\n");
23 if (first == std::string::npos)
return "";
24 return value.substr(first, value.find_last_not_of(
" \t\r\n") - first + 1);
26inline spdlog::level::level_enum level(
const std::string& input) {
27 std::string name = normalized(input);
28 std::transform(name.begin(), name.end(), name.begin(), [](
unsigned char c) { return std::toupper(c); });
29 if (name ==
"DEBUG")
return spdlog::level::debug;
30 if (name ==
"WARNING")
return spdlog::level::warn;
31 if (name ==
"ERROR")
return spdlog::level::err;
32 if (name ==
"CRITICAL")
return spdlog::level::critical;
33 return spdlog::level::info;
35inline spdlog::level::level_enum threshold(
const std::string& component) {
36 const char* global = std::getenv(
"ORC_LOG_LEVEL");
37 auto selected = level(global ? global :
"INFO");
38 const char* overrides = std::getenv(
"ORC_LOG_COMPONENT_LEVELS");
39 std::istringstream entries(overrides ? overrides :
"");
41 std::size_t longest = 0;
42 while (std::getline(entries, entry,
',')) {
43 const auto equals = entry.find(
'=');
44 if (equals == std::string::npos)
continue;
45 auto prefix = normalized(entry.substr(0, equals));
46 if ((component == prefix || component.rfind(prefix +
".", 0) == 0) && prefix.size() >= longest) {
47 selected = level(entry.substr(equals + 1));
48 longest = prefix.size();
53inline void log(
const char* severity,
const char* component,
const char* event,
54 const std::string& message,
const std::string& operationId =
"",
55 const std::string& command =
"",
const std::string& endpoint =
"") {
56 const auto severityLevel = level(severity);
57 if (severityLevel < threshold(component))
return;
58 static auto logger = [] {
59 auto result = std::make_shared<spdlog::logger>(
"orc", std::make_shared<spdlog::sinks::stderr_sink_mt>());
60 result->set_pattern(
"%v");
61 result->set_level(spdlog::level::debug);
62 result->flush_on(spdlog::level::debug);
65 const auto now = std::chrono::system_clock::now();
66 const auto seconds = std::chrono::system_clock::to_time_t(now);
68 gmtime_r(&seconds, &utc);
69 const auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() % 1000;
70 std::ostringstream timestamp;
71 timestamp << std::put_time(&utc,
"%Y-%m-%dT%H:%M:%S") <<
'.' << std::setfill(
'0') << std::setw(3) << millis <<
'Z';
72 rapidjson::StringBuffer buffer;
73 rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
75 auto field = [&](
const char* key,
const std::string& value) {
76 writer.Key(key); writer.String(value.c_str(),
static_cast<rapidjson::SizeType
>(value.size()));
78 field(
"timestamp", timestamp.str());
79 field(
"level", severity);
80 field(
"component", component);
81 field(
"event", event);
82 field(
"message", message);
83 writer.Key(
"pid"); writer.Int(
static_cast<int>(getpid()));
84 if (!operationId.empty()) field(
"operation_id", operationId);
85 if (!command.empty()) field(
"command", command);
86 if (!endpoint.empty()) field(
"endpoint", endpoint);
88 logger->log(severityLevel,
"{}", buffer.GetString());