APACHE LOG4J™ 2 学习笔记-log4j2 环境部署到各种类型输出+maven\mysql\滚动文件\控制台\异步\过滤器

目录

前言

SpringBoot 配置

普通项目配置

1、maven的pom.xml配置Log4j2的依赖

2、创建log4j2.xml

3、编写log4j2.xml的一般形式

4、将日志以某种格式输出到控制台及测试方法

5、将日志输出到文件

6、以输出到控制台为例,编写异步日志(当然对于其他类型的日志也是一样的)

7、使用过滤器输出日志

8、在maven/ssh环境中将日志输出到数据库(这里是mysql,用到了数据库主键的自增)

9、日志保存到数据库(必看)的最佳实践-log4j2的启动在spring加载配置文件之前

10、类中声明日志的最佳实践

11、查看 Spring 日志最佳时间-关键是开启 debug模式



前言

本文将展示在maven/spring+hibernate环境中实现log4j2日志的输出,包括分级输出到控制台、日志(按日志文件大小、日期分割、限定文件个数)、数据库。

Log4j2可以实现对log4j、slf4j、java自带日志的兼容。

Log4j2的官方文档地址如下

http://logging.apache.org/log4j/2.x/manual/appenders.html#FailoverAppender

SpringBoot 配置

参考:Springboot整合log4j2日志全解 - 上帝爱吃苹果-Soochow - 博客园

* 去除springboot默认的依赖

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-web</artifactId>  
    <exclusions><!-- 去掉springboot默认配置 -->  
        <exclusion>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-logging</artifactId>  
        </exclusion>  
    </exclusions>  
</dependency> 

* 增加独立的pom依赖

<dependency> <!-- 引入log4j2依赖 -->
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-log4j2</artifactId>
		</dependency>

但是问题没有这么简单,有些简介依赖的项目需要 log4j,比如 durid,那么需要加上下面这段,在不修改代码的情况下 更多可参考 Slf4j与log4j及log4j2的关系及使用方法

<!-- 添加log4j2依赖  - start-->
        <!-- log4j-1.2-api为log4j和log4j2的连接包。 -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-1.2-api</artifactId>
            <version>2.3</version>
        </dependency>
        <!--log4j2核心jar包-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.3</version>
            <!-- <scope>provided</scope> -->
        </dependency>

 * 配置log4j2

参考本文

指定路径和级别 logging.config

logging :
  level :
    org :
      springframework : INFO
  config: classpath:log4j2.xml

*引入 lombok

<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
            <optional>true</optional>
        </dependency>

使用 @slfj4 注解

import lombok.extern.slf4j.Slf4j;
@Slf4j

log.info("111");
        log.error("111{}","测试");

普通项目配置

1、maven的pom.xml配置Log4j2的依赖

<!-- 日志框架声明:http://blog.csdn.net/edward0830ly/article/details/8250412 http://blog.csdn.net/ziruobing/article/details/3919501 http://blog.csdn.net/autfish/article/details/51203709-->
		<dependency>
			<groupId>org.apache.logging.log4j</groupId>
			<artifactId>log4j-core</artifactId>
			<version>2.7</version>
		</dependency>
		<dependency>
		    <groupId>org.apache.logging.log4j</groupId>
		    <artifactId>log4j-api</artifactId>
		    <version>2.7</version>
		</dependency>
		
		<!-- log4j2-链接数据库 -->
		<dependency>
		    <groupId>commons-pool</groupId>
		    <artifactId>commons-pool</artifactId>
		    <version>1.6</version>
		</dependency>

2、创建log4j2.xml

3、编写log4j2.xml的一般形式

在log4j2.xml文件中我们需要做出这样一些配置。

·日志的名字是什么,比如root日志是根日志,可以在调用的时候直接声明调用根日志,也可以任意命名一个日志去调用,当然,跟日志也可以引用自定义日志。

·日志的级别是什么,比如是info或者warn,也可以使用过滤器,让不同的级别的日志输出到不同的日志中去。

·日志输出到什么地方,比如是控制台,日志文件或者是数据库。

·同步输出日志还是异步输出日志。

·输出日志的格式,比如仅仅显示日期和信息还是类名和方法名等。

·在配置文件中配置常量。

·高级用法,以及以json格式描述的log4j.xml(本文不涉及,更多介绍请参考官方文档)

4、将日志以某种格式输出到控制台及测试方法

