summaryrefslogtreecommitdiffstats
path: root/yql/essentials/utils/log/format.cpp
blob: 6c46c2ceac1b5f4a4fe0fc0b11e1b56c94dff87e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#include "format.h"

#include "context.h"
#include "fwd_backend.h"

#include <yql/essentials/utils/yql_panic.h>

#include <library/cpp/logger/record.h>
#include <library/cpp/json/json_writer.h>

#include <util/string/builder.h>

namespace NYql::NLog {

    namespace {

        constexpr size_t MaxRequiredContextKey = static_cast<size_t>(EContextKey::Line);

        auto RequiredContextAccessor(const TLogRecord& rec) {
            return [&](EContextKey key) -> TStringBuf {
                return rec.MetaFlags.at(static_cast<size_t>(key)).second;
            };
        }

        auto OptionalContextAccessor(const TLogRecord& rec) {
            return [&](TStringBuf key) -> TMaybe<TStringBuf> {
                const auto isContextKeyPath = [&](const auto& pair) {
                    return pair.first == key;
                };

                const auto* path = FindIfPtr(
                    rec.MetaFlags.begin() + MaxRequiredContextKey + 1,
                    rec.MetaFlags.end(),
                    isContextKeyPath);

                if (!path) {
                    return Nothing();
                }

                return path->second;
            };
        }

        template <std::invocable<EContextKey> Action>
        void ForEachRequiredContextKey(Action&& action) {
            const size_t min = static_cast<size_t>(EContextKey::DateTime);
            for (size_t i = min; i <= MaxRequiredContextKey; ++i) {
                action(static_cast<EContextKey>(i));
            }
        }

        class TFormattingLogBackend final: public TForwardingLogBackend {
        public:
            explicit TFormattingLogBackend(TFormatter formatter, TAutoPtr<TLogBackend> child)
                : TForwardingLogBackend(std::move(child))
                , Formatter_(std::move(formatter))
            {
            }

            void WriteData(const TLogRecord& rec) final {
                Validate(rec);

                TString message = Formatter_(rec);
                message.append('\n');

                const TLogRecord formatted(rec.Priority, message.data(), message.size());

                return TForwardingLogBackend::WriteData(formatted);
            }

        protected:
            void Validate(const TLogRecord& rec) const {
                const TLogRecord::TMetaFlags& flags = rec.MetaFlags;

                ForEachRequiredContextKey([&](EContextKey key) {
                    size_t i = static_cast<int>(key);

                    const TStringBuf expected = ToStringBuf(key);
                    YQL_ENSURE(
                        i < flags.size(),
                        "ContextKey #" << i << " named'" << expected << "' is out of range " << flags.size());

                    const TStringBuf actual = rec.MetaFlags[i].first;
                    YQL_ENSURE(
                        actual == expected,
                        "MetaFlag #" << i << " key was expected to be '" << expected << "', but got '" << actual << "'");
                });
            }

        private:
            TFormatter Formatter_;
        };

    } // namespace

    TString LegacyFormat(const TLogRecord& rec) {
        const auto get = RequiredContextAccessor(rec);
        const auto opt = OptionalContextAccessor(rec);

        TStringBuilder out;
        out << get(EContextKey::DateTime) << ' '
            << get(EContextKey::Level) << ' '
            << get(EContextKey::ProcessName)
            << "(pid=" << get(EContextKey::ProcessID)
            << ", tid=" << get(EContextKey::ThreadID)
            << ") [" << get(EContextKey::Component) << "] "
            << get(EContextKey::FileName)
            << ':' << get(EContextKey::Line) << ": ";

        if (auto path = opt(ToStringBuf(EContextKey::Path))) {
            out << "{" << *path << "} ";
        }

        return out << TStringBuf(rec.Data, rec.Len);
    }

    TString JsonFormat(const TLogRecord& rec) {
        TStringStream out;
        NJsonWriter::TBuf buf(NJsonWriter::HEM_DONT_ESCAPE_HTML, &out);
        buf.BeginObject();
        buf.WriteKey("message");
        buf.WriteString(TStringBuf(rec.Data, rec.Len));
        buf.WriteKey("@fields");
        buf.BeginObject();
        for (const auto& [key, value] : rec.MetaFlags) {
            buf.WriteKey(key);
            buf.WriteString(value);
        }
        buf.EndObject();
        buf.EndObject();
        return std::move(out.Str());
    }

    TAutoPtr<TLogBackend> MakeFormattingLogBackend(TFormatter formatter, TAutoPtr<TLogBackend> child) {
        return new TFormattingLogBackend(std::move(formatter), std::move(child));
    }

} // namespace NYql::NLog