我想创建三个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的第二点”?
提前致谢。
答案 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级别与应用程序级别之间的等效。
然而,这是帮助我做我想要的事情的骨架。