ae152b0a5b64d49c6a2a8db947d620a17b4af074
[asdcplib.git] / src / KM_log.cpp
1 /*
2 Copyright (c) 2004-2009, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27   /*! \file    KM_log.cpp
28     \version $Id$
29     \brief   message logging API
30   */
31
32 #include <KM_util.h>
33 #include <KM_log.h>
34 #include <KM_mutex.h>
35 #include <sys/types.h>
36 #include <string.h>
37 #include <stdarg.h>
38 #include <iostream>
39 #include <sstream>
40
41 #ifdef KM_WIN32
42 #define getpid GetCurrentProcessId
43 #else
44 #include <unistd.h>
45 #endif
46
47 //------------------------------------------------------------------------------------------
48 //
49
50 void
51 Kumu::ILogSink::vLogf(LogType_t type, const char* fmt, va_list* list)
52 {
53   char buf[MaxLogLength];
54   vsnprintf(buf, MaxLogLength, fmt, *list);
55
56   WriteEntry(LogEntry(getpid(), type, buf));
57 }
58
59 //------------------------------------------------------------------------------------------
60 //
61
62 static Kumu::Mutex     s_DefaultLogSinkLock;
63 static Kumu::ILogSink* s_DefaultLogSink = 0;
64 static Kumu::StdioLogSink s_StderrLogSink;
65
66 //
67 void
68 Kumu::SetDefaultLogSink(ILogSink* Sink)
69 {
70   AutoMutex L(s_DefaultLogSinkLock);
71   s_DefaultLogSink = Sink;
72 }
73
74 // Returns the internal default sink.
75 Kumu::ILogSink&
76 Kumu::DefaultLogSink()
77 {
78   AutoMutex L(s_DefaultLogSinkLock);
79
80   if ( s_DefaultLogSink == 0 )
81     s_DefaultLogSink = &s_StderrLogSink;
82
83   return *s_DefaultLogSink;
84 }
85
86
87 //------------------------------------------------------------------------------------------
88 //
89
90 void
91 Kumu::EntryListLogSink::WriteEntry(const LogEntry& Entry)
92 {
93   AutoMutex L(m_Lock);
94
95   if ( Entry.TestFilter(m_filter) )
96     m_Target.push_back(Entry);
97 }
98
99 //------------------------------------------------------------------------------------------
100 //
101
102 void
103 Kumu::StdioLogSink::WriteEntry(const LogEntry& Entry)
104 {
105   AutoMutex L(m_Lock);
106   std::string buf;
107
108   if ( Entry.TestFilter(m_filter) )
109     {
110       Entry.CreateStringWithOptions(buf, m_options);
111       fputs(buf.c_str(), m_stream);
112     }
113 }
114
115 //---------------------------------------------------------------------------------
116
117 #ifdef KM_WIN32
118 //
119 // http://www.codeguru.com/forum/showthread.php?t=231165
120 //
121 void
122 Kumu::WinDbgLogSink::WriteEntry(const LogEntry& Entry)
123 {
124   AutoMutex L(m_Lock);
125   std::string buf;
126
127   if ( Entry.TestFilter(m_filter) )
128     {
129       Entry.CreateStringWithOptions(buf, m_options);
130       int lenW = ::MultiByteToWideChar(CP_ACP, 0, buf.c_str(), buf.size(), 0, 0);
131
132       if ( lenW > 0 )
133         {
134           // Check whether conversion was successful
135           BSTR unicodestr = ::SysAllocStringLen(0, lenW);
136           ::MultiByteToWideChar(CP_ACP, 0, buf.c_str(), buf.size(), unicodestr, lenW);
137           ::OutputDebugString(unicodestr);
138           ::SysFreeString(unicodestr);
139         }
140       else
141         {
142           ::OutputDebugString(L("MultiByteToWideChar failed, log sink output discarded.\n"));
143         }
144     }
145 }
146 #endif
147
148 //------------------------------------------------------------------------------------------
149 //
150
151 #ifndef KM_WIN32
152 //
153 void
154 Kumu::StreamLogSink::WriteEntry(const LogEntry& Entry)
155 {
156   AutoMutex L(m_Lock);
157   std::string buf;
158
159   if ( Entry.TestFilter(m_filter) )
160     {
161       Entry.CreateStringWithOptions(buf, m_options);
162       write(m_fd, buf.c_str(), buf.size());
163     }
164 }
165
166 // foolin with symbols
167 //------------------------------------------------------------------------------------------
168 #include <syslog.h>
169 int const SYSLOG_ALERT = LOG_ALERT;
170 int const SYSLOG_CRIT = LOG_CRIT;
171 int const SYSLOG_ERR = LOG_ERR;
172 int const SYSLOG_WARNING = LOG_WARNING;
173 int const SYSLOG_NOTICE = LOG_NOTICE;
174 int const SYSLOG_INFO = LOG_INFO;
175 int const SYSLOG_DEBUG = LOG_DEBUG;
176 #undef LOG_ALERT
177 #undef LOG_CRIT
178 #undef LOG_ERR
179 #undef LOG_WARNING
180 #undef LOG_NOTICE
181 #undef LOG_INFO
182 #undef LOG_DEBUG
183 //------------------------------------------------------------------------------------------
184
185 Kumu::SyslogLogSink::SyslogLogSink(const std::string& source_name, int facility)
186 {
187   if ( facility == 0 )
188     facility = LOG_DAEMON;
189
190   openlog(source_name.c_str(), LOG_CONS|LOG_NDELAY||LOG_PID, facility);
191 }
192
193 Kumu::SyslogLogSink::~SyslogLogSink()
194 {
195   closelog();
196 }
197
198 //
199 void
200 Kumu::SyslogLogSink::WriteEntry(const LogEntry& Entry)
201 {
202   int priority;
203
204   switch ( Entry.Type )
205     {
206     case Kumu::LOG_ALERT:   priority = SYSLOG_ALERT; break;
207     case Kumu::LOG_CRIT:    priority = SYSLOG_CRIT; break;
208     case Kumu::LOG_ERROR:   priority = SYSLOG_ERR; break;
209     case Kumu::LOG_WARN:    priority = SYSLOG_WARNING; break;
210     case Kumu::LOG_NOTICE:  priority = SYSLOG_NOTICE; break;
211     case Kumu::LOG_INFO:    priority = SYSLOG_INFO; break;
212     case Kumu::LOG_DEBUG:   priority = SYSLOG_DEBUG; break;
213     }
214
215   AutoMutex L(m_Lock);
216
217   if ( Entry.TestFilter(m_filter) )
218     {
219       syslog(priority, "%s", Entry.Msg.substr(0, Entry.Msg.size() - 1).c_str());
220     }
221 }
222
223 //
224 int
225 Kumu::SyslogNameToFacility(const std::string& facility_name)
226 {
227   if ( facility_name == "LOG_DAEMON" ) return LOG_DAEMON;
228   if ( facility_name == "LOG_LOCAL0" ) return LOG_LOCAL0;
229   if ( facility_name == "LOG_LOCAL1" ) return LOG_LOCAL1;
230   if ( facility_name == "LOG_LOCAL2" ) return LOG_LOCAL2;
231   if ( facility_name == "LOG_LOCAL3" ) return LOG_LOCAL3;
232   if ( facility_name == "LOG_LOCAL4" ) return LOG_LOCAL4;
233   if ( facility_name == "LOG_LOCAL5" ) return LOG_LOCAL5;
234   if ( facility_name == "LOG_LOCAL6" ) return LOG_LOCAL6;
235   if ( facility_name == "LOG_LOCAL7" ) return LOG_LOCAL7;
236
237   DefaultLogSink().Error("Unsupported facility name: %s, using default value LOG_DAEMON\n", facility_name.c_str());
238   return LOG_DAEMON;
239 }
240
241 #endif
242
243 //------------------------------------------------------------------------------------------
244
245 //
246 std::basic_ostream<char, std::char_traits<char> >&
247 Kumu::operator<<(std::basic_ostream<char, std::char_traits<char> >& strm, LogEntry const& Entry)
248 {
249   std::basic_ostringstream<char, std::char_traits<char> > s;
250   s.copyfmt(strm);
251   s.width(0);
252   std::string buf;
253
254   s << Entry.CreateStringWithOptions(buf, LOG_OPTION_ALL);
255
256   strm << s.str();
257   return strm;
258 }
259
260 //------------------------------------------------------------------------------------------
261
262
263 //
264 bool
265 Kumu::LogEntry::TestFilter(i32_t filter) const
266 {
267   switch ( Type )
268     {
269     case LOG_CRIT:
270       if ( (filter & LOG_ALLOW_CRIT) == 0 )
271         return false;
272       break;
273
274     case LOG_ALERT:
275       if ( (filter & LOG_ALLOW_ALERT) == 0 )
276         return false;
277       break;
278
279     case LOG_NOTICE:
280       if ( (filter & LOG_ALLOW_NOTICE) == 0 )
281         return false;
282       break;
283
284     case LOG_ERROR:
285       if ( (filter & LOG_ALLOW_ERROR) == 0 )
286         return false;
287       break;
288
289     case LOG_WARN:
290       if ( (filter & LOG_ALLOW_WARN) == 0 )
291         return false;
292       break;
293
294     case LOG_INFO:
295       if ( (filter & LOG_ALLOW_INFO) == 0 )
296         return false;
297       break;
298
299     case LOG_DEBUG:
300       if ( (filter & LOG_ALLOW_DEBUG) == 0 )
301         return false;
302       break;
303
304     }
305
306  return true;
307 }
308
309 //
310 std::string&
311 Kumu::LogEntry::CreateStringWithOptions(std::string& out_buf, i32_t opt) const
312 {
313   out_buf.erase();
314
315   if ( opt != 0 )
316     {
317       char buf[64];
318
319       if ( (opt & LOG_OPTION_TIMESTAMP) != 0 )
320         {
321           Timestamp Now;
322           out_buf += Now.EncodeString(buf, 64);
323         }
324
325       if ( (opt & LOG_OPTION_PID) != 0 )
326         {
327           if ( ! out_buf.empty() )  out_buf += " ";
328           snprintf(buf, 64, "%d", PID);
329           out_buf += buf;
330         }
331
332       if ( (opt & LOG_OPTION_TYPE) != 0 )
333         {
334           if ( ! out_buf.empty() )  out_buf += " ";
335           
336           switch ( Type )
337             {
338             case LOG_CRIT:   out_buf += "CRT";      break;
339             case LOG_ALERT:  out_buf += "ALR";      break;
340             case LOG_NOTICE: out_buf += "NTC";      break;
341             case LOG_ERROR:  out_buf += "ERR";      break;
342             case LOG_WARN:   out_buf += "WRN";      break;
343             case LOG_INFO:   out_buf += "INF";      break;
344             case LOG_DEBUG:  out_buf += "DBG";      break;
345             default:         out_buf += "DFL";      break;
346             }
347         }
348
349       out_buf.insert(0, "[");
350       out_buf += "]: ";
351     }
352
353   out_buf += Msg;
354   return out_buf;
355 }
356
357
358 //
359 ui32_t
360 Kumu::LogEntry::ArchiveLength() const
361 {
362   return sizeof(ui32_t)
363     + EventTime.ArchiveLength()
364     + sizeof(ui32_t)
365     + sizeof(ui32_t) + Msg.size();
366 }
367
368 //
369 bool
370 Kumu::LogEntry::Archive(Kumu::MemIOWriter* Writer) const
371 {
372   if ( ! Writer->WriteUi32BE(PID) ) return false;
373   if ( ! EventTime.Archive(Writer) ) return false;
374   if ( ! Writer->WriteUi32BE(Type) ) return false;
375   if ( ! ArchiveString(*Writer, Msg) ) return false;
376   return true;
377 }
378
379 //
380 bool
381 Kumu::LogEntry::Unarchive(Kumu::MemIOReader* Reader)
382 {
383   if ( ! Reader->ReadUi32BE(&PID) ) return false;
384   if ( ! EventTime.Unarchive(Reader) ) return false;
385   if ( ! Reader->ReadUi32BE((ui32_t*)&Type) ) return false;
386   if ( ! UnarchiveString(*Reader, Msg) ) return false;
387   return true;
388 }
389
390 //
391 // end
392 //