context.h 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. #pragma once
  2. #include <util/system/defaults.h>
  3. #include <util/generic/strbuf.h>
  4. #include <util/stream/output.h>
  5. #include <util/system/src_location.h>
  6. #include <util/system/yassert.h>
  7. #include <array>
  8. // continues existing contexts chain
  9. #define YQL_LOG_CTX_SCOPE(...) \
  10. auto Y_CAT(c, __LINE__) = ::NYql::NLog::MakeCtx(__VA_ARGS__); \
  11. Y_UNUSED(Y_CAT(c, __LINE__))
  12. #define YQL_LOG_CTX_BLOCK(...) \
  13. if (auto Y_GENERATE_UNIQUE_ID(c) = ::NYql::NLog::MakeCtx(__VA_ARGS__)) { \
  14. goto Y_CAT(YQL_LOG_CTX_LABEL, __LINE__); \
  15. } else Y_CAT(YQL_LOG_CTX_LABEL, __LINE__):
  16. // starts new contexts chain, after leaving current scope restores
  17. // previous contexts chain
  18. #define YQL_LOG_CTX_ROOT_SESSION_SCOPE(sessionId, ...) \
  19. auto Y_CAT(c, __LINE__) = ::NYql::NLog::MakeRootCtx(sessionId, ##__VA_ARGS__); \
  20. Y_UNUSED(Y_CAT(c, __LINE__))
  21. #define YQL_LOG_CTX_ROOT_SCOPE(...) \
  22. auto Y_CAT(c, __LINE__) = ::NYql::NLog::MakeRootCtx("", __VA_ARGS__); \
  23. Y_UNUSED(Y_CAT(c, __LINE__))
  24. #define YQL_LOG_CTX_ROOT_BLOCK(...) \
  25. if (auto Y_GENERATE_UNIQUE_ID(c) = ::NYql::NLog::MakeRootCtx(__VA_ARGS__)) { \
  26. goto Y_CAT(YQL_LOG_CTX_LABEL, __LINE__); \
  27. } else Y_CAT(YQL_LOG_CTX_LABEL, __LINE__):
  28. // adds current contexts path to exception message before throwing it
  29. #define YQL_LOG_CTX_THROW throw ::NYql::NLog::TYqlLogContextLocation(__LOCATION__) +
  30. class TLogElement;
  31. namespace NYql {
  32. namespace NLog {
  33. namespace NImpl {
  34. /**
  35. * @brief Represents item of logging context list.
  36. */
  37. class TLogContextListItem {
  38. public:
  39. TLogContextListItem* Next;
  40. TLogContextListItem* Prev;
  41. size_t NamesCount;
  42. explicit TLogContextListItem(size_t namesCount = 0, size_t headerSize = 0)
  43. : Next(this)
  44. , Prev(this)
  45. , NamesCount(namesCount)
  46. , HeaderSize_(headerSize)
  47. {
  48. // initialize HeaderSize_ if child didn't
  49. if (headerSize == 0) {
  50. HeaderSize_ = sizeof(*this);
  51. }
  52. }
  53. virtual ~TLogContextListItem() {
  54. }
  55. const TString* begin() const {
  56. auto* ptr = reinterpret_cast<const ui8*>(this);
  57. return reinterpret_cast<const TString*>(ptr + HeaderSize_);
  58. }
  59. const TString* end() const {
  60. return begin() + NamesCount;
  61. }
  62. bool HasNext() const {
  63. return Next != this;
  64. }
  65. void LinkBefore(TLogContextListItem* item) {
  66. Y_DEBUG_ABORT_UNLESS(!HasNext());
  67. Next = item;
  68. Prev = item->Prev;
  69. Prev->Next = this;
  70. Next->Prev = this;
  71. }
  72. void Unlink() {
  73. if (!HasNext()) return;
  74. Prev->Next = Next;
  75. Next->Prev = Prev;
  76. Next = Prev = this;
  77. }
  78. private:
  79. // Additional memory before Names_ used in child class
  80. size_t HeaderSize_;
  81. };
  82. /**
  83. * @brief Returns pointer to thread local log context list.
  84. */
  85. TLogContextListItem* GetLogContextList();
  86. /**
  87. * @brief Context element with stored SessionId.
  88. */
  89. class TLogContextSessionItem : public TLogContextListItem {
  90. public:
  91. TLogContextSessionItem(size_t size, bool hasSessionId_)
  92. : TLogContextListItem(size, sizeof(*this)) {
  93. HasSessionId_ = hasSessionId_;
  94. }
  95. bool HasSessionId() const {
  96. return HasSessionId_;
  97. }
  98. private:
  99. bool HasSessionId_;
  100. };
  101. } // namspace NImpl
  102. /**
  103. * @brief YQL logger context element. Each element can contains several names.
  104. */
  105. template <size_t Size>
  106. class TLogContext: public NImpl::TLogContextListItem {
  107. public:
  108. template <typename... TArgs>
  109. TLogContext(TArgs... args)
  110. : TLogContextListItem(Size)
  111. , Names_{{ TString{std::forward<TArgs>(args)}... }}
  112. {
  113. LinkBefore(NImpl::GetLogContextList());
  114. }
  115. ~TLogContext() {
  116. Unlink();
  117. }
  118. explicit inline operator bool() const noexcept {
  119. return true;
  120. }
  121. private:
  122. std::array<TString, Size> Names_;
  123. };
  124. /**
  125. * @brief Special Root context elements which replaces previous log context
  126. * list head by itself and restores previous one on destruction.
  127. */
  128. template <size_t Size>
  129. class TRootLogContext: public NImpl::TLogContextSessionItem {
  130. public:
  131. template <typename... TArgs>
  132. TRootLogContext(const TString& sessionId, TArgs... args)
  133. : TLogContextSessionItem(Size, !sessionId.empty())
  134. , Names_{{ sessionId, TString{std::forward<TArgs>(args)}... }}
  135. {
  136. NImpl::TLogContextListItem* ctxList = NImpl::GetLogContextList();
  137. PrevLogContextHead_.Prev = ctxList->Prev;
  138. PrevLogContextHead_.Next = ctxList->Next;
  139. ctxList->Next = ctxList->Prev = ctxList;
  140. LinkBefore(ctxList);
  141. }
  142. ~TRootLogContext() {
  143. Unlink();
  144. NImpl::TLogContextListItem* ctxList = NImpl::GetLogContextList();
  145. ctxList->Prev = PrevLogContextHead_.Prev;
  146. ctxList->Next = PrevLogContextHead_.Next;
  147. }
  148. explicit inline operator bool() const noexcept {
  149. return true;
  150. }
  151. private:
  152. std::array<TString, Size> Names_;
  153. NImpl::TLogContextListItem PrevLogContextHead_;
  154. };
  155. /**
  156. * @brief Helper function to construct TLogContext from variable
  157. * arguments list.
  158. */
  159. template <typename... TArgs>
  160. inline auto MakeCtx(TArgs&&... args) -> TLogContext<sizeof...(args)> {
  161. return TLogContext<sizeof...(args)>(std::forward<TArgs>(args)...);
  162. }
  163. template <typename... TArgs>
  164. inline auto MakeRootCtx(const TString& sessionId, TArgs&&... args) -> TRootLogContext<sizeof...(args) + 1> {
  165. return TRootLogContext<sizeof...(args) + 1>(sessionId, std::forward<TArgs>(args)...);
  166. }
  167. inline auto MakeRootCtx(const std::pair<TString, TString>& ctx) -> TRootLogContext<2> {
  168. return TRootLogContext<2>(ctx.first, ctx.second);
  169. }
  170. /**
  171. * @brief Returns pair with sessionId and
  172. * current logger contexts path as string. Each element
  173. * is separated with '/'.
  174. */
  175. std::pair<TString, TString> CurrentLogContextPath();
  176. /**
  177. * @brief If last throwing exception was performed with YQL_LOG_CTX_THROW
  178. * macro this function returns location and context of that throw point.
  179. */
  180. TString ThrowedLogContextPath();
  181. /**
  182. * @brief Adds context preffix before logging message.
  183. */
  184. struct TContextPreprocessor {
  185. static TAutoPtr<TLogElement> Preprocess(TAutoPtr<TLogElement> element);
  186. };
  187. /**
  188. * @brief Outputs current logger context into stream
  189. */
  190. void OutputLogCtx(IOutputStream* out, bool withBraces, bool skipSessionId = false);
  191. /**
  192. * @brief Outputs current logger context into exception message.
  193. */
  194. class TYqlLogContextLocation {
  195. public:
  196. TYqlLogContextLocation(const TSourceLocation& location)
  197. : Location_(location.File, location.Line)
  198. {
  199. }
  200. void SetThrowedLogContextPath() const;
  201. template <class T>
  202. inline T&& operator+(T&& t) {
  203. SetThrowedLogContextPath();
  204. return std::forward<T>(t);
  205. }
  206. private:
  207. TSourceLocation Location_;
  208. };
  209. } // namespace NLog
  210. } // namespace NYql