Modified to enforce execution order of the predicates
[asdcplib.git] / src / KM_log.h
index 2bbc330b2758fb4cc3376221762d5a4b55609988..9279f300a41c6d0e9736a53be8f969adf7e22184 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2006, John Hurst
+Copyright (c) 2004-2009, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -35,10 +35,35 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <KM_platform.h>
 #include <KM_mutex.h>
+#include <KM_util.h>
 #include <stdarg.h>
 #include <errno.h>
+#include <iosfwd>
+#include <set>
 
-#define LOG_MSG_IMPL(t) va_list args; va_start(args, fmt); vLogf((t), fmt, &args); va_end(args)
+#define LOG_MSG_IMPL(t) \
+  va_list args; \
+  va_start(args, fmt); \
+  vLogf((t), fmt, &args); \
+  va_end(args)
+
+// Returns RESULT_PTR if the given argument is NULL.
+# define KM_TEST_NULL_L(p) \
+  if ( (p) == 0  ) { \
+    DefaultLogSink().Error("NULL pointer in file %s, line %d\n", __FILE__, __LINE__); \
+    return Kumu::RESULT_PTR; \
+  }
+
+// Returns RESULT_PTR if the given argument is NULL. It then
+// assumes that the argument is a pointer to a string and returns
+// RESULT_NULL_STR if the first character is '\0'.
+//
+# define KM_TEST_NULL_STR_L(p) \
+  KM_TEST_NULL_L(p); \
+  if ( (p)[0] == '\0' ) { \
+    DefaultLogSink().Error("Empty string in file %s, line %d\n", __FILE__, __LINE__); \
+    return Kumu::RESULT_NULL_STR; \
+  }
 
 
 namespace Kumu
@@ -49,30 +74,141 @@ namespace Kumu
   //---------------------------------------------------------------------------------
   // message logging
 
