version bump
[asdcplib.git] / src / KM_log.cpp
1 /*
2 Copyright (c) 2004-2011, 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   WriteEntryToListeners(Entry);
95
96   if ( Entry.TestFilter(m_filter) )
97     m_Target.push_back(Entry);
98 }
99
100 //------------------------------------------------------------------------------------------
101 //
102
103 void
104 Kumu::StdioLogSink::WriteEntry(const LogEntry& Entry)
105 {
106   std::string buf;
107   AutoMutex L(m_lock);
108   WriteEntryToListeners(Entry);
109
110   if ( Entry.TestFilter(m_filter) )
111     {
112       Entry.CreateStringWithOptions(buf, m_options);
113       fputs(buf.c_str(), m_stream);
114     }
115 }
116
117 //---------------------------------------------------------------------------------
118
119 #ifdef KM_WIN32
120 //
121 // http://www.codeguru.com/forum/showthread.php?t=231165
122 //
123 void
124 Kumu::WinDbgLogSink::WriteEntry(const LogEntry& Entry)
125 {
126   std::string buf;
127   AutoMutex L(m_lock);
128   WriteEntryToListeners(Entry);
129
130   if ( Entry.TestFilter(m_filter) )
131     {
132       Entry.CreateStringWithOptions(buf, m_options);
133       ::OutputDebugStringA(buf.c_str());
134     }
135 }
136 #endif
137
138 //------------------------------------------------------------------------------------------
139 //
140
141 #ifndef KM_WIN32
142 //
143 void
144 Kumu::StreamLogSink::WriteEntry(const LogEntry& Entry)
145 {
146   std::string buf;
147   AutoMutex L(m_lock);
148   WriteEntryToListeners(Entry);
149
150   if ( Entry.TestFilter(m_filter) )
151     {
152       Entry.CreateStringWithOptions(buf, m_options);
153       write(m_fd, buf.c_str(), buf.size());
154     }
155 }
156
157 // foolin with symbols
158 //------------------------------------------------------------------------------------------
159 #include <syslog.h>
160 int const SYSLOG_ALERT = LOG_ALERT;
161 int const SYSLOG_CRIT = LOG_CRIT;
162 int const SYSLOG_ERR = LOG_ERR;
163 int const SYSLOG_WARNING = LOG_WARNING;
164 int const SYSLOG_NOTICE = LOG_NOTICE;
165 int const SYSLOG_INFO = LOG_INFO;
166 int const SYSLOG_DEBUG = LOG_DEBUG;
167 #undef LOG_ALERT
168 #undef LOG_CRIT
169 #undef LOG_ERR
170 #undef LOG_WARNING
171 #undef LOG_NOTICE
172 #undef LOG_INFO
173 #undef LOG_DEBUG
174 //------------------------------------------------------------------------------------------
175
176 Kumu::SyslogLogSink::SyslogLogSink(const std::string& source_name, int facility)
177 {
178   if ( facility == 0 )
179     facility = LOG_DAEMON;
180
181   openlog(source_name.c_str(), LOG_CONS|LOG_NDELAY||LOG_PID, facility);
182 }
183
184 Kumu::SyslogLogSink::~SyslogLogSink()
185 {
186   closelog();
187 }
188
189 //
190 void
191 Kumu::SyslogLogSink::WriteEntry(const LogEntry& Entry)
192 {
193   int priority;
194
195   switch ( Entry.Type )
196     {
197     case Kumu::LOG_ALERT:   priority = SYSLOG_ALERT; break;
198     case Kumu::LOG_CRIT:    priority = SYSLOG_CRIT; break;
199     case Kumu::LOG_ERROR:   priority = SYSLOG_ERR; break;
200     case Kumu::LOG_WARN:    priority = SYSLOG_WARNING; break;
201     case Kumu::LOG_NOTICE:  priority = SYSLOG_NOTICE; break;
202     case Kumu::LOG_INFO:    priority = SYSLOG_INFO; break;
203     case Kumu::LOG_DEBUG:   priority = SYSLOG_DEBUG; break;
204     }
205
206   AutoMutex L(m_lock);
207   WriteEntryToListeners(Entry);
208
209   if ( Entry.TestFilter(m_filter) )
210     {
211       syslog(priority, "%s", Entry.Msg.substr(0, Entry.Msg.size() - 1).c_str());
212     }
213 }
214
215 //
216 int
217 Kumu::SyslogNameToFacility(const std::string& facility_name)
218 {
219   if ( facility_name == "LOG_DAEMON" ) return LOG_DAEMON;
220   if ( facility_name == "LOG_LOCAL0" ) return LOG_LOCAL0;
221   if ( facility_name == "LOG_LOCAL1" ) return LOG_LOCAL1;
222   if ( facility_name == "LOG_LOCAL2" ) return LOG_LOCAL2;
223   if ( facility_name == "LOG_LOCAL3" ) return LOG_LOCAL3;
224   if ( facility_name == "LOG_LOCAL4" ) return LOG_LOCAL4;
225   if ( facility_name == "LOG_LOCAL5" ) return LOG_LOCAL5;
226   if ( facility_name == "LOG_LOCAL6" ) return LOG_LOCAL6;
227   if ( facility_name == "LOG_LOCAL7" ) return LOG_LOCAL7;
228
229   DefaultLogSink().Error("Unsupported facility name: %s, using default value LOG_DAEMON\n", facility_name.c_str());
230   return LOG_DAEMON;
231 }
232
233 #endif
234
235 //------------------------------------------------------------------------------------------
236
237 //
238 std::basic_ostream<char, std::char_traits<char> >&
239 Kumu::operator<<(std::basic_ostream<char, std::char_traits<char> >& strm, LogEntry const& Entry)
240 {
241   std::basic_ostringstream<char, std::char_traits<char> > s;
242   s.copyfmt(strm);
243   s.width(0);
244   std::string buf;
245
246   s << Entry.CreateStringWithOptions(buf, LOG_OPTION_ALL);
247
248   strm << s.str();
249   return strm;
250 }
251
252 //------------------------------------------------------------------------------------------
253
254
255 //
256 bool
257 Kumu::LogEntry::TestFilter(i32_t filter) const
258 {
259   switch ( Type )
260     {
261     case LOG_CRIT:
262       if ( (filter & LOG_ALLOW_CRIT) == 0 )
263         return false;
264       break;
265
266     case LOG_ALERT:
267       if ( (filter & LOG_ALLOW_ALERT) == 0 )
268         return false;
269       break;
270
271     case LOG_NOTICE:
272       if ( (filter & LOG_ALLOW_NOTICE) == 0 )
273         return false;
274       break;
275
276     case LOG_ERROR:
277       if ( (filter & LOG_ALLOW_ERROR) == 0 )
278         return false;
279       break;
280
281     case LOG_WARN:
282       if ( (filter & LOG_ALLOW_WARN) == 0 )
283         return false;
284       break;
285
286     case LOG_INFO:
287       if ( (filter & LOG_ALLOW_INFO) == 0 )
288         return false;
289       break;
290
291     case LOG_DEBUG:
292       if ( (filter & LOG_ALLOW_DEBUG) == 0 )
293         return false;
294       break;
295
296     }
297
298  return true;
299 }
300
301 //
302 std::string&
303 Kumu::LogEntry::CreateStringWithOptions(std::string& out_buf, i32_t opt) const
304 {
305   out_buf.erase();
306
307   if ( opt != 0 )
308     {
309       char buf[64];
310
311       if ( (opt & LOG_OPTION_TIMESTAMP) != 0 )
312         {
313           Timestamp Now;
314           out_buf += Now.EncodeString(buf, 64);
315         }
316
317       if ( (opt & LOG_OPTION_PID) != 0 )
318         {
319           if ( ! out_buf.empty() )  out_buf += " ";
320           snprintf(buf, 64, "%d", PID);
321           out_buf += buf;
322         }
323
324       if ( (opt & LOG_OPTION_TYPE) != 0 )
325         {
326           if ( ! out_buf.empty() )  out_buf += " ";
327           
328           switch ( Type )
329             {
330             case LOG_CRIT:   out_buf += "CRT";      break;
331             case LOG_ALERT:  out_buf += "ALR";      break;
332             case LOG_NOTICE: out_buf += "NTC";      break;
333             case LOG_ERROR:  out_buf += "ERR";      break;
334             case LOG_WARN:   out_buf += "WRN";      break;
335             case LOG_INFO:   out_buf += "INF";      break;
336             case LOG_DEBUG:  out_buf += "DBG";      break;
337             default:         out_buf += "DFL";      break;
338             }
339         }
340
341       out_buf.insert(0, "[");
342       out_buf += "]: ";
343     }
344
345   out_buf += Msg;
346   return out_buf;
347 }
348
349
350 //
351 ui32_t
352 Kumu::LogEntry::ArchiveLength() const
353 {
354   return sizeof(ui32_t)
355     + EventTime.ArchiveLength()
356     + sizeof(ui32_t)
357     + sizeof(ui32_t) + Msg.size();
358 }
359
360 //
361 bool
362 Kumu::LogEntry::Archive(Kumu::MemIOWriter* Writer) const
363 {
364   if ( ! Writer->WriteUi32BE(PID) ) return false;
365   if ( ! EventTime.Archive(Writer) ) return false;
366   if ( ! Writer->WriteUi32BE(Type) ) return false;
367   if ( ! ArchiveString(*Writer, Msg) ) return false;
368   return true;
369 }
370
371 //
372 bool
373 Kumu::LogEntry::Unarchive(Kumu::MemIOReader* Reader)
374 {
375   if ( ! Reader->ReadUi32BE(&PID) ) return false;
376   if ( ! EventTime.Unarchive(Reader) ) return false;
377   if ( ! Reader->ReadUi32BE((ui32_t*)&Type) ) return false;
378   if ( ! UnarchiveString(*Reader, Msg) ) return false;
379   return true;
380 }
381
382 //
383 // end
384 //