[ 
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)

Reply via email to