log4j2.xml内容,<Appenders>决定日志以哪种格式输出到什么地方,<Loggers>则约定了被具体代码应用的日志的名字,通过 <AppenderRef>和<Appenders>联系起来

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"  monitorInterval="300"><!-- 每隔300秒重新读取配置文件,对web应用很实用 -->
    <Appenders>
    	<!-- 日志输出格式 :<Console是控制台,<File是文件,<RollingRandomAccessFile按时间和文件大小生成多个日志,<JDBC是数据库,<Async异步-->  
    	<!-- Console 输出到控制台及格式--> 
        <Console name="toConsole" target="SYSTEM_OUT"> 
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t]  %F %M %l- %-5level %logger{36} %msg%n" />  
        </Console> 
    </Appenders>  
    
    <Loggers><!-- 日志类别 --> 
    	<!-- 定义根日志类别  查看level http://blog.csdn.net/techq/article/details/6636287--> 
        <Root level="trace">  
            <AppenderRef ref="toConsole" />
        </Root>
        
        <!-- 输出到控制台 -->
    	<Logger name="mylog" additivity="false" level="TRACE"><!--additivity="false" 不再输出父级日志  -->
    		<AppenderRef ref="toConsole" />
    	</Logger>
    </Loggers> 
</Configuration>  
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;


测试方法-根日志输出到控制台

/**
	 * 测试root日志打印到控制台
	 */
	@Test
	public void testLog4j2(){
		Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);  
	    logger.trace("trace level");  
	    logger.debug("debug level");  
	    logger.info("info level");  
	    logger.warn("warn level");  
	    logger.error("error level");  
	    logger.fatal("fatal level");  
	}

测试方法-name为“mylog”的Logger输出到控制台-additivity=“false”则跟日志不再输出,否则根日志默认会再次输出一次

/**
	 * 测试自定义日志打印到控制台
	 */
	@Test
	public void testLog4j2SC(){
	    Logger logger = LogManager.getLogger("mylog");  
	    logger.trace("trace level");  
	    logger.debug("debug level");  
	    logger.info("info level");  
	    logger.warn("warn level");  
	    logger.error("error level");  
	    logger.fatal("fatal level");
	}


日志格式和输出内容比对如下

<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t]  %F %M %l- %-5level %logger{36} %msg%n" />  

2017-01-16 22:20:11.224 [main]  TestForAll.java testLog4j2 com.bestcxx.mavenstu.mavenssh.util.TestForAll.testLog4j2(TestForAll.java:14)- TRACE  trace level

5、将日志输出到文件

输出到文件有2种应用模式,1是直接输出到一个文件,2是限定日志文件大小、名字和总数来滚动输出,第2种也是使用较为广泛的

·直接输出到一个文件

log4j2.xml内容

 	<!-- 日志输出格式 :<Console是控制台,<File是文件,<RollingRandomAccessFile按时间和文件大小生成多个日志,<JDBC是数据库,<Async异步-->  
    	 <!-- File 输出到文件及格式 -->
        <File name="toFile" fileName="D://a/log/mavenssh.log">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />  
        </File> 
    </Appenders>  
    
    <Loggers><!-- 日志类别 --> 
    	<!-- 定义根日志类别  查看level http://blog.csdn.net/techq/article/details/6636287--> 
        <Root level="trace">  
            <AppenderRef ref="toFile" />
        </Root>
        <!-- 输出到文件 -->
    	<Logger name="mylogtofile" additivity="false" level="TRACE"><!--additivity="false" 不再输出父级日志  -->
    		<AppenderRef ref="toFile" />
    	</Logger> 
        
    </Loggers> 
</Configuration>  


测试代码

/**
	 * 自动以日志输出到文件
	 */
	@Test
	public void testLog4j2SF(){
		Logger logger = LogManager.getLogger("mylogtofile");  
	    logger.trace("trace level");  
	    logger.debug("debug level");  
	    logger.info("info level");  
	    logger.warn("warn level");  
	    logger.error("error level");  
	    logger.fatal("fatal level");
	}

·将日志按照一定的命名格式、限定日志大小和日志数量滚动输出到日志文件中

log4j2.xml内容,这里我们增加了常量,而且在调用测试的代码中,使用的不是root或者logger名字,而是类名.class,你会发现系统会在找不到对应的Logger时自动使用

