Skip to content

Commit

Permalink
feat logger: json string
Browse files Browse the repository at this point in the history
JsonString from #492

Tests: протестировано CI

Pull Request resolved: #855
commit_hash:12f966c020326dea3d4ee51dfe5bee24bbbba811
  • Loading branch information
akhoroshev authored and apolukhin committed Feb 14, 2025
1 parent 98ad535 commit 7e2332f
Show file tree
Hide file tree
Showing 12 changed files with 204 additions and 11 deletions.
3 changes: 3 additions & 0 deletions .mapping.json
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,7 @@
"core/src/logging/impl/unix_socket_sink.cpp":"taxi/uservices/userver/core/src/logging/impl/unix_socket_sink.cpp",
"core/src/logging/impl/unix_socket_sink.hpp":"taxi/uservices/userver/core/src/logging/impl/unix_socket_sink.hpp",
"core/src/logging/impl/unowned_file_sinks_test.cpp":"taxi/uservices/userver/core/src/logging/impl/unowned_file_sinks_test.cpp",
"core/src/logging/json_string_test.cpp":"taxi/uservices/userver/core/src/logging/json_string_test.cpp",
"core/src/logging/level_serialization.cpp":"taxi/uservices/userver/core/src/logging/level_serialization.cpp",
"core/src/logging/log_extra_test.cpp":"taxi/uservices/userver/core/src/logging/log_extra_test.cpp",
"core/src/logging/log_json_test.cpp":"taxi/uservices/userver/core/src/logging/log_json_test.cpp",
Expand Down Expand Up @@ -4137,6 +4138,7 @@
"universal/include/userver/logging/impl/logger_base.hpp":"taxi/uservices/userver/universal/include/userver/logging/impl/logger_base.hpp",
"universal/include/userver/logging/impl/mem_logger.hpp":"taxi/uservices/userver/universal/include/userver/logging/impl/mem_logger.hpp",
"universal/include/userver/logging/impl/tag_writer.hpp":"taxi/uservices/userver/universal/include/userver/logging/impl/tag_writer.hpp",
"universal/include/userver/logging/json_string.hpp":"taxi/uservices/userver/universal/include/userver/logging/json_string.hpp",
"universal/include/userver/logging/level.hpp":"taxi/uservices/userver/universal/include/userver/logging/level.hpp",
"universal/include/userver/logging/log.hpp":"taxi/uservices/userver/universal/include/userver/logging/log.hpp",
"universal/include/userver/logging/log_extra.hpp":"taxi/uservices/userver/universal/include/userver/logging/log_extra.hpp",
Expand Down Expand Up @@ -4414,6 +4416,7 @@
"universal/src/logging/impl/logger_base.cpp":"taxi/uservices/userver/universal/src/logging/impl/logger_base.cpp",
"universal/src/logging/impl/mem_logger.cpp":"taxi/uservices/userver/universal/src/logging/impl/mem_logger.cpp",
"universal/src/logging/impl/tag_writer.cpp":"taxi/uservices/userver/universal/src/logging/impl/tag_writer.cpp",
"universal/src/logging/json_string.cpp":"taxi/uservices/userver/universal/src/logging/json_string.cpp",
"universal/src/logging/level.cpp":"taxi/uservices/userver/universal/src/logging/level.cpp",
"universal/src/logging/log.cpp":"taxi/uservices/userver/universal/src/logging/log.cpp",
"universal/src/logging/log_extra.cpp":"taxi/uservices/userver/universal/src/logging/log_extra.cpp",
Expand Down
45 changes: 45 additions & 0 deletions core/src/logging/json_string_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#include <userver/logging/json_string.hpp>

#include <gtest/gtest.h>

#include <userver/formats/json/value.hpp>
#include <userver/utest/assert_macros.hpp>

USERVER_NAMESPACE_BEGIN

TEST(JsonString, ConstructFromJson) {
using formats::literals::operator""_json;

auto json = R"({
"a": "foo",
"b": {
"c": "d",
"e": [
1,
2
]
}
})"_json;

logging::JsonString json_string(json);

EXPECT_EQ(json_string.GetView(), R"({"a":"foo","b":{"c":"d","e":[1,2]}})");
}

TEST(JsonString, ConstructFromString) {
std::string json = R"({"a":"foo",
"b":{"c":"d","e":
[1,2]}})";

logging::JsonString json_string(json);