-  // Error and debug messages will be delivered to an object having this interface.
-  // The default implementation sends only LOG_ERROR and LOG_WARN messages to stderr.
-  // To receive LOG_INFO or LOG_DEBUG messages, or to send messages somewhere other
-  // than stderr, implement this interface and register an instance of your new class
-  // by calling SetDefaultLogSink().
+  // Log messages are recorded by objects which implement the interface given
+  // in the class ILogSink below. The library maintains a pointer to a default
+  // log sink which is used by the library to report messages.
+  //
+
+  // types of log messages
+  enum LogType_t {
+    LOG_DEBUG,    // detailed developer info
+    LOG_INFO,     // developer info
+    LOG_WARN,     // library non-fatal or near-miss error
+    LOG_ERROR,    // library fatal error
+    LOG_NOTICE,   // application user info
+    LOG_ALERT,    // application non-fatal or near-miss error
+    LOG_CRIT,     // application fatal error
+    LOG_MAX
+  };
+
+
+  // OR these values together to come up with sink filter flags.
+  // The default mask is LOG_ALLOW_ALL (all messages).
+  const i32_t LOG_ALLOW_DEBUG      = 0x00000001;
+  const i32_t LOG_ALLOW_INFO       = 0x00000002;
+  const i32_t LOG_ALLOW_WARN       = 0x00000004;
+  const i32_t LOG_ALLOW_ERROR      = 0x00000008;
+  const i32_t LOG_ALLOW_NOTICE     = 0x00000010;
+  const i32_t LOG_ALLOW_ALERT      = 0x00000020;
+  const i32_t LOG_ALLOW_CRIT       = 0x00000040;
+  const i32_t LOG_ALLOW_NONE       = 0x00000000;
+  const i32_t LOG_ALLOW_ALL        = 0x000fffff;
+
+  // options are used to control display format default is 0.
+  const i32_t LOG_OPTION_TYPE      = 0x01000000;
+  const i32_t LOG_OPTION_TIMESTAMP = 0x02000000;
+  const i32_t LOG_OPTION_PID       = 0x04000000;
+  const i32_t LOG_OPTION_NONE      = 0x00000000;
+  const i32_t LOG_OPTION_ALL       = 0xfff00000;
+
+  // A log message with environmental metadata
+ class LogEntry : public IArchive
+  {
+  public:
+    ui32_t      PID;
+    Timestamp   EventTime;
+    LogType_t   Type;
+    std::string Msg;
+
+    LogEntry() {}
+    LogEntry(ui32_t pid, LogType_t t, const char* m) : PID(pid), Type(t), Msg(m) { assert(m); }
+    virtual ~LogEntry() {}
+
+    // returns true if the message Type is present in the mask
+    bool   TestFilter(i32_t mask_value) const;
+
+    // renders the message into outstr using the given dispaly options
+    // returns outstr&
+    std::string& CreateStringWithOptions(std::string& outstr, i32_t mask_value) const;
+
+    // IArchive
+    bool   HasValue() const { return ! Msg.empty(); }
+    ui32_t ArchiveLength() const;
+    bool   Archive(MemIOWriter* Writer) const;
+    bool   Unarchive(MemIOReader* Reader);
+  };
+
+  //
+  std::basic_ostream<char, std::char_traits<char> >&
+    operator<<(std::basic_ostream<char, std::char_traits<char> >& strm, LogEntry const& Entry);
+
+
+  typedef ArchivableList<LogEntry> LogEntryList;
+  
+  //
   class ILogSink
     {
-    public:
-      enum LogType_t { LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR,
-                      LOG_NOTICE, LOG_ALERT, LOG_CRIT };
+    protected:
+      i32_t m_filter;
+      i32_t m_options;
+      Mutex m_lock;
+      std::set<ILogSink*> m_listeners;
+
+      // you must obtain m_lock BEFORE calling this from your own WriteEntry
+      void WriteEntryToListeners(const LogEntry& entry)
+      {
+       std::set<ILogSink*>::iterator i;
+       for ( i = m_listeners.begin(); i != m_listeners.end(); ++i )
+         (*i)->WriteEntry(entry);
+      }
 
+      KM_NO_COPY_CONSTRUCT(ILogSink);
+
+    public:
+    ILogSink() : m_filter(LOG_ALLOW_ALL), m_options(LOG_OPTION_NONE) {}
       virtual ~ILogSink() {}
 
-      void Critical(const char* fmt, ...) { LOG_MSG_IMPL(LOG_CRIT); }
-      void Alert(const char* fmt, ...)    { LOG_MSG_IMPL(LOG_ALERT); }
-      void Notice(const char* fmt, ...)   { LOG_MSG_IMPL(LOG_NOTICE); }
+      void  SetFilterFlag(i32_t f) { m_filter |= f; }
+      void  UnsetFilterFlag(i32_t f) { m_filter &= ~f; }
+      bool  TestFilterFlag(i32_t f) const  { return ((m_filter & f) == f); }
+
+      void  SetOptionFlag(i32_t o) { m_options |= o; }
+      void  UnsetOptionFlag(i32_t o) { m_options &= ~o; }
+      bool  TestOptionFlag(i32_t o) const  { return ((m_options & o) == o); }
+
+      void AddListener(ILogSink& s) {
+       if ( &s != this )
+         {
+           AutoMutex l(m_lock);
+           m_listeners.insert(&s);
+         }
+      }
+
+      void DelListener(ILogSink& s) {
+       AutoMutex l(m_lock);
+       m_listeners.erase(&s);
+      }
+
+      // library messages
       void Error(const char* fmt, ...)    { LOG_MSG_IMPL(LOG_ERROR); }
       void Warn(const char* fmt, ...)     { LOG_MSG_IMPL(LOG_WARN);  }
       void Info(const char* fmt, ...)     { LOG_MSG_IMPL(LOG_INFO);  }
       void Debug(const char* fmt, ...)    { LOG_MSG_IMPL(LOG_DEBUG); }
-      void Logf(ILogSink::LogType_t type, const char* fmt, ...) { LOG_MSG_IMPL(type); }
-      virtual void vLogf(LogType_t, const char*, va_list*) = 0; // log a formatted string with a va_list struct
+
+      // application messages
+      void Critical(const char* fmt, ...) { LOG_MSG_IMPL(LOG_CRIT); }
+      void Alert(const char* fmt, ...)    { LOG_MSG_IMPL(LOG_ALERT); }
+      void Notice(const char* fmt, ...)   { LOG_MSG_IMPL(LOG_NOTICE); }
+
+      // message with type
+      void Logf(LogType_t type, const char* fmt, ...) { LOG_MSG_IMPL(type); }
+
+      // actual log sink input
+      virtual void vLogf(LogType_t, const char*, va_list*);
+      virtual void WriteEntry(const LogEntry&) = 0;
     };
 