Root这个日志配置

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"  monitorInterval="300"><!-- 每隔300秒重新读取配置文件,对web应用很实用 -->
    <properties><!-- 定义常量 -->
        <property name="LOG_HOME">D://a/log</property>
        <property name="FILE_NAME">mavenssh2</property>  
    </properties>  
    
    <Appenders>
    	<!-- 日志输出格式 :<Console是控制台,<File是文件,<RollingRandomAccessFile按时间和文件大小生成多个日志,<JDBC是数据库,<Async异步-->  
    	<!-- RollingRandomAccessFile 
        TimeBasedTriggeringPolicy interval="1"一个最小时间单位生成一个文件
        SizeBasedTriggeringPolicy size="1 MB" 当文件大小超过1MB生成一个日志文件,你可以写成0.001MB实验一下效果
        DefaultRolloverStrategy max="2" 同一个时间节点最多生成2个日志文件,否则后面的覆盖前面的,如1分钟10MB,允许2个,则最终是第9MB和第10MB的日志存在 时间节点-index(1,2)
         -->
        <RollingRandomAccessFile name="toFileByRoll"  
            fileName="${LOG_HOME}/${FILE_NAME}.log"  
            filePattern="${LOG_HOME}/${date:yyyy-MM}/${FILE_NAME}-%d{yyyy-MM-dd HH-mm}-%i.log">  
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />  
            <Policies>  
                <TimeBasedTriggeringPolicy interval="1" />
                <SizeBasedTriggeringPolicy size="0.001 MB" />  
            </Policies>  
            <DefaultRolloverStrategy max="2" />  
        </RollingRandomAccessFile>
    </Appenders>  
    
    <Loggers><!-- 日志类别 --> 
    	<!-- 定义根日志类别  查看level http://blog.csdn.net/techq/article/details/6636287--> 
        <Root level="trace">  
            <AppenderRef ref="toFileByRoll" />
        </Root>
        <!-- 按照时间节点和文件大小滚动生成日志 -->
    	<Logger name="mylogtofileroll" additivity="false" level="TRACE"><!--additivity="false" 不再输出父级日志  -->
    		<AppenderRef ref="toFileByRoll" />
    	</Logger>  
    </Loggers> 
</Configuration>  


测试代码

/**
	 * 自动以日志输出到文件
	 * 滚动输出
	 */
	@Test
	public void testLog4j2SFR(){
		//Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);//根日志
		//Logger logger = LogManager.getLogger("mylogtofileroll");//自定义日志
		Logger logger = LogManager.getLogger(TestForAll.class);//以类名作为日志的名字
		
	    logger.trace("trace level");  
	    logger.debug("debug level");  
	    logger.info("info level");  
	    logger.warn("warn level");  
	    logger.error("error level");  
	    logger.fatal("fatal level");
	}
	

效果

6、以输出到控制台为例,编写异步日志(当然对于其他类型的日志也是一样的)

原理就是,如果有多个日志需要输出,则可以将这多个日志统一于<Appenders>下的<Async,然后被Root日志引用或者被自定义日志引用

例子为两个输出到控制台的格式定义被异步输出

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"  monitorInterval="300"><!-- 每隔300秒重新读取配置文件,对web应用很实用 -->    
    <Appenders>
    	<!-- Console 输出到控制台及格式--> 
        <Console name="toConsole" target="SYSTEM_OUT"> 
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t]  %F %M %l- %-5level %logger{36} %msg%n" />  
        </Console> 
        <Console name="toConsole2" target="SYSTEM_OUT"> 
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS}%msg[2]%n" />  
        </Console> 
        
        <!-- 异步日志 -->
        <Async name="Async">  
            <AppenderRef ref="toConsole" /> 
            <AppenderRef ref="toConsole2"/> 
        </Async> 
    </Appenders>  
    
    <Loggers><!-- 日志类别 --> 
    	<!-- 定义根日志类别  查看level http://blog.csdn.net/techq/article/details/6636287--> 
        <Root level="trace">  
            <AppenderRef ref="Async" />
        </Root>
        
        <!-- 使用异步 -->
        <Logger name="asyncConsole" additivity="false" level="trace">
        	<AppenderRef ref="Async"/>
        </Logger>
    </Loggers> 
</Configuration>  


测试代码

/**
	 * 异步输出日志
	 * 
	 */
	@Test
	public void testLog4j2asyncConsole(){
		Logger logger = LogManager.getLogger("asyncConsole");  
	    logger.trace("trace level");  
	    logger.debug("debug level");  
	    logger.info("info level");  
	    logger.warn("warn level");  
	    logger.error("error level");  
	    logger.fatal("fatal level");
	}