EXPECT_EQ(json_string.GetView(), R"({"a":"foo","b":{"c":"d","e":[1,2]}})");
}

TEST(JsonString, ConstructNull) {
logging::JsonString json_string;

EXPECT_EQ(json_string.GetView(), R"(null)");
}

USERVER_NAMESPACE_END
32 changes: 32 additions & 0 deletions core/src/logging/log_json_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,36 @@ TEST_F(LoggingJsonTest, Smoke) {
EXPECT_NO_THROW(json["thread_id"].As<std::string>());
}

TEST_F(LoggingJsonTest, LogExtraJsonString) {
using formats::literals::operator""_json;

auto object = R"({
"inner": {
"number": 10
}
})"_json;

logging::LogExtra extra;
extra.Extend("object", object); // implicit JsonString constructor
extra.Extend("null_object", logging::JsonString());

LOG_CRITICAL() << extra;

auto str = GetStreamString();
auto json = formats::json::FromString(str);

EXPECT_EQ(str.back(), '\n');

EXPECT_EQ(json["level"].As<std::string>(), "CRITICAL");
EXPECT_NO_THROW(json["module"].As<std::string>());
EXPECT_NO_THROW(json["timestamp"].As<std::string>());
EXPECT_EQ(json["task_id"].As<std::string>(), "0");
EXPECT_EQ(json["text"].As<std::string>(), "");
EXPECT_NO_THROW(json["thread_id"].As<std::string>());

EXPECT_TRUE(json["object"].IsObject());
EXPECT_EQ(json["object"]["inner"]["number"].As<int>(), 10);
EXPECT_TRUE(json["null_object"].IsNull());
}

USERVER_NAMESPACE_END
3 changes: 2 additions & 1 deletion core/src/tracing/span.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ void Span::Impl::PutIntoLogger(logging::impl::TagWriter writer) && {
// and log_extra_local_. Merge to deduplicate such tags.
log_extra_inheritable_.Extend(std::move(*log_extra_local_));
}
if (log_extra_inheritable_.GetValue(tracing::kSpanKind) == logging::LogExtra::Value{}) {
if (auto& value = log_extra_inheritable_.GetValue(tracing::kSpanKind);
std::holds_alternative<std::string>(value) && std::get<std::string>(value).empty()) {
log_extra_inheritable_.Extend(tracing::kSpanKind, tracing::kSpanKindInternal);
}
writer.PutLogExtra(log_extra_inheritable_);
Expand Down
2 changes: 2 additions & 0 deletions core/src/tracing/span_opentracing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ struct LogExtraValueVisitor {
void operator()(const std::string& val) { string_value = val; }

void operator()(int val) { string_value = std::to_string(val); }

void operator()(const logging::JsonString& val) { string_value = val.GetView(); }
};

void GetTagObject(
Expand Down
6 changes: 5 additions & 1 deletion otlp/src/otlp/logs/logger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,11 @@ void AddAttribute(Item& item, std::string_view key, const Value& value) {
[mutable_value](unsigned long long x) { mutable_value->set_int_value(x); },
[mutable_value](float x) { mutable_value->set_double_value(x); },
[mutable_value](double x) { mutable_value->set_double_value(x); },
[mutable_value](const std::string& x) { mutable_value->set_string_value(x); }},
[mutable_value](const std::string& x) { mutable_value->set_string_value(x); },
[mutable_value](const logging::JsonString& x) {
const auto value = x.GetView();
mutable_value->set_string_value(value.data(), value.size());
}},
value
);
UASSERT(attribute->has_value());
Expand Down
48 changes: 48 additions & 0 deletions universal/include/userver/logging/json_string.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#pragma once

/// @file userver/logging/json_string.hpp
/// @brief @copybrief logging::JsonString

#include <string>
#include <string_view>

#include <userver/formats/json_fwd.hpp>

USERVER_NAMESPACE_BEGIN

namespace logging {

/// One line json string.
/// JSON log formats write such tags directly as a sub-JSON, without packing them in a string.
class JsonString {
public:
/// @brief Constructs from provided json object.
/// The generated json string is an one line string.
/*implicit*/ JsonString(const formats::json::Value& value);

/// @brief Constructs from provided json string. It is the user's
/// responsibility to ensure that the input json string is valid.
/// New lines will be removed during construction.
explicit JsonString(std::string json) noexcept;

/// @brief Constructs null json (see GetValue for details)
JsonString() noexcept = default;

JsonString(JsonString&&) noexcept = default;
JsonString(const JsonString&) = default;

JsonString& operator=(JsonString&&) noexcept = default;
JsonString& operator=(const JsonString&) = default;

/// @brief Returns view to json
std::string_view GetView() const noexcept;

private:
std::string json_;
};

void WriteToStream(const JsonString& value, formats::json::StringBuilder& sw);

} // namespace logging

