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