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)
406 {
407  m_allComponents = m_components.count("*") > 0;
408  m_allCategories = m_categories.count("*") > 0;
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 
char const LOG_1_MAX_FILE_SIZE_opt[]
Definition: LogAppender.cpp:71
char const LOG_1_SYSLOG_IDENTITY_opt[]
Definition: LogAppender.cpp:74
Taken from RFC 1321.
static const GlobalStringArray ALL_CATEGORIES
Pass to createLogAppender to indicate all categories.
LogMessagePatternFormatter m_formatter
bool equalsIgnoreCase(const String &arg) const
Determine if another String object is equal to this String object, ignoring case in the comparision...
Definition: String.cpp:529
#define BLOCXX_DEFAULT_LOG_1_SYSLOG_IDENTITY
Definition: LogConfig.hpp:69
bool categoryIsEnabled(const String &category) const
#define BLOCXX_ASSERTMSG(CON, MSG)
BLOCXX_ASSERTMSG works the same as BLOCXX_ASSERT, but with a second string parameter that will be add...
Definition: Assertion.hpp:71
const char *const LOG_1_FLUSH_opt
Definition: LogConfig.cpp:46
const char * name
Definition: SignalUtils.cpp:54
SortedVectorSet< String > m_components
char const LOG_1_MAX_BACKUP_INDEX_opt[]
Definition: LogAppender.cpp:72
char const LOG_1_LOCATION_opt[]
Definition: LogAppender.cpp:70
IntrusiveReference< LogAppender > LogAppenderRef
Definition: CommonFwd.hpp:67
const char *const LOG_1_MAX_FILE_SIZE_opt
Definition: LogConfig.cpp:44
static const GlobalString STR_DEBUG2_CATEGORY
Definition: Logger.hpp:96
bool empty() const
Definition: String.hpp:237
#define BLOCXX_THROW_ERR(exType, msg, err)
Throw an exception using FILE and LINE.
Definition: Exception.hpp:287
#define BLOCXX_GLOBAL_PTR_INIT
This macro is provided to abstract the details of GlobalPtr.
Definition: GlobalPtr.hpp:146
static bool setDefaultLogAppender(const LogAppenderRef &ref)
Set the default global LogAppenderRef.
static bool setThreadLogAppender(const LogAppenderRef &ref)
Set a per thread LogAppenderRef that overrides the default one.
This String class is an abstract data type that represents as NULL terminated string of characters...
Definition: String.hpp:66
static const GlobalString STR_INFO_CATEGORY
Definition: Logger.hpp:94
void logMessage(const LogMessage &message) const
Log a message using the specified component and category.
void formatMessage(const LogMessage &message, StringBuffer &output) const
static const GlobalString STR_DEBUG3_CATEGORY
Definition: Logger.hpp:97
static const GlobalString TYPE_MPFILE
String of the type of the multi-process file log appender.
const char * c_str() const
Definition: String.cpp:905
static const GlobalString STR_FATAL_CATEGORY
Definition: Logger.hpp:91
static LogAppenderRef getThreadLogAppender()
Returns a copy of the thread LogAppenderRef.
#define BLOCXX_THROW_ERR_SUBEX(exType, msg, err, subex)
Throw an exception using FILE and LINE.
Definition: Exception.hpp:334
static const GlobalString TYPE_STDERR
String of the type of the stderr log appender.
const char *const LOG_1_MAX_BACKUP_INDEX_opt
Definition: LogConfig.cpp:45
static const GlobalString STR_TTCC_MESSAGE_FORMAT
The Log4j TTCC message format - TTCC is acronym for Time Thread Category Component.
This class sends log messges to a file, for use when there may be multiple processes logging to the s...
virtual const char * getMessage() const
Returns the message.
Definition: Exception.cpp:203
static LogAppenderRef getDefaultLogAppender()
Returns a copy of default LogAppenderRef.
static const GlobalString TYPE_NULL
String of the type of the null log appender.
SortedVectorSet< String > m_categories
#define BLOCXX_DEFAULT_LOG_1_MAX_FILE_SIZE
Definition: LogConfig.hpp:57
#define BLOCXX_DEFAULT_LOG_1_FLUSH
Definition: LogConfig.hpp:65
#define BLOCXX_GLOBAL_STRING_INIT(str)
static const GlobalStringArray ALL_COMPONENTS
Pass to createLogAppender to indicate all components.
#define BLOCXX_DEFAULT_LOG_1_SYSLOG_FACILITY
Definition: LogConfig.hpp:73
container_t::const_iterator const_iterator
This class sends log messges to a file.
static const GlobalString STR_WARNING_CATEGORY
Definition: Logger.hpp:93
#define BLOCXX_DEFAULT_LOG_1_MAX_BACKUP_INDEX
Definition: LogConfig.hpp:61
static void freeThreadLogAppender(void *ptr)
Definition: LogAppender.cpp:87
virtual void doProcessLogMessage(const String &formattedMessage, const LogMessage &message) const =0
const char *const LOG_1_LOCATION_opt
Definition: LogConfig.cpp:43
void BLOCXX_COMMON_API callOnce(OnceFlag &flag, FuncT F)
The first time callOnce is called with a given onceFlag argument, it calls func with no argument and ...
static const GlobalString STR_DEBUG_CATEGORY
Definition: Logger.hpp:95
static LogAppenderRef getCurrentLogAppender()
Get a copy of the per thread LogAppenderRef or if not set, the default one.
bool componentAndCategoryAreEnabled(const String &component, const String &category) const
This class can be used to store a global variable that is lazily initialized in a thread safe manner...
Definition: LazyGlobal.hpp:114
const_iterator find(const key_type &x) const
static const GlobalString STR_ERROR_CATEGORY
Definition: Logger.hpp:92
static const GlobalString TYPE_SYSLOG
String of the type of the syslog log appender.
char const LOG_1_SYSLOG_FACILITY_opt[]
Definition: LogAppender.cpp:75
const char *const LOG_1_SYSLOG_IDENTITY_opt
Definition: LogConfig.cpp:47
Note that descriptions of what exceptions may be thrown assumes that object is used correctly...
Note that descriptions of what exceptions may be thrown assumes that object is used correctly...
void swap(IntrusiveReference &rhs)
char const LOG_1_FLUSH_opt[]
Definition: LogAppender.cpp:73
This class sends log messges to syslog.
The CerrAppender is a LogAppender derivative that sends log message to stderr.
static LogAppenderRef createLogAppender(const String &name, const StringArray &components, const StringArray &categories, const String &messageFormat, const String &type, const LoggerConfigMap &configItems)
Create a concrete log appender depending on the type string passed in.
static const GlobalString TYPE_FILE
String of the type of the file log appender.
This class sends log messges to the bit bucket.
LogAppender(const StringArray &components=ALL_COMPONENTS, const StringArray &categories=ALL_CATEGORIES, const String &pattern=STR_TTCC_MESSAGE_FORMAT)
This class can be used to store a global pointer.
Definition: GlobalPtr.hpp:83
const char *const LOG_1_SYSLOG_FACILITY_opt
Definition: LogConfig.cpp:48