效果

7、使用过滤器输出日志

好处是,将制定级别的日志保留到指定位置,而不是全部的

log4j2.xml内容

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"  monitorInterval="300"><!-- 每隔300秒重新读取配置文件,对web应用很实用 -->    
    <Appenders>
    	<!-- Console 输出到控制台及格式-并使用过滤器 <Filters--> 
        <Console name="toConsoleFilter" target="SYSTEM_OUT"> 
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t]  %F %M %l- %-5level %logger{36} %msg%n" />  
        	<Filters>
        		<ThresholdFilter level="fatal" onMatch="DENY" onMismatch="NEUTRAL" /><!-- 高级别优先设置,NEUTRAL不影响低等级设置 -->
        		<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY" /> <!-- 低级别及以后遵循同样的标准-已经声明的高级别除外 -->  
            </Filters>  
        </Console> 
    </Appenders>  
    
    <Loggers><!-- 日志类别 --> 
    	<!-- 定义根日志类别  查看level http://blog.csdn.net/techq/article/details/6636287--> 
        <Root level="trace">  
            <AppenderRef ref="toConsoleFilter" />
        </Root>
        
        <!-- 使用过滤器 -->
        <Logger name="toConsoleFilter" level="trace" additivity="false">  
            <AppenderRef ref="toConsoleFilter" />
        </Logger>
    </Loggers> 
</Configuration>  


测试代码

/**
	 * 自动以日志输出到控制台
	 * 过滤器
	 * 日志级别是
	 * trace->debug->info->warn->error->fatal
	 * 本例中是
	 * 1、fatal 级别不输出
	 * 2、warn级别以及之后输出
	 * 综合是 warn之后以及fatal(不含fatal)级别之前输出
	 * 
	 */
	@Test
	public void testLog4j2ErrorNoFatal(){
		Logger logger = LogManager.getLogger("toConsoleFilter");  
	    logger.trace("trace level");  
	    logger.debug("debug level");  
	    logger.info("info level");  
	    logger.warn("warn level");  
	    logger.error("error level");  
	    logger.fatal("fatal level");
	}	 * 自动以日志输出到控制台
	 * 过滤器
	 * 日志级别是
	 * trace->debug->info->warn->error->fatal
	 * 本例中是
	 * 1、fatal 级别不输出
	 * 2、warn级别以及之后输出
	 * 综合是 warn之后以及fatal(不含fatal)级别之前输出
	 * 
	 */
	@Test
	public void testLog4j2ErrorNoFatal(){
		Logger logger = LogManager.getLogger("toConsoleFilter");  
	    logger.trace("trace level");  
	    logger.debug("debug level");  
	    logger.info("info level");  
	    logger.warn("warn level");  
	    logger.error("error level");  
	    logger.fatal("fatal level");
	}


效果是仅输出了warn和error级别的


8、在maven/ssh环境中将日志输出到数据库(这里是mysql,用到了数据库主键的自增)

首先,本文是在maven/ssh环境中运行的,所以需要在测试的时候需要加载配置文件的内容

需要读者具备ssh的基本知识,可以参考文章

http://blog.csdn.net/bestcxx/article/details/52975675

8.1 首先需要新建日志表

这里我使用的是自动建表,所以只新建了实体

package com.bestcxx.mavenstu.mavenssh.model;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.hibernate.annotations.GenericGenerator;

@SuppressWarnings("serial")
@Table(name="LOGGER")
@Entity(name="LOGGER")
public class LoggerModel implements Serializable{
	private long eventId;//EVENT_ID  日志id
	private Date eventDate;//EVENT_DATE -注意这里必须使用sqlDate
	private String level;//LEVEL       日志的级别
	private String logger;//LOGGER     日志内容
	private String message;//MESSAGE   其他信息
	private String throwable;//THROWABLE   异常抛出
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)//主键自增-mysql
	//@GeneratedValue(strategy = GenerationType.IDENTITY)
	//@GenericGenerator(name = "persistenceGenerator", strategy = "increment") 
	@Column(name="EVENT_ID",unique=true,nullable=false)
	public long getEventId() {
		return eventId;
	}
	public void setEventId(long eventId) {
		this.eventId = eventId;
	}
	@Column(name="EVENT_DATE")
	@Temporal(TemporalType.TIMESTAMP)
	public Date getEventDate() {
		return eventDate;
	}
	public void setEventDate(Date eventDate) {
		this.eventDate = eventDate;
	}
	@Column(name="LEVEL",nullable=false,length=10)
	public String getLevel() {
		return level;
	}
	public void setLevel(String level) {
		this.level = level;
	}
	@Column(name="LOGGER",nullable=true,length=100)
	public String getLogger() {
		return logger;
	}
	public void setLogger(String logger) {
		this.logger = logger;
	}
	@Column(name="MESSAGE",nullable=true,length=100)
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	@Column(name="THROWABLE",nullable=true,length=100)
	public String getThrowable() {
		return throwable;
	}
	public void setThrowable(String throwable) {
		this.throwable = throwable;
	}
	
	
	
	
}

