| /* |
| * 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; |
| } |
| |