| /* |
| * 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 <chrono> |
| #include <log4cxx/spi/loggingevent.h> |
| #include <log4cxx/ndc.h> |
| |
| #include <log4cxx/level.h> |
| #include <log4cxx/helpers/loglog.h> |
| #include <log4cxx/helpers/system.h> |
| #include <log4cxx/helpers/socket.h> |
| #if !defined(LOG4CXX) |
| #define LOG4CXX 1 |
| #endif |
| #include <log4cxx/helpers/aprinitializer.h> |
| #include <log4cxx/helpers/threadspecificdata.h> |
| #include <log4cxx/helpers/transcoder.h> |
| |
| #include <apr_portable.h> |
| #include <apr_strings.h> |
| #include <log4cxx/helpers/stringhelper.h> |
| #include <log4cxx/helpers/bytebuffer.h> |
| #include <log4cxx/logger.h> |
| #include <log4cxx/private/log4cxx_private.h> |
| #include <log4cxx/helpers/date.h> |
| |
| using namespace LOG4CXX_NS; |
| using namespace LOG4CXX_NS::spi; |
| using namespace LOG4CXX_NS::helpers; |
| |
| struct LoggingEvent::LoggingEventPrivate |
| { |
| LoggingEventPrivate() : |
| ndc(0), |
| mdcCopy(0), |
| properties(0), |
| ndcLookupRequired(true), |
| mdcCopyLookupRequired(true), |
| timeStamp(0), |
| locationInfo(), |
| threadName(getCurrentThreadName()), |
| threadUserName(getCurrentThreadUserName()) |
| { |
| } |
| |
| LoggingEventPrivate |
| ( const LogString& logger1 |
| , const LevelPtr& level1 |
| , const LocationInfo& locationInfo1 |
| , LogString&& message1 |
| ) : |
| logger(logger1), |
| level(level1), |
| ndc(0), |
| mdcCopy(0), |
| properties(0), |
| ndcLookupRequired(true), |
| mdcCopyLookupRequired(true), |
| message(std::move(message1)), |
| timeStamp(Date::currentTime()), |
| locationInfo(locationInfo1), |
| threadName(getCurrentThreadName()), |
| threadUserName(getCurrentThreadUserName()), |
| chronoTimeStamp(std::chrono::microseconds(timeStamp)) |
| { |
| } |
| |
| LoggingEventPrivate( |
| const LogString& logger1, const LevelPtr& level1, |
| const LogString& message1, const LocationInfo& locationInfo1) : |
| logger(logger1), |
| level(level1), |
| ndc(0), |
| mdcCopy(0), |
| properties(0), |
| ndcLookupRequired(true), |
| mdcCopyLookupRequired(true), |
| message(message1), |
| timeStamp(Date::currentTime()), |
| locationInfo(locationInfo1), |
| threadName(getCurrentThreadName()), |
| threadUserName(getCurrentThreadUserName()), |
| chronoTimeStamp(std::chrono::microseconds(timeStamp)) |
| { |
| } |
| |
| ~LoggingEventPrivate() |
| { |
| delete ndc; |
| delete mdcCopy; |
| delete properties; |
| } |
| |
| /** |
| * The logger of the logging event. |
| **/ |
| LogString logger; |
| |
| /** level of logging event. */ |
| LevelPtr level; |
| |
| /** The nested diagnostic context (NDC) of logging event. */ |
| mutable LogString* ndc; |
| |
| /** The mapped diagnostic context (MDC) of logging event. */ |
| mutable MDC::Map* mdcCopy; |
| |
| /** |
| * A map of String keys and String values. |
| */ |
| std::map<LogString, LogString>* properties; |
| |
| /** Have we tried to do an NDC lookup? If we did, there is no need |
| * to do it again. Note that its value is always false when |
| * serialized. Thus, a receiving SocketNode will never use it's own |
| * (incorrect) NDC. See also writeObject method. |
| */ |
| mutable bool ndcLookupRequired; |
| |
| /** |
| * Have we tried to do an MDC lookup? If we did, there is no need to do it |
| * again. Note that its value is always false when serialized. See also |
| * the getMDC and getMDCCopy methods. |
| */ |
| mutable bool mdcCopyLookupRequired; |
| |
| /** The application supplied message of logging event. */ |
| LogString message; |
| |
| |
| /** The number of microseconds elapsed from 01.01.1970 until logging event |
| was created. */ |
| log4cxx_time_t timeStamp; |
| |
| /** The is the location where this log statement was written. */ |
| const LOG4CXX_NS::spi::LocationInfo locationInfo; |
| |
| |
| /** The identifier of thread in which this logging event |
| was generated. |
| */ |
| const LogString& threadName; |
| |
| /** |
| * The user-specified name of the thread(on a per-platform basis). |
| * This is set using a method such as pthread_setname_np on POSIX |
| * systems or SetThreadDescription on Windows. |
| */ |
| const LogString& threadUserName; |
| |
| std::chrono::time_point<std::chrono::system_clock> chronoTimeStamp; |
| }; |
| |
| IMPLEMENT_LOG4CXX_OBJECT(LoggingEvent) |
| |
| |
| // |
| // Accessor for start time. |
| // |
| log4cxx_time_t LoggingEvent::getStartTime() |
| { |
| return LOG4CXX_NS::helpers::APRInitializer::initialize(); |
| } |
| |
| LoggingEvent::LoggingEvent() : |
| m_priv(std::make_unique<LoggingEventPrivate>()) |
| { |
| } |
| |
| LoggingEvent::LoggingEvent |
| ( const LogString& logger |
| , const LevelPtr& level |
| , const LocationInfo& location |
| , LogString&& message |
| ) |
| : m_priv(std::make_unique<LoggingEventPrivate>(logger, level, location, std::move(message))) |
| { |
| } |
| |
| LoggingEvent::LoggingEvent( |
| const LogString& logger1, const LevelPtr& level1, |
| const LogString& message1, const LocationInfo& locationInfo1) : |
| m_priv(std::make_unique<LoggingEventPrivate>(logger1, level1, message1, locationInfo1)) |
| { |
| } |
| |
| LoggingEvent::~LoggingEvent() |
| { |
| } |
| |
| const LogString& LoggingEvent::getThreadUserName() const{ |
| return m_priv->threadUserName; |
| } |
| |
| bool LoggingEvent::getNDC(LogString& dest) const |
| { |
| if (m_priv->ndcLookupRequired) |
| { |
| m_priv->ndcLookupRequired = false; |
| LogString val; |
| |
| if (NDC::get(val)) |
| { |
| m_priv->ndc = new LogString(val); |
| } |
| } |
| |
| if (m_priv->ndc) |
| { |
| dest.append(*m_priv->ndc); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool LoggingEvent::getMDC(const LogString& key, LogString& dest) const |
| { |
| // Note the mdcCopy is used if it exists. Otherwise we use the MDC |
| // that is associated with the thread. |
| if (m_priv->mdcCopy != 0 && !m_priv->mdcCopy->empty()) |
| { |
| MDC::Map::const_iterator it = m_priv->mdcCopy->find(key); |
| |
| if (it != m_priv->mdcCopy->end()) |
| { |
| if (!it->second.empty()) |
| { |
| dest.append(it->second); |
| return true; |
| } |
| } |
| } |
| |
| return MDC::get(key, dest); |
| |
| } |
| |
| LoggingEvent::KeySet LoggingEvent::getMDCKeySet() const |
| { |
| LoggingEvent::KeySet set; |
| |
| if (m_priv->mdcCopy != 0 && !m_priv->mdcCopy->empty()) |
| { |
| MDC::Map::const_iterator it; |
| |
| for (it = m_priv->mdcCopy->begin(); it != m_priv->mdcCopy->end(); it++) |
| { |
| set.push_back(it->first); |
| |
| } |
| } |
| else |
| { |
| ThreadSpecificData* data = ThreadSpecificData::getCurrentData(); |
| |
| if (data != 0) |
| { |
| MDC::Map& m = data->getMap(); |
| |
| for (MDC::Map::const_iterator it = m.begin(); it != m.end(); it++) |
| { |
| set.push_back(it->first); |
| } |
| } |
| } |
| |
| return set; |
| } |
| |
| void LoggingEvent::getMDCCopy() const |
| { |
| if (m_priv->mdcCopyLookupRequired) |
| { |
| m_priv->mdcCopyLookupRequired = false; |
| // the clone call is required for asynchronous logging. |
| ThreadSpecificData* data = ThreadSpecificData::getCurrentData(); |
| |
| if (data != 0) |
| { |
| m_priv->mdcCopy = new MDC::Map(data->getMap()); |
| } |
| else |
| { |
| m_priv->mdcCopy = new MDC::Map(); |
| } |
| } |
| } |
| |
| bool LoggingEvent::getProperty(const LogString& key, LogString& dest) const |
| { |
| if (m_priv->properties == 0) |
| { |
| return false; |
| } |
| |
| std::map<LogString, LogString>::const_iterator it = m_priv->properties->find(key); |
| |
| if (it != m_priv->properties->end()) |
| { |
| dest.append(it->second); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| LoggingEvent::KeySet LoggingEvent::getPropertyKeySet() const |
| { |
| LoggingEvent::KeySet set; |
| |
| if (m_priv->properties != 0) |
| { |
| std::map<LogString, LogString>::const_iterator it; |
| |
| for (it = m_priv->properties->begin(); it != m_priv->properties->end(); it++) |
| { |
| set.push_back(it->first); |
| } |
| } |
| |
| return set; |
| } |
| |
| |
| const LogString& LoggingEvent::getCurrentThreadName() |
| { |
| #if defined(_WIN32) |
| using ThreadIdType = DWORD; |
| ThreadIdType threadId = GetCurrentThreadId(); |
| #elif LOG4CXX_HAS_PTHREAD_SELF |
| using ThreadIdType = pthread_t; |
| ThreadIdType threadId = pthread_self(); |
| #else |
| using ThreadIdType = int; |
| ThreadIdType threadId = 0; |
| #endif |
| |
| #if LOG4CXX_HAS_THREAD_LOCAL |
| thread_local LogString thread_id_string; |
| #else |
| using ListItem = std::pair<ThreadIdType, LogString>; |
| static WideLife<std::list<ListItem>> thread_id_map; |
| static WideLife<std::mutex> mutex; |
| std::lock_guard<std::mutex> lock(mutex); |
| auto pThreadId = std::find_if(thread_id_map.begin(), thread_id_map.end() |
| , [threadId](const ListItem& item) { return threadId == item.first; }); |
| if (thread_id_map.end() == pThreadId) |
| pThreadId = thread_id_map.insert(thread_id_map.begin(), ListItem(threadId, LogString())); |
| LogString& thread_id_string = pThreadId->second; |
| #endif |
| if ( !thread_id_string.empty() ) |
| { |
| return thread_id_string; |
| } |
| |
| #if defined(_WIN32) |
| char result[20]; |
| apr_snprintf(result, sizeof(result), LOG4CXX_WIN32_THREAD_FMTSPEC, threadId); |
| Transcoder::decode(reinterpret_cast<const char*>(result), thread_id_string); |
| #elif LOG4CXX_HAS_PTHREAD_SELF |
| // pthread_t encoded in HEX takes needs as many characters |
| // as two times the size of the type, plus an additional null byte. |
| char result[sizeof(pthread_t) * 3 + 10]; |
| apr_snprintf(result, sizeof(result), LOG4CXX_APR_THREAD_FMTSPEC, (void*) &threadId); |
| Transcoder::decode(reinterpret_cast<const char*>(result), thread_id_string); |
| #else |
| thread_id_string = LOG4CXX_STR("0x00000000"); |
| #endif |
| return thread_id_string; |
| } |
| |
| const LogString& LoggingEvent::getCurrentThreadUserName() |
| { |
| #if LOG4CXX_HAS_THREAD_LOCAL |
| thread_local LogString thread_name; |
| #else |
| static WideLife<LogString> thread_name = LOG4CXX_STR("(noname)"); |
| #endif |
| if( !thread_name.empty() ){ |
| return thread_name; |
| } |
| |
| #if LOG4CXX_HAS_PTHREAD_GETNAME |
| char result[16]; |
| pthread_t current_thread = pthread_self(); |
| if( pthread_getname_np( current_thread, result, sizeof(result) ) < 0 ){ |
| thread_name = LOG4CXX_STR("(noname)"); |
| } |
| |
| LOG4CXX_NS::helpers::Transcoder::decode(reinterpret_cast<const char*>(result), thread_name); |
| #elif 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)) |
| { |
| std::wstring wresult = result; |
| LOG4CXX_DECODE_WCHAR(decoded, wresult); |
| LocalFree(result); |
| thread_name = decoded; |
| } |
| }else{ |
| thread_name = LOG4CXX_STR("(noname)"); |
| } |
| #else |
| thread_name = LOG4CXX_STR("(noname)"); |
| #endif |
| return thread_name; |
| } |
| |
| void LoggingEvent::setProperty(const LogString& key, const LogString& value) |
| { |
| if (m_priv->properties == 0) |
| { |
| m_priv->properties = new std::map<LogString, LogString>; |
| } |
| |
| (*m_priv->properties)[key] = value; |
| } |
| |
| const LevelPtr& LoggingEvent::getLevel() const |
| { |
| return m_priv->level; |
| } |
| |
| const LogString& LoggingEvent::getLoggerName() const |
| { |
| return m_priv->logger; |
| } |
| |
| const LogString& LoggingEvent::getMessage() const |
| { |
| return m_priv->message; |
| } |
| |
| const LogString& LoggingEvent::getRenderedMessage() const |
| { |
| return m_priv->message; |
| } |
| |
| const LogString& LoggingEvent::getThreadName() const |
| { |
| return m_priv->threadName; |
| } |
| |
| log4cxx_time_t LoggingEvent::getTimeStamp() const |
| { |
| return m_priv->timeStamp; |
| } |
| |
| const LOG4CXX_NS::spi::LocationInfo& LoggingEvent::getLocationInformation() const |
| { |
| return m_priv->locationInfo; |
| } |
| |
| std::chrono::time_point<std::chrono::system_clock> LoggingEvent::getChronoTimeStamp() const{ |
| return m_priv->chronoTimeStamp; |
| } |
| |