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