8.2 编写log4j2,xml调用的方法类

下面的方法是直接从官网上复制的,不同之处在于原文中数据库 url、用户名和密码是写在代码中的,这不符合我们的实际,因而稍微做了修改,使得该类可以直接从配置文件中获取相关的值

ConnectionFactory.java

package com.bestcxx.mavenstu.mavenssh.util;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
 
import javax.sql.DataSource;
 
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnection;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
 
public class ConnectionFactory {
	final private static String  jdbcurl=CustomizedPropertyPlaceholderConfigurer.getContextProperty("jdbc.url");
	final private static String  jdbcusername=CustomizedPropertyPlaceholderConfigurer.getContextProperty("jdbc.username");
	final private static String  jdbcpassword=CustomizedPropertyPlaceholderConfigurer.getContextProperty("jdbc.password");
	
	private static interface Singleton {
        final ConnectionFactory INSTANCE = new ConnectionFactory();
    }
 
    private final DataSource dataSource;
 
    private ConnectionFactory() {
    	
       	Properties properties = new Properties();
        
        properties.setProperty("user", jdbcusername);
        properties.setProperty("password", jdbcpassword); // or get properties from some configuration file
 
        GenericObjectPool<PoolableConnection> pool = new GenericObjectPool<PoolableConnection>();
        DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
        		jdbcurl, properties
        );
        new PoolableConnectionFactory(
                connectionFactory, pool, null, "SELECT 1", 3, false, false, Connection.TRANSACTION_READ_COMMITTED
        );
 
        this.dataSource = new PoolingDataSource(pool);
    }
 
    public static Connection getDatabaseConnection() throws SQLException {
        return Singleton.INSTANCE.dataSource.getConnection();
    }
}


这样就需要新写一个类CustomizedPropertyPlaceholderConfigurer.java

这个类继承自PropertyPlaceholderConfigurer,而后者熟悉Spring的话一定可以认出来,其就是Spring用于加载常量配置文件的方法

可以参考Spring 加载标准属性值配置文件application.properties的两种方式

CustomizedPropertyPlaceholderConfigurer.java的内容

package com.bestcxx.mavenstu.mavenssh.util;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

/**
 * 自定义PropertyPlaceholderConfigurer返回properties内容
 * Spring的applicationContext.xml的
 * <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 * 需要改变为
 * <bean class="本类路径.CustomizedPropertyPlaceholderConfigurer">
 */

public class CustomizedPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {

	private static Map<String, String> ctxPropertiesMap;

	@Override
	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,Properties props) throws BeansException {
		super.processProperties(beanFactoryToProcess, props);
		ctxPropertiesMap = new HashMap<String, String>();
		
		for (Object key : props.keySet()) {
			String keyStr = key.toString();
			String value = props.getProperty(keyStr);
			ctxPropertiesMap.put(keyStr, value);
		}
	}

	public static String getContextProperty(String name) {
		return ctxPropertiesMap.get(name);
	}

}


然后,正如该类中的注释中声明的那样,需要修改一下 applicationContext.xml的获取配置*.properties文件的那部分代码

<!-- 定义受环境影响易变的变量 -->
	<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> -->
	<bean class="com.bestcxx.mavenstu.mavenssh.util.CustomizedPropertyPlaceholderConfigurer"><!-- 覆盖重写这个类,可以在java代码中获取加载文件的值key-value形式 -->
		<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
		<property name="ignoreResourceNotFound" value="true" />
		<property name="locations">
			<list>
				<!-- 数据库配置 -->
				<value>classpath:config/jdbc.properties</value>
			</list>
		</property>
	</bean>


