Fix a type-punning warning.
[asdcplib-cth.git] / src / KM_log.h
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.h
28     \version $Id: KM_log.h,v 1.15 2013/06/17 17:55:54 jhurst Exp $
29     \brief   message logging API
30   */
31
32
33 #ifndef _KM_LOG_H_
34 #define _KM_LOG_H_
35
36 #include <KM_platform.h>
37 #include <KM_mutex.h>
38 #include <KM_util.h>
39 #include <stdarg.h>
40 #include <errno.h>
41 #include <iosfwd>
42 #include <set>
43
44 #define LOG_MSG_IMPL(t) \
45   va_list args; \
46   va_start(args, fmt); \
47   vLogf((t), fmt, &args); \
48   va_end(args)
49
50 // Returns RESULT_PTR if the given argument is NULL.
51 # define KM_TEST_NULL_L(p) \
52   if ( (p) == 0  ) { \
53     DefaultLogSink().Error("NULL pointer in file %s, line %d\n", __FILE__, __LINE__); \
54     return Kumu::RESULT_PTR; \
55   }
56
57 // Returns RESULT_PTR if the given argument is NULL. It then
58 // assumes that the argument is a pointer to a string and returns
59 // RESULT_NULL_STR if the first character is '\0'.
60 //
61 # define KM_TEST_NULL_STR_L(p) \
62   KM_TEST_NULL_L(p); \
63   if ( (p)[0] == '\0' ) { \
64     DefaultLogSink().Error("Empty string in file %s, line %d\n", __FILE__, __LINE__); \
65     return Kumu::RESULT_NULL_STR; \
66   }
67
68
69 namespace Kumu
70 {
71   // no log message will exceed this length
72   const ui32_t MaxLogLength = 512;
73
74   //---------------------------------------------------------------------------------
75   // message logging
76
77   // Log messages are recorded by objects which implement the interface given
78   // in the class ILogSink below. The library maintains a pointer to a default
79   // log sink which is used by the library to report messages.
80   //
81
82   // types of log messages
83   enum LogType_t {
84     LOG_DEBUG,    // detailed developer info
85     LOG_INFO,     // developer info
86     LOG_WARN,     // library non-fatal or near-miss error
87     LOG_ERROR,    // library fatal error
88     LOG_NOTICE,   // application user info
89     LOG_ALERT,    // application non-fatal or near-miss error
90     LOG_CRIT,     // application fatal error
91     LOG_MAX
92   };
93
94
95   // OR these values together to come up with sink filter flags.
96   // The default mask is LOG_ALLOW_ALL (all messages).
97   const i32_t LOG_ALLOW_DEBUG      = 0x00000001;
98   const i32_t LOG_ALLOW_INFO       = 0x00000002;
99   const i32_t LOG_ALLOW_WARN       = 0x00000004;
100   const i32_t LOG_ALLOW_ERROR      = 0x00000008;
101   const i32_t LOG_ALLOW_NOTICE     = 0x00000010;
102   const i32_t LOG_ALLOW_ALERT      = 0x00000020;
103   const i32_t LOG_ALLOW_CRIT       = 0x00000040;
104   const i32_t LOG_ALLOW_NONE       = 0x00000000;
105   const i32_t LOG_ALLOW_ALL        = 0x000fffff;
106
107   // options are used to control display format default is 0.
108   const i32_t LOG_OPTION_TYPE      = 0x01000000;
109   const i32_t LOG_OPTION_TIMESTAMP = 0x02000000;
110   const i32_t LOG_OPTION_PID       = 0x04000000;
111   const i32_t LOG_OPTION_NONE      = 0x00000000;
112   const i32_t LOG_OPTION_ALL       = 0xfff00000;
113
114   // A log message with environmental metadata
115  class LogEntry : public IArchive
116   {
117   public:
118     ui32_t      PID;
119     Timestamp   EventTime;
120     LogType_t   Type;
121     std::string Msg;
122
123     LogEntry() {}
124     LogEntry(ui32_t pid, LogType_t t, const char* m) : PID(pid), Type(t), Msg(m) { assert(m); }
125     virtual ~LogEntry() {}
126
127     // returns true if the message Type is present in the mask
128     bool   TestFilter(i32_t mask_value) const;
129
130     // renders the message into outstr using the given dispaly options
131     // returns outstr&
132     std::string& CreateStringWithOptions(std::string& outstr, i32_t mask_value) const;
133
134     // IArchive
135     bool   HasValue() const { return ! Msg.empty(); }
136     ui32_t ArchiveLength() const;
137     bool   Archive(MemIOWriter* Writer) const;
138     bool   Unarchive(MemIOReader* Reader);
139   };
140
141   //
142   std::basic_ostream<char, std::char_traits<char> >&
143     operator<<(std::basic_ostream<char, std::char_traits<char> >& strm, LogEntry const& Entry);
144
145
146   typedef ArchivableList<LogEntry> LogEntryList;
147   
148   //
149   class ILogSink
150     {
151     protected:
152       i32_t m_filter;
153       i32_t m_options;
154       Mutex m_lock;
155       std::set<ILogSink*> m_listeners;
156
157       // you must obtain m_lock BEFORE calling this from your own WriteEntry
158       void WriteEntryToListeners(const LogEntry& entry)
159       {
160         std::set<ILogSink*>::iterator i;
161         for ( i = m_listeners.begin(); i != m_listeners.end(); ++i )
162           (*i)->WriteEntry(entry);
163       }
164
165       KM_NO_COPY_CONSTRUCT(ILogSink);
166
167     public:
168     ILogSink() : m_filter(LOG_ALLOW_ALL), m_options(LOG_OPTION_NONE) {}
169       virtual ~ILogSink() {}
170
171       void  SetFilterFlag(i32_t f) { m_filter |= f; }
172       void  UnsetFilterFlag(i32_t f) { m_filter &= ~f; }
173       bool  TestFilterFlag(i32_t f) const  { return ((m_filter & f) == f); }
174
175       void  SetOptionFlag(i32_t o) { m_options |= o; }
176       void  UnsetOptionFlag(i32_t o) { m_options &= ~o; }
177       bool  TestOptionFlag(i32_t o) const  { return ((m_options & o) == o); }
178
179       void AddListener(ILogSink& s) {
180         if ( &s != this )
181           {
182             AutoMutex l(m_lock);
183             m_listeners.insert(&s);
184           }
185       }
186
187       void DelListener(ILogSink& s) {
188         AutoMutex l(m_lock);
189         m_listeners.erase(&s);
190       }
191
192       // library messages
193       void Error(const char* fmt, ...)    { LOG_MSG_IMPL(LOG_ERROR); }
194       void Warn(const char* fmt, ...)     { LOG_MSG_IMPL(LOG_WARN);  }
195       void Info(const char* fmt, ...)     { LOG_MSG_IMPL(LOG_INFO);  }
196       void Debug(const char* fmt, ...)    { LOG_MSG_IMPL(LOG_DEBUG); }
197
198       // application messages
199       void Critical(const char* fmt, ...) { LOG_MSG_IMPL(LOG_CRIT); }
200       void Alert(const char* fmt, ...)    { LOG_MSG_IMPL(LOG_ALERT); }
201       void Notice(const char* fmt, ...)   { LOG_MSG_IMPL(LOG_NOTICE); }
202
203       // message with type
204       void Logf(LogType_t type, const char* fmt, ...) { LOG_MSG_IMPL(type); }
205
206       // actual log sink input
207       virtual void vLogf(LogType_t, const char*, va_list*);
208       virtual void WriteEntry(const LogEntry&) = 0;
209     };
210
211
212   // Sets the internal default sink to the given receiver. If the given value
213   // is zero, sets the default sink to the internally allocated stderr sink.
214   void SetDefaultLogSink(ILogSink* = 0);
215
216   // Returns the internal default sink.
217   ILogSink& DefaultLogSink();
218
219
220   // attach a log sink as a listener until deleted
221   class LogSinkListenContext
222     {
223       ILogSink* m_log_source;
224       ILogSink* m_sink;
225       KM_NO_COPY_CONSTRUCT(LogSinkListenContext);
226       LogSinkListenContext();
227
228     public:
229       LogSinkListenContext(ILogSink& source, ILogSink& sink)
230         {
231           m_log_source = &source;
232           m_sink = &sink;
233           m_log_source->AddListener(*m_sink);
234         }
235
236       ~LogSinkListenContext()
237         {
238           m_log_source->DelListener(*m_sink);
239         }
240     };
241
242
243   //------------------------------------------------------------------------------------------
244   //
245
246   // collect log messages into the given list, does not test filter
247   class EntryListLogSink : public ILogSink
248   {
249     LogEntryList& m_Target;
250     KM_NO_COPY_CONSTRUCT(EntryListLogSink);
251     EntryListLogSink();
252
253   public:
254     EntryListLogSink(LogEntryList& target) : m_Target(target) {}
255     virtual ~EntryListLogSink() {}
256
257     void WriteEntry(const LogEntry& Entry);
258   };
259
260
261   // write messages to a POSIX stdio stream
262   class StdioLogSink : public ILogSink
263     {
264       FILE* m_stream;
265       KM_NO_COPY_CONSTRUCT(StdioLogSink);
266
267     public:
268     StdioLogSink() : m_stream(stderr) {}
269     StdioLogSink(FILE* stream) : m_stream(stream) {}
270       virtual ~StdioLogSink() {}
271
272     void WriteEntry(const LogEntry&);
273     };
274
275 #ifdef KM_WIN32
276   // write messages to the Win32 debug stream
277   class WinDbgLogSink : public ILogSink
278     {
279       KM_NO_COPY_CONSTRUCT(WinDbgLogSink);
280
281     public:
282       WinDbgLogSink() {}
283       virtual ~WinDbgLogSink() {}
284
285       void WriteEntry(const LogEntry&);
286     };
287 #endif
288
289 #ifndef KM_WIN32
290   // write messages to a POSIX file descriptor
291   class StreamLogSink : public ILogSink
292     {
293       int   m_fd;
294       KM_NO_COPY_CONSTRUCT(StreamLogSink);
295       StreamLogSink();
296
297     public:
298       StreamLogSink(int fd) : m_fd(fd) {}
299       virtual ~StreamLogSink() {}
300
301       void WriteEntry(const LogEntry&);
302     };
303
304   // write messages to the syslog facility
305   class SyslogLogSink : public ILogSink
306     {
307       KM_NO_COPY_CONSTRUCT(SyslogLogSink);
308       SyslogLogSink();
309   
310     public:
311       SyslogLogSink(const std::string& source_name, int facility);
312       virtual ~SyslogLogSink();
313       void WriteEntry(const LogEntry&);
314     };
315
316   // convert a string into the appropriate syslog facility id
317   int SyslogNameToFacility(const std::string& facility_name);
318
319 #endif
320
321
322 } // namespace Kumu
323
324 #endif // _KM_LOG_H_
325
326 //
327 // end KM_log.h
328 //