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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
|
#pragma once
#include <util/system/defaults.h>
#include <util/generic/strbuf.h>
#include <util/stream/output.h>
#include <util/system/src_location.h>
#include <util/system/yassert.h>
#include <array>
// continues existing contexts chain
#define YQL_LOG_CTX_SCOPE(...) \
auto Y_CAT(c, __LINE__) = ::NYql::NLog::MakeCtx(__VA_ARGS__); \
Y_UNUSED(Y_CAT(c, __LINE__))
#define YQL_LOG_CTX_BLOCK(...) \
if (auto Y_GENERATE_UNIQUE_ID(c) = ::NYql::NLog::MakeCtx(__VA_ARGS__)) { \
goto Y_CAT(YQL_LOG_CTX_LABEL, __LINE__); \
} else Y_CAT(YQL_LOG_CTX_LABEL, __LINE__):
// starts new contexts chain, after leaving current scope restores
// previous contexts chain
#define YQL_LOG_CTX_ROOT_SESSION_SCOPE(sessionId, ...) \
auto Y_CAT(c, __LINE__) = ::NYql::NLog::MakeRootCtx(sessionId, ##__VA_ARGS__); \
Y_UNUSED(Y_CAT(c, __LINE__))
#define YQL_LOG_CTX_ROOT_SCOPE(...) \
auto Y_CAT(c, __LINE__) = ::NYql::NLog::MakeRootCtx("", __VA_ARGS__); \
Y_UNUSED(Y_CAT(c, __LINE__))
#define YQL_LOG_CTX_ROOT_BLOCK(...) \
if (auto Y_GENERATE_UNIQUE_ID(c) = ::NYql::NLog::MakeRootCtx(__VA_ARGS__)) { \
goto Y_CAT(YQL_LOG_CTX_LABEL, __LINE__); \
} else Y_CAT(YQL_LOG_CTX_LABEL, __LINE__):
// adds current contexts path to exception message before throwing it
#define YQL_LOG_CTX_THROW throw ::NYql::NLog::TYqlLogContextLocation(__LOCATION__) +
class TLogElement;
namespace NYql {
namespace NLog {
namespace NImpl {
/**
* @brief Represents item of logging context list.
*/
class TLogContextListItem {
public:
TLogContextListItem* Next;
TLogContextListItem* Prev;
size_t NamesCount;
explicit TLogContextListItem(size_t namesCount = 0, size_t headerSize = 0)
: Next(this)
, Prev(this)
, NamesCount(namesCount)
, HeaderSize_(headerSize)
{
// initialize HeaderSize_ if child didn't
if (headerSize == 0) {
HeaderSize_ = sizeof(*this);
}
}
virtual ~TLogContextListItem() {
}
const TString* begin() const {
auto* ptr = reinterpret_cast<const ui8*>(this);
return reinterpret_cast<const TString*>(ptr + HeaderSize_);
}
const TString* end() const {
return begin() + NamesCount;
}
bool HasNext() const {
return Next != this;
}
void LinkBefore(TLogContextListItem* item) {
Y_DEBUG_ABORT_UNLESS(!HasNext());
Next = item;
Prev = item->Prev;
Prev->Next = this;
Next->Prev = this;
}
void Unlink() {
if (!HasNext()) return;
Prev->Next = Next;
Next->Prev = Prev;
Next = Prev = this;
}
private:
// Additional memory before Names_ used in child class
size_t HeaderSize_;
};
/**
* @brief Returns pointer to thread local log context list.
*/
TLogContextListItem* GetLogContextList();
/**
* @brief Context element with stored SessionId.
*/
class TLogContextSessionItem : public TLogContextListItem {
public:
TLogContextSessionItem(size_t size, bool hasSessionId_)
: TLogContextListItem(size, sizeof(*this)) {
HasSessionId_ = hasSessionId_;
}
bool HasSessionId() const {
return HasSessionId_;
}
private:
bool HasSessionId_;
};
} // namspace NImpl
/**
* @brief YQL logger context element. Each element can contains several names.
*/
template <size_t Size>
class TLogContext: public NImpl::TLogContextListItem {
public:
template <typename... TArgs>
TLogContext(TArgs... args)
: TLogContextListItem(Size)
, Names_{{ TString{std::forward<TArgs>(args)}... }}
{
LinkBefore(NImpl::GetLogContextList());
}
~TLogContext() {
Unlink();
}
explicit inline operator bool() const noexcept {
return true;
}
private:
std::array<TString, Size> Names_;
};
/**
* @brief Special Root context elements which replaces previous log context
* list head by itself and restores previous one on destruction.
*/
template <size_t Size>
class TRootLogContext: public NImpl::TLogContextSessionItem {
public:
template <typename... TArgs>
TRootLogContext(const TString& sessionId, TArgs... args)
: TLogContextSessionItem(Size, !sessionId.empty())
, Names_{{ sessionId, TString{std::forward<TArgs>(args)}... }}
{
NImpl::TLogContextListItem* ctxList = NImpl::GetLogContextList();
PrevLogContextHead_.Prev = ctxList->Prev;
PrevLogContextHead_.Next = ctxList->Next;
ctxList->Next = ctxList->Prev = ctxList;
LinkBefore(ctxList);
}
~TRootLogContext() {
Unlink();
NImpl::TLogContextListItem* ctxList = NImpl::GetLogContextList();
ctxList->Prev = PrevLogContextHead_.Prev;
ctxList->Next = PrevLogContextHead_.Next;
}
explicit inline operator bool() const noexcept {
return true;
}
private:
std::array<TString, Size> Names_;
NImpl::TLogContextListItem PrevLogContextHead_;
};
/**
* @brief Helper function to construct TLogContext from variable
* arguments list.
*/
template <typename... TArgs>
inline auto MakeCtx(TArgs&&... args) -> TLogContext<sizeof...(args)> {
return TLogContext<sizeof...(args)>(std::forward<TArgs>(args)...);
}
template <typename... TArgs>
inline auto MakeRootCtx(const TString& sessionId, TArgs&&... args) -> TRootLogContext<sizeof...(args) + 1> {
return TRootLogContext<sizeof...(args) + 1>(sessionId, std::forward<TArgs>(args)...);
}
inline auto MakeRootCtx(const std::pair<TString, TString>& ctx) -> TRootLogContext<2> {
return TRootLogContext<2>(ctx.first, ctx.second);
}
/**
* @brief Returns pair with sessionId and
* current logger contexts path as string. Each element
* is separated with '/'.
*/
std::pair<TString, TString> CurrentLogContextPath();
/**
* @brief If last throwing exception was performed with YQL_LOG_CTX_THROW
* macro this function returns location and context of that throw point.
*/
TString ThrowedLogContextPath();
/**
* @brief Adds context preffix before logging message.
*/
struct TContextPreprocessor {
static TAutoPtr<TLogElement> Preprocess(TAutoPtr<TLogElement> element);
};
/**
* @brief Outputs current logger context into stream
*/
void OutputLogCtx(IOutputStream* out, bool withBraces, bool skipSessionId = false);
/**
* @brief Outputs current logger context into exception message.
*/
class TYqlLogContextLocation {
public:
TYqlLogContextLocation(const TSourceLocation& location)
: Location_(location.File, location.Line)
{
}
void SetThrowedLogContextPath() const;
template <class T>
inline T&& operator+(T&& t) {
SetThrowedLogContextPath();
return std::forward<T>(t);
}
private:
TSourceLocation Location_;
};
} // namespace NLog
} // namespace NYql
|