jdbc.properties的内容也贴一下

#jdbc settings
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=*
jdbc.password=*
jdbc.initialSize=5
jdbc.maxActive=10

#hibernate settings
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.show_sql=false
hibernate.format_sql=false
hibernate.hbm2ddl.auto=update


这里涉及到 hibernaye.hbm2ddl.auto=update  表不存在则创建表,表已存在若存在改变则更新表,其应用也是在applicationContext.xml中

<!--  声明Hibernate 的 Session 工厂-->
	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
		<property name="dataSource" ref="dataSource"/>
		<!-- model里含有具体的实体类若干 -->
		<property name="packagesToScan" value="com.bestcxx.mavenstu.mavenssh.model"/>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">${hibernate.dialect}</prop>
				<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
				<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
				<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop><!--自动建表 http://www.cnblogs.com/feilong3540717/archive/2011/12/19/2293038.html -->
			</props>
		</property>
	</bean>


当然,如果你觉得这样很麻烦,可以手动建表,也可以把数据库url、用户名和密码写在代码中

8.3、然后是log4j2.xml的配置

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN"  monitorInterval="300"><!-- 每隔300秒重新读取配置文件,对web应用很实用 -->    
    <Appenders>
    	<!-- 日志输入到数据库 -->
        <!-- 必须先创建表结构,所以建议保留日志表实体,在项目启动时自动创建或更新表 -->
        <JDBC name="databaseAppender" tableName="LOGGER">
	      <ConnectionFactory class="com.bestcxx.mavenstu.mavenssh.util.ConnectionFactory" method="getDatabaseConnection" />
	      <!-- mysql 设置主键自增策略,对于数据库不支持的则开启下面这字段 
	      <Column name="EVENT_ID" literal="LOGGER_SEQUENCE.NEXTVAL" /> 
	      -->
	      <Column name="EVENT_DATE" isEventTimestamp="true" />
	      <Column name="LEVEL" pattern="%level" />
	      <Column name="LOGGER" pattern="%logger" />
	      <Column name="MESSAGE" pattern="%message" />
	      <Column name="THROWABLE" pattern="%ex{full}" />
       </JDBC>
    </Appenders>  
    
    <Loggers><!-- 日志类别 --> 
    	<!-- 定义根日志类别  查看level http://blog.csdn.net/techq/article/details/6636287--> 
        <Root level="trace">  
            <AppenderRef ref="databaseAppender" />
        </Root>
        
        <!-- 保存到数据库 -->
    	<Logger name="databaseAppender" additivity="false" level="INFO"><!--additivity="false" 不再输出父级日志  -->
    		<AppenderRef ref="databaseAppender" />
    	</Logger> 
    </Loggers> 
</Configuration>  

8.4、测试代码

package com.bestcxx.mavenstu.mavenssh.util;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@DirtiesContext
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:spring/applicationContext.xml"})
//@TransactionConfiguration(transactionManager = "defaultTransactionManager",defaultRollback=false)//事务管理  
@Rollback(false)
public class LoggerDaoImplTest {

	@Test
	public void testAddLogger(){
//		Logger logger = LogManager.getLogger(LoggerDaoImplTest.class); 
		
		Logger logger = LogManager.getLogger("databaseAppender");  
	    logger.trace("trace level");  
	    logger.debug("debug level");  
	    logger.info("info level");  
	    logger.warn("warn level");  
	    logger.error("error level");  
	    logger.fatal("fatal level");  
		
	}
	

}


效果

9、日志保存到数据库(必看)的最佳实践-log4j2的启动在spring加载配置文件之前

在上面的日志保存到数据库的例子中,由于测试的缘故,我们把日志的声明写在了方法体中,类似于下面的

	@Test
	public void testLog4j2(){
		Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);  
	

但是在实际的运用中,我们总是习惯于把日志对象声明为所在类的全局变量,也就是下面这种