USERVER_NAMESPACE_END
14 changes: 12 additions & 2 deletions universal/include/userver/logging/log_extra.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <userver/compiler/select.hpp>
#include <userver/logging/fwd.hpp>
#include <userver/logging/json_string.hpp>
#include <userver/utils/fast_pimpl.hpp>

USERVER_NAMESPACE_BEGIN
Expand All @@ -35,8 +36,17 @@ class TagWriter;
/// Extra tskv fields storage
class LogExtra final {
public:
using Value =
std::variant<std::string, int, long, long long, unsigned int, unsigned long, unsigned long long, float, double>;
using Value = std::variant<
std::string,
int,
long,
long long,
unsigned int,
unsigned long,
unsigned long long,
float,
double,
JsonString>;
using Key = std::string;
using Pair = std::pair<Key, Value>;

Expand Down
2 changes: 0 additions & 2 deletions universal/include/userver/logging/log_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,6 @@ class LogHelper final {
/// Extends internal LogExtra
LogHelper& operator<<(LogExtra&& extra) noexcept;

LogHelper& operator<<(const LogExtra::Value& value) noexcept;

LogHelper& operator<<(Hex hex) noexcept;

LogHelper& operator<<(HexShort hex) noexcept;
Expand Down
2 changes: 2 additions & 0 deletions universal/src/logging/impl/formatters/tskv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ void Tskv::AddTag(std::string_view key, const LogExtra::Value& value) {
[&, this](const auto& x) {
if constexpr (std::is_same_v<decltype(x), const std::string&>)
DoAddTag(key, std::string_view{x}, false);
else if constexpr (std::is_same_v<decltype(x), const JsonString&>)
DoAddTag(key, x.GetView(), false);
else
DoAddTag(key, fmt::to_string(x), true);
},
Expand Down
53 changes: 53 additions & 0 deletions universal/src/logging/json_string.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#include <userver/logging/json_string.hpp>

#include <algorithm>

#include <userver/formats/json/string_builder.hpp>
#include <userver/formats/json/value.hpp>
#include <userver/logging/log.hpp>
#include <userver/utils/assert.hpp>

USERVER_NAMESPACE_BEGIN

namespace logging {

namespace {

constexpr std::string_view kNull = "null";

} // namespace

JsonString::JsonString(const formats::json::Value& value) : json_{ToString(value)} {
// ToString builds one line string with RapidJson
UASSERT(json_.find_first_of("\n\r") == std::string::npos);
}

JsonString::JsonString(std::string json) noexcept : json_{std::move(json)} {
#ifndef NDEBUG
try {
formats::json::FromString(json_);
} catch (const formats::json::ParseException& error) {
UASSERT_MSG(false, fmt::format("Invalid json: {}, error: {}", json_, error.what()));
}
#endif

// Remove extra new lines from user provided json string for two reasons:
// 1. To avoid additional escaping in TSKV format
// 2. To ensure a single line log in JSON format
json_.erase(
std::remove_if(json_.begin(), json_.end(), [](auto ch) { return ch == '\n' || ch == '\r'; }), json_.end()
);
}

std::string_view JsonString::GetView() const noexcept {
if (json_.empty()) {
return kNull;
}
return json_;
}

void WriteToStream(const JsonString& value, formats::json::StringBuilder& sw) { sw.WriteRawString(value.GetView()); }

} // namespace logging

USERVER_NAMESPACE_END
5 changes: 0 additions & 5 deletions universal/src/logging/log_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,6 @@ LogHelper& LogHelper::PutSwTag(std::string_view key, std::string_view value) noe
return *this;
}

LogHelper& LogHelper::operator<<(const LogExtra::Value& value) noexcept {
std::visit([this](const auto& unwrapped) { *this << unwrapped; }, value);
return *this;
}

void LogHelper::PutFloatingPoint(float value) {
fmt::format_to(fmt::appender(pimpl_->GetBufferForRawValuePart()), FMT_COMPILE("{}"), value);
}
Expand Down

0 comments on commit 7e2332f

Please sign in to comment.