blob: ffaf15665acceef24627821837bf25587862fb34 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 <log4cxx/log4cxx.h>
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/threadspecificdata.h>
#include <log4cxx/helpers/exception.h>
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/helpers/transcoder.h>
#include <apr_thread_proc.h>
#include <apr_strings.h>
#if !defined(LOG4CXX)
#define LOG4CXX 1
#endif
#include <log4cxx/private/log4cxx_private.h>
#include <log4cxx/helpers/aprinitializer.h>
#include <sstream>
#include <algorithm>
#include <thread>
#include <mutex>
#include <list>
using namespace LOG4CXX_NS;
using namespace LOG4CXX_NS::helpers;
struct ThreadSpecificData::ThreadSpecificDataPrivate{
ThreadSpecificDataPrivate()
: pNamePair(std::make_shared<NamePair>())
{
setThreadIdName();
setThreadUserName();
}
NDC::Stack ndcStack;
MDC::Map mdcMap;
std::shared_ptr<NamePair> pNamePair;
#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
std::basic_ostringstream<logchar> logchar_stringstream;
#endif
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
std::basic_ostringstream<wchar_t> wchar_stringstream;
#endif
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
std::basic_ostringstream<UniChar> unichar_stringstream;
#endif
void setThreadIdName();
void setThreadUserName();
};
/* Generate an identifier for the current thread
*/
void ThreadSpecificData::ThreadSpecificDataPrivate::setThreadIdName()
{
#if LOG4CXX_HAS_PTHREAD_SELF && !(defined(_WIN32) && defined(_LIBCPP_VERSION))
// pthread_t encoded in HEX takes needs as many characters
// as two times the size of the type, plus an additional null byte.
auto threadId = pthread_self();
char result[sizeof(pthread_t) * 3 + 10];
apr_snprintf(result, sizeof(result), LOG4CXX_APR_THREAD_FMTSPEC, (void*) &threadId);
this->pNamePair->idString = Transcoder::decode(result);
#elif defined(_WIN32)
char result[20];
apr_snprintf(result, sizeof(result), LOG4CXX_WIN32_THREAD_FMTSPEC, GetCurrentThreadId());
this->pNamePair->idString = Transcoder::decode(result);
#else
std::stringstream ss;
ss << std::hex << "0x" << std::this_thread::get_id();
this->pNamePair->idString = Transcoder::decode(ss.str().c_str());
#endif
}
/*
* Get the user-specified name of the current thread (on a per-platform basis).
* This is set using a method such as pthread_setname_np on POSIX
* systems or SetThreadDescription on Windows.
*/
void ThreadSpecificData::ThreadSpecificDataPrivate::setThreadUserName()
{
#if LOG4CXX_HAS_PTHREAD_GETNAME && !(defined(_WIN32) && defined(_LIBCPP_VERSION))
char result[16];
pthread_t current_thread = pthread_self();
if (pthread_getname_np(current_thread, result, sizeof(result)) < 0 || 0 == result[0])
this->pNamePair->threadName = this->pNamePair->idString;
else
this->pNamePair->threadName = Transcoder::decode(result);
#elif defined(_WIN32)
typedef HRESULT (WINAPI *TGetThreadDescription)(HANDLE, PWSTR*);
static struct initialiser
{
HMODULE hKernelBase;
TGetThreadDescription GetThreadDescription;
initialiser()
: hKernelBase(GetModuleHandleA("KernelBase.dll"))
, GetThreadDescription(nullptr)
{
if (hKernelBase)
GetThreadDescription = reinterpret_cast<TGetThreadDescription>(GetProcAddress(hKernelBase, "GetThreadDescription"));
}
} win32func;
if (win32func.GetThreadDescription)
{
PWSTR result = 0;
HRESULT hr = win32func.GetThreadDescription(GetCurrentThread(), &result);
if (SUCCEEDED(hr) && result)
{
std::wstring wresult = result;
LOG4CXX_DECODE_WCHAR(decoded, wresult);
LocalFree(result);
this->pNamePair->threadName = decoded;
}
}
if (this->pNamePair->threadName.empty())
this->pNamePair->threadName = this->pNamePair->idString;
#else
this->pNamePair->threadName = this->pNamePair->idString;
#endif
}
ThreadSpecificData::ThreadSpecificData()
: m_priv(std::make_unique<ThreadSpecificDataPrivate>())
{
}
ThreadSpecificData::ThreadSpecificData(ThreadSpecificData&& other)
: m_priv(std::move(other.m_priv))
{
}
ThreadSpecificData::~ThreadSpecificData()
{
m_priv.reset();
}
NDC::Stack& ThreadSpecificData::getStack()
{
return m_priv->ndcStack;
}
MDC::Map& ThreadSpecificData::getMap()
{
return m_priv->mdcMap;
}
auto ThreadSpecificData::getNames() -> NamePairPtr
{
auto p = getCurrentData();
return p ? p->m_priv->pNamePair : std::make_shared<NamePair>();
}
#if !LOG4CXX_LOGCHAR_IS_UNICHAR && !LOG4CXX_LOGCHAR_IS_WCHAR
std::basic_ostringstream<logchar>& ThreadSpecificData::getStream(const logchar&)
{
return getCurrentData()->m_priv->logchar_stringstream;
}
#endif
#if LOG4CXX_WCHAR_T_API || LOG4CXX_LOGCHAR_IS_WCHAR
std::basic_ostringstream<wchar_t>& ThreadSpecificData::getStream(const wchar_t&)
{
return getCurrentData()->m_priv->wchar_stringstream;
}
#endif
#if LOG4CXX_UNICHAR_API || LOG4CXX_LOGCHAR_IS_UNICHAR
std::basic_ostringstream<UniChar>& ThreadSpecificData::getStream(const UniChar&)
{
return getCurrentData()->m_priv->unichar_stringstream;
}
#endif
ThreadSpecificData* ThreadSpecificData::getCurrentData()
{
#if LOG4CXX_HAS_THREAD_LOCAL
thread_local ThreadSpecificData data;
return data.m_priv ? &data : NULL;
#elif APR_HAS_THREADS
void* pData = NULL;
if (APR_SUCCESS == apr_threadkey_private_get(&pData, APRInitializer::getTlsKey())
&& !pData)
{
pData = new ThreadSpecificData();
if (APR_SUCCESS != apr_threadkey_private_set(pData, APRInitializer::getTlsKey()))
{
delete (ThreadSpecificData*)pData;
pData = NULL;
}
}
if (pData)
return (ThreadSpecificData*) pData;
#endif
// Fallback implementation that is not expected to be used
using TaggedData = std::pair<std::thread::id, ThreadSpecificData>;
static std::list<TaggedData> thread_id_map;
static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);
auto threadId = std::this_thread::get_id();
auto pThreadId = std::find_if(thread_id_map.begin(), thread_id_map.end()
, [threadId](const TaggedData& item) { return threadId == item.first; });
if (thread_id_map.end() == pThreadId)
pThreadId = thread_id_map.emplace(thread_id_map.begin(), threadId, ThreadSpecificData());
return &pThreadId->second;
}
void ThreadSpecificData::recycle()
{
#if !LOG4CXX_HAS_THREAD_LOCAL && APR_HAS_THREADS
if (m_priv->ndcStack.empty() && m_priv->mdcMap.empty())
{
void* pData = NULL;
if (APR_SUCCESS == apr_threadkey_private_get(&pData, APRInitializer::getTlsKey())
&& pData == this
&& APR_SUCCESS == apr_threadkey_private_set(0, APRInitializer::getTlsKey()))
delete this;
}
#endif
}
void ThreadSpecificData::put(const LogString& key, const LogString& val)
{
if (auto p = getCurrentData())
p->getMap()[key] = val;
}
void ThreadSpecificData::push(const LogString& val)
{
auto p = getCurrentData();
if (!p)
return;
NDC::Stack& stack = p->getStack();
if (stack.empty())
{
stack.push(NDC::DiagnosticContext(val, val));
}
else
{
LogString fullMessage(stack.top().second);
fullMessage.append(1, (logchar) 0x20);
fullMessage.append(val);
stack.push(NDC::DiagnosticContext(val, fullMessage));
}
}
void ThreadSpecificData::inherit(const NDC::Stack& src)
{
if (auto p = getCurrentData())
p->getStack() = src;
}