+
   // Sets the internal default sink to the given receiver. If the given value
   // is zero, sets the default sink to the internally allocated stderr sink.
   void SetDefaultLogSink(ILogSink* = 0);
@@ -80,39 +216,80 @@ namespace Kumu
   // Returns the internal default sink.
   ILogSink& DefaultLogSink();
 
+
+  // attach a log sink as a listener until deleted
+  class LogSinkListenContext
+    {
+      ILogSink* m_log_source;
+      ILogSink* m_sink;
+      KM_NO_COPY_CONSTRUCT(LogSinkListenContext);
+      LogSinkListenContext();
+
+    public:
+      LogSinkListenContext(ILogSink& source, ILogSink& sink)
+       {
+         m_log_source = &source;
+         m_sink = &sink;
+         m_log_source->AddListener(*m_sink);
+       }
+
+      ~LogSinkListenContext()
+       {
+         m_log_source->DelListener(*m_sink);
+       }
+    };
+
+
+  //------------------------------------------------------------------------------------------
   //
+
+  // collect log messages into the given list, does not test filter
+  class EntryListLogSink : public ILogSink
+  {
+    LogEntryList& m_Target;
+    KM_NO_COPY_CONSTRUCT(EntryListLogSink);
+    EntryListLogSink();
+
+  public:
+    EntryListLogSink(LogEntryList& target) : m_Target(target) {}
+    virtual ~EntryListLogSink() {}
+
+    void WriteEntry(const LogEntry& Entry);
+  };
+
+
+  // write messages to a POSIX stdio stream
   class StdioLogSink : public ILogSink
     {
-      Mutex m_Lock;
       FILE* m_stream;
       KM_NO_COPY_CONSTRUCT(StdioLogSink);
 
     public:
-      StdioLogSink() : m_stream(stderr) {};
-      StdioLogSink(FILE* stream) : m_stream(stream) {}
+    StdioLogSink() : m_stream(stderr) {}
+    StdioLogSink(FILE* stream) : m_stream(stream) {}
       virtual ~StdioLogSink() {}
-      virtual void vLogf(LogType_t, const char*, va_list*);
+
+    void WriteEntry(const LogEntry&);
     };
 
 #ifdef KM_WIN32
-  //
+  // write messages to the Win32 debug stream
   class WinDbgLogSink : public ILogSink
     {
-      Mutex m_Lock;
       KM_NO_COPY_CONSTRUCT(WinDbgLogSink);
 
     public:
       WinDbgLogSink() {}
       virtual ~WinDbgLogSink() {}
-      virtual void vLogf(LogType_t, const char*, va_list*);
-    };
 
-#else
+      void WriteEntry(const LogEntry&);
+    };
+#endif
 
-  //
+#ifndef KM_WIN32
+  // write messages to a POSIX file descriptor
   class StreamLogSink : public ILogSink
     {
-      Mutex m_Lock;
       int   m_fd;
       KM_NO_COPY_CONSTRUCT(StreamLogSink);
       StreamLogSink();
@@ -120,10 +297,28 @@ namespace Kumu
     public:
       StreamLogSink(int fd) : m_fd(fd) {}
       virtual ~StreamLogSink() {}
-      virtual void vLogf(LogType_t, const char*, va_list*);
+
+      void WriteEntry(const LogEntry&);
+    };
+
+  // write messages to the syslog facility
+  class SyslogLogSink : public ILogSink
+    {
+      KM_NO_COPY_CONSTRUCT(SyslogLogSink);
+      SyslogLogSink();
+  
+    public:
+      SyslogLogSink(const std::string& source_name, int facility);
+      virtual ~SyslogLogSink();
+      void WriteEntry(const LogEntry&);
     };
+
+  // convert a string into the appropriate syslog facility id
+  int SyslogNameToFacility(const std::string& facility_name);
+
 #endif
 
+
 } // namespace Kumu
 
 #endif // _KM_LOG_H_