| /* | |
| * 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 <mutex> | |
| #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/bytebuffer.h> | |
| #include <log4cxx/helpers/messagebuffer.h> | |
| #include <log4cxx/helpers/date.h> | |
| #include <log4cxx/helpers/optional.h> | |
| #include <log4cxx/helpers/transcoder.h> | |
| #include <atomic> | |
| #include <memory> | |
| #include <thread> | |
| using namespace LOG4CXX_NS; | |
| using namespace LOG4CXX_NS::spi; | |
| using namespace LOG4CXX_NS::helpers; | |
| struct LoggingEvent::LoggingEventPrivate | |
| { | |
| LoggingEventPrivate(const ThreadSpecificData::NamePairPtr p = ThreadSpecificData::getNames()) : | |
| timeStamp(0), | |
| pNames(p) | |
| , renderState(RenderingRequired) | |
| { | |
| } | |
| LoggingEventPrivate | |
| ( const LogString& logger1 | |
| , const LevelPtr& level1 | |
| , const LocationInfo& locationInfo1 | |
| , LogString&& message1 | |
| , const ThreadSpecificData::NamePairPtr p = ThreadSpecificData::getNames() | |
| ) : | |
| logger(logger1), | |
| level(level1), | |
| message(std::move(message1)), | |
| timeStamp(Date::currentTime()), | |
| locationInfo(locationInfo1), | |
| chronoTimeStamp(std::chrono::microseconds(timeStamp)), | |
| pNames(p), | |
| renderState(RenderingCompleted) | |
| { | |
| } | |
| LoggingEventPrivate | |
| ( const LogString& logger1 | |
| , const LevelPtr& level1 | |
| , const LocationInfo& locationInfo1 | |
| , helpers::AsyncBuffer&& messageAppenderArg | |
| , const ThreadSpecificData::NamePairPtr p = ThreadSpecificData::getNames() | |
| ) | |
| : logger(logger1) | |
| , level(level1) | |
| , timeStamp(Date::currentTime()) | |
| , locationInfo(locationInfo1) | |
| , chronoTimeStamp(std::chrono::microseconds(timeStamp)) | |
| , pNames(p) | |
| , messageAppender(std::move(messageAppenderArg)) | |
| , renderState(RenderingRequired) | |
| { | |
| } | |
| LoggingEventPrivate( | |
| const LogString& logger1, const LevelPtr& level1, | |
| const LogString& message1, const LocationInfo& locationInfo1, | |
| const ThreadSpecificData::NamePairPtr p = ThreadSpecificData::getNames() | |
| ) : | |
| logger(logger1), | |
| level(level1), | |
| message(message1), | |
| timeStamp(Date::currentTime()), | |
| locationInfo(locationInfo1), | |
| chronoTimeStamp(std::chrono::microseconds(timeStamp)), | |
| pNames(p), | |
| renderState(RenderingCompleted) | |
| { | |
| } | |
| ~LoggingEventPrivate() | |
| { | |
| delete properties; | |
| } | |
| /** | |
| * The name of the logger used to make the logging request | |
| **/ | |
| LogString logger; | |
| /** severity level of logging event. */ | |
| LevelPtr level; | |
| /** | |
| * A map of String keys and String values. | |
| */ | |
| std::map<LogString, LogString>* properties{NULL}; | |
| /** The application supplied message. */ | |
| LogString message; | |
| /** The number of microseconds elapsed since 1970-01-01 | |
| * at the time this logging event was created. | |
| */ | |
| log4cxx_time_t timeStamp; | |
| /** The source code location where the logging request was made. */ | |
| const spi::LocationInfo locationInfo; | |
| std::chrono::time_point<std::chrono::system_clock> chronoTimeStamp; | |
| /** | |
| * Thread names that remain valid for the lifetime of this LoggingEvent | |
| * (i.e. even after thread termination). | |
| */ | |
| ThreadSpecificData::NamePairPtr pNames; | |
| struct DiagnosticContext | |
| { | |
| Optional<NDC::DiagnosticContext> ctx; | |
| MDC::Map map; | |
| }; | |
| /** | |
| * Used to hold the diagnostic context when the lifetime | |
| * of this LoggingEvent exceeds the duration of the logging request. | |
| */ | |
| mutable std::unique_ptr<DiagnosticContext> dc; | |
| /** Application supplied message builders. | |
| */ | |
| helpers::AsyncBuffer messageAppender; | |
| /** Ensures an async message is rendered exactly once. | |
| * | |
| * A LoggingEvent may be shared between threads, e.g. when it is | |
| * queued to an AsyncAppender dispatcher while the logging thread | |
| * formats the same event for a synchronous appender. Without this, | |
| * two threads could pass the empty() check concurrently: one clears | |
| * the closure vector while the other iterates it, and both | |
| * move-assign \c message - a concurrent free/assign of the same | |
| * heap buffer. | |
| * Note: use of std::call_once was found to degrade throughput by up to 120% | |
| */ | |
| std::atomic<uint8_t> renderState; | |
| static const uint8_t RenderingRequired = 0; | |
| static const uint8_t RenderingActive = 1; | |
| static const uint8_t RenderingCompleted = 2; | |
| void renderMessage() | |
| { | |
| if (renderState.load(std::memory_order_acquire) == RenderingCompleted) | |
| return; | |
| uint8_t unrenderedState{ RenderingRequired }; | |
| if (renderState.compare_exchange_strong(unrenderedState, RenderingActive, std::memory_order_acquire)) | |
| { | |
| if (this->messageAppender.empty()) | |
| ; | |
| else try | |
| { | |
| helpers::LogCharMessageBuffer buf; | |
| this->messageAppender.renderMessage(buf); | |
| this->message = buf.extract_str(buf); | |
| this->messageAppender.clear(); | |
| } | |
| catch (const std::exception& e) | |
| { | |
| helpers::Transcoder::decode(e.what(), this->message); | |
| } | |
| catch (...) | |
| { | |
| this->message = LOG4CXX_STR("mesage rendering failed"); | |
| } | |
| renderState.store(RenderingCompleted, std::memory_order_release); | |
| } | |
| else while (renderState.load(std::memory_order_acquire) != RenderingCompleted) | |
| std::this_thread::yield(); | |
| } | |
| }; | |
| IMPLEMENT_LOG4CXX_OBJECT(LoggingEvent) | |
| // | |
| // Accessor for start time. | |
| // | |
| log4cxx_time_t LoggingEvent::getStartTime() | |
| { | |
| return APRInitializer::getStartTime(); | |
| } | |
| 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& logger | |
| , const LevelPtr& level | |
| , const LocationInfo& location | |
| , helpers::AsyncBuffer&& messageAppender | |
| ) | |
| : m_priv(std::make_unique<LoggingEventPrivate>(logger, level, location, std::move(messageAppender))) | |
| { | |
| } | |
| 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->pNames->threadName; | |
| } | |
| bool LoggingEvent::getNDC(LogString& dest) const | |
| { | |
| bool result = false; | |
| // Use the copy of the diagnostic context if it exists. | |
| // Otherwise use the NDC that is associated with the thread. | |
| if (m_priv->dc) | |
| { | |
| result = bool(m_priv->dc->ctx); | |
| if (result) | |
| dest.append(NDC::getFullMessage(m_priv->dc->ctx.value())); | |
| } | |
| else | |
| result = NDC::get(dest); | |
| return result; | |
| } | |
| bool LoggingEvent::getMDC(const LogString& key, LogString& dest) const | |
| { | |
| bool result = false; | |
| // Use the copy of the diagnostic context if it exists. | |
| // Otherwise use the MDC that is associated with the thread. | |
| if (m_priv->dc) | |
| { | |
| auto& map = m_priv->dc->map; | |
| auto it = map.find(key); | |
| if (it != map.end() && !it->second.empty()) | |
| { | |
| dest.append(it->second); | |
| result = true; | |
| } | |
| } | |
| else | |
| result = MDC::get(key, dest); | |
| return result; | |
| } | |
| LoggingEvent::KeySet LoggingEvent::getMDCKeySet() const | |
| { | |
| LoggingEvent::KeySet result; | |
| if (m_priv->dc) | |
| { | |
| for (auto const& item : m_priv->dc->map) | |
| result.push_back(item.first); | |
| } | |
| else if (auto pData = ThreadSpecificData::getCurrentData()) | |
| { | |
| for (auto const& item : pData->getMap()) | |
| result.push_back(item.first); | |
| } | |
| return result; | |
| } | |
| void LoggingEvent::LoadDC() const | |
| { | |
| m_priv->dc = std::make_unique<LoggingEventPrivate::DiagnosticContext>(); | |
| if (auto pData = ThreadSpecificData::getCurrentData()) | |
| { | |
| m_priv->dc->map = pData->getMap(); | |
| auto& stack = pData->getStack(); | |
| if (!stack.empty()) | |
| m_priv->dc->ctx = stack.top(); | |
| } | |
| } | |
| 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) | |
| { | |
| for (auto item : *m_priv->properties) | |
| { | |
| set.push_back(item.first); | |
| } | |
| } | |
| return set; | |
| } | |
| void LoggingEvent::renderMessage() | |
| { | |
| m_priv->renderMessage(); | |
| } | |
| 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 | |
| { | |
| m_priv->renderMessage(); | |
| return m_priv->message; | |
| } | |
| const LogString& LoggingEvent::getThreadName() const | |
| { | |
| return m_priv->pNames->idString; | |
| } | |
| 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; | |
| } | |