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