@Controller
@SuppressWarnings("serial") 
public class PersonAction extends BaseAction<Person> {
	private Logger logger=LogManager.getLogger(PersonAction.class);


但是以目前的项目配置会出问题,会报空指针,经排查是由于log4j2在这种情况下,使用spring加载的配置文件来获取参数的时候,获取的参数为null,更直白的说就是log4j2要连接数据库的时候,spring还没有加载或者没有加载完配置文件,所以需要对代码进行修改,改动也非常小,java获取spring中配置文件 的工具类还是可以留着的,这不过log4j2连接数据库的用户名、密码、url采取直接从配置文件读取了,绕过了spring。修改ConnectionFactory.java 为如下即可

package com.bestcxx.mavenstu.mavenssh.util;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
 
import javax.sql.DataSource;
 
import org.apache.commons.dbcp.DriverManagerConnectionFactory;
import org.apache.commons.dbcp.PoolableConnection;
import org.apache.commons.dbcp.PoolableConnectionFactory;
import org.apache.commons.dbcp.PoolingDataSource;
import org.apache.commons.pool.impl.GenericObjectPool;
 
public class ConnectionFactory {
	/*final private static String  jdbcurl=CustomizedPropertyPlaceholderConfigurer.getContextProperty("jdbc.url");
	final private static String  jdbcusername=CustomizedPropertyPlaceholderConfigurer.getContextProperty("jdbc.username");
	final private static String  jdbcpassword=CustomizedPropertyPlaceholderConfigurer.getContextProperty("jdbc.password");
	*/
	
	private static interface Singleton {
        final ConnectionFactory INSTANCE = new ConnectionFactory();
    }
 
    private final DataSource dataSource;
 
    private static InputStream inStream = ConnectionFactory.class.getClassLoader().getResourceAsStream("config/jdbc.properties");
    private static Properties prop = new Properties();

	private ConnectionFactory() {

		try {
			prop.load(inStream);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String jdbcusername = prop.getProperty("jdbc.username");//数据库连接用户名
		String jdbcpassword = prop.getProperty("jdbc.password");//数据库连接密码
		String jdbcurl = prop.getProperty("jdbc.url");//数据库连接url

		Properties properties = new Properties();
		properties.setProperty("user", jdbcusername);
		properties.setProperty("password", jdbcpassword); // or get properties from some configuration file
 
        GenericObjectPool<PoolableConnection> pool = new GenericObjectPool<PoolableConnection>();
        DriverManagerConnectionFactory connectionFactory = new DriverManagerConnectionFactory(
        		jdbcurl, properties
        );
        new PoolableConnectionFactory(
            connectionFactory, pool, null, "SELECT 1", 3, false, false, Connection.TRANSACTION_READ_COMMITTED
        );
 
        this.dataSource = new PoolingDataSource(pool);
    }
 
    public static Connection getDatabaseConnection() throws SQLException {
        return Singleton.INSTANCE.dataSource.getConnection();
    }
}

jdbc.properties 的位置

config/jdbc.properties 作为常量其实可以保存到枚举类中

 private static InputStream inStream = ConnectionFactory.class.getClassLoader().getResourceAsStream(EnumUtil.COMMON_DATABASE_PROPERTIES.toString());

完整的ssh代码请查看 GitHub - Bestcxy/SSH-ajax-axis2-maven-log4j-redis: spring+hibernate+struts2+ajax+axis2+log4j2+redis 组合起来的项目

如果你还没有加入github 请先阅读 Eclipse 使用 github_bestcxx的博客-CSDN博客
 

10、类中声明日志的最佳实践

一般声明一个静态、final日志对象

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

下民两种写法从输出来看没有区别

private Logger logger=LogManager.getLogger(类名.class);
private Logger logger=LogManager.getLogger(this.getClass());

和其他类目结合,请参考 Slf4j与log4j及log4j2的关系及使用方法_Andrew_Yuan的博客-CSDN博客

11、查看 Spring 日志最佳时间-关键是开启 debug模式

> 由于 Spring 代码的日志都是有一个 debug模式判断,所有配置为debug才可以看到 Spring日志

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="debug" monitorInterval="300"><!-- 每隔300秒重新读取配置文件,对web应用很实用 -->
	<Appenders>
		<!-- 日志输出格式 :<Console是控制台,<File是文件,<RollingRandomAccessFile按时间和文件大小生成多个日志,<JDBC是数据库,<Async异步 -->
		<!-- Console 输出到控制台及格式 -->
		<Console name="toConsole" target="SYSTEM_OUT">
			<PatternLayout
				pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t]  %F %M %l- %-5level %logger{36} %msg%n" />
		</Console>
	</Appenders>
	
	<Loggers><!-- 日志类别 -->
		<!-- 定义根日志类别 查看level http://blog.csdn.net/techq/article/details/6636287 -->
		<Root level="debug">
			<AppenderRef ref="toConsole" />
		</Root>
	</Loggers>
</Configuration>


 


 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值