代码之家  ›  专栏  ›  技术社区  ›  Andrea Girardi

登录关系数据库并扩展日志接口

  •  1
  • Andrea Girardi  · 技术社区  · 8 年前

    为了满足Spring MVC应用程序的新安全性要求,除了正常的日志消息之外,我还需要记录额外的信息,如userId,并将它们存储到关系数据库中。

    我想减少更改的数量(如果可能的话),添加一个appender和一个新的日志接口实现。我正在使用log4j 2.10并查看文档,可以扩展AbstractAppender来添加appender,但我不知道如何扩展日志接口来实现以下功能:

    logger.info(userName, message);
    

    有什么线索吗?

    1 回复  |  直到 8 年前
        1
  •  0
  •   Andrea Girardi    8 年前

    一种可能是在log4j2 xml配置中添加JDBC appender,但Log4j是在Spring之前初始化的,因此数据源在运行时不可用,因此唯一的解决方案是以编程方式添加appender。当然,可以使用log4j2jdbc appender,但这种方法允许使用spring。属性文件,以根据我的配置文件在中使用正确的环境设置覆盖spring应用程序上下文。

    这是在应用程序上下文xml上定义的数据源:

    <!--  ############ SQLSERVER DATABASE SECTION ############ -->
    <bean id="dataSourceMSSqlServer" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
        <property name="url" value="jdbc:sqlserver://${sqlserver.hostname};databaseName=${sqlserver.database};" />
        <property name="username" value="${sqlserver.user}" />
        <property name="password" value="${sqlserver.pass}" />
    </bean>
    

    这允许我为每个环境配置不同的数据库。 这是我要记录条目的表:

    [Id] [int] IDENTITY(1,1) NOT NULL,
    [CreatedTimeStamp] [datetimeoffset](7) NOT NULL,
    [Level] [int] NOT NULL,
    [Source] [nvarchar](max) NULL,
    [Message] [nvarchar](max) NULL,
    [Content] [nvarchar](max) NULL,
    [ProductName] [nvarchar](max) NULL,
    [Version] [nvarchar](max) NULL,
    [LogType] [int] NOT NULL DEFAULT ((0)),
    [AuditEventType] [int] NULL,
    [UserId] [nvarchar](128) NULL,
    

    计划是创建一个Springbean,注入DataSourcebean,并在中动态添加JDBC Appender配置 @施工后 方法

    package com.afm.web.utility;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import javax.annotation.PostConstruct;
    import javax.sql.DataSource;
    
    import org.apache.logging.log4j.Level;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.core.Appender;
    import org.apache.logging.log4j.core.LoggerContext;
    import org.apache.logging.log4j.core.appender.db.ColumnMapping;
    import org.apache.logging.log4j.core.appender.db.jdbc.ColumnConfig;
    import org.apache.logging.log4j.core.appender.db.jdbc.ConnectionSource;
    import org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender;
    import org.apache.logging.log4j.core.config.AppenderRef;
    import org.apache.logging.log4j.core.config.Configuration;
    import org.apache.logging.log4j.core.config.LoggerConfig;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    @Component
    public class JDBCLog {
    
        @Autowired
        private DataSource dataSourceMSSqlServer;
    
        // Inner class
        class Connect implements ConnectionSource {
    
        private DataSource dsource;
    
        public Connect(DataSource dsource) {
            this.dsource = dsource;
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            return this.dsource.getConnection();
        }
    
        }
    
        public JDBCLog() {}
    
        @PostConstruct
        private void init(){
    
        System.out.println("####### JDBCLog init() ########");      
        final LoggerContext ctx = (LoggerContext) LogManager.getContext(false); 
        final Configuration config = ctx.getConfiguration();
    
        // Here I define the columns I want to log. 
        ColumnConfig[] columnConfigs = new ColumnConfig[] {
            ColumnConfig.newBuilder()
                    .setName("CreatedTimeStamp")
                    .setPattern(null)
                    .setLiteral(null)
                    .setEventTimestamp(true)
                    .setUnicode(false)
                    .setClob(false).build(),
            ColumnConfig.newBuilder()
                    .setName("Source")
                    .setPattern("%K{className}")
                    .setLiteral(null)
                    .setEventTimestamp(false)
                    .setUnicode(false)
                    .setClob(false).build(),
            ColumnConfig.newBuilder()
                    .setName("Level")
                    .setPattern("%level")
                    .setLiteral(null)
                    .setEventTimestamp(false)
                    .setUnicode(false)
                    .setClob(false).build(),
            ColumnConfig.newBuilder()
                    .setName("Message")
                    .setPattern("%K{message}")
                    .setLiteral(null)
                    .setEventTimestamp(false)
                    .setUnicode(false)
                    .setClob(false).build(),
            ColumnConfig.newBuilder()
                    .setName("Content")
                    .setPattern("%K{exception}")
                    .setLiteral(null)
                    .setEventTimestamp(false)
                    .setUnicode(false)
                    .setClob(false).build(),
            ColumnConfig.newBuilder()
                    .setName("ProductName")
                    .setPattern(null)
                    .setLiteral("'DHC'")
                    .setEventTimestamp(false)
                    .setUnicode(false)
                    .setClob(false).build(),
            ColumnConfig.newBuilder()
                    .setName("Version")
                    .setPattern(null)
                    .setLiteral("'1.0'")
                    .setEventTimestamp(false)
                    .setUnicode(false)
                    .setClob(false).build(),
            ColumnConfig.newBuilder()
                    .setName("AuditEventType")
                    .setPattern("%K{eventId}")
                    .setLiteral(null)
                    .setEventTimestamp(false)
                    .setUnicode(false)
                    .setClob(false).build(),
            ColumnConfig.newBuilder()
                    .setName("UserId"
                    .setPattern("%K{userId}")
                    .setLiteral(null)
                    .setEventTimestamp(false)
                    .setUnicode(false)
                    .setClob(false).build(),
            ColumnConfig.newBuilder()
                    .setName("LogType")
                    .setPattern("%K{logType}")
                    .setLiteral(null)
                    .setEventTimestamp(false)
                    .setUnicode(false)
                    .setClob(false).build()
            };
    
        Appender jdbcAppender = JdbcAppender.newBuilder()
            .setBufferSize(0)
                    .setColumnConfigs(columnConfigs)
                    .setColumnMappings(new ColumnMapping[]{})
                    .setConnectionSource(new Connect(dataSourceMSSqlServer))
                    .setTableName("dhc.LogItems")
                    .withName("databaseAppender")
                    .withIgnoreExceptions(true)
                    .withFilter(null)
                    .build();
    
        jdbcAppender.start();
        config.addAppender(jdbcAppender);
    
        // Create an Appender reference.
        // @param ref The name of the Appender.
        // @param level The Level to filter against.
        // @param filter The filter(s) to use.
        // @return The name of the Appender.
        AppenderRef ref= AppenderRef.createAppenderRef("JDBC_Appender", null, null);
            AppenderRef[] refs = new AppenderRef[] {ref};
    
            /*
             * Factory method to create a LoggerConfig.
             *
             * @param additivity true if additive, false otherwise.
             * @param level The Level to be associated with the Logger.
             * @param loggerName The name of the Logger.
             * @param includeLocation whether location should be passed downstream
             * @param refs An array of Appender names.
             * @param properties Properties to pass to the Logger.
             * @param config The Configuration.
             * @param filter A Filter.
             * @return A new LoggerConfig.
             * @since 2.6
             */
            LoggerConfig loggerConfig = LoggerConfig.createLogger(
                    false, Level.DEBUG, "JDBC_Logger", null, refs, null, config, null);        
            loggerConfig.addAppender(jdbcAppender, null, null);
    
            config.addLogger("JDBC_Logger", loggerConfig);       
            ctx.updateLoggers();  
    
            System.out.println("####### JDBCLog init() - DONE ########");  
    
        }
    
        public DataSource getDataSource() {
        return dataSourceMSSqlServer;
        }
    
        public void setDataSource(DataSource dataSourceMSSqlServer) {
        this.dataSourceMSSqlServer = dataSourceMSSqlServer;
        }     
    
    }
    

    此时,可以通过以下方式从代码调用记录器:

    Logger jdbcLogger = LogManager.getContext(false).getLogger("JDBC_Logger"); 
    jdbcLogger.info(new StringMapMessage()
        .with("eventId", AuditEventType.Logger_General.toString())
        .with("exception", "")
        .with("userId", "TESTUSER")
        .with("message", "TEST!!")
        .with("className", 
            this.getClass().getPackage().toString().replaceAll("package ", "") 
            + "." + this.getClass().getSimpleName() 
            + "." + new Object() {}.getClass().getEnclosingMethod().getName())
    );
    

    如果您在Servlet 2.5 web应用程序中使用Log4j,或者如果您使用isLog4jAutoInitializationDisabled上下文参数禁用了自动初始化,则必须配置 Log4jServletContextListener Log4jServletFilter 在部署描述符中或以编程方式。筛选器应匹配任何类型的所有请求。监听器应该是应用程序中定义的第一个监听器,过滤器应该是应用程序中定义和映射的第一个过滤器。这可以使用以下web轻松完成。xml代码:

    <listener>
        <listener-class>
            org.apache.logging.log4j.web.Log4jServletContextListener
        </listener-class>
    </listener>
    
    <filter>
        <filter-name>log4jServletFilter</filter-name>
        <filter-class>
            org.apache.logging.log4j.web.Log4jServletFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <filter-name>log4jServletFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>REQUEST</dispatcher>
        <dispatcher>FORWARD</dispatcher>
        <dispatcher>INCLUDE</dispatcher>
        <dispatcher>ERROR</dispatcher>
        <!-- 
            Servlet 3.0 w/ disabled auto-initialization only; 
            not supported in 2.5 
        -->
        <dispatcher>ASYNC</dispatcher>
    </filter-mapping>
    

    必须将此依赖项添加到pom。xml:

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-web</artifactId>
        <version>2.10.0</version>
    </dependency>
    

    当然,如果您尚未包括在内,请将其添加到组件扫描到Spring应用程序上下文中:

    <context:component-scan base-package="com.afm.web.utility" />