blob: 99879e5ef478a440db51b53c1de5d65cbb4d7364 [file]
/*
* 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;
}