自定义将管理记录器消息的appender

时间:2014-03-11 08:35:20

标签: java logback log4j2

我想创建三个appender: file console database 。此外,我在数据库中存储了三个变量: info 错误警告,其值对应于上述一个appender。

所以,当我运行以下语句时:

Logger.info ("bla bla bla")

我需要动态地在数据库中获取 info 的当前值,并在右侧的appender上显示消息(文件 console 或< EM>数据库)。

使用 Logback

1。我需要创建一个完整的DBAppender类(如this),因为我不想将信息存储在三个不同的表中(仅限于一个)。

2。由于filters,捕获 info 的数据库值似乎很简单。所以我可以在每个appender中包含过滤器,并根据 info 的值,我可以决定是否使用“当前的appender”。

使用 Log4j 2

1。我可以使用JDBCAppender(而不必创建新类)。

2. 如何创建自定义filter,以便我在数据库中获取 info 的值,并帮助我决定是否应该或者不应该使用其中一个appender( file console database )?

问题是:

有没有办法让“第一点Logback”更容易,或者执行“Log4j2的第二点”

提前致谢。

2 个答案:

答案 0 :(得分:2)

Log4j2支持过滤器,我相信与Logback非常相似。听起来您正在寻找基于级别的过滤,您可以使用ThresholdFilter

执行此操作

或许您正在寻找RoutingAppender之类的内容(请参阅extended example的常见问题解答)?这允许您配置多个appender并根据ThreadContext映射动态地将日志事件路由到不同的appender。

答案 1 :(得分:0)

最后我找到了解决问题的方法。在 logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>

   <!-- Loads the datasource properties file -->
   <property resource="datasource.properties" />

   <!-- Directory where log files will be stored -->
   <property name="LOG_DIRECTORY" value="/tmp" />

   <!-- Appenders -->
   <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
      <filter class="com.myproject.logback.ConsoleFilter" />
      <encoder>
         <pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level.%msg - %class.%method : %line]%n</pattern>
      </encoder>
   </appender>

   <appender name="database" class="com.myproject.logback.CustomDBAppender">
      <filter class="com.myproject.logback.DBFilter" />
      <connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
         <dataSource class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <driverClass>${jdbc.driverClass}</driverClass>
            <jdbcUrl>${jdbc.jdbcUrl}</jdbcUrl>
            <databaseName>${jdbc.databaseName}</databaseName>
            <user>${jdbc.user}</user>
            <password>${jdbc.password}</password>
        </dataSource>
     </connectionSource>
  </appender>

  <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
     <filter class="com.myproject.logback.FileFilter" />
     <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!-- Rollover daily -->
        <fileNamePattern>${LOG_DIRECTORY}/mylog-%d{yyyy-MM-dd}.txt</fileNamePattern>
        <!-- Keep 10 days of history -->
        <maxHistory>10</maxHistory>
     </rollingPolicy>
     <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%-5level.%msg - %class.%method : %line]%n</pattern>
     </encoder>
  </appender>

  <!-- Logger for my project -->
  <logger name="com.myproject" additivity="false" level="all">
     <appender-ref ref="console" />
     <appender-ref ref="database" />
     <appender-ref ref="file" />
  </logger>

  <root level="info">
     <appender-ref ref="console" />
  </root>

</configuration>

现在 CustomDBAppender.java

public class CustomDBAppender extends DBAppenderBase<ILoggingEvent> {

    protected String insertSQL;

    // Indices of the fields of the table in which the logging information is stored
    private static final int EVENTTIME_INDEX     = 1;
    private static final int MESSAGE_INDEX       = 2;
    private static final int LOGGER_INDEX        = 3;
    private static final int LEVEL_INDEX         = 4;
    private static final int CALLER_CLASS_INDEX  = 5;
    private static final int CALLER_METHOD_INDEX = 6;
    private static final int CALLER_LINE_INDEX   = 7;
    private static final int TRACE_INDEX         = 8;

    static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance();

    @Override
    public void append (ILoggingEvent eventObject) {

       Connection connection = null;
       PreparedStatement insertStatement = null;
       try {
          connection = connectionSource.getConnection();
          connection.setAutoCommit (false);

          insertStatement = connection.prepareStatement (getInsertSQL());

          // Inserting the event in database
          synchronized (this) {
             subAppend (eventObject, connection, insertStatement);
          }
          secondarySubAppend (eventObject, connection, 1);

          connection.commit();

       } catch (Throwable sqle) {
          addError("problem appending event", sqle);
       } finally {
          DBHelper.closeStatement (insertStatement);
          DBHelper.closeConnection (connection);
       }
    }

