Allow fractional frames per second when computing Time from frames.
[libdcp.git] / asdcplib / 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: KM_log.cpp,v 1.15 2011/03/08 19:03:47 jhurst Exp $
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
95   if ( Entry.TestFilter(m_filter) )
96     m_Target.push_back(Entry);
97 }
98
99 //------------------------------------------------------------------------------------------
100 //
101
102 void
103 Kumu::StdioLogSink::WriteEntry(const LogEntry& Entry)
104 {
105   AutoMutex L(m_Lock);
106   std::string buf;
107
108   if ( Entry.TestFilter(m_filter) )
109     {
110       Entry.CreateStringWithOptions(buf, m_options);
111       fputs(buf.c_str(), m_stream);
112     }
113 }
114
115 //---------------------------------------------------------------------------------
116
117 #ifdef KM_WIN32
118 //
119 // http://www.codeguru.com/forum/showthread.php?t=231165
120 //
121 void
122 Kumu::WinDbgLogSink::WriteEntry(const LogEntry& Entry)
123 {
124   AutoMutex L(m_Lock);
125   std::string buf;
126
127   if ( Entry.TestFilter(m_filter) )
128     {
129       Entry.CreateStringWithOptions(buf, m_options);
130       ::OutputDebugStringA(buf.c_str());
131     }
132 }
133 #endif
134
135 //------------------------------------------------------------------------------------------
136 //
137
138 #ifndef KM_WIN32
139 //
140 void
141 Kumu::StreamLogSink::WriteEntry(const LogEntry& Entry)
142 {
143   AutoMutex L(m_Lock);
144   std::string buf;
145
146   if ( Entry.TestFilter(m_filter) )
147     {
148       Entry.CreateStringWithOptions(buf, m_options);
149       write(m_fd, buf.c_str(), buf.size());
150     }
151 }
152
153 // foolin with symbols
154 //------------------------------------------------------------------------------------------
155 #include <syslog.h>
156 int const SYSLOG_ALERT = LOG_ALERT;
157 int const SYSLOG_CRIT = LOG_CRIT;
158 int const SYSLOG_ERR = LOG_ERR;
159 int const SYSLOG_WARNING = LOG_WARNING;
160 int const SYSLOG_NOTICE = LOG_NOTICE;
161 int const SYSLOG_INFO = LOG_INFO;
162 int const SYSLOG_DEBUG = LOG_DEBUG;
163 #undef LOG_ALERT
164 #undef LOG_CRIT
165 #undef LOG_ERR
166 #undef LOG_WARNING
167 #undef LOG_NOTICE
168 #undef LOG_INFO
169 #undef LOG_DEBUG
170 //------------------------------------------------------------------------------------------
171
172 Kumu::SyslogLogSink::SyslogLogSink(const std::string& source_name, int facility)
173 {
174   if ( facility == 0 )
175     facility = LOG_DAEMON;
176
177   openlog(source_name.c_str(), LOG_CONS|LOG_NDELAY||LOG_PID, facility);
178 }
179
180 Kumu::SyslogLogSink::~SyslogLogSink()
181 {
182   closelog();
183 }
184
185 //
186 void
187 Kumu::SyslogLogSink::WriteEntry(const LogEntry& Entry)
188 {
189   int priority = 0;
190
191   switch ( Entry.Type )
192     {
193     case Kumu::LOG_ALERT:   priority = SYSLOG_ALERT; break;
194     case Kumu::LOG_CRIT:    priority = SYSLOG_CRIT; break;
195     case Kumu::LOG_ERROR:   priority = SYSLOG_ERR; break;
196     case Kumu::LOG_WARN:    priority = SYSLOG_WARNING; break;
197     case Kumu::LOG_NOTICE:  priority = SYSLOG_NOTICE; break;
198     case Kumu::LOG_INFO:    priority = SYSLOG_INFO; break;
199     case Kumu::LOG_DEBUG:   priority = SYSLOG_DEBUG; break;
200     }
201
202   AutoMutex L(m_Lock);
203
204   if ( Entry.TestFilter(m_filter) )
205     {
206       syslog(priority, "%s", Entry.Msg.substr(0, Entry.Msg.size() - 1).c_str());
207     }
208 }
209
210 //
211 int
212 Kumu::SyslogNameToFacility(const std::string& facility_name)
213 {
214   if ( facility_name == "LOG_DAEMON" ) return LOG_DAEMON;
215   if ( facility_name == "LOG_LOCAL0" ) return LOG_LOCAL0;
216   if ( facility_name == "LOG_LOCAL1" ) return LOG_LOCAL1;
217   if ( facility_name == "LOG_LOCAL2" ) return LOG_LOCAL2;
218   if ( facility_name == "LOG_LOCAL3" ) return LOG_LOCAL3;
219   if ( facility_name == "LOG_LOCAL4" ) return LOG_LOCAL4;
220   if ( facility_name == "LOG_LOCAL5" ) return LOG_LOCAL5;
221   if ( facility_name == "LOG_LOCAL6" ) return LOG_LOCAL6;
222   if ( facility_name == "LOG_LOCAL7" ) return LOG_LOCAL7;
223
224   DefaultLogSink().Error("Unsupported facility name: %s, using default value LOG_DAEMON\n", facility_name.c_str());
225   return LOG_DAEMON;
226 }
227
228 #endif
229
230 //------------------------------------------------------------------------------------------
231
232 //
233 std::basic_ostream<char, std::char_traits<char> >&
234 Kumu::operator<<(std::basic_ostream<char, std::char_traits<char> >& strm, LogEntry const& Entry)
235 {
236   std::basic_ostringstream<char, std::char_traits<char> > s;
237   s.copyfmt(strm);
238   s.width(0);
239   std::string buf;
240
241   s << Entry.CreateStringWithOptions(buf, LOG_OPTION_ALL);
242
243   strm << s.str();
244   return strm;
245 }
246
247 //------------------------------------------------------------------------------------------
248
249
250 //
251 bool
252 Kumu::LogEntry::TestFilter(i32_t filter) const
253 {
254   switch ( Type )
255     {
256     case LOG_CRIT:
257       if ( (filter & LOG_ALLOW_CRIT) == 0 )
258         return false;
259       break;
260
261     case LOG_ALERT:
262       if ( (filter & LOG_ALLOW_ALERT) == 0 )
263         return false;
264       break;
265
266     case LOG_NOTICE:
267       if ( (filter & LOG_ALLOW_NOTICE) == 0 )
268         return false;
269       break;
270
271     case LOG_ERROR:
272       if ( (filter & LOG_ALLOW_ERROR) == 0 )
273         return false;
274       break;
275
276     case LOG_WARN:
277       if ( (filter & LOG_ALLOW_WARN) == 0 )
278         return false;
279       break;
280
281     case LOG_INFO:
282       if ( (filter & LOG_ALLOW_INFO) == 0 )
283         return false;
284       break;
285
286     case LOG_DEBUG:
287       if ( (filter & LOG_ALLOW_DEBUG) == 0 )
288         return false;
289       break;
290
291     }
292
293  return true;
294 }
295
296 //
297 std::string&
298 Kumu::LogEntry::CreateStringWithOptions(std::string& out_buf, i32_t opt) const
299 {
300   out_buf.erase();
301
302   if ( opt != 0 )
303     {
304       char buf[64];
305
306       if ( (opt & LOG_OPTION_TIMESTAMP) != 0 )
307         {
308           Timestamp Now;
309           out_buf += Now.EncodeString(buf, 64);
310         }
311
312       if ( (opt & LOG_OPTION_PID) != 0 )
313         {
314           if ( ! out_buf.empty() )  out_buf += " ";
315           snprintf(buf, 64, "%d", PID);
316           out_buf += buf;
317         }
318
319       if ( (opt & LOG_OPTION_TYPE) != 0 )
320         {
321           if ( ! out_buf.empty() )  out_buf += " ";
322           
323           switch ( Type )
324             {
325             case LOG_CRIT:   out_buf += "CRT";      break;
326             case LOG_ALERT:  out_buf += "ALR";      break;
327             case LOG_NOTICE: out_buf += "NTC";      break;
328             case LOG_ERROR:  out_buf += "ERR";      break;
329             case LOG_WARN:   out_buf += "WRN";      break;
330             case LOG_INFO:   out_buf += "INF";      break;
331             case LOG_DEBUG:  out_buf += "DBG";      break;
332             default:         out_buf += "DFL";      break;
333             }
334         }
335
336       out_buf.insert(0, "[");
337       out_buf += "]: ";
338     }
339
340   out_buf += Msg;
341   return out_buf;
342 }
343
344
345 //
346 ui32_t
347 Kumu::LogEntry::ArchiveLength() const
348 {
349   return sizeof(ui32_t)
350     + EventTime.ArchiveLength()
351     + sizeof(ui32_t)
352     + sizeof(ui32_t) + Msg.size();
353 }
354
355 //
356 bool
357 Kumu::LogEntry::Archive(Kumu::MemIOWriter* Writer) const
358 {
359   if ( ! Writer->WriteUi32BE(PID) ) return false;
360   if ( ! EventTime.Archive(Writer) ) return false;
361   if ( ! Writer->WriteUi32BE(Type) ) return false;
362   if ( ! ArchiveString(*Writer, Msg) ) return false;
363   return true;
364 }
365
366 //
367 bool
368 Kumu::LogEntry::Unarchive(Kumu::MemIOReader* Reader)
369 {
370   if ( ! Reader->ReadUi32BE(&PID) ) return false;
371   if ( ! EventTime.Unarchive(Reader) ) return false;
372   if ( ! Reader->ReadUi32BE((ui32_t*)&Type) ) return false;
373   if ( ! UnarchiveString(*Reader, Msg) ) return false;
374   return true;
375 }
376
377 //
378 // end
379 //