You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
562 lines
12 KiB
562 lines
12 KiB
// Module: Log4CPLUS
|
|
// File: global-init.cxx
|
|
// Created: 5/2003
|
|
// Author: Tad E. Smith
|
|
//
|
|
//
|
|
// Copyright 2003-2013 Tad E. Smith
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <log4cplus/config.hxx>
|
|
#include <log4cplus/config/windowsh-inc.h>
|
|
#include <log4cplus/logger.h>
|
|
#include <log4cplus/ndc.h>
|
|
#include <log4cplus/mdc.h>
|
|
#include <log4cplus/helpers/loglog.h>
|
|
#include <log4cplus/internal/internal.h>
|
|
#include <log4cplus/thread/impl/tls.h>
|
|
#include <log4cplus/thread/syncprims-pub-impl.h>
|
|
#include <log4cplus/helpers/loglog.h>
|
|
#include <log4cplus/spi/factory.h>
|
|
#include <log4cplus/hierarchy.h>
|
|
#include <cstdio>
|
|
#include <iostream>
|
|
#include <stdexcept>
|
|
|
|
|
|
// Forward Declarations
|
|
namespace log4cplus
|
|
{
|
|
|
|
#ifdef UNICODE
|
|
LOG4CPLUS_EXPORT tostream & tcout = std::wcout;
|
|
LOG4CPLUS_EXPORT tostream & tcerr = std::wcerr;
|
|
|
|
#else
|
|
LOG4CPLUS_EXPORT tostream & tcout = std::cout;
|
|
LOG4CPLUS_EXPORT tostream & tcerr = std::cerr;
|
|
|
|
#endif // UNICODE
|
|
|
|
|
|
namespace
|
|
{
|
|
|
|
|
|
//! Default context.
|
|
struct DefaultContext
|
|
{
|
|
log4cplus::thread::Mutex console_mutex;
|
|
helpers::LogLog loglog;
|
|
LogLevelManager log_level_manager;
|
|
helpers::Time TTCCLayout_time_base;
|
|
NDC ndc;
|
|
MDC mdc;
|
|
Hierarchy hierarchy;
|
|
spi::AppenderFactoryRegistry appender_factory_registry;
|
|
spi::LayoutFactoryRegistry layout_factory_registry;
|
|
spi::FilterFactoryRegistry filter_factory_registry;
|
|
spi::LocaleFactoryRegistry locale_factory_registry;
|
|
};
|
|
|
|
|
|
enum DCState
|
|
{
|
|
DC_UNINITIALIZED,
|
|
DC_INITIALIZED,
|
|
DC_DESTROYED
|
|
};
|
|
|
|
|
|
static DCState default_context_state;
|
|
static DefaultContext * default_context;
|
|
|
|
|
|
struct destroy_default_context
|
|
{
|
|
~destroy_default_context ()
|
|
{
|
|
delete default_context;
|
|
default_context = 0;
|
|
default_context_state = DC_DESTROYED;
|
|
}
|
|
} static destroy_default_context_;
|
|
|
|
|
|
static
|
|
void
|
|
alloc_dc ()
|
|
{
|
|
assert (! default_context);
|
|
assert (default_context_state == DC_UNINITIALIZED);
|
|
|
|
if (default_context)
|
|
throw std::logic_error (
|
|
"alloc_dc() called with non-NULL default_context.");
|
|
|
|
if (default_context_state == DC_INITIALIZED)
|
|
throw std::logic_error ("alloc_dc() called in DC_INITIALIZED state.");
|
|
|
|
default_context = new DefaultContext;
|
|
|
|
if (default_context_state == DC_DESTROYED)
|
|
default_context->loglog.error (
|
|
LOG4CPLUS_TEXT ("Re-initializing default context after it has")
|
|
LOG4CPLUS_TEXT (" already been destroyed.\n")
|
|
LOG4CPLUS_TEXT ("The memory will be leaked."));
|
|
|
|
default_context_state = DC_INITIALIZED;
|
|
}
|
|
|
|
|
|
static
|
|
DefaultContext *
|
|
get_dc (bool alloc = true)
|
|
{
|
|
if (LOG4CPLUS_UNLIKELY (! default_context && alloc))
|
|
alloc_dc ();
|
|
return default_context;
|
|
}
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
namespace helpers
|
|
{
|
|
|
|
|
|
log4cplus::thread::Mutex const &
|
|
getConsoleOutputMutex ()
|
|
{
|
|
return get_dc ()->console_mutex;
|
|
}
|
|
|
|
|
|
LogLog &
|
|
getLogLog ()
|
|
{
|
|
return get_dc ()->loglog;
|
|
}
|
|
|
|
|
|
} // namespace helpers
|
|
|
|
|
|
helpers::Time const &
|
|
getTTCCLayoutTimeBase ()
|
|
{
|
|
return get_dc ()->TTCCLayout_time_base;
|
|
}
|
|
|
|
|
|
LogLevelManager &
|
|
getLogLevelManager ()
|
|
{
|
|
return get_dc ()->log_level_manager;
|
|
}
|
|
|
|
|
|
Hierarchy &
|
|
getDefaultHierarchy ()
|
|
{
|
|
return get_dc ()->hierarchy;
|
|
}
|
|
|
|
|
|
NDC &
|
|
getNDC ()
|
|
{
|
|
return get_dc ()->ndc;
|
|
}
|
|
|
|
|
|
MDC &
|
|
getMDC ()
|
|
{
|
|
return get_dc ()->mdc;
|
|
}
|
|
|
|
|
|
namespace spi
|
|
{
|
|
|
|
|
|
AppenderFactoryRegistry &
|
|
getAppenderFactoryRegistry ()
|
|
{
|
|
return get_dc ()->appender_factory_registry;
|
|
}
|
|
|
|
|
|
LayoutFactoryRegistry &
|
|
getLayoutFactoryRegistry ()
|
|
{
|
|
return get_dc ()->layout_factory_registry;
|
|
}
|
|
|
|
|
|
FilterFactoryRegistry &
|
|
getFilterFactoryRegistry ()
|
|
{
|
|
return get_dc ()->filter_factory_registry;
|
|
}
|
|
|
|
|
|
LocaleFactoryRegistry &
|
|
getLocaleFactoryRegistry()
|
|
{
|
|
return get_dc ()->locale_factory_registry;
|
|
}
|
|
|
|
|
|
} // namespace spi
|
|
|
|
|
|
namespace internal
|
|
{
|
|
|
|
|
|
gft_scratch_pad::gft_scratch_pad ()
|
|
: uc_q_str_valid (false)
|
|
, q_str_valid (false)
|
|
, s_str_valid (false)
|
|
{ }
|
|
|
|
|
|
gft_scratch_pad::~gft_scratch_pad ()
|
|
{ }
|
|
|
|
|
|
appender_sratch_pad::appender_sratch_pad ()
|
|
{ }
|
|
|
|
|
|
appender_sratch_pad::~appender_sratch_pad ()
|
|
{ }
|
|
|
|
|
|
per_thread_data::per_thread_data ()
|
|
: fnull (0)
|
|
{ }
|
|
|
|
|
|
per_thread_data::~per_thread_data ()
|
|
{
|
|
if (fnull)
|
|
std::fclose (fnull);
|
|
}
|
|
|
|
|
|
log4cplus::thread::impl::tls_key_type tls_storage_key;
|
|
|
|
|
|
#if ! defined (LOG4CPLUS_SINGLE_THREADED) \
|
|
&& defined (LOG4CPLUS_THREAD_LOCAL_VAR)
|
|
|
|
LOG4CPLUS_THREAD_LOCAL_VAR per_thread_data * ptd = 0;
|
|
|
|
|
|
per_thread_data *
|
|
alloc_ptd ()
|
|
{
|
|
per_thread_data * tmp = new per_thread_data;
|
|
set_ptd (tmp);
|
|
// This is a special hack. We set the keys' value to non-NULL to
|
|
// get the ptd_cleanup_func to execute when this thread ends. The
|
|
// cast is safe; the associated value will never be used if read
|
|
// again using the key.
|
|
thread::impl::tls_set_value (tls_storage_key,
|
|
reinterpret_cast<void *>(1));
|
|
|
|
return tmp;
|
|
}
|
|
|
|
# else
|
|
|
|
per_thread_data *
|
|
alloc_ptd ()
|
|
{
|
|
per_thread_data * tmp = new per_thread_data;
|
|
set_ptd (tmp);
|
|
return tmp;
|
|
}
|
|
|
|
# endif
|
|
|
|
|
|
} // namespace internal
|
|
|
|
|
|
void initializeFactoryRegistry();
|
|
|
|
|
|
//! Thread local storage clean up function for POSIX threads.
|
|
static
|
|
void
|
|
ptd_cleanup_func (void * arg)
|
|
{
|
|
internal::per_thread_data * const arg_ptd
|
|
= static_cast<internal::per_thread_data *>(arg);
|
|
internal::per_thread_data * const ptd = internal::get_ptd (false);
|
|
(void) ptd;
|
|
|
|
// Either it is a dummy value or it should be the per thread data
|
|
// pointer we get from internal::get_ptd().
|
|
assert (arg == reinterpret_cast<void *>(1)
|
|
|| arg_ptd == ptd
|
|
|| (! ptd && arg_ptd));
|
|
|
|
if (arg == reinterpret_cast<void *>(1))
|
|
// Setting the value through the key here is necessary in case
|
|
// we are using TLS using __thread or __declspec(thread) or
|
|
// similar constructs with POSIX threads. Otherwise POSIX
|
|
// calls this cleanup routine more than once if the value
|
|
// stays non-NULL after it returns.
|
|
thread::impl::tls_set_value (internal::tls_storage_key, 0);
|
|
else if (arg)
|
|
{
|
|
// Instead of using internal::get_ptd(false) here we are using
|
|
// the value passed to this function directly. This is
|
|
// necessary because of the following (from SUSv4):
|
|
//
|
|
// A call to pthread_getspecific() for the thread-specific
|
|
// data key being destroyed shall return the value NULL,
|
|
// unless the value is changed (after the destructor starts)
|
|
// by a call to pthread_setspecific().
|
|
delete arg_ptd;
|
|
thread::impl::tls_set_value (internal::tls_storage_key, 0);
|
|
}
|
|
else
|
|
{
|
|
// In this case we fall through to threadCleanup() and it does
|
|
// all the necessary work itself.
|
|
;
|
|
}
|
|
|
|
threadCleanup ();
|
|
}
|
|
|
|
|
|
static
|
|
void
|
|
threadSetup ()
|
|
{
|
|
internal::get_ptd (true);
|
|
}
|
|
|
|
|
|
void
|
|
initializeLog4cplus()
|
|
{
|
|
static bool initialized = false;
|
|
if (initialized)
|
|
return;
|
|
|
|
internal::tls_storage_key = thread::impl::tls_init (ptd_cleanup_func);
|
|
threadSetup ();
|
|
|
|
DefaultContext * dc = get_dc (true);
|
|
dc->TTCCLayout_time_base = helpers::Time::gettimeofday ();
|
|
Logger::getRoot();
|
|
initializeFactoryRegistry();
|
|
|
|
initialized = true;
|
|
}
|
|
|
|
|
|
void
|
|
initialize ()
|
|
{
|
|
initializeLog4cplus ();
|
|
}
|
|
|
|
|
|
void
|
|
threadCleanup ()
|
|
{
|
|
// Do thread-specific cleanup.
|
|
internal::per_thread_data * ptd = internal::get_ptd (false);
|
|
delete ptd;
|
|
internal::set_ptd (0);
|
|
}
|
|
|
|
|
|
#if defined (_WIN32)
|
|
static
|
|
VOID CALLBACK
|
|
initializeLog4cplusApcProc (ULONG_PTR /*dwParam*/)
|
|
{
|
|
initializeLog4cplus ();
|
|
threadSetup ();
|
|
}
|
|
|
|
|
|
static
|
|
void
|
|
queueLog4cplusInitializationThroughAPC ()
|
|
{
|
|
if (! QueueUserAPC (initializeLog4cplusApcProc, GetCurrentThread (),
|
|
0))
|
|
throw std::runtime_error ("QueueUserAPC() has failed");
|
|
}
|
|
|
|
|
|
static
|
|
void NTAPI
|
|
thread_callback (LPVOID /*hinstDLL*/, DWORD fdwReason, LPVOID /*lpReserved*/)
|
|
{
|
|
// Perform actions based on the reason for calling.
|
|
switch (fdwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
{
|
|
// We cannot initialize log4cplus directly here. This is because
|
|
// DllMain() is called under loader lock. When we are using C++11
|
|
// threads and synchronization primitives then there is a deadlock
|
|
// somewhere in internals of std::mutex::lock().
|
|
queueLog4cplusInitializationThroughAPC ();
|
|
break;
|
|
}
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
{
|
|
// We could call threadSetup() here but that imposes overhead
|
|
// on threads that do not use log4cplus. Thread local data will
|
|
// be initialized lazily instead.
|
|
break;
|
|
}
|
|
|
|
case DLL_THREAD_DETACH:
|
|
{
|
|
// Do thread-specific cleanup.
|
|
log4cplus::threadCleanup ();
|
|
|
|
break;
|
|
}
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
{
|
|
// Perform any necessary cleanup.
|
|
|
|
// Do thread-specific cleanup.
|
|
log4cplus::threadCleanup ();
|
|
#if ! defined (LOG4CPLUS_THREAD_LOCAL_VAR)
|
|
log4cplus::thread::impl::tls_cleanup (
|
|
log4cplus::internal::tls_storage_key);
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
} // switch
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
} // namespace log4cplus
|
|
|
|
|
|
#if defined (_WIN32) && defined (LOG4CPLUS_BUILD_DLL) && defined (_DLL)
|
|
extern "C"
|
|
BOOL
|
|
WINAPI
|
|
DllMain (LOG4CPLUS_DLLMAIN_HINSTANCE hinstDLL, DWORD fdwReason,
|
|
LPVOID lpReserved)
|
|
{
|
|
log4cplus::thread_callback (hinstDLL, fdwReason, lpReserved);
|
|
|
|
return TRUE; // Successful DLL_PROCESS_ATTACH.
|
|
}
|
|
|
|
#elif defined (_WIN32) \
|
|
&& defined (_MSC_VER) && _MSC_VER >= 1400 && defined (_DLL)
|
|
extern "C"
|
|
{
|
|
|
|
// This magic has been pieced together from several sources:
|
|
// - <http://www.nynaeve.net/?p=183>
|
|
// - <http://lists.cs.uiuc.edu/pipermail/cfe-dev/2011-November/018818.html>
|
|
|
|
#pragma data_seg (push, old_seg)
|
|
#ifdef _WIN64
|
|
#pragma const_seg (".CRT$XLX")
|
|
extern const
|
|
#else
|
|
#pragma data_seg (".CRT$XLX")
|
|
#endif
|
|
PIMAGE_TLS_CALLBACK log4cplus_p_thread_callback = log4cplus::thread_callback;
|
|
#pragma data_seg (pop, old_seg)
|
|
#ifdef _WIN64
|
|
#pragma comment (linker, "/INCLUDE:_tls_used")
|
|
#pragma comment (linker, "/INCLUDE:log4cplus_p_thread_callback")
|
|
#else
|
|
#pragma comment (linker, "/INCLUDE:__tls_used")
|
|
#pragma comment (linker, "/INCLUDE:_log4cplus_p_thread_callback")
|
|
#endif
|
|
|
|
} // extern "C"
|
|
|
|
#elif defined (_WIN32)
|
|
namespace {
|
|
|
|
struct _static_log4cplus_initializer
|
|
{
|
|
_static_log4cplus_initializer ()
|
|
{
|
|
// It is not possible to reliably call initializeLog4cplus() here
|
|
// when we are using Visual Studio and C++11 threads
|
|
// and synchronization primitives. It would result into a deadlock
|
|
// on loader lock.
|
|
#if ! (defined (LOG4CPLUS_WITH_CXX11_THREADS) && defined (_MSC_VER))
|
|
log4cplus::initializeLog4cplus ();
|
|
#endif
|
|
}
|
|
|
|
~_static_log4cplus_initializer ()
|
|
{
|
|
// Last thread cleanup.
|
|
log4cplus::threadCleanup ();
|
|
#if ! defined (LOG4CPLUS_THREAD_LOCAL_VAR)
|
|
log4cplus::thread::impl::tls_cleanup (
|
|
log4cplus::internal::tls_storage_key);
|
|
#endif
|
|
}
|
|
} static initializer;
|
|
|
|
} // namespace
|
|
|
|
#else
|
|
namespace {
|
|
|
|
struct _static_log4cplus_initializer
|
|
{
|
|
_static_log4cplus_initializer ()
|
|
{
|
|
log4cplus::initializeLog4cplus();
|
|
}
|
|
|
|
~_static_log4cplus_initializer ()
|
|
{
|
|
// Last thread cleanup.
|
|
log4cplus::threadCleanup ();
|
|
|
|
log4cplus::thread::impl::tls_cleanup (
|
|
log4cplus::internal::tls_storage_key);
|
|
}
|
|
} static initializer;
|
|
|
|
} // namespace
|
|
|
|
#endif
|