Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/main/cpp/appenderskeleton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,11 @@ void AppenderSkeleton::setOption(const LogString& option,
{
setThreshold(Level::toLevelLS(value));
}
else if (StringHelper::equalsIgnoreCase(option,
LOG4CXX_STR("NAME"), LOG4CXX_STR("name")))
{
setName(value);
}
}

const spi::ErrorHandlerPtr AppenderSkeleton::getErrorHandler() const
Expand Down
2 changes: 1 addition & 1 deletion src/main/cpp/asyncappender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,7 @@ void AsyncAppender::dispatch()
if (LogLog::isDebugEnabled())
{
Pool p;
LogString msg(LOG4CXX_STR("AsyncAppender"));
LogString msg(LOG4CXX_STR("[") + getName() + LOG4CXX_STR("] AsyncAppender"));
#ifdef _DEBUG
msg += LOG4CXX_STR(" iterationCount ");
StringHelper::toString(iterationCount, p, msg);
Expand Down
30 changes: 28 additions & 2 deletions src/main/cpp/domconfigurator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <log4cxx/logstring.h>
#include <log4cxx/xml/domconfigurator.h>
#include <log4cxx/appender.h>
#include <log4cxx/asyncappender.h>
#include <log4cxx/layout.h>
#include <log4cxx/logger.h>
#include <log4cxx/logmanager.h>
Expand Down Expand Up @@ -132,6 +133,7 @@ IMPLEMENT_LOG4CXX_OBJECT(DOMConfigurator)
#define ERROR_HANDLER_TAG "errorHandler"
#define REF_ATTR "ref"
#define ADDITIVITY_ATTR "additivity"
#define ASYNCHRONOUS_ATTR "asynchronous"
#define THRESHOLD_ATTR "threshold"
#define STRINGSTREAM_ATTR "stringstream"
#define CONFIG_DEBUG_ATTR "configDebug"
Expand Down Expand Up @@ -560,8 +562,18 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
AppenderMap& appenders)
{
PropertySetter propSetter(logger);
std::vector<AppenderPtr> newappenders;
auto loggerName = m_priv->repository->getRootLogger() == logger
? LogString(LOG4CXX_STR("root"))
: logger->getName();
AsyncAppenderPtr async;
auto lsAsynchronous = subst(getAttribute(utf8Decoder, loggerElement, ASYNCHRONOUS_ATTR));
if (!lsAsynchronous.empty() && OptionConverter::toBoolean(lsAsynchronous, true))
{
async = std::make_shared<AsyncAppender>();
async->setName(loggerName);
}

std::vector<AppenderPtr> newappenders;
for (apr_xml_elem* currentElement = loggerElement->first_child;
currentElement;
currentElement = currentElement->next)
Expand All @@ -572,13 +584,17 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
{
if (auto appender = findAppenderByReference(p, utf8Decoder, currentElement, doc, appenders))
{
if (log4cxx::cast<AsyncAppender>(appender)) // An explicitly configured AsyncAppender?
async.reset(); // Not required
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Adding ") + Appender::getStaticClass().getName()
+ LOG4CXX_STR(" named [") + appender->getName() + LOG4CXX_STR("]")
+ LOG4CXX_STR(" to logger [") + logger->getName() + LOG4CXX_STR("]"));
}
newappenders.push_back(appender);
if (async)
async->addAppender(appender);
}
}
else if (tagName == LEVEL_TAG)
Expand All @@ -594,7 +610,17 @@ void DOMConfigurator::parseChildrenOfLoggerElement(
setParameter(p, utf8Decoder, currentElement, propSetter);
}
}
if (newappenders.empty())
if (async && !newappenders.empty())
{
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Asynchronous logging for [")
+ logger->getName() + LOG4CXX_STR("] is on"));
}
logger->replaceAppenders({async});
m_priv->appenderAdded = true;
}
else if (newappenders.empty())
logger->removeAllAppenders();
else
{
Expand Down
34 changes: 26 additions & 8 deletions src/main/cpp/propertyconfigurator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <log4cxx/logstring.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/asyncappender.h>
#include <log4cxx/helpers/properties.h>
#include <log4cxx/helpers/loglog.h>
#include <log4cxx/helpers/exception.h>
Expand Down Expand Up @@ -413,13 +414,18 @@ void PropertyConfigurator::parseLogger(

}

AppenderPtr appender;
LogString appenderName;
std::vector<AppenderPtr> newappenders;
AsyncAppenderPtr async;
auto lsAsynchronous = OptionConverter::findAndSubst(LOG4CXX_STR("log4j.asynchronous.") + loggerName, props);
if (!lsAsynchronous.empty() && OptionConverter::toBoolean(lsAsynchronous, true))
{
async = std::make_shared<AsyncAppender>();
async->setName(loggerName);
}

std::vector<AppenderPtr> newappenders;
while (st.hasMoreTokens())
{
appenderName = StringHelper::trim(st.nextToken());
auto appenderName = StringHelper::trim(st.nextToken());

if (appenderName.empty() || appenderName == LOG4CXX_STR(","))
{
Expand All @@ -431,18 +437,30 @@ void PropertyConfigurator::parseLogger(
LogLog::debug(LOG4CXX_STR("Parsing ") + Appender::getStaticClass().getName()
+ LOG4CXX_STR(" named [") + appenderName + LOG4CXX_STR("]"));
}
appender = parseAppender(props, appenderName);

if (appender != 0)
if (auto appender = parseAppender(props, appenderName))
{
newappenders.push_back(appender);
if (log4cxx::cast<AsyncAppender>(appender)) // An explicitly configured AsyncAppender?
async.reset(); // Not required
if (async)
async->addAppender(appender);
}
}
#if 15 < LOG4CXX_ABI_VERSION
if (!newappenders.empty())
m_priv->appenderAdded = true;
#endif
logger->reconfigure( newappenders, additivity );
if (async && !newappenders.empty())
{
if (LogLog::isDebugEnabled())
{
LogLog::debug(LOG4CXX_STR("Asynchronous logging for [")
+ loggerName + LOG4CXX_STR("] is on"));
}
logger->reconfigure( {async}, additivity );
}
else
logger->reconfigure( newappenders, additivity );
}

AppenderPtr PropertyConfigurator::parseAppender(
Expand Down
1 change: 1 addition & 0 deletions src/main/include/log4cxx/appenderskeleton.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class LOG4CXX_EXPORT AppenderSkeleton :

Supported options | Supported values | Default value |
-------------- | ---------------- | --------------- |
Name | {any} | - |
Threshold | Trace,Debug,Info,Warn,Error,Fatal,Off,All | All |
*/
void setOption(const LogString& option, const LogString& value) override;
Expand Down
11 changes: 9 additions & 2 deletions src/main/include/log4cxx/asyncappender.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,21 @@ The AsyncAppender stores the logging event in a bounded buffer
and then returns control to the application.
A separate thread forwards events to the attached appender(s).

An AsyncAppender is used when you configure a logger to be asynchronous.
These AsyncAppender(s) use [the default values](@ref log4cxx::AsyncAppender::setOption) for all options
and they cannot be changed using configuration file entries.
For more control over the AsyncAppender options,
use <b>appender-ref</b> element in the logger configuration instead.

<b>Important notes:</b>
- Your application must call LogManager::shutdown when it exits
to prevent undefined behaviour when using this appender.
- Runtime configuration requires an XML configuration file
- Runtime configuration of options requires an XML configuration file
(see the example below).

This appender is useful when outputting to a slow event sink,
for example, a remote SMTP server or a database.
for example, unbuffered output to a file,
a remote SMTP server or a database.
Note that configuring a FileAppender to use [buffered output](@ref log4cxx::FileAppender::setOption)
usually results in lower overhead than
attaching the FileAppender to an AsyncAppender
Expand Down
2 changes: 2 additions & 0 deletions src/site/markdown/change-report-gh.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ and the LOG4CXX_CONFIGURATION environment variable (see log4cxx::spi::Configurat
\[[#529](https://github.com/apache/logging-log4cxx/pull/529)\]
* New logging macros that defer binary-to-text conversion until used in AsyncAppender's background thread
\[[#548](https://github.com/apache/logging-log4cxx/pull/548)\]
* A simplified way to attach an AsyncAppender to a logger using a configuration file
\[[#550](https://github.com/apache/logging-log4cxx/pull/550)\]

The following issues have been addressed:

Expand Down
18 changes: 17 additions & 1 deletion src/site/markdown/configuration-samples.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,21 @@ The variable names are [documented here](@ref log4cxx.spi.Configurator.propertie
To check the correct values are used when your configuration file is loaded,
use [Log4cxx internal debugging].

# Configuring a logger to use asynchronous output

Log4cxx 1.6 allows you to more easily attach an [AsyncAppender](@ref log4cxx.AsyncAppender)
to a logger using a configuration file.
- If using a properties file, add the line <code>log4j.asynchronous.{LOGGER_NAME}=true</code> to your file
- If using an XML file, add the <code>asynchronous="true"</code> attribute in the <code>\<logger></code> element.

The "asynchronous" attribute is only relevent for a logger with attached appenders.
The attribute is ignored if the logger does not have any directly attached appenders.

The "asynchronous" attribute results in the configured appender(s)
being attached an [AsyncAppender](@ref log4cxx.AsyncAppender)
and it is the [AsyncAppender](@ref log4cxx.AsyncAppender)
that is attached directly to the logger.

# Configuration Samples {#configuration-samples}

The following snippets show various ways of configuring Log4cxx.
Expand Down Expand Up @@ -124,6 +139,7 @@ to store a log file per executable in a product related logs directory:
~~~{.properties}
# Uncomment a line to enable debugging for a category
log4j.rootCategory=INFO, A1
log4j.asynchronous.root=true

log4j.appender.A1=org.apache.log4j.RollingFileAppender
log4j.appender.A1.MaxFileSize=5MB
Expand Down Expand Up @@ -286,7 +302,7 @@ to store a log file per executable in a product related logs directory:
</layout>
</appender>

<root>
<root asynchronous="true" >
<priority value="info" />
<appender-ref ref="ConsoleAppender"/>
<appender-ref ref="FileAppender"/>
Expand Down
65 changes: 63 additions & 2 deletions src/test/cpp/asyncappendertestcase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <log4cxx/helpers/stringhelper.h>
#include <log4cxx/spi/location/locationinfo.h>
#include <log4cxx/xml/domconfigurator.h>
#include <log4cxx/propertyconfigurator.h>
#include <log4cxx/file.h>
#include <thread>

Expand Down Expand Up @@ -138,8 +139,10 @@ class AsyncAppenderTestCase : public AppenderSkeletonTestCase
LOGUNIT_TEST(testBufferOverflowBehavior);
LOGUNIT_TEST(testLoggingAppender);
#if LOG4CXX_HAS_DOMCONFIGURATOR
LOGUNIT_TEST(testConfiguration);
LOGUNIT_TEST(testXMLConfiguration);
LOGUNIT_TEST(testAsyncLoggerXML);
#endif
LOGUNIT_TEST(testAsyncLoggerProperties);
LOGUNIT_TEST_SUITE_END();

#ifdef _DEBUG
Expand Down Expand Up @@ -531,7 +534,7 @@ class AsyncAppenderTestCase : public AppenderSkeletonTestCase
}

#if LOG4CXX_HAS_DOMCONFIGURATOR
void testConfiguration()
void testXMLConfiguration()
{
// Configure Log4cxx
auto status = xml::DOMConfigurator::configure("input/xml/asyncAppender1.xml");
Expand Down Expand Up @@ -562,8 +565,66 @@ class AsyncAppenderTestCase : public AppenderSkeletonTestCase
LOGUNIT_ASSERT_EQUAL(LEN, v.size());
LOGUNIT_ASSERT(vectorAppender->isClosed());
}

void testAsyncLoggerXML()
{
// Configure Log4cxx
auto status = xml::DOMConfigurator::configure("input/xml/asyncLogger.xml");
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);

// Check configuration is as expected
auto root = Logger::getRootLogger();
auto appenders = root->getAllAppenders();
LOGUNIT_ASSERT_EQUAL(1, int(appenders.size()));
auto asyncAppender = log4cxx::cast<AsyncAppender>(appenders.front());
LOGUNIT_ASSERT(asyncAppender);

// Log some messages
size_t LEN = 20;
for (size_t i = 0; i < LEN; i++)
{
LOG4CXX_INFO_ASYNC(root, "message" << i);
}
asyncAppender->close();

// Check all message were received
auto vectorAppender = log4cxx::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
LOGUNIT_ASSERT(vectorAppender);
auto& v = vectorAppender->getVector();
LOGUNIT_ASSERT_EQUAL(LEN, v.size());
LOGUNIT_ASSERT(vectorAppender->isClosed());
}
#endif

void testAsyncLoggerProperties()
{
// Configure Log4cxx
auto status = PropertyConfigurator::configure("input/asyncLogger.properties");
LOGUNIT_ASSERT_EQUAL(status, spi::ConfigurationStatus::Configured);

// Check configuration is as expected
auto root = Logger::getRootLogger();
auto appenders = root->getAllAppenders();
LOGUNIT_ASSERT_EQUAL(1, int(appenders.size()));
auto asyncAppender = log4cxx::cast<AsyncAppender>(appenders.front());
LOGUNIT_ASSERT(asyncAppender);

// Log some messages
size_t LEN = 20;
for (size_t i = 0; i < LEN; i++)
{
LOG4CXX_INFO_ASYNC(root, "message" << i);
}
asyncAppender->close();

// Check all message were received
auto vectorAppender = log4cxx::cast<VectorAppender>(asyncAppender->getAppender(LOG4CXX_STR("VECTOR")));
LOGUNIT_ASSERT(vectorAppender);
auto& v = vectorAppender->getVector();
LOGUNIT_ASSERT_EQUAL(LEN, v.size());
LOGUNIT_ASSERT(vectorAppender->isClosed());
}


};

Expand Down
20 changes: 20 additions & 0 deletions src/test/resources/input/asyncLogger.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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.
#
log4j.rootCategory=INFO, A1
log4j.asynchronous.root=true

log4j.appender.A1=org.apache.log4j.VectorAppender
log4j.appender.A1.name=VECTOR
27 changes: 27 additions & 0 deletions src/test/resources/input/xml/asyncLogger.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<!--
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.

-->

<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
<appender name="VECTOR" class="org.apache.log4j.VectorAppender"/>
<root asynchronous="true" >
<level value="INFO"/>
<appender-ref ref="VECTOR" />
</root>
</log4j:configuration>