Added debug-mode test of ignored return value
[asdcplib.git] / src / KM_log.cpp
index a76df09136437523715e5490174ea26a14d1f003..d820dba50df002aaf82e652c664d765e829dd386 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2006, John Hurst
+Copyright (c) 2004-2011, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -31,9 +31,12 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <KM_util.h>
 #include <KM_log.h>
+#include <KM_mutex.h>
 #include <sys/types.h>
 #include <string.h>
 #include <stdarg.h>
+#include <iostream>
+#include <sstream>
 
 #ifdef KM_WIN32
 #define getpid GetCurrentProcessId
@@ -41,114 +44,342 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <unistd.h>
 #endif
 
+//------------------------------------------------------------------------------------------
+//
 
 void
-Kumu::StdioLogSink::vLogf(ILogSink::LogType_t type, const char* fmt, va_list* list)
+Kumu::ILogSink::vLogf(LogType_t type, const char* fmt, va_list* list)
 {
-  AutoMutex L(m_Lock);
-
-  switch ( type )
-    {
-    case LOG_CRIT:   fprintf(m_stream, "[%d CRT]: ", getpid()); break;
-    case LOG_ALERT:  fprintf(m_stream, "[%d ALR]: ", getpid()); break;
-    case LOG_NOTICE: fprintf(m_stream, "[%d NTC]: ", getpid()); break;
-    case LOG_ERROR:  fprintf(m_stream, "[%d ERR]: ", getpid()); break;
-    case LOG_WARN:   fprintf(m_stream, "[%d WRN]: ", getpid()); break;
-    case LOG_INFO:   fprintf(m_stream, "[%d INF]: ", getpid()); break;
-    case LOG_DEBUG:  fprintf(m_stream, "[%d DBG]: ", getpid()); break;
-    default:         fprintf(m_stream, "[%d DFL]: ", getpid());
-    }
+  char buf[MaxLogLength];
+  vsnprintf(buf, MaxLogLength, fmt, *list);
 
-  vfprintf(m_stream, fmt, *list);
+  WriteEntry(LogEntry(getpid(), type, buf));
 }
 
-static Kumu::ILogSink* s_DefaultLogSink;
+//------------------------------------------------------------------------------------------
+//
+
+static Kumu::Mutex     s_DefaultLogSinkLock;
+static Kumu::ILogSink* s_DefaultLogSink = 0;
 static Kumu::StdioLogSink s_StderrLogSink;
 
 //
 void
 Kumu::SetDefaultLogSink(ILogSink* Sink)
 {
-    s_DefaultLogSink = Sink;
+  AutoMutex L(s_DefaultLogSinkLock);
+  s_DefaultLogSink = Sink;
 }
 
 // Returns the internal default sink.
 Kumu::ILogSink&
 Kumu::DefaultLogSink()
 {
+  AutoMutex L(s_DefaultLogSinkLock);
+
   if ( s_DefaultLogSink == 0 )
     s_DefaultLogSink = &s_StderrLogSink;
 
   return *s_DefaultLogSink;
 }
 
+
+//------------------------------------------------------------------------------------------
+//
+
+void
+Kumu::EntryListLogSink::WriteEntry(const LogEntry& Entry)
+{
+  AutoMutex L(m_lock);
+  WriteEntryToListeners(Entry);
+
+  if ( Entry.TestFilter(m_filter) )
+    m_Target.push_back(Entry);
+}
+
+//------------------------------------------------------------------------------------------
+//
+
+void
+Kumu::StdioLogSink::WriteEntry(const LogEntry& Entry)
+{
+  std::string buf;
+  AutoMutex L(m_lock);
+  WriteEntryToListeners(Entry);
+
+  if ( Entry.TestFilter(m_filter) )
+    {
+      Entry.CreateStringWithOptions(buf, m_options);
+      fputs(buf.c_str(), m_stream);
+      fflush(m_stream);
+    }
+}
+
 //---------------------------------------------------------------------------------
-#ifdef KM_WIN32
 
+#ifdef KM_WIN32
+//
+// http://www.codeguru.com/forum/showthread.php?t=231165
 //
 void
