blocxx
LogAppender.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2 * Copyright (C) 2005, Quest Software, Inc. All rights reserved.
3 * Copyright (C) 2006, Novell, Inc. 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 are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * 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 * * Neither the name of
14 * Quest Software, Inc.,
15 * nor Novell, Inc.,
16 * nor the names of its contributors or employees may be used to
17 * endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *******************************************************************************/
32 
33 
39 #include "blocxx/BLOCXX_config.h"
40 #include "blocxx/LogAppender.hpp"
41 #include "blocxx/String.hpp"
42 #include "blocxx/Array.hpp"
43 #include "blocxx/LogMessage.hpp"
44 #include "blocxx/Logger.hpp"
45 #include "blocxx/Assertion.hpp"
46 #include "blocxx/StringBuffer.hpp"
47 #include "blocxx/NullAppender.hpp"
48 #ifndef BLOCXX_WIN32
50 #endif
51 #include "blocxx/CerrAppender.hpp"
52 #include "blocxx/FileAppender.hpp"
54 #include "blocxx/Format.hpp"
58 #include "blocxx/ThreadOnce.hpp"
59 #include "blocxx/NullLogger.hpp"
60 #include "blocxx/GlobalPtr.hpp"
61 
62 
63 namespace BLOCXX_NAMESPACE
64 {
65 
66 #ifdef BLOCXX_WIN32
67 DWORD dwTlsIndex = 0;
68 #endif
69 
70 char const LOG_1_LOCATION_opt[] = "log.%1.location";
71 char const LOG_1_MAX_FILE_SIZE_opt[] = "log.%1.max_file_size";
72 char const LOG_1_MAX_BACKUP_INDEX_opt[] = "log.%1.max_backup_index";
73 char const LOG_1_FLUSH_opt[] = "log.%1.flush";
74 char const LOG_1_SYSLOG_IDENTITY_opt[] = "log.%1.identity";
75 char const LOG_1_SYSLOG_FACILITY_opt[] = "log.%1.facility";
76 
77 
80 {
81 }
82 
84 // we're passing a pointer to this to pthreads, it has to have C linkage.
85 extern "C"
86 {
87 static void freeThreadLogAppender(void *ptr)
88 {
89  delete static_cast<LogAppenderRef *>(ptr);
90 }
91 } // end extern "C"
92 
94 namespace
95 {
96 
97 OnceFlag g_onceGuard = BLOCXX_ONCE_INIT;
98 NonRecursiveMutex* g_mutexGuard = NULL;
99 
100 struct NullAppenderFactory
101 {
102  static LogAppenderRef* create()
103  {
104  return new LogAppenderRef(new NullAppender());
105  }
106 };
108 
109 
111 void initGuardAndKey()
112 {
113  g_mutexGuard = new NonRecursiveMutex();
114 #ifdef BLOCXX_WIN32
115  LPVOID thread_data = NULL;
116  BOOL ret = TlsSetValue(dwTlsIndex, thread_data)
117  BLOCXX_ASSERTMSG(ret, "failed create a thread specific key");
118 #elif BLOCXX_NCR
119  int ret = pthread_keycreate(&g_loggerKey, freeThreadLogAppender);
120  BLOCXX_ASSERTMSG(ret == 0, "failed create a thread specific key");
121 #else
122  int ret = pthread_key_create(&g_loggerKey, freeThreadLogAppender);
123  BLOCXX_ASSERTMSG(ret == 0, "failed create a thread specific key");
124 #endif
125 }
126 
127 
128 } // end unnamed namespace
129 
131 // STATIC
134 {
135  LogAppenderRef threadLogAppender = getThreadLogAppender();
136  if(threadLogAppender)
137  {
138  return threadLogAppender;
139  }
140  else
141  {
142  return getDefaultLogAppender();
143  }
144 }
145 
147 // STATIC
150 {
151  callOnce(g_onceGuard, initGuardAndKey);
152  NonRecursiveMutexLock lock(*g_mutexGuard);
153 
154  // This looks unsafe, but the get() method (called indirectly by operator*),
155  // if it has never been previously called, will allocate a new
156  // LogAppenderRef wich will have a NullAppender inside it.
157  return *g_defaultLogAppender;
158 }
159 
160 
162 // STATIC
163 bool
165 {
166  if (ref)
167  {
168  callOnce(g_onceGuard, initGuardAndKey);
169  NonRecursiveMutexLock lock(*g_mutexGuard);
170 
171  LogAppenderRef(ref).swap(*g_defaultLogAppender);
172  return true;
173  }
174  return false;
175 }
176 
177 
179 // STATIC
182 {
183  callOnce(g_onceGuard, initGuardAndKey);
184  LogAppenderRef *ptr = NULL;
185 
186 #ifdef BLOCXX_WIN32
187  ptr = static_cast<LogAppenderRef *>(TlsGetValue(dwTlsIndex));
188 #elif BLOCXX_NCR
189  pthread_addr_t addr_ptr = NULL;
190  int ret = pthread_getspecific(g_loggerKey, &addr_ptr);
191  if (ret == 0)
192  {
193  ptr = static_cast<LogAppenderRef *>(addr_ptr);
194  }
195 #else
196  ptr = static_cast<LogAppenderRef *>(pthread_getspecific(g_loggerKey));
197 #endif
198 
199  if(ptr)
200  {
201  return *ptr;
202  }
203  else
204  {
205  return LogAppenderRef();
206  }
207 }
208 
210 // STATIC
211 bool
213 {
214  callOnce(g_onceGuard, initGuardAndKey);
215  LogAppenderRef *ptr = 0;
216  if (ref)
217  {
218  ptr = new LogAppenderRef(ref);
219  }
220 #ifdef BLOCXX_WIN32
221  LogAppenderRef *ptr_old = static_cast<LogAppenderRef *>(TlsGetValue(dwTlsIndex));
222  if (ptr_old)
223  {
224  delete ptr_old;
225  }
226 
227  BOOL ret = FALSE;
228  if (!(ret = TlsSetValue(dwTlsIndex, ptr)))
229  {
230  if (ptr)
231  {
232  delete ptr;
233  }
234  }
235  BLOCXX_ASSERTMSG(ret, "failed to set a thread specific logger");
236 #elif BLOCXX_NCR
237  pthread_addr_t addr_ptr = NULL;
238  pthread_getspecific(g_loggerKey, &addr_ptr);
239  freeThreadLogAppender(addr_ptr);
240  int ret = pthread_setspecific(g_loggerKey, ptr);
241  BLOCXX_ASSERTMSG(ret == 0, "failed to set a thread specific logger");
242 #else
243  freeThreadLogAppender(pthread_getspecific(g_loggerKey));
244 
245  int ret = pthread_setspecific(g_loggerKey, ptr);
246  if (ret != 0)
247  {
248  delete ptr;
249  }
250  BLOCXX_ASSERTMSG(ret == 0, "failed to set a thread specific logger");
251 #endif
252 
253  return (ref != 0);
254 }
255 
256 
258 void
259 LogAppender::logMessage(const LogMessage& message) const
260 {
261  if (componentAndCategoryAreEnabled(message.component, message.category))
262  {
263  StringBuffer buf;
264  m_formatter.formatMessage(message, buf);
265  doProcessLogMessage(buf.releaseString(), message);
266  }
267 }
268 
270 bool
272 {
273  return m_allCategories || m_categories.count(category) > 0;
274 }
275 
277 bool
278 LogAppender::componentAndCategoryAreEnabled(const String& component, const String& category) const
279 {
280  return (m_allComponents || m_components.count(component) > 0) &&
281  categoryIsEnabled(category);
282 }
283 
285 namespace
286 {
287  String
288  getConfigItem(const LoggerConfigMap& configItems, const String &itemName, const String& defRetVal = "")
289  {
290  LoggerConfigMap::const_iterator i = configItems.find(itemName);
291  if (i != configItems.end())
292  {
293  return i->second;
294  }
295  else
296  {
297  return defRetVal;
298  }
299  }
300 }
301 
305  const String& name,
306  const StringArray& components,
307  const StringArray& categories,
308  const String& messageFormat,
309  const String& type,
310  const LoggerConfigMap& configItems)
311 {
312  LogAppenderRef appender;
313  if (type.empty() || type.equalsIgnoreCase(TYPE_NULL))
314  {
315  appender = new NullAppender(components, categories, messageFormat);
316  }
317 #ifndef BLOCXX_WIN32
318  else if ( type == TYPE_SYSLOG )
319  {
322 
323  appender = new SyslogAppender(components, categories, messageFormat, identity, facility);
324  }
325 #endif
326  else if (type == TYPE_STDERR || type == "cerr")
327  {
328  appender = new CerrAppender(components, categories, messageFormat);
329  }
330  else if (type == TYPE_FILE || type == TYPE_MPFILE)
331  {
333  String filename = getConfigItem(configItems, configItem);
334 
335  UInt64 maxFileSize(0);
336  try
337  {
338  maxFileSize = getConfigItem(configItems, Format(LogConfigOptions::LOG_1_MAX_FILE_SIZE_opt, name),
340  }
341  catch (StringConversionException& e)
342  {
344  Format("%1: Invalid config value: %2", LogConfigOptions::LOG_1_MAX_FILE_SIZE_opt, e.getMessage()).c_str(),
346  }
347 
348  unsigned int maxBackupIndex(0);
349  try
350  {
351  maxBackupIndex = getConfigItem(configItems, Format(LogConfigOptions::LOG_1_MAX_BACKUP_INDEX_opt, name),
352  BLOCXX_DEFAULT_LOG_1_MAX_BACKUP_INDEX).toUnsignedInt();
353  }
354  catch (StringConversionException& e)
355  {
357  Format("%1: Invalid config value: %2", LogConfigOptions::LOG_1_MAX_BACKUP_INDEX_opt, e.getMessage()).c_str(),
359  }
360 
361  if (type == TYPE_FILE)
362  {
363  bool flushLog =
364  getConfigItem(
365  configItems,
368  ).equalsIgnoreCase("true");
369  appender = new FileAppender(
370  components, categories, filename.c_str(), messageFormat,
371  maxFileSize, maxBackupIndex, flushLog
372  );
373  }
374  else // type == TYPE_MPFILE
375  {
376  appender = new MultiProcessFileAppender(
377  components, categories, filename, messageFormat,
378  maxFileSize, maxBackupIndex
379  );
380  }
381  }
382  else
383  {
384  BLOCXX_THROW_ERR(LoggerException, Format("Unknown log type: %1", type).c_str(), Logger::E_UNKNOWN_LOG_APPENDER_TYPE);
385  }
386 
387  return appender;
388 }
389 
399 
401 LogAppender::LogAppender(const StringArray& components, const StringArray& categories, const String& pattern)
402  : m_components(components.begin(), components.end())
403  , m_categories(categories.begin(), categories.end())
404  , m_formatter(pattern)
405  , m_logLevel(E_NONE_LEVEL)
406 {
409 
410  // set up the log level
411  size_t numCategories = m_categories.size();
412  size_t debug3Count = m_categories.count(Logger::STR_DEBUG3_CATEGORY);
413  size_t debug2Count = m_categories.count(Logger::STR_DEBUG2_CATEGORY);
414  size_t debugCount = m_categories.count(Logger::STR_DEBUG_CATEGORY);
415  size_t infoCount = m_categories.count(Logger::STR_INFO_CATEGORY);
416  size_t warningCount = m_categories.count(Logger::STR_WARNING_CATEGORY);
417  size_t errorCount = m_categories.count(Logger::STR_ERROR_CATEGORY);
418  size_t fatalCount = m_categories.count(Logger::STR_FATAL_CATEGORY);
419  int nonLevelCategoryCount = numCategories - debug3Count - debug2Count - debugCount - infoCount - warningCount - errorCount - fatalCount;
420 
421  if (numCategories == 0)
422  {
424  }
425  else if (m_allCategories || nonLevelCategoryCount > 0)
426  {
428  }
429  else if (debug3Count > 0)
430  {
432  }
433  else if (debug2Count > 0)
434  {
436  }
437  else if (debugCount > 0)
438  {
440  }
441  else if (infoCount > 0)
442  {
444  }
445  else if (warningCount > 0)
446  {
448  }
449  else if (errorCount > 0)
450  {
452  }
453  else if (fatalCount > 0)
454  {
456  }
457  else
458  {
459  BLOCXX_ASSERTMSG(0, "Internal error. LogAppender unable to determine log level!");
460  }
461 }
462 
463 
464 
465 } // end namespace BLOCXX_NAMESPACE
466 
467