[
https://issues.apache.org/jira/browse/LOG4J2-1949?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=16061093#comment-16061093
]
Chris Slater commented on LOG4J2-1949:
--------------------------------------
I was able to work around the issue (and LOG4J2-1951) with the use of a custom
ConfigurationFactory, JdbcAppender, and FailoverAppender.
I created two interfaces:
{code:java}
public interface CustomLifeCycle extends LifeCycle2 {
void beforeStop();
}
{code}
{code:java}
public interface FailoverAware {
List<LogEvent> onFailover(LogEvent causalEvent, Exception causalException);
List<LogEvent> onFailover(LogEvent causalEvent);
void onFailoverAppenderBeforeStop();
List<LogEvent> onFailoverAppenderBeforeStopException(Exception
causalException);
}
{code}
And created a custom configuration and ConfigurationFactory to tie in the new
lifecycle method:
{code:java}
public class CustomXmlConfiguration extends XmlConfiguration {
public CustomXmlConfiguration(LoggerContext loggerContext,
ConfigurationSource configSource) {
super(loggerContext, configSource);
}
@Override
public boolean stop(final long timeout, final TimeUnit timeUnit) {
getAppenders().values().stream()
.filter(appender -> appender instanceof CustomLifeCycle)
.map(appender -> (CustomLifeCycle) appender)
.forEach(CustomLifeCycle::beforeStop);
return super.stop(timeout, timeUnit);
}
}
{code}
{code:java}
@Plugin(name = "CustomXmlConfigurationFactory", category =
ConfigurationFactory.CATEGORY)
@Order(10)
public class CustomXmlConfigurationFactory extends XmlConfigurationFactory
{
@Override
public Configuration getConfiguration(final LoggerContext loggerContext,
final ConfigurationSource source)
{
return new CustomXmlConfiguration(loggerContext, source);
}
}
{code}
I then copied the existing FailoverAppender and modified it to implement
CustomLifecycle, modified the failover() method to accept a collection of
events and added a method to resolve failover events passed to failover():
{code:java}
@Plugin(name = "CustomFailover", category = "Core", elementType = "appender",
printObject = true)
public final class CustomFailoverAppender extends AbstractAppender implements
CustomLifeCycle {
//Code omitted
private List<LogEvent> resolveFailoverEvents(
LogEvent causalEvent,
//Nullable
Exception causalException) {
List<LogEvent> events;
Appender appender = primary.getAppender();
if (appender instanceof FailoverAware) {
FailoverAware failoverListener = (FailoverAware) appender;
events = (causalException == null)
? failoverListener.onFailover(causalEvent)
: failoverListener.onFailover(causalEvent,
causalException);
} else {
events = Arrays.asList(causalEvent);
}
return events;
}
private void failover(final List<LogEvent> events, final Exception ex) {
//Code omitted
for (final AppenderControl control : failoverAppenders) {
try {
events.forEach(control::callAppender);
//Code omitted
break;
} catch (final Exception fex) {
//Code omitted
}
}
//Code omitted
}
@Override
public void beforeStop() {
Appender appender = primary.getAppender();
if (appender instanceof FailoverAware) {
try {
((FailoverAware) appender).onFailoverAppenderBeforeStop();
}
catch (Exception e) {
List<LogEvent> events = ((FailoverAware)
appender).onFailoverAppenderBeforeStopException(e);
if (!events.isEmpty()) {
failover(events, e);
}
}
}
}
}
{code}
A custom JdbcAppender was created by copying the provided JdbcAppender and
AbstractDatabaseAppender and modified to implement FailoverAware:
{code:java}
@Plugin(name = "CustomJDBC", category = Core.CATEGORY_NAME, elementType =
Appender.ELEMENT_TYPE, printObject = true)
public final class CustomJdbcAppender extends
CustomAbstractDatabaseAppender<CustomJdbcDatabaseManager> {
//Code omitted
}
{code}
{code:java}
public abstract class CustomAbstractDatabaseAppender<T extends
CustomAbstractDatabaseManager> extends AbstractAppender implements
FailoverAware {
//Code omitted
@Override
public List<LogEvent> onFailover(LogEvent causalEvent) {
return manager.onFailover(causalEvent);
}
@Override
public List<LogEvent> onFailover(LogEvent causalEvent, Exception
causalException) {
return manager.onFailover(causalEvent, causalException);
}
@Override
public List<LogEvent> onFailoverAppenderBeforeStopException(Exception
causalEvent) {
return manager.onFailoverAppenderBeforeStopException(causalEvent);
}
@Override
public void onFailoverAppenderBeforeStop() {
manager.onFailoverAppenderBeforeStop();
}
}
{code}
{code:java}
public final class CustomJdbcDatabaseManager extends
CustomAbstractDatabaseManager {
//Code omitted
}
{code}
{code:java}
public abstract class CustomAbstractDatabaseManager extends AbstractManager
implements Flushable, FailoverAware {
//Code omitted
@Override
public List<LogEvent> onFailover(LogEvent causalEvent) {
return prepareFailoverEvents(causalEvent);
}
@Override
public List<LogEvent> onFailover(LogEvent causalEvent, Exception
causalException) {
return prepareFailoverEvents(causalEvent);
}
@Override
public void onFailoverAppenderBeforeStop() {
flush();
}
@Override
public List<LogEvent> onFailoverAppenderBeforeStopException(Exception
causalException) {
return prepareFailoverEvents(null);
}
private synchronized List<LogEvent> prepareFailoverEvents(/* Nullable */
LogEvent causalEvent) {
if (bufferSize > 0) {
List<LogEvent> events = new ArrayList<>(buffer);
if (causalEvent != null && !events.contains(causalEvent)) {
events.add(causalEvent);
}
buffer.clear();
return events;
} else if(causalEvent != null) {
return Arrays.asList(causalEvent);
} else {
return Collections.emptyList();
}
}
}
{code}
> On failover from JDBC appender, contents of buffer are not written to
> failover appender
> ---------------------------------------------------------------------------------------
>
> Key: LOG4J2-1949
> URL: https://issues.apache.org/jira/browse/LOG4J2-1949
> Project: Log4j 2
> Issue Type: Bug
> Affects Versions: 2.8.2
> Reporter: Chris Slater
>
> With the following sample config:
> {code:xml}
> <JDBC name="AuditLog-Database" tableName="audit"
> ignoreExceptions="false" bufferSize="250">
> <ConnectionFactory class="com.example.MyConnectionFactory"
> method="getConnection"/>
> <Column name="D_TIME" isEventTimestamp="true"/>
> <ColumnMapping name="AUDIT_ID" pattern="%X{AUDIT.ID}"/>
> <ColumnMapping name="AUDIT_MESSAGE" pattern="%X{AUDIT.MESSAGE}"/>
> </JDBC>
> <RollingFile name="AuditLog-Failover-File"
> fileName="${sys:env.home}/log/failover.log"
>
> filePattern="${sys:env.home}/log/failover.%d{yyyy-MM-dd}.log"
> ignoreExceptions="false">
> <PatternLayout>
> <pattern>%d{ISO8601}| %X{AUDIT.ID}|
> %X{AUDIT.MESSAGE}%n</pattern>
> </PatternLayout>
> <Policies>
> <TimeBasedTriggeringPolicy />
> </Policies>
> </RollingFile>
> <Failover name="AuditLog" primary="AuditLog-Database">
> <Failovers>
> <AppenderRef ref="AuditLog-Failover-File"/>
> </Failovers>
> </Failover>
> {code}
> If the database fails, the contents of the buffer are not written to the
> failover log file.
--
This message was sent by Atlassian JIRA
(v6.4.14#64029)