log.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. #include "log.h"
  2. #include <yql/essentials/utils/log/proto/logger_config.pb.h>
  3. #include <yql/essentials/utils/backtrace/backtrace.h>
  4. #include <library/cpp/logger/stream.h>
  5. #include <library/cpp/logger/system.h>
  6. #include <library/cpp/logger/composite.h>
  7. #include <util/datetime/systime.h>
  8. #include <util/generic/strbuf.h>
  9. #include <util/stream/format.h>
  10. #include <util/system/getpid.h>
  11. #include <util/system/mutex.h>
  12. #include <util/system/progname.h>
  13. #include <util/system/thread.i>
  14. #include <stdio.h>
  15. #include <time.h>
  16. static TMutex g_InitLoggerMutex;
  17. static int g_LoggerInitialized = 0;
  18. namespace {
  19. class TLimitedLogBackend final : public TLogBackend {
  20. public:
  21. TLimitedLogBackend(TAutoPtr<TLogBackend> b, TAtomic& flag, ui64 limit) noexcept
  22. : Backend(b)
  23. , Flag(flag)
  24. , Limit(limit)
  25. {
  26. }
  27. ~TLimitedLogBackend() final {
  28. }
  29. void ReopenLog() final {
  30. Backend->ReopenLog();
  31. }
  32. void WriteData(const TLogRecord& rec) final {
  33. const auto remaining = AtomicGet(Limit);
  34. const bool final = remaining > 0 && AtomicSub(Limit, rec.Len) <= 0;
  35. if (remaining > 0 || rec.Priority <= TLOG_WARNING) {
  36. Backend->WriteData(rec);
  37. }
  38. if (final) {
  39. AtomicSet(Flag, 1);
  40. }
  41. }
  42. private:
  43. THolder<TLogBackend> Backend;
  44. TAtomic& Flag;
  45. TAtomic Limit;
  46. };
  47. class TEmergencyLogOutput: public IOutputStream {
  48. public:
  49. TEmergencyLogOutput()
  50. : Current_(Buf_)
  51. , End_(Y_ARRAY_END(Buf_))
  52. {
  53. }
  54. ~TEmergencyLogOutput() {
  55. }
  56. private:
  57. inline size_t Avail() const noexcept {
  58. return End_ - Current_;
  59. }
  60. void DoFlush() override {
  61. if (Current_ != Buf_) {
  62. NYql::NLog::YqlLogger().Write(TLOG_EMERG, Buf_, Current_ - Buf_);
  63. Current_ = Buf_;
  64. }
  65. }
  66. void DoWrite(const void* buf, size_t len) override {
  67. len = Min(len, Avail());
  68. if (len) {
  69. char* end = Current_ + len;
  70. memcpy(Current_, buf, len);
  71. Current_ = end;
  72. }
  73. }
  74. private:
  75. char Buf_[1 << 20];
  76. char* Current_;
  77. char* const End_;
  78. };
  79. TEmergencyLogOutput EMERGENCY_LOG_OUT;
  80. void LogBacktraceOnSignal(int signum) {
  81. if (NYql::NLog::IsYqlLoggerInitialized()) {
  82. EMERGENCY_LOG_OUT <<
  83. #ifdef _win_
  84. signum
  85. #else
  86. strsignal(signum)
  87. #endif
  88. << TStringBuf(" (pid=") << GetPID() << TStringBuf("): ");
  89. NYql::NBacktrace::KikimrBackTraceFormatImpl(&EMERGENCY_LOG_OUT);
  90. EMERGENCY_LOG_OUT.Flush();
  91. }
  92. }
  93. // Conversions between NYql::NProto::TLoggingConfig enums and NYql::NLog enums
  94. NYql::NLog::ELevel ConvertLevel(NYql::NProto::TLoggingConfig::ELevel level) {
  95. using namespace NYql::NProto;
  96. using namespace NYql::NLog;
  97. switch (level) {
  98. case TLoggingConfig::FATAL: return ELevel::FATAL;
  99. case TLoggingConfig::ERROR: return ELevel::ERROR;
  100. case TLoggingConfig::WARN: return ELevel::WARN;
  101. case TLoggingConfig::INFO: return ELevel::INFO;
  102. case TLoggingConfig::DEBUG: return ELevel::DEBUG;
  103. case TLoggingConfig::TRACE: return ELevel::TRACE;
  104. }
  105. ythrow yexception() << "unknown log level: "
  106. << TLoggingConfig::ELevel_Name(level);
  107. }
  108. NYql::NLog::EComponent ConvertComponent(NYql::NProto::TLoggingConfig::EComponent c) {
  109. using namespace NYql::NProto;
  110. using namespace NYql::NLog;
  111. switch (c) {
  112. case TLoggingConfig::DEFAULT: return EComponent::Default;
  113. case TLoggingConfig::CORE: return EComponent::Core;
  114. case TLoggingConfig::CORE_EVAL: return EComponent::CoreEval;
  115. case TLoggingConfig::CORE_PEEPHOLE: return EComponent::CorePeepHole;
  116. case TLoggingConfig::CORE_EXECUTION: return EComponent::CoreExecution;
  117. case TLoggingConfig::SQL: return EComponent::Sql;
  118. case TLoggingConfig::PROVIDER_COMMON: return EComponent::ProviderCommon;
  119. case TLoggingConfig::PROVIDER_CONFIG: return EComponent::ProviderConfig;
  120. case TLoggingConfig::PROVIDER_RESULT: return EComponent::ProviderResult;
  121. case TLoggingConfig::PROVIDER_YT: return EComponent::ProviderYt;
  122. case TLoggingConfig::PROVIDER_KIKIMR: return EComponent::ProviderKikimr;
  123. case TLoggingConfig::PROVIDER_KQP: return EComponent::ProviderKqp;
  124. case TLoggingConfig::PROVIDER_RTMR: return EComponent::ProviderRtmr;
  125. case TLoggingConfig::PERFORMANCE: return EComponent::Perf;
  126. case TLoggingConfig::NET: return EComponent::Net;
  127. case TLoggingConfig::PROVIDER_STAT: return EComponent::ProviderStat;
  128. case TLoggingConfig::PROVIDER_SOLOMON: return EComponent::ProviderSolomon;
  129. case TLoggingConfig::PROVIDER_DQ: return EComponent::ProviderDq;
  130. case TLoggingConfig::PROVIDER_CLICKHOUSE: return EComponent::ProviderClickHouse;
  131. case TLoggingConfig::PROVIDER_YDB: return EComponent::ProviderYdb;
  132. case TLoggingConfig::PROVIDER_PQ: return EComponent::ProviderPq;
  133. case TLoggingConfig::PROVIDER_S3: return EComponent::ProviderS3;
  134. case TLoggingConfig::CORE_DQ: return EComponent::CoreDq;
  135. case TLoggingConfig::HTTP_GATEWAY: return EComponent::HttpGateway;
  136. case TLoggingConfig::PROVIDER_GENERIC: return EComponent::ProviderGeneric;
  137. case TLoggingConfig::PROVIDER_PG: return EComponent::ProviderPg;
  138. case TLoggingConfig::PROVIDER_PURE: return EComponent::ProviderPure;
  139. case TLoggingConfig::FAST_MAP_REDUCE: return EComponent::FastMapReduce;
  140. case TLoggingConfig::PROVIDER_YTFLOW: return EComponent::ProviderYtflow;
  141. }
  142. ythrow yexception() << "unknown log component: "
  143. << TLoggingConfig::EComponent_Name(c);
  144. }
  145. TString ConvertDestinationType(NYql::NProto::TLoggingConfig::ELogTo c) {
  146. switch (c) {
  147. case NYql::NProto::TLoggingConfig::STDOUT: return "cout";
  148. case NYql::NProto::TLoggingConfig::STDERR: return "cerr";
  149. case NYql::NProto::TLoggingConfig::CONSOLE: return "console";
  150. default : {
  151. ythrow yexception() << "unsupported ELogTo destination in Convert";
  152. }
  153. }
  154. ythrow yexception() << "unknown ELogTo destination";
  155. }
  156. NYql::NProto::TLoggingConfig::TLogDestination CreateLogDestination(const TString& c) {
  157. NYql::NProto::TLoggingConfig::TLogDestination destination;
  158. if (c == "cout") {
  159. destination.SetType(NYql::NProto::TLoggingConfig::STDOUT);
  160. } else if (c == "cerr") {
  161. destination.SetType(NYql::NProto::TLoggingConfig::STDERR);
  162. } else if (c == "console") {
  163. destination.SetType(NYql::NProto::TLoggingConfig::CONSOLE);
  164. } else {
  165. destination.SetType(NYql::NProto::TLoggingConfig::FILE);
  166. destination.SetTarget(c);
  167. }
  168. return destination;
  169. }
  170. } // namspace
  171. namespace NYql {
  172. namespace NLog {
  173. void WriteLocalTime(IOutputStream* out) {
  174. struct timeval now;
  175. gettimeofday(&now, nullptr);
  176. struct tm tm;
  177. time_t seconds = static_cast<time_t>(now.tv_sec);
  178. localtime_r(&seconds, &tm);
  179. char buf[sizeof("2016-01-02 03:04:05.006")];
  180. int n = strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S.", &tm);
  181. snprintf(buf + n, sizeof(buf) - n, "%03" PRIu32, static_cast<ui32>(now.tv_usec) / 1000);
  182. out->Write(buf, sizeof(buf) - 1);
  183. }
  184. /**
  185. * TYqlLogElement
  186. * automaticaly adds new line char
  187. */
  188. class TYqlLogElement: public TLogElement {
  189. public:
  190. TYqlLogElement(const TLog* parent, ELevel level)
  191. : TLogElement(parent, ELevelHelpers::ToLogPriority(level))
  192. {
  193. }
  194. ~TYqlLogElement() {
  195. *this << '\n';
  196. }
  197. };
  198. TYqlLog::TYqlLog()
  199. : TLog()
  200. , ProcName_()
  201. , ProcId_()
  202. , WriteTruncMsg_(0) {}
  203. TYqlLog::TYqlLog(const TString& logType, const TComponentLevels& levels)
  204. : TLog(logType)
  205. , ProcName_(GetProgramName())
  206. , ProcId_(GetPID())
  207. , WriteTruncMsg_(0)
  208. {
  209. for (size_t component = 0; component < levels.size(); ++component) {
  210. SetComponentLevel(EComponentHelpers::FromInt(component), levels[component]);
  211. }
  212. }
  213. TYqlLog::TYqlLog(TAutoPtr<TLogBackend> backend, const TComponentLevels& levels)
  214. : TLog(backend)
  215. , ProcName_(GetProgramName())
  216. , ProcId_(GetPID())
  217. , WriteTruncMsg_(0)
  218. {
  219. for (size_t component = 0; component < levels.size(); ++component) {
  220. SetComponentLevel(EComponentHelpers::FromInt(component), levels[component]);
  221. }
  222. }
  223. void TYqlLog::UpdateProcInfo(const TString& procName) {
  224. ProcName_ = procName;
  225. ProcId_ = GetPID();
  226. }
  227. TAutoPtr<TLogElement> TYqlLog::CreateLogElement(
  228. EComponent component, ELevel level,
  229. TStringBuf file, int line) const
  230. {
  231. const bool writeMsg = AtomicCas(&WriteTruncMsg_, 0, 1);
  232. auto element = MakeHolder<TYqlLogElement>(this, writeMsg ? ELevel::FATAL : level);
  233. if (writeMsg) {
  234. WriteLogPrefix(element.Get(), EComponent::Default, ELevel::FATAL, __FILE__, __LINE__);
  235. *element << "Log is truncated by limit\n";
  236. *element << ELevelHelpers::ToLogPriority(level);
  237. }
  238. WriteLogPrefix(element.Get(), component, level, file, line);
  239. return element.Release();
  240. }
  241. void TYqlLog::WriteLogPrefix(IOutputStream* out, EComponent component, ELevel level, TStringBuf file, int line) const {
  242. // LOG FORMAT:
  243. // {datetime} {level} {procname}(pid={pid}, tid={tid}) [{component}] {source_location}: {message}\n
  244. //
  245. WriteLocalTime(out);
  246. *out << ' '
  247. << ELevelHelpers::ToString(level) << ' '
  248. << ProcName_ << TStringBuf("(pid=") << ProcId_
  249. << TStringBuf(", tid=")
  250. #ifdef _unix_
  251. << Hex(SystemCurrentThreadIdImpl())
  252. #else
  253. << SystemCurrentThreadIdImpl()
  254. #endif
  255. << TStringBuf(") [") << EComponentHelpers::ToString(component)
  256. << TStringBuf("] ")
  257. << file.RAfter(LOCSLASH_C) << ':' << line << TStringBuf(": ");
  258. }
  259. void TYqlLog::SetMaxLogLimit(ui64 limit) {
  260. auto backend = TLog::ReleaseBackend();
  261. TLog::ResetBackend(THolder(new TLimitedLogBackend(backend, WriteTruncMsg_, limit)));
  262. }
  263. void InitLogger(const TString& logType, bool startAsDaemon) {
  264. NProto::TLoggingConfig config;
  265. *config.AddLogDest() = CreateLogDestination(logType);
  266. InitLogger(config, startAsDaemon);
  267. }
  268. void InitLogger(const NProto::TLoggingConfig& config, bool startAsDaemon) {
  269. with_lock(g_InitLoggerMutex) {
  270. ++g_LoggerInitialized;
  271. if (g_LoggerInitialized > 1) {
  272. return;
  273. }
  274. TComponentLevels levels;
  275. if (config.HasAllComponentsLevel()) {
  276. levels.fill(ConvertLevel(config.GetAllComponentsLevel()));
  277. } else {
  278. levels.fill(ELevel::INFO);
  279. }
  280. for (const auto& cmpLevel: config.GetLevels()) {
  281. auto component = ConvertComponent(cmpLevel.GetC());
  282. auto level = ConvertLevel(cmpLevel.GetL());
  283. levels[EComponentHelpers::ToInt(component)] = level;
  284. }
  285. TLoggerOperator<TYqlLog>::Set(new TYqlLog("null", levels));
  286. std::vector<THolder<TLogBackend>> backends;
  287. // Set stderr log destination if none was described in config
  288. if (config.LogDestSize() == 0) {
  289. backends.emplace_back(CreateLogBackend("cerr", LOG_MAX_PRIORITY, false));
  290. }
  291. for (const auto& logDest : config.GetLogDest()) {
  292. // Generate the backend we need and temporary store it
  293. switch (logDest.GetType()) {
  294. case NProto::TLoggingConfig::STDERR:
  295. case NProto::TLoggingConfig::STDOUT:
  296. case NProto::TLoggingConfig::CONSOLE: {
  297. if (!startAsDaemon) {
  298. backends.emplace_back(CreateLogBackend(ConvertDestinationType(logDest.GetType()), LOG_MAX_PRIORITY, false));
  299. }
  300. break;
  301. }
  302. case NProto::TLoggingConfig::FILE: {
  303. backends.emplace_back(CreateLogBackend(logDest.GetTarget(), LOG_MAX_PRIORITY, false));
  304. break;
  305. }
  306. case NProto::TLoggingConfig::SYSLOG: {
  307. backends.emplace_back(MakeHolder<TSysLogBackend>(GetProgramName().data(), TSysLogBackend::TSYSLOG_LOCAL1));
  308. break;
  309. }
  310. default: {
  311. break;
  312. }
  313. }
  314. }
  315. // Combine created backends and set them for logger
  316. auto& logger = TLoggerOperator<TYqlLog>::Log();
  317. if (backends.size() == 1) {
  318. logger.ResetBackend(std::move(backends[0]));
  319. } else if (backends.size() > 1) {
  320. THolder<TCompositeLogBackend> compositeBackend = MakeHolder<TCompositeLogBackend>();
  321. for (auto& backend : backends) {
  322. compositeBackend->AddLogBackend(std::move(backend));
  323. }
  324. logger.ResetBackend(std::move(compositeBackend));
  325. }
  326. }
  327. NYql::NBacktrace::AddAfterFatalCallback([](int signo){ LogBacktraceOnSignal(signo); });
  328. }
  329. void InitLogger(TAutoPtr<TLogBackend> backend) {
  330. with_lock(g_InitLoggerMutex) {
  331. ++g_LoggerInitialized;
  332. if (g_LoggerInitialized > 1) {
  333. return;
  334. }
  335. TComponentLevels levels;
  336. levels.fill(ELevel::INFO);
  337. TLoggerOperator<TYqlLog>::Set(new TYqlLog(backend, levels));
  338. }
  339. NYql::NBacktrace::AddAfterFatalCallback([](int signo){ LogBacktraceOnSignal(signo); });
  340. }
  341. void InitLogger(IOutputStream* out) {
  342. InitLogger(new TStreamLogBackend(out));
  343. }
  344. void CleanupLogger() {
  345. with_lock(g_InitLoggerMutex) {
  346. --g_LoggerInitialized;
  347. if (g_LoggerInitialized > 0) {
  348. return;
  349. }
  350. TLoggerOperator<TYqlLog>::Set(new TYqlLog());
  351. }
  352. }
  353. void ReopenLog() {
  354. with_lock(g_InitLoggerMutex) {
  355. TLoggerOperator<TYqlLog>::Log().ReopenLog();
  356. }
  357. }
  358. } // namespace NLog
  359. } // namespace NYql
  360. /**
  361. * creates default YQL logger writing to /dev/null
  362. */
  363. template <>
  364. NYql::NLog::TYqlLog* CreateDefaultLogger<NYql::NLog::TYqlLog>() {
  365. NYql::NLog::TComponentLevels levels;
  366. levels.fill(NYql::NLog::ELevel::INFO);
  367. return new NYql::NLog::TYqlLog("null", levels);
  368. }