1 /*  $Id: logging.cpp 589151 2019-07-08 13:13:17Z gouriano $
2  * ===========================================================================
3  *
4  *                            PUBLIC DOMAIN NOTICE
5  *               National Center for Biotechnology Information
6  *
7  *  This software/database is a "United States Government Work" under the
8  *  terms of the United States Copyright Act.  It was written as part of
9  *  the author's official duties as a United States Government employee and
10  *  thus cannot be copyrighted.  This software/database is freely available
11  *  to the public for use. The National Library of Medicine and the U.S.
12  *  Government have not placed any restriction on its use or reproduction.
13  *
14  *  Although all reasonable efforts have been taken to ensure the accuracy
15  *  and reliability of the software and data, the NLM and the U.S.
16  *  Government do not and cannot warrant the performance or results that
17  *  may be obtained by using this software or data. The NLM and the U.S.
18  *  Government disclaim all warranties, express or implied, including
19  *  warranties of performance, merchantability or fitness for any particular
20  *  purpose.
21  *
22  *  Please cite the author in any work or product based on this material.
23  *
24  * ===========================================================================
25  *
26  * Author: Pavel Ivanov
27  *
28  */
29 
30 #include "task_server_pch.hpp"
31 
32 // ncbimtx.hpp is needed for proper compilation of ncbifile.hpp
33 #include <corelib/ncbimtx.hpp>
34 #include <corelib/ncbifile.hpp>
35 #include <corelib/request_ctx.hpp>
36 #include <corelib/ncbireg.hpp>
37 
38 #include "logging.hpp"
39 #include "threads_man.hpp"
40 #include "memory_man.hpp"
41 #include "server_core.hpp"
42 
43 #include <fcntl.h>
44 
45 
46 BEGIN_NCBI_SCOPE;
47 
48 extern CSrvTime s_JiffyTime;
49 
50 struct SLogData
51 {
52     char* buf;
53     char* end_ptr;
54     char* cur_ptr;
55     char* cur_msg_ptr;
56     Uint8 post_num;
57     string prefix;
58     string tmp_str;
59     bool has_params;
60     CSrvDiagMsg::ESeverity severity;
61 
62     const char* msg_file;
63     const char* msg_func;
64     int msg_line;
65     int err_code;
66     int err_subcode;
67     int last_flush_time;
68 
SLogDataSLogData69     SLogData(void)
70         : buf(0), end_ptr(0), cur_ptr(0), cur_msg_ptr(0), post_num(0),
71           has_params(false), severity(CSrvDiagMsg::Trace), msg_file(0), msg_func(0),
72           msg_line(0), err_code(0), err_subcode(0), last_flush_time(0)
73     {
74     }
75 };
76 
77 
78 class CLogWriter : public CSrvTask
79 {
80 public:
81     CLogWriter(void);
82     virtual ~CLogWriter(void);
83 
84 private:
85     virtual void ExecuteSlice(TSrvThreadNum thr_num);
86 };
87 
88 
89 static CSrvDiagMsg::ESeverity s_VisibleSev = CSrvDiagMsg::Warning;
90 static bool s_LogRequests = true;
91 static string s_UnkClient = "UNK_CLIENT";
92 static string s_UnkSession = "UNK_SESSION";
93 static const char* s_SevNames[] = {"Trace", "Info", "Warning", "Error", "Critical", "Fatal", "Fatal", NULL};
94 static const char* s_SoftFatalActions[] = {"abort", "shutdown", "log", NULL};
95 static int s_SoftFatal = 0;
96 static const size_t kOneRecReserve = 500;
97 static const size_t kInitLogBufSize = 10000000;
98 static size_t s_LogBufSize = kInitLogBufSize;
99 static int s_MaxFlushPeriod = 60;
100 static int s_FileReopenPeriod = 60;
101 static Uint8 s_LongCmd = 30000000;
102 static string s_CmdLine;
103 static string s_Pid;
104 static string s_AppUID;
105 static string s_FileName;
106 static Uint8 s_ProcessPostNum = 0;
107 static SLogData* s_MainData = NULL;
108 static bool s_InApplog = false;
109 static CMiniMutex s_WriteQueueLock;
110 static list<CTempString> s_WriteQueue;
111 static CLogWriter* s_LogWriter = NULL;
112 static int s_LogFd = -1;
113 static int s_LastReopenTime = 0;
114 static bool s_NeedFatalHalt = false;
115 static bool s_FileNameInitialized = false;
116 static bool s_ThreadsStarted = false;
117 static bool s_DiskSpaceAlert = false;
118 static CFutex s_CntHaltedThreads;
119 static CFutex s_Halt;
120 
121 extern string s_AppBaseName;
122 extern SSrvThread** s_Threads;
123 
124 
125 
Logging_DiskSpaceAlert(void)126 void Logging_DiskSpaceAlert(void)
127 {
128     s_DiskSpaceAlert = true;
129 }
130 
131 void
SaveAppCmdLine(const string & cmd_line)132 SaveAppCmdLine(const string& cmd_line)
133 {
134     s_CmdLine = cmd_line;
135 }
136 
137 void
SetLogFileName(CTempString name)138 SetLogFileName(CTempString name)
139 {
140     s_FileName = name;
141     if (!CDirEntry::IsAbsolutePath(s_FileName)) {
142         s_FileName = CDirEntry::NormalizePath(CDirEntry::ConcatPath(CDir::GetCwd(), s_FileName));
143     }
144 }
145 
GetLogFileName(void)146 string GetLogFileName(void)
147 {
148     return s_FileName;
149 }
150 
GetLogVisibility(void)151 string GetLogVisibility(void)
152 {
153     return s_SevNames[s_VisibleSev];
154 }
155 
GetSoftFatalAction(void)156 string GetSoftFatalAction(void)
157 {
158     return s_SoftFatalActions[s_SoftFatal];
159 }
160 
161 static void
s_InitConstants(void)162 s_InitConstants(void)
163 {
164     Int8 pid = CCurrentProcess::GetPid();
165     time_t t = time(0);
166     const string& host = CTaskServer::GetHostName();
167     Int8 h = 212;
168     ITERATE(string, s, host) {
169         h = h*1265 + *s;
170     }
171     h &= 0xFFFF;
172     // The low 4 bits are reserved as GUID generator version number.
173     Int8 uid = (h << 48) |
174                ((pid & 0xFFFF) << 32) |
175                ((Int8(t) & 0xFFFFFFF) << 4) |
176                1; // version #1 - fixed type conversion bug
177 
178     s_AppUID = NStr::UInt8ToString(Uint8(uid), 0, 16);
179     if (s_AppUID.size() != 16)
180         s_AppUID.insert(s_AppUID.begin(), 16 - s_AppUID.size(), '0');
181     s_Pid = NStr::UInt8ToString(Uint8(pid));
182     if (s_Pid.size() < 5)
183         s_Pid.insert(s_Pid.begin(), 5 - s_Pid.size(), '0');
184 }
185 
186 static inline void
s_OpenLogFile(void)187 s_OpenLogFile(void)
188 {
189 #ifdef NCBI_OS_LINUX
190     if (s_DiskSpaceAlert) {
191         s_DiskSpaceAlert = false;
192         unlink(s_FileName.c_str());
193     }
194     s_LogFd = open(s_FileName.c_str(), O_WRONLY | O_APPEND | O_CREAT,
195                    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
196 #endif
197 }
198 
199 static void
s_WriteLog(const char * buf,size_t size)200 s_WriteLog(const char* buf, size_t size)
201 {
202     if (!buf) {
203         return;
204     }
205     if (s_LogFd == -1) {
206         s_OpenLogFile();
207         if (s_LogFd == -1)
208             return;
209         s_LastReopenTime = CSrvTime::CurSecs();
210     }
211 
212 #ifdef NCBI_OS_LINUX
213     if (write(s_LogFd, buf, size) != ssize_t(size)) {
214         // There's nothing we can do about it here
215     }
216 #endif
217 }
218 
219 static void
s_QueueLogWrite(char * buf,size_t size)220 s_QueueLogWrite(char* buf, size_t size)
221 {
222     if (!buf) {
223         return;
224     }
225     s_WriteQueueLock.Lock();
226     bool need_signal = s_WriteQueue.empty();
227     s_WriteQueue.push_back(CTempString(buf, size));
228     s_WriteQueueLock.Unlock();
229 
230     if (need_signal)
231         s_LogWriter->SetRunnable();
232 }
233 
234 static inline void
s_AllocNewBuf(SLogData * data,size_t buf_size)235 s_AllocNewBuf(SLogData* data, size_t buf_size)
236 {
237     data->buf = (char*)malloc(buf_size);
238     data->cur_ptr = data->cur_msg_ptr = data->buf;
239     data->end_ptr = data->buf ? data->buf + buf_size - 8 : data->buf;
240 }
241 
242 static void
s_RotateLogBuf(SLogData * data)243 s_RotateLogBuf(SLogData* data)
244 {
245     char* old_buf = data->buf;
246     char* old_msg_ptr = data->cur_msg_ptr;
247     size_t old_msg_size = data->cur_ptr - data->cur_msg_ptr;
248     s_AllocNewBuf(data, s_LogBufSize);
249     if (old_msg_size != 0 && data->buf) {
250         memcpy(data->buf, old_msg_ptr, old_msg_size);
251         data->cur_ptr += old_msg_size;
252     }
253     s_QueueLogWrite(old_buf, old_msg_ptr - old_buf);
254     data->last_flush_time = CSrvTime::CurSecs();
255 }
256 
257 static inline void
s_InitLogPrefix(SLogData * data,TSrvThreadNum thr_num)258 s_InitLogPrefix(SLogData* data, TSrvThreadNum thr_num)
259 {
260     data->prefix.assign(s_Pid);
261     data->prefix.append(1, '/');
262     NStr::UInt8ToString(data->tmp_str, thr_num);
263     if (data->tmp_str.size() < 3)
264         data->prefix.append(3 - data->tmp_str.size(), '0');
265     data->prefix.append(data->tmp_str.data(), data->tmp_str.size());
266     data->prefix.append(1, '/');
267 }
268 
269 static inline bool
s_CheckBufSize(SLogData * data,size_t need_size)270 s_CheckBufSize(SLogData* data, size_t need_size)
271 {
272     if (data->cur_ptr + need_size >= data->end_ptr)
273         s_RotateLogBuf(data);
274     return data->buf != nullptr;
275 }
276 
277 static inline void
s_AddToLog(SLogData * data,const char * str,size_t size)278 s_AddToLog(SLogData* data, const char* str, size_t size)
279 {
280     if (s_CheckBufSize(data, size)) {
281         memcpy(data->cur_ptr, str, size);
282         data->cur_ptr += size;
283     }
284 }
285 
286 static inline void
s_AddToLogStripLF(SLogData * data,const char * str,size_t size)287 s_AddToLogStripLF(SLogData* data, const char* str, size_t size)
288 {
289     if (s_CheckBufSize(data, size)) {
290         for (; size != 0; --size, ++str) {
291             char c = *str;
292             *data->cur_ptr++ = c == '\n'? ';': c;
293         }
294     }
295 }
296 
297 static inline void
s_AddToLog(SLogData * data,const char * str)298 s_AddToLog(SLogData* data, const char* str)
299 {
300     s_AddToLog(data, str, strlen(str));
301 }
302 
303 static inline void
s_AddToLogStripLF(SLogData * data,const char * str)304 s_AddToLogStripLF(SLogData* data, const char* str)
305 {
306     s_AddToLogStripLF(data, str, strlen(str));
307 }
308 
309 static inline void
s_AddToLog(SLogData * data,const string & str)310 s_AddToLog(SLogData* data, const string& str)
311 {
312     s_AddToLog(data, str.data(), str.size());
313 }
314 
315 static inline void
s_AddToLogStripLF(SLogData * data,const string & str)316 s_AddToLogStripLF(SLogData* data, const string& str)
317 {
318     s_AddToLogStripLF(data, str.data(), str.size());
319 }
320 
321 static inline void
s_AddToLog(SLogData * data,const string & str,Uint1 min_chars)322 s_AddToLog(SLogData* data, const string& str, Uint1 min_chars)
323 {
324     if (s_CheckBufSize(data, max(str.size(), size_t(min_chars)))) {
325         s_AddToLog(data, str.data(), str.size());
326         for (Uint1 i = min_chars; i > str.size(); --i)
327             *data->cur_ptr++ = ' ';
328     }
329 }
330 
331 static inline void
s_AddToLog(SLogData * data,Uint8 num)332 s_AddToLog(SLogData* data, Uint8 num)
333 {
334     NStr::UInt8ToString(data->tmp_str, num);
335     s_AddToLog(data, data->tmp_str);
336 }
337 
338 static inline void
s_AddToLog(SLogData * data,Int8 num)339 s_AddToLog(SLogData* data, Int8 num)
340 {
341     NStr::Int8ToString(data->tmp_str, num);
342     s_AddToLog(data, data->tmp_str);
343 }
344 
345 static inline void
s_AddToLog(SLogData * data,int num)346 s_AddToLog(SLogData* data, int num)
347 {
348     s_AddToLog(data, Uint8(num));
349 }
350 
351 static inline void
s_AddToLog(SLogData * data,Uint8 num,Uint1 min_digs)352 s_AddToLog(SLogData* data, Uint8 num, Uint1 min_digs)
353 {
354     NStr::UInt8ToString(data->tmp_str, num);
355     if (s_CheckBufSize(data, max(data->tmp_str.size(), size_t(min_digs)))) {
356         for (Uint1 i = min_digs; i > data->tmp_str.size(); --i)
357             *data->cur_ptr++ = '0';
358         s_AddToLog(data, data->tmp_str);
359     }
360 }
361 
362 static inline void
s_AddToLog(SLogData * data,double num)363 s_AddToLog(SLogData* data, double num)
364 {
365     NStr::DoubleToString(data->tmp_str, num);
366     s_AddToLog(data, data->tmp_str);
367 }
368 
369 static void
s_AddLogPrefix(SSrvThread * thr,SLogData * data,CRequestContext * diag_ctx=NULL)370 s_AddLogPrefix(SSrvThread* thr, SLogData* data, CRequestContext* diag_ctx = NULL)
371 {
372     if (!data->buf) {
373         return;
374     }
375     s_AddToLog(data, data->prefix);
376     if (!diag_ctx  &&  thr  &&  thr->cur_task)
377         diag_ctx = thr->cur_task->m_DiagCtx;
378 
379     Uint8 req_id = diag_ctx? diag_ctx->GetRequestID()
380                            : CRequestContext::GetNextRequestID();
381     s_AddToLog(data, req_id, 4);
382     s_AddToLog(data, "/A  ");
383 
384     s_AddToLog(data, s_AppUID);
385     *data->cur_ptr++ = ' ';
386 
387     Uint8 proc_post_num = AtomicAdd(s_ProcessPostNum, 1);
388     s_AddToLog(data, proc_post_num, 4);
389     *data->cur_ptr++ = '/';
390     s_AddToLog(data, ++data->post_num, 4);
391     *data->cur_ptr++ = ' ';
392 
393     CSrvTime cur_time = CSrvTime::Current();
394     if (!s_CheckBufSize(data, 50)) {
395         return;
396     }
397     Uint1 len = cur_time.Print(data->cur_ptr, CSrvTime::eFmtLogging);
398     data->cur_ptr += len;
399     *data->cur_ptr++ = ' ';
400 
401     s_AddToLog(data, CTaskServer::GetHostName(), 15);
402     *data->cur_ptr++ = ' ';
403 
404     string str = diag_ctx? diag_ctx->GetClientIP(): s_UnkClient;
405     if (str.empty())
406         str = s_UnkClient;
407     s_AddToLog(data, str, 15);
408     *data->cur_ptr++ = ' ';
409 
410     str = diag_ctx? diag_ctx->GetSessionID(): s_UnkSession;
411     if (str.empty())
412         str = s_UnkSession;
413     s_AddToLog(data, str, 24);
414     *data->cur_ptr++ = ' ';
415 
416     s_AddToLog(data, s_AppBaseName);
417     *data->cur_ptr++ = ' ';
418 }
419 
420 static void
s_AddParamName(SLogData * data,CTempString name)421 s_AddParamName(SLogData* data, CTempString name)
422 {
423     if (!s_CheckBufSize(data, name.size() + 2)) {
424         return;
425     }
426     if (data->has_params)
427         *data->cur_ptr++ = '&';
428     else
429         data->has_params = true;
430     s_AddToLog(data, name.data(), name.size());
431     *data->cur_ptr++ = '=';
432 }
433 
434 static SLogData*
s_AllocNewData(TSrvThreadNum thr_num)435 s_AllocNewData(TSrvThreadNum thr_num)
436 {
437     SLogData* data = new SLogData;
438     data->post_num = 0;
439     data->last_flush_time = 0;
440     s_AllocNewBuf(data, s_LogBufSize);
441     s_InitLogPrefix(data, thr_num);
442     return data;
443 }
444 
445 static void
s_AllocMainData(void)446 s_AllocMainData(void)
447 {
448     s_InitConstants();
449     s_MainData = s_AllocNewData(0);
450     s_AddLogPrefix(NULL, s_MainData);
451     s_AddToLog(s_MainData, "start         ");
452     s_AddToLog(s_MainData, s_CmdLine);
453     *s_MainData->cur_ptr++ = '\n';
454     s_MainData->cur_msg_ptr = s_MainData->cur_ptr;
455 }
456 
457 static void
s_InitFileName(void)458 s_InitFileName(void)
459 {
460     if (s_FileName.empty()) {
461         s_FileName = CDirEntry::MakePath("/log/srv", s_AppBaseName, "log");
462         s_OpenLogFile();
463         if (s_LogFd == -1)
464             s_FileName = CDirEntry::MakePath(kEmptyStr, s_AppBaseName, "log");
465         else
466             s_InApplog = true;
467     }
468     s_FileNameInitialized = true;
469 }
470 
471 NCBI_NORETURN static void
s_DoFatalAbort(SLogData * data)472 s_DoFatalAbort(SLogData* data)
473 {
474     s_NeedFatalHalt = true;
475 #if 1
476     int cnt_halted = s_CntHaltedThreads.AddValue(1);
477     int cnt_need = GetCntRunningThreads() + 2;
478     if (!s_ThreadsStarted)
479         cnt_need = 1;
480     for (int attempt=0; attempt<500 && cnt_halted != cnt_need; ++attempt) {
481         s_CntHaltedThreads.WaitValueChange(cnt_halted, s_JiffyTime);
482         cnt_halted = s_CntHaltedThreads.GetValue();
483         // In a very rare situation CntRunningThreads can change here
484         cnt_need = GetCntRunningThreads() + 2;
485     }
486 #endif
487     if (!s_FileNameInitialized)
488         s_InitFileName();
489     ITERATE(list<CTempString>, it, s_WriteQueue) {
490         CTempString str = *it;
491         s_WriteLog(str.data(), str.size());
492     }
493     if (data->cur_ptr != data->buf)
494         s_WriteLog(data->buf, data->cur_ptr - data->buf);
495 
496 #ifdef NCBI_OS_LINUX
497     close(s_LogFd);
498 #endif
499     abort();
500 }
501 
502 static inline void
s_CheckFatalAbort(void)503 s_CheckFatalAbort(void)
504 {
505     if (!s_NeedFatalHalt)
506         return;
507 
508     SLogData* data = GetCurThread()->log_data;
509     if (data->cur_ptr != data->buf)
510         s_QueueLogWrite(data->buf, data->cur_ptr - data->buf);
511     s_CntHaltedThreads.AddValue(1);
512     s_CntHaltedThreads.WakeUpWaiters(1);
513     s_Halt.WaitValueChange(0);
514 }
515 
516 void
LogNoteThreadsStarted(void)517 LogNoteThreadsStarted(void)
518 {
519     s_ThreadsStarted = true;
520     s_RotateLogBuf(s_MainData);
521 }
522 
523 void
ConfigureLogging(const CNcbiRegistry * reg,CTempString section)524 ConfigureLogging(const CNcbiRegistry* reg, CTempString section)
525 {
526     s_LogRequests = reg->GetBool(section, "log_requests", true);
527     s_LogBufSize = NStr::StringToUInt8_DataSize(
528                     reg->GetString(section, "log_thread_buf_size", "10 MB"));
529     s_MaxFlushPeriod = reg->GetInt(section, "log_flush_period", 60);
530     s_FileReopenPeriod = reg->GetInt(section, "log_reopen_period", 60);
531     string vis = reg->GetString(section, "log_visible", "Warning");
532     size_t i = 0;
533     for (i = 0; s_SevNames[i] != NULL; ++i) {
534         if (vis.compare(s_SevNames[i]) == 0) {
535             s_VisibleSev = (CSrvDiagMsg::ESeverity)i;
536             break;
537         }
538     }
539     string tmp = reg->GetString(section, "soft_fatal_action", "abort");
540     for (i = 0; s_SoftFatalActions[i] != NULL; ++i) {
541         if (tmp.compare(s_SoftFatalActions[i]) == 0) {
542             s_SoftFatal = i;
543             break;
544         }
545     }
546     s_LongCmd = Uint8(reg->GetInt(section, "log_long_cmd_after", 30000)) * kUSecsPerMSec;
547 }
548 
ReConfig_Logging(const CTempString & section,const CNcbiRegistry & new_reg,string &)549 bool ReConfig_Logging(const CTempString& section, const CNcbiRegistry& new_reg, string& /*err_message*/)
550 {
551     ConfigureLogging(&new_reg, section);
552     return true;
553 }
554 
IsLongCommand(Uint8 cmd_len)555 bool IsLongCommand(Uint8 cmd_len)
556 {
557     return cmd_len >= s_LongCmd;
558 }
559 
WriteSetup_Logging(CSrvSocketTask & task)560 void WriteSetup_Logging(CSrvSocketTask& task)
561 {
562     string is("\": "), iss("\": \""), eol(",\n\"");
563     task.WriteText(eol).WriteText("log_requests").WriteText(is ).WriteBool( s_LogRequests);
564     task.WriteText(eol).WriteText("log_thread_buf_size").WriteText(is ).WriteNumber( s_LogBufSize);
565     task.WriteText(eol).WriteText("log_flush_period").WriteText(is ).WriteNumber( s_MaxFlushPeriod);
566     task.WriteText(eol).WriteText("log_reopen_period").WriteText(is ).WriteNumber( s_FileReopenPeriod);
567     task.WriteText(eol).WriteText("log_visible").WriteText(iss).WriteText(GetLogVisibility()).WriteText("\"");
568     task.WriteText(eol).WriteText("soft_fatal_action").WriteText(iss).WriteText(GetSoftFatalAction()).WriteText("\"");
569     task.WriteText(eol).WriteText("log_long_cmd_after").WriteText(is).WriteNumber( s_LongCmd / kUSecsPerMSec);
570 }
571 
572 void
InitLogging(void)573 InitLogging(void)
574 {
575     if (!s_MainData)
576         s_AllocMainData();
577 
578     s_InitFileName();
579     s_LogWriter = new CLogWriter();
580 }
581 
582 void
FinalizeLogging(void)583 FinalizeLogging(void)
584 {
585     if (s_MainData->cur_ptr != s_MainData->buf) {
586         s_WriteLog(s_MainData->buf, s_MainData->cur_ptr - s_MainData->buf);
587         s_MainData->cur_ptr = s_MainData->buf;
588     }
589 
590     s_AddLogPrefix(NULL, s_MainData);
591     s_AddToLog(s_MainData, "stop          0 ");
592     CSrvTime cur_time = CSrvTime::Current();
593     cur_time -= CTaskServer::GetStartTime();
594     s_AddToLog(s_MainData, cur_time.Sec());
595     *s_MainData->cur_ptr++ = '.';
596     s_AddToLog(s_MainData, cur_time.NSec(), 9);
597     *s_MainData->cur_ptr++ = '\n';
598     s_WriteLog(s_MainData->buf, s_MainData->cur_ptr - s_MainData->buf);
599 
600 #ifdef NCBI_OS_LINUX
601     close(s_LogFd);
602 #endif
603 }
604 
605 void
AssignThreadLogging(SSrvThread * thr)606 AssignThreadLogging(SSrvThread* thr)
607 {
608     if (thr->thread_num == 0) {
609         if (!s_MainData)
610             s_AllocMainData();
611         thr->log_data = s_MainData;
612     }
613     else {
614         thr->log_data = s_AllocNewData(thr->thread_num);
615     }
616 }
617 
618 void
CheckLoggingFlush(SSrvThread * thr)619 CheckLoggingFlush(SSrvThread* thr)
620 {
621     s_CheckFatalAbort();
622 
623     SLogData* data = thr->log_data;
624     int cur_time = CSrvTime::CurSecs();
625     if (cur_time - data->last_flush_time < s_MaxFlushPeriod)
626         return;
627 
628     if (data->buf && data->cur_ptr == data->buf)
629         data->last_flush_time = cur_time;
630     else
631         s_RotateLogBuf(data);
632 }
633 
634 void
StartThreadLogging(SSrvThread * thr)635 StartThreadLogging(SSrvThread* thr)
636 {
637     thr->log_data = s_AllocNewData(thr->thread_num);
638 }
639 
640 void
StopThreadLogging(SSrvThread * thr)641 StopThreadLogging(SSrvThread* thr)
642 {
643     SLogData* data = thr->log_data;
644     if (data->cur_ptr != data->buf)
645         s_QueueLogWrite(data->buf, data->cur_ptr - data->buf);
646     else if (data->buf)
647         free(data->buf);
648     delete data;
649     s_CheckFatalAbort();
650 }
651 
652 void
ReleaseThreadLogging(SSrvThread * thr)653 ReleaseThreadLogging(SSrvThread* thr)
654 {
655     SLogData* data = thr->log_data;
656     if (data->cur_ptr != data->buf) {
657         s_WriteLog(data->buf, data->cur_ptr - data->buf);
658         data->cur_ptr = data->buf;
659     }
660 }
661 
662 static const char*
find_match(char lsep,char rsep,const char * start,const char * stop)663 find_match(char lsep, char rsep, const char* start, const char* stop)
664 {
665     if (*(stop - 1) != rsep) return stop;
666     int balance = 1;
667     const char* pos = stop - 2;
668     for (; pos > start; pos--) {
669         if (*pos == rsep) {
670             balance++;
671         }
672         else if (*pos == lsep) {
673             if (--balance == 0) break;
674         }
675     }
676     return (pos <= start) ? NULL : pos;
677 }
678 
679 static const char*
str_rev_str(const char * begin_str,const char * end_str,const char * str_search)680 str_rev_str(const char* begin_str, const char* end_str, const char* str_search)
681 {
682     if (begin_str == NULL)
683         return NULL;
684     if (end_str == NULL)
685         return NULL;
686     if (str_search == NULL)
687         return NULL;
688 
689     const char* search_char = str_search + strlen(str_search);
690     const char* cur_char = end_str;
691 
692     do {
693         --search_char;
694         do {
695             --cur_char;
696         } while(*cur_char != *search_char && cur_char != begin_str);
697         if (*cur_char != *search_char)
698             return NULL;
699     }
700     while (search_char != str_search);
701 
702     return cur_char;
703 }
704 
705 static void
s_ParseFuncName(const char * func,CTempString & class_name,CTempString & func_name)706 s_ParseFuncName(const char* func, CTempString& class_name, CTempString& func_name)
707 {
708     if (!func  ||  *func == '\0')
709         return;
710 
711     // Skip function arguments
712     size_t len = strlen(func);
713     const char* end_str = find_match('(', ')',
714                                      func,
715                                      func + len);
716     if (end_str == func + len) {
717         // Missing '('
718         return;
719     }
720     if (end_str) {
721         // Skip template arguments
722         end_str = find_match('<', '>', func, end_str);
723     }
724     if (!end_str)
725         return;
726     // Get a function/method name
727     const char* start_str = NULL;
728 
729     // Get a function start position.
730     const char* start_str_tmp = str_rev_str(func, end_str, "::");
731     bool has_class = start_str_tmp != NULL;
732     if (start_str_tmp != NULL) {
733         start_str = start_str_tmp + 2;
734     } else {
735         start_str_tmp = str_rev_str(func, end_str, " ");
736         if (start_str_tmp != NULL) {
737             start_str = start_str_tmp + 1;
738         }
739     }
740 
741     const char* cur_funct_name = (start_str == NULL? func: start_str);
742     size_t cur_funct_name_len = end_str - cur_funct_name;
743     func_name.assign(cur_funct_name, cur_funct_name_len);
744 
745     // Get a class name
746     if (has_class) {
747         end_str = find_match('<', '>', func, start_str - 2);
748         start_str = str_rev_str(func, end_str, " ");
749         const char* cur_class_name = (start_str == NULL? func: start_str + 1);
750         size_t cur_class_name_len = end_str - cur_class_name;
751         class_name.assign(cur_class_name, cur_class_name_len);
752     }
753 }
754 
755 static inline bool
s_CheckOldStyleStart(const CSrvDiagMsg * msg)756 s_CheckOldStyleStart(const CSrvDiagMsg* msg)
757 {
758     SLogData* data = msg->m_Data;
759     if (!CSrvDiagMsg::IsSeverityVisible(data->severity))
760         return false;
761     if (data->cur_msg_ptr == data->cur_ptr) {
762         if (msg->m_OldStyle) {
763             msg->StartSrvLog(data->severity, data->msg_file,
764                              data->msg_line, data->msg_func);
765         }
766         else {
767             SRV_FATAL("Unrecognized CSrvDiagMsg style");
768         }
769     }
770     return true;
771 }
772 
773 static inline CSrvDiagMsg::ESeverity
s_ConvertSeverity(EOldStyleSeverity sev)774 s_ConvertSeverity(EOldStyleSeverity sev)
775 {
776     switch (sev) {
777     case Trace:
778         return CSrvDiagMsg::Trace;
779     case Info:
780         return CSrvDiagMsg::Info;
781     case Warning:
782         return CSrvDiagMsg::Warning;
783     case Error:
784         return CSrvDiagMsg::Error;
785     case Critical:
786         return CSrvDiagMsg::Critical;
787     case Fatal:
788         return CSrvDiagMsg::Fatal;
789     default:
790         SRV_FATAL("Unsupported severity: " << sev);
791     }
792     return CSrvDiagMsg::Trace;
793 }
794 
795 static inline CSrvDiagMsg::ESeverity
s_ConvertSeverity(EDiagSev sev)796 s_ConvertSeverity(EDiagSev sev)
797 {
798     switch (sev) {
799     case eDiag_Trace:
800         return CSrvDiagMsg::Trace;
801     case eDiag_Info:
802         return CSrvDiagMsg::Info;
803     case eDiag_Warning:
804         return CSrvDiagMsg::Warning;
805     case eDiag_Error:
806         return CSrvDiagMsg::Error;
807     case eDiag_Critical:
808         return CSrvDiagMsg::Critical;
809     case eDiag_Fatal:
810         return CSrvDiagMsg::Fatal;
811     default:
812         SRV_FATAL("Unsupported severity: " << sev);
813     }
814     return CSrvDiagMsg::Trace;
815 }
816 
817 CDiagContext&
GetDiagContext(void)818 GetDiagContext(void)
819 {
820     static CDiagContext ctx;
821     return ctx;
822 }
823 
UpdateOnFork(TOnForkFlags)824 void CDiagContext::UpdateOnFork(TOnForkFlags /*flags*/)
825 {
826     CDiagContext::UpdatePID();
827 }
828 
829 void
UpdatePID(void)830 CDiagContext::UpdatePID(void)
831 {
832     string old_uid = s_AppUID;
833     s_InitConstants();
834     if (s_Threads) {
835         for (TSrvThreadNum i = 0; i <= s_MaxRunningThreads + 1; ++i) {
836             SSrvThread* thr = s_Threads[i];
837             if (thr)
838                 s_InitLogPrefix(thr->log_data, thr->thread_num);
839         }
840     }
841     else if (s_MainData) {
842         s_InitLogPrefix(s_MainData, 0);
843     }
844     CSrvDiagMsg().PrintExtra(NULL)
845                  .PrintParam("action", "fork")
846                  .PrintParam("parent_guid", old_uid);
847 }
848 
849 void
StartCtxRequest(CRequestContext * ctx)850 CDiagContext::StartCtxRequest(CRequestContext* ctx)
851 {
852     ctx->StartRequest();
853 }
854 
855 void
StopCtxRequest(CRequestContext * ctx)856 CDiagContext::StopCtxRequest(CRequestContext* ctx)
857 {
858     ctx->StopRequest();
859 }
860 
861 bool
IsCtxRunning(CRequestContext * ctx)862 CDiagContext::IsCtxRunning(CRequestContext* ctx)
863 {
864     return ctx->IsRunning();
865 }
866 
867 const string&
GetDefaultSessionID(void)868 CDiagContext::GetDefaultSessionID(void)
869 {
870     return kEmptyStr;
871 }
872 
873 const string&
GetEncodedSessionID(void)874 CDiagContext::GetEncodedSessionID(void)
875 {
876     return kEmptyStr;
877 }
878 
879 
CSrvDiagMsg(void)880 CSrvDiagMsg::CSrvDiagMsg(void)
881     : m_Thr(GetCurThread()),
882       m_Data(NULL),
883       m_OldStyle(false)
884 {
885     if (m_Thr) {
886         m_Data = m_Thr->log_data;
887         s_CheckFatalAbort();
888     }
889     else {
890         if (!s_MainData)
891             s_AllocMainData();
892         m_Data = s_MainData;
893     }
894 }
895 
~CSrvDiagMsg(void)896 CSrvDiagMsg::~CSrvDiagMsg(void)
897 {
898     if (m_Data->cur_msg_ptr != m_Data->cur_ptr)
899         Flush();
900 }
901 
902 bool
IsSeverityVisible(ESeverity sev)903 CSrvDiagMsg::IsSeverityVisible(ESeverity sev)
904 {
905     return int(sev) >= int(s_VisibleSev);
906 }
907 
908 const CSrvDiagMsg&
StartSrvLog(ESeverity sev,const char * file,int line,const char * func) const909 CSrvDiagMsg::StartSrvLog(ESeverity sev,
910                          const char* file,
911                          int line,
912                          const char* func) const
913 {
914     if (m_Data->cur_msg_ptr != m_Data->cur_ptr
915         /* && *m_Data->cur_ptr != '\n' */) {
916         *m_Data->cur_ptr++ = '\n';
917     }
918     m_Data->severity = sev;
919     s_AddLogPrefix(m_Thr, m_Data);
920     s_AddToLog(m_Data, s_SevNames[int(sev)]);
921     if (m_OldStyle) {
922         s_AddToLog(m_Data, ": ");
923         if (m_Data->err_code != 0  ||  m_Data->err_subcode != 0) {
924             s_AddToLog(m_Data, "(");
925             s_AddToLog(m_Data, m_Data->err_code);
926             s_AddToLog(m_Data, ".");
927             s_AddToLog(m_Data, m_Data->err_subcode);
928             s_AddToLog(m_Data, ")");
929         }
930         s_AddToLog(m_Data, " \"");
931     }
932     else {
933         s_AddToLog(m_Data, ":  \"");
934     }
935     const char* file_name;
936     size_t name_size;
937     ExtractFileName(file, file_name, name_size);
938     s_AddToLog(m_Data, file_name, name_size);
939     s_AddToLog(m_Data, "\", line ");
940     s_AddToLog(m_Data, line);
941     s_AddToLog(m_Data, ": ");
942     CTempString class_name, func_name;
943     s_ParseFuncName(func, class_name, func_name);
944     s_AddToLog(m_Data, class_name);
945     s_AddToLog(m_Data, "::");
946     s_AddToLog(m_Data, func_name);
947     s_AddToLog(m_Data, "() --- ");
948 
949     return *this;
950 }
951 
952 const CSrvDiagMsg&
StartInfo(void) const953 CSrvDiagMsg::StartInfo(void) const
954 {
955     CRequestContext* ctx = NULL;
956     if (m_Thr->cur_task)
957         ctx = m_Thr->cur_task->m_DiagCtx;
958     return StartInfo(ctx);
959 }
960 
961 const CSrvDiagMsg&
StartInfo(CRequestContext * ctx) const962 CSrvDiagMsg::StartInfo(CRequestContext* ctx) const
963 {
964     m_Data->severity = Warning;
965     s_AddLogPrefix(m_Thr, m_Data, ctx);
966     s_AddToLog(m_Data, "Message[W]:  \"UNK_FILE\", line 0: UNK_FUNC --- ");
967     return *this;
968 }
969 
970 const CSrvDiagMsg&
StartOldStyle(const char * file,int line,const char * func)971 CSrvDiagMsg::StartOldStyle(const char* file, int line, const char* func)
972 {
973     m_OldStyle = true;
974     m_Data->severity = CSrvDiagMsg::Error;
975     m_Data->msg_file = file;
976     m_Data->msg_func = func;
977     m_Data->msg_line = line;
978     m_Data->err_code = m_Data->err_subcode = 0;
979     return *this;
980 }
981 
982 const CSrvDiagMsg&
operator <<(const CSrvDiagMsg & msg,EOldStyleSeverity sev)983 operator<< (const CSrvDiagMsg& msg, EOldStyleSeverity sev)
984 {
985     msg.m_Data->severity = s_ConvertSeverity(sev);
986     return msg;
987 }
988 
989 const CSrvDiagMsg&
operator <<(const CSrvDiagMsg & msg,ErrCode err_code)990 operator<< (const CSrvDiagMsg& msg, ErrCode err_code)
991 {
992     msg.m_Data->err_code = err_code.m_Code;
993     msg.m_Data->err_subcode = err_code.m_SubCode;
994     return msg;
995 }
996 
997 CSrvDiagMsg&
StartRequest(void)998 CSrvDiagMsg::StartRequest(void)
999 {
1000     return StartRequest(m_Thr->cur_task->m_DiagCtx);
1001 }
1002 
1003 CSrvDiagMsg&
StartRequest(CRequestContext * ctx)1004 CSrvDiagMsg::StartRequest(CRequestContext* ctx)
1005 {
1006     if (CDiagContext::IsCtxRunning(ctx)) {
1007         SRV_FATAL("Unexpected StartRequest call");
1008     }
1009     CDiagContext::StartCtxRequest(ctx);
1010     ctx->SetRequestStatus(200);
1011 
1012     if (!s_LogRequests)
1013         return *this;
1014 
1015     s_AddLogPrefix(m_Thr, m_Data, ctx);
1016     s_AddToLog(m_Data, "request-start ");
1017     m_Data->has_params = false;
1018 
1019     return *this;
1020 }
1021 
1022 CSrvDiagMsg&
PrintExtra(void)1023 CSrvDiagMsg::PrintExtra(void)
1024 {
1025     return PrintExtra(m_Thr->cur_task->m_DiagCtx);
1026 }
1027 
1028 CSrvDiagMsg&
PrintExtra(CRequestContext * ctx)1029 CSrvDiagMsg::PrintExtra(CRequestContext* ctx)
1030 {
1031     if (s_LogRequests) {
1032         s_AddLogPrefix(m_Thr, m_Data, ctx);
1033         s_AddToLog(m_Data, "extra         ");
1034         m_Data->has_params = false;
1035     }
1036     return *this;
1037 }
1038 
1039 CSrvDiagMsg&
PrintParam(CTempString name,CTempString value)1040 CSrvDiagMsg::PrintParam(CTempString name, CTempString value)
1041 {
1042     if (!s_LogRequests)
1043         return *this;
1044 
1045     s_AddParamName(m_Data, name);
1046     if (NStr::NeedsURLEncoding(value))
1047         s_AddToLog(m_Data, NStr::URLEncode(value));
1048     else
1049         s_AddToLog(m_Data, value);
1050     return *this;
1051 }
1052 
1053 CSrvDiagMsg&
PrintParam(CTempString name,Int8 value)1054 CSrvDiagMsg::PrintParam(CTempString name, Int8 value)
1055 {
1056     if (s_LogRequests) {
1057         s_AddParamName(m_Data, name);
1058         s_AddToLog(m_Data, value);
1059     }
1060     return *this;
1061 }
1062 
1063 CSrvDiagMsg&
PrintParam(CTempString name,Uint8 value)1064 CSrvDiagMsg::PrintParam(CTempString name, Uint8 value)
1065 {
1066     if (s_LogRequests) {
1067         s_AddParamName(m_Data, name);
1068         s_AddToLog(m_Data, value);
1069     }
1070     return *this;
1071 }
1072 
1073 CSrvDiagMsg&
PrintParam(CTempString name,double value)1074 CSrvDiagMsg::PrintParam(CTempString name, double value)
1075 {
1076     if (s_LogRequests) {
1077         s_AddParamName(m_Data, name);
1078         s_AddToLog(m_Data, value);
1079     }
1080     return *this;
1081 }
1082 
1083 void
StopRequest(void)1084 CSrvDiagMsg::StopRequest(void)
1085 {
1086     StopRequest(m_Thr->cur_task->m_DiagCtx);
1087 }
1088 
1089 void
StopRequest(CRequestContext * ctx)1090 CSrvDiagMsg::StopRequest(CRequestContext* ctx)
1091 {
1092     if (!CDiagContext::IsCtxRunning(ctx)) {
1093         SRV_FATAL("Unexpected StopRequest call");
1094     }
1095 
1096     if (s_LogRequests) {
1097         s_AddLogPrefix(m_Thr, m_Data, ctx);
1098         s_AddToLog(m_Data, "request-stop  ");
1099         s_AddToLog(m_Data, ctx->GetRequestStatus());
1100         *m_Data->cur_ptr++ = ' ';
1101         CTimeSpan span(ctx->GetRequestTimer().Elapsed());
1102         s_AddToLog(m_Data, span.GetCompleteSeconds());
1103         *m_Data->cur_ptr++ = '.';
1104         s_AddToLog(m_Data, span.GetNanoSecondsAfterSecond(), 9);
1105         *m_Data->cur_ptr++ = ' ';
1106         s_AddToLog(m_Data, ctx->GetBytesRd());
1107         *m_Data->cur_ptr++ = ' ';
1108         s_AddToLog(m_Data, ctx->GetBytesWr());
1109         Flush();
1110     }
1111 
1112     CDiagContext::StopCtxRequest(ctx);
1113 }
1114 
1115 void
Flush(void)1116 CSrvDiagMsg::Flush(void)
1117 {
1118     s_CheckBufSize(m_Data, 1);
1119     if (m_Data->buf) {
1120         *m_Data->cur_ptr++ = '\n';
1121     }
1122     m_Data->cur_msg_ptr = m_Data->cur_ptr;
1123     if (m_Data->severity == SoftFatal) {
1124         switch (s_SoftFatal) {
1125         default:
1126         case 0: m_Data->severity = Fatal; break;
1127         case 1: CTaskServer::RequestShutdown(eSrvFastShutdown); break;
1128         case 2: break;
1129         }
1130     }
1131     if (m_Data->severity == Fatal) {
1132         s_DoFatalAbort(m_Data);
1133     }
1134     s_CheckBufSize(m_Data, kOneRecReserve);
1135 }
1136 
1137 const CSrvDiagMsg&
operator <<(CTempString str) const1138 CSrvDiagMsg::operator<< (CTempString str) const
1139 {
1140     if (s_CheckOldStyleStart(this)) {
1141         if (s_InApplog)
1142             s_AddToLogStripLF(m_Data, str);
1143         else
1144             s_AddToLog(m_Data, str);
1145     }
1146     return *this;
1147 }
1148 
1149 const CSrvDiagMsg&
operator <<(Int8 num) const1150 CSrvDiagMsg::operator<< (Int8 num) const
1151 {
1152     if (s_CheckOldStyleStart(this))
1153         s_AddToLog(m_Data, num);
1154     return *this;
1155 }
1156 
1157 const CSrvDiagMsg&
operator <<(Uint8 num) const1158 CSrvDiagMsg::operator<< (Uint8 num) const
1159 {
1160     if (s_CheckOldStyleStart(this))
1161         s_AddToLog(m_Data, num);
1162     return *this;
1163 }
1164 
1165 const CSrvDiagMsg&
operator <<(double num) const1166 CSrvDiagMsg::operator<< (double num) const
1167 {
1168     if (s_CheckOldStyleStart(this))
1169         s_AddToLog(m_Data, num);
1170     return *this;
1171 }
1172 
1173 const CSrvDiagMsg&
operator <<(const void * ptr) const1174 CSrvDiagMsg::operator<< (const void* ptr) const
1175 {
1176     if (s_CheckOldStyleStart(this)) {
1177         NStr::PtrToString(m_Data->tmp_str, ptr);
1178         s_AddToLog(m_Data, m_Data->tmp_str);
1179     }
1180     return *this;
1181 }
1182 
1183 const CSrvDiagMsg&
operator <<(const exception & ex) const1184 CSrvDiagMsg::operator<< (const exception& ex) const
1185 {
1186     if (s_CheckOldStyleStart(this)) {
1187         if (s_InApplog)
1188             s_AddToLogStripLF(m_Data, ex.what());
1189         else
1190             s_AddToLog(m_Data, ex.what());
1191     }
1192     return *this;
1193 }
1194 
1195 
1196 void
SetDiagCtx(CRequestContext * ctx)1197 CSrvTask::SetDiagCtx(CRequestContext* ctx)
1198 {
1199     if (!ctx) {
1200         return;
1201     }
1202     ctx->AddReference();
1203     if (!m_DiagCtx) {
1204         m_DiagCtx = ctx;
1205         if (m_DiagChain)
1206             m_DiagChain[0] = ctx;
1207         return;
1208     }
1209     if (!m_DiagChain) {
1210         m_DiagChain = (CRequestContext**)malloc(sizeof(m_DiagCtx) * 2);
1211 #if __NC_MEMMAN_USE_STD_MALLOC
1212         m_DiagChainSize = sizeof(m_DiagCtx) * 2;
1213         memset(m_DiagChain, 0, m_DiagChainSize);
1214 #else
1215         memset(m_DiagChain, 0, GetMemSize(m_DiagChain));
1216 #endif
1217         m_DiagChain[0] = m_DiagCtx;
1218         m_DiagChain[1] = m_DiagCtx = ctx;
1219         return;
1220     }
1221 
1222 #if __NC_MEMMAN_USE_STD_MALLOC
1223     Uint4 mem_cap = Uint4(m_DiagChainSize / sizeof(m_DiagCtx));
1224 #else
1225     Uint4 mem_cap = Uint4(GetMemSize(m_DiagChain) / sizeof(m_DiagCtx));
1226 #endif
1227     Uint4 cnt_in_chain = mem_cap;
1228     while (!m_DiagChain[cnt_in_chain - 1]) {
1229         if (--cnt_in_chain == 0) {
1230             SRV_FATAL("m_DiagChain broken");
1231         }
1232     }
1233     if (cnt_in_chain == mem_cap) {
1234         m_DiagChain = (CRequestContext**)
1235                       realloc(m_DiagChain, mem_cap * 2 * sizeof(m_DiagCtx));
1236 #if __NC_MEMMAN_USE_STD_MALLOC
1237         m_DiagChainSize = mem_cap * 2 * sizeof(m_DiagCtx);
1238         memset(&m_DiagChain[mem_cap], 0,
1239                m_DiagChainSize - mem_cap * sizeof(m_DiagCtx));
1240 #else
1241         memset(&m_DiagChain[mem_cap], 0,
1242                GetMemSize(m_DiagChain) - mem_cap * sizeof(m_DiagCtx));
1243 #endif
1244     }
1245     m_DiagChain[cnt_in_chain] = m_DiagCtx = ctx;
1246 }
1247 
1248 void
CreateNewDiagCtx(void)1249 CSrvTask::CreateNewDiagCtx(void)
1250 {
1251     CRequestContext* ctx = new CRequestContext();
1252     ctx->SetRequestID();
1253     SetDiagCtx(ctx);
1254 }
1255 
1256 void
ReleaseDiagCtx(void)1257 CSrvTask::ReleaseDiagCtx(void)
1258 {
1259     CRequestContext* was_ctx = m_DiagCtx;
1260     m_DiagCtx->RemoveReference();
1261     m_DiagCtx = NULL;
1262     if (!m_DiagChain)
1263         return;
1264 
1265 #if __NC_MEMMAN_USE_STD_MALLOC
1266     Uint4 mem_cap = Uint4(m_DiagChainSize / sizeof(m_DiagCtx));
1267 #else
1268     Uint4 mem_cap = Uint4(GetMemSize(m_DiagChain) / sizeof(m_DiagCtx));
1269 #endif
1270     Uint4 cnt_in_chain = mem_cap;
1271     while (!m_DiagChain[cnt_in_chain - 1]) {
1272         if (--cnt_in_chain == 0) {
1273             SRV_FATAL("m_DiagChain broken");
1274         }
1275     }
1276     if (m_DiagChain[--cnt_in_chain] != was_ctx) {
1277         SRV_FATAL("m_DiagChain broken");
1278     }
1279     m_DiagChain[cnt_in_chain] = NULL;
1280     if (cnt_in_chain != 0)
1281         m_DiagCtx = m_DiagChain[cnt_in_chain - 1];
1282 }
1283 
CDiagCompileInfo(void)1284 CDiagCompileInfo::CDiagCompileInfo(void)
1285     : m_File(""),
1286       m_Line(0),
1287       m_CurrFunctName(0),
1288       m_Parsed(false),
1289       m_StrFile(0),
1290       m_StrCurrFunctName(0)
1291 {}
1292 
CDiagCompileInfo(const char * file,int line,const char * curr_funct)1293 CDiagCompileInfo::CDiagCompileInfo(const char* file,
1294                                    int         line,
1295                                    const char* curr_funct)
1296     : m_File(file),
1297       m_Line(line),
1298       m_CurrFunctName(curr_funct),
1299       m_Parsed(false),
1300       m_StrFile(0),
1301       m_StrCurrFunctName(0)
1302 {
1303     if (!file) {
1304         m_File = "";
1305         return;
1306     }
1307 }
1308 
CDiagCompileInfo(const string & file,int line,const string & curr_funct,const string & module)1309 CDiagCompileInfo::CDiagCompileInfo(const string& file,
1310                                    int           line,
1311                                    const string& curr_funct,
1312                                    const string& module)
1313     : m_File(""),
1314       m_Line(line),
1315       m_CurrFunctName(""),
1316       m_Parsed(false),
1317       m_StrFile(0),
1318       m_StrCurrFunctName(0)
1319 {
1320     if ( !file.empty() ) {
1321         m_StrFile = new char[file.size() + 1];
1322         strcpy(m_StrFile, file.c_str());
1323         m_File = m_StrFile;
1324     }
1325     if ( !curr_funct.empty() ) {
1326         m_StrCurrFunctName = new char[curr_funct.size() + 1];
1327         strcpy(m_StrCurrFunctName, curr_funct.c_str());
1328         m_CurrFunctName = m_StrCurrFunctName;
1329     }
1330 }
1331 
~CDiagCompileInfo(void)1332 CDiagCompileInfo::~CDiagCompileInfo(void)
1333 {
1334     delete[] m_StrFile;
1335     delete[] m_StrCurrFunctName;
1336 }
1337 
1338 void
ParseCurrFunctName(void) const1339 CDiagCompileInfo::ParseCurrFunctName(void) const
1340 {
1341     m_Parsed = true;
1342     CTempString class_name, func_name;
1343     s_ParseFuncName(m_CurrFunctName, class_name, func_name);
1344     m_FunctName.assign(func_name.data(), func_name.size());
1345     m_ClassName.assign(class_name.data(), class_name.size());
1346 }
1347 
1348 
1349 CNcbiOstream&
Write(CNcbiOstream & os,int flags) const1350 SDiagMessage::Write(CNcbiOstream& os, int flags /* = 0 */) const
1351 {
1352     string sev = s_SevNames[s_ConvertSeverity(severity)];
1353     os << setfill(' ') << setw(13) // add 1 for space
1354        << setiosflags(IOS_BASE::left) << setw(0)
1355        << sev << ':'
1356        << resetiosflags(IOS_BASE::left)
1357        << ' ';
1358 
1359     // <module>-<err_code>.<err_subcode> or <module>-<err_text>
1360     if (err_code  ||  err_subcode || err_text) {
1361         if (err_text) {
1362             os << '(' << err_text << ')';
1363         } else {
1364             os << '(' << err_code << '.' << err_subcode << ')';
1365         }
1366     }
1367     os << ' ';
1368 
1369     // "<file>"
1370     bool print_file = file  &&  *file;
1371     if ( print_file ) {
1372         const char* x_file;
1373         size_t x_file_len;
1374         ExtractFileName(file, x_file, x_file_len);
1375         os << '"' << string(x_file, x_file_len) << '"';
1376     }
1377     else {
1378         os << "\"UNK_FILE\"";
1379     }
1380     // , line <line>
1381     os << ", line " << line;
1382     os << ": ";
1383 
1384     // Class::Function
1385     bool print_loc = (nclass && *nclass ) || (func && *func);
1386     if (print_loc) {
1387         // Class:: Class::Function() ::Function()
1388         if (nclass  &&  *nclass) {
1389             os << nclass;
1390         }
1391         os << "::";
1392         if (func  &&  *func) {
1393             os << func << "() ";
1394         }
1395     }
1396     else {
1397         os << "UNK_FUNC ";
1398     }
1399 
1400     os << "--- ";
1401 
1402     // [<prefix1>::<prefix2>::.....]
1403     if (prefix  &&  *prefix)
1404         os << '[' << prefix << "] ";
1405 
1406     // <message>
1407     if (len)
1408         os.write(buf, len);
1409 
1410     // Endl
1411     if ((flags & fNoEndl) == 0)
1412         os << NcbiEndl;
1413 
1414     return os;
1415 }
1416 
1417 
CLogWriter(void)1418 CLogWriter::CLogWriter(void)
1419 {
1420 #if __NC_TASKS_MONITOR
1421     m_TaskName = "CLogWriter";
1422 #endif
1423 }
1424 
~CLogWriter(void)1425 CLogWriter::~CLogWriter(void)
1426 {}
1427 
1428 void
ExecuteSlice(TSrvThreadNum)1429 CLogWriter::ExecuteSlice(TSrvThreadNum /* thr_num */)
1430 {
1431 // those who add data into queue, make this runnable
1432 
1433     s_CheckFatalAbort();
1434 // if log file was open for too long, close it
1435 // so that app_log could move it
1436     if (CSrvTime::CurSecs() - s_LastReopenTime >= s_FileReopenPeriod  &&  s_LogFd != -1)
1437     {
1438 #ifdef NCBI_OS_LINUX
1439         close(s_LogFd);
1440 #endif
1441         s_LogFd = -1;
1442     }
1443 
1444 // get from the queue, write into log
1445     s_WriteQueueLock.Lock();
1446     if (s_WriteQueue.empty()) {
1447         s_WriteQueueLock.Unlock();
1448         return;
1449     }
1450     CTempString str = s_WriteQueue.front();
1451     s_WriteQueue.pop_front();
1452     bool have_more = !s_WriteQueue.empty();
1453     s_WriteQueueLock.Unlock();
1454 
1455     s_WriteLog(str.data(), str.size());
1456     free((void*)str.data());
1457 
1458     if (have_more)
1459         SetRunnable();
1460 }
1461 
1462 END_NCBI_SCOPE;
1463