    @Override
    protected Method getGeneratedKeysMethod() {
       return null;
    }

    @Override
    protected String getInsertSQL() {
       return insertSQL;
    }

    @Override
    protected void secondarySubAppend (ILoggingEvent eventObject,
                       Connection connection, long eventId) throws Throwable {}

    @Override
    public void start() {
       insertSQL = CustomDBAppender.buildInsertSQL();
       super.start();
    }

    @Override
    protected void subAppend (ILoggingEvent event, Connection connection,
                      PreparedStatement insertStatement) throws Throwable {

       bindLoggingEventWithInsertStatement (insertStatement, event);
       bindCallerDataWithPreparedStatement (insertStatement, event.getCallerData());

       int updateCount = insertStatement.executeUpdate();
       if (updateCount != 1) {
          addWarn("Failed to insert loggingEvent");
       }
    }

    void bindCallerDataWithPreparedStatement (PreparedStatement stmt,
            StackTraceElement[] callerDataArray) throws SQLException {

       StackTraceElement caller = extractFirstCaller (callerDataArray);

       stmt.setString (CALLER_CLASS_INDEX, caller.getClassName());
       stmt.setString (CALLER_METHOD_INDEX, caller.getMethodName());
       stmt.setString (CALLER_LINE_INDEX, Integer.toString (caller.getLineNumber()));
    }

    void bindLoggingEventWithInsertStatement (PreparedStatement stmt,
            ILoggingEvent event) throws SQLException {

       stmt.setTimestamp (EVENTTIME_INDEX, new Timestamp (event.getTimeStamp()));
       stmt.setString (MESSAGE_INDEX, event.getFormattedMessage());
       stmt.setString (LOGGER_INDEX, event.getLoggerName());
       stmt.setString (LEVEL_INDEX, event.getLevel().toString());

       if (event.getThrowableProxy() != null && 
           event.getThrowableProxy().getStackTraceElementProxyArray() != null)
          stmt.setString (TRACE_INDEX, ThrowableProxyUtil.asString (event.getThrowableProxy()));
       else
          stmt.setString (TRACE_INDEX, null);
    }

    private static String buildInsertSQL () {

       return "INSERT INTO mylogtable "
            + " (eventtime, message, logger, level, callerclass, callermethod, callerline, trace) "
            + "VALUES "
            + "(?, ?, ?, ?, ?, ?, ?, ?)";
    }

    private StackTraceElement extractFirstCaller (StackTraceElement[] callerDataArray) {

       StackTraceElement caller = EMPTY_CALLER_DATA;
       if (hasAtLeastOneNonNullElement (callerDataArray))
          caller = callerDataArray[0];

       return caller;
    }

    private boolean hasAtLeastOneNonNullElement (StackTraceElement[] callerDataArray) {
       return callerDataArray != null && callerDataArray.length > 0 && callerDataArray[0] != null;
    }
 }

每个过滤器都有类似的结构,因此我只显示一个 FileFilter.java

public class FileFilter extends Filter<ILoggingEvent> {

   @Override
   public FilterReply decide (ILoggingEvent event) {

      try {
         // I have in a database table something like this:
         //    Level   -   Appender
         //    ERROR         FILE
         //    DEBUG        CONSOLE
         //    ...

         // Here I check with a service if "the actual logger level"
         // [event.getLevel()] in the previous database table, is set
         // to record their messages with the "current appender"

         if (thePreviousCondition == true)
            return FilterReply.ACCEPT;

         return FilterReply.DENY;

      } catch (Exception e) {
         return FilterReply.DENY;
      }
   }
}

前面的代码是“我的真实代码”的简化,主要是因为我在 CustomDBAppender 每个记录器级别有一个表,而不是只有一个“常规表”。此外,我使用 REDIS 来缓存Level-Appender的值,以避免始终在数据库中搜索。

此外,我只在我的应用程序中定义了4个级别: ERROR WARN INFO DEBUG 。因此,通过枚举,我执行Logback级别与应用程序级别之间的等效。

然而,这是帮助我做我想要的事情的骨架。