-Kumu::WinDbgLogSink::vLogf(ILogSink::LogType_t type, const char* fmt, va_list* list)
+Kumu::WinDbgLogSink::WriteEntry(const LogEntry& Entry)
 {
-  AutoMutex L(m_Lock);
-  char msg_buf[MaxLogLength];
+  std::string buf;
+  AutoMutex L(m_lock);
+  WriteEntryToListeners(Entry);
 
-  DWORD pid = GetCurrentProcessId();
+  if ( Entry.TestFilter(m_filter) )
+    {
+      Entry.CreateStringWithOptions(buf, m_options);
+      ::OutputDebugStringA(buf.c_str());
+    }
+}
+#endif
+
+//------------------------------------------------------------------------------------------
+//
 
-  switch ( type )
+#ifndef KM_WIN32
+//
+void
+Kumu::StreamLogSink::WriteEntry(const LogEntry& Entry)
+{
+  std::string buf;
+  AutoMutex L(m_lock);
+  WriteEntryToListeners(Entry);
+
+  if ( Entry.TestFilter(m_filter) )
     {
-    case LOG_CRIT:   snprintf(msg_buf, MaxLogLength, "[%d CRT]: ", pid); break;
-    case LOG_ALERT:  snprintf(msg_buf, MaxLogLength, "[%d ALR]: ", pid); break;
-    case LOG_NOTICE: snprintf(msg_buf, MaxLogLength, "[%d NTC]: ", pid); break;
-    case LOG_ERROR:  snprintf(msg_buf, MaxLogLength, "[%d ERR]: ", pid); break;
-    case LOG_WARN:   snprintf(msg_buf, MaxLogLength, "[%d WRN]: ", pid); break;
-    case LOG_INFO:   snprintf(msg_buf, MaxLogLength, "[%d INF]: ", pid); break;
-    case LOG_DEBUG:  snprintf(msg_buf, MaxLogLength, "[%d DBG]: ", pid); break;
-    default:         snprintf(msg_buf, MaxLogLength, "[%d DFL]: ", pid);
+      Entry.CreateStringWithOptions(buf, m_options);
+      ssize_t n = write(m_fd, buf.c_str(), buf.size());
+      assert(n==buf.size());
     }
-  
-  ui32_t len = strlen(msg_buf);
-  vsnprintf(msg_buf + len, MaxLogLength - len, fmt, *list);
-  msg_buf[MaxLogLength-1] = 0;
-  ::OutputDebugString(msg_buf);
 }
 
-#else
+// foolin with symbols
+//------------------------------------------------------------------------------------------
+#include <syslog.h>
+int const SYSLOG_ALERT = LOG_ALERT;
+int const SYSLOG_CRIT = LOG_CRIT;
+int const SYSLOG_ERR = LOG_ERR;
+int const SYSLOG_WARNING = LOG_WARNING;
+int const SYSLOG_NOTICE = LOG_NOTICE;
+int const SYSLOG_INFO = LOG_INFO;
+int const SYSLOG_DEBUG = LOG_DEBUG;
+#undef LOG_ALERT
+#undef LOG_CRIT
+#undef LOG_ERR
+#undef LOG_WARNING
+#undef LOG_NOTICE
+#undef LOG_INFO
+#undef LOG_DEBUG
+//------------------------------------------------------------------------------------------
+
+Kumu::SyslogLogSink::SyslogLogSink(const std::string& source_name, int facility)
+{
+  if ( facility == 0 )
+    facility = LOG_DAEMON;
+
+  openlog(source_name.c_str(), LOG_CONS|LOG_NDELAY||LOG_PID, facility);
+}
 
+Kumu::SyslogLogSink::~SyslogLogSink()
+{
+  closelog();
+}
+
+//
 void
-Kumu::StreamLogSink::vLogf(ILogSink::LogType_t type, const char* fmt, va_list* list)
+Kumu::SyslogLogSink::WriteEntry(const LogEntry& Entry)
 {
-  AutoMutex L(m_Lock);
-  char msg_buf[MaxLogLength];
-  char ts_buf[MaxLogLength];
-  Timestamp Now;
+  int priority;
 
-  switch ( type )
+  switch ( Entry.Type )
     {
-    case LOG_CRIT:   snprintf(msg_buf, MaxLogLength, "[%s %d CRT]: ",
-                             Now.EncodeString(ts_buf, MaxLogLength), getpid()); break;
-    case LOG_ALERT:  snprintf(msg_buf, MaxLogLength, "[%s %d ALR]: ",
-                             Now.EncodeString(ts_buf, MaxLogLength), getpid()); break;
-    case LOG_NOTICE: snprintf(msg_buf, MaxLogLength, "[%s %d NTC]: ",
-                             Now.EncodeString(ts_buf, MaxLogLength), getpid()); break;
-    case LOG_ERROR:  snprintf(msg_buf, MaxLogLength, "[%s %d ERR]: ",
-                             Now.EncodeString(ts_buf, MaxLogLength), getpid()); break;
-    case LOG_WARN:   snprintf(msg_buf, MaxLogLength, "[%s %d WRN]: ",
-                             Now.EncodeString(ts_buf, MaxLogLength), getpid()); break;
-    case LOG_INFO:   snprintf(msg_buf, MaxLogLength, "[%s %d INF]: ",
-                             Now.EncodeString(ts_buf, MaxLogLength), getpid()); break;
-    case LOG_DEBUG:  snprintf(msg_buf, MaxLogLength, "[%s %d DBG]: ",
-                             Now.EncodeString(ts_buf, MaxLogLength), getpid()); break;
-    default:         snprintf(msg_buf, MaxLogLength, "[%s %d DFL]: ",
-                             Now.EncodeString(ts_buf, MaxLogLength), getpid());
+    case Kumu::LOG_ALERT:   priority = SYSLOG_ALERT; break;
+    case Kumu::LOG_CRIT:    priority = SYSLOG_CRIT; break;
+    case Kumu::LOG_ERROR:   priority = SYSLOG_ERR; break;
+    case Kumu::LOG_WARN:    priority = SYSLOG_WARNING; break;
+    case Kumu::LOG_NOTICE:  priority = SYSLOG_NOTICE; break;
+    case Kumu::LOG_INFO:    priority = SYSLOG_INFO; break;
+    case Kumu::LOG_DEBUG:   priority = SYSLOG_DEBUG; break;
+    }
+
+  AutoMutex L(m_lock);
+  WriteEntryToListeners(Entry);
+
+  if ( Entry.TestFilter(m_filter) )
+    {
+      syslog(priority, "%s", Entry.Msg.substr(0, Entry.Msg.size() - 1).c_str());
     }
-  
-  ui32_t len = strlen(msg_buf);
-  vsnprintf(msg_buf + len, MaxLogLength - len, fmt, *list);
-  msg_buf[MaxLogLength-1] = 0;
-  write(m_fd, msg_buf, strlen(msg_buf));
 }
+
+//
+int
+Kumu::SyslogNameToFacility(const std::string& facility_name)
+{
+  if ( facility_name == "LOG_DAEMON" ) return LOG_DAEMON;
+  if ( facility_name == "LOG_LOCAL0" ) return LOG_LOCAL0;
+  if ( facility_name == "LOG_LOCAL1" ) return LOG_LOCAL1;
+  if ( facility_name == "LOG_LOCAL2" ) return LOG_LOCAL2;
+  if ( facility_name == "LOG_LOCAL3" ) return LOG_LOCAL3;
+  if ( facility_name == "LOG_LOCAL4" ) return LOG_LOCAL4;
+  if ( facility_name == "LOG_LOCAL5" ) return LOG_LOCAL5;
+  if ( facility_name == "LOG_LOCAL6" ) return LOG_LOCAL6;
+  if ( facility_name == "LOG_LOCAL7" ) return LOG_LOCAL7;
+
+  DefaultLogSink().Error("Unsupported facility name: %s, using default value LOG_DAEMON\n", facility_name.c_str());
+  return LOG_DAEMON;
+}
+
 #endif
 
+//------------------------------------------------------------------------------------------
+
+//
+std::basic_ostream<char, std::char_traits<char> >&
+Kumu::operator<<(std::basic_ostream<char, std::char_traits<char> >& strm, LogEntry const& Entry)
+{
+  std::basic_ostringstream<char, std::char_traits<char> > s;
+  s.copyfmt(strm);
+  s.width(0);
+  std::string buf;
+
+  s << Entry.CreateStringWithOptions(buf, LOG_OPTION_ALL);
+
+  strm << s.str();
+  return strm;
+}
+
+//------------------------------------------------------------------------------------------
+
+
+//
+bool
+Kumu::LogEntry::TestFilter(i32_t filter) const
+{
+  switch ( Type )
+    {
+    case LOG_CRIT:
+      if ( (filter & LOG_ALLOW_CRIT) == 0 )
+       return false;
+      break;
+
+    case LOG_ALERT:
+      if ( (filter & LOG_ALLOW_ALERT) == 0 )
+       return false;
+      break;
+
+    case LOG_NOTICE:
+      if ( (filter & LOG_ALLOW_NOTICE) == 0 )
+       return false;
+      break;
+
+    case LOG_ERROR:
+      if ( (filter & LOG_ALLOW_ERROR) == 0 )
+       return false;
+      break;
+
+    case LOG_WARN:
+      if ( (filter & LOG_ALLOW_WARN) == 0 )
+       return false;
+      break;
+
+    case LOG_INFO:
+      if ( (filter & LOG_ALLOW_INFO) == 0 )
+       return false;
+      break;
+
+    case LOG_DEBUG:
+      if ( (filter & LOG_ALLOW_DEBUG) == 0 )
+       return false;
+      break;
+
+    }
+
+ return true;
+}
+
+//
+std::string&
+Kumu::LogEntry::CreateStringWithOptions(std::string& out_buf, i32_t opt) const
+{
+  out_buf.erase();
+
+  if ( opt != 0 )
+    {
+      char buf[64];
+
+      if ( (opt & LOG_OPTION_TIMESTAMP) != 0 )
+       {
+         Timestamp Now;
+         out_buf += Now.EncodeString(buf, 64);
+       }
+
+      if ( (opt & LOG_OPTION_PID) != 0 )
+       {
+         if ( ! out_buf.empty() )  out_buf += " ";
+         snprintf(buf, 64, "%d", PID);
+         out_buf += buf;
+       }
+
+      if ( (opt & LOG_OPTION_TYPE) != 0 )
+       {
+         if ( ! out_buf.empty() )  out_buf += " ";
+         
+         switch ( Type )
+           {
+           case LOG_CRIT:   out_buf += "CRT";      break;
+           case LOG_ALERT:  out_buf += "ALR";      break;
+           case LOG_NOTICE: out_buf += "NTC";      break;
+           case LOG_ERROR:  out_buf += "ERR";      break;
+           case LOG_WARN:   out_buf += "WRN";      break;
+           case LOG_INFO:   out_buf += "INF";      break;
+           case LOG_DEBUG:  out_buf += "DBG";      break;
+           default:         out_buf += "DFL";      break;
+           }
+       }
+
+      out_buf.insert(0, "[");
+      out_buf += "]: ";
+    }
+
+  out_buf += Msg;
+  return out_buf;
+}
+
+
+//
+ui32_t
+Kumu::LogEntry::ArchiveLength() const
+{
+  return sizeof(ui32_t)
+    + EventTime.ArchiveLength()
+    + sizeof(ui32_t)
+    + sizeof(ui32_t) + Msg.size();
+}
+
+//
+bool
+Kumu::LogEntry::Archive(Kumu::MemIOWriter* Writer) const
+{
+  if ( ! Writer->WriteUi32BE(PID) ) return false;
+  if ( ! EventTime.Archive(Writer) ) return false;
+  if ( ! Writer->WriteUi32BE(Type) ) return false;
+  if ( ! ArchiveString(*Writer, Msg) ) return false;
+  return true;
+}
+
+//
+bool
+Kumu::LogEntry::Unarchive(Kumu::MemIOReader* Reader)
+{
+  if ( ! Reader->ReadUi32BE(&PID) ) return false;
+  if ( ! EventTime.Unarchive(Reader) ) return false;
+  if ( ! Reader->ReadUi32BE((ui32_t*)&Type) ) return false;
+  if ( ! UnarchiveString(*Reader, Msg) ) return false;
+  return true;
+}
 
 //
 // end