1. junit
1.1 最小junit
<!--junit 测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
测试的时候仅使用
@Test
method(){...}
直接引入junit
即可。
说明:在spring中使用junit测试(如仅测试dao层),只需maven中加入两个依赖:
spring-test
junit
2. springMVC+Junit最小依赖
这里除了Spring以及其他模块所需要的jar包之外,还需要引入:
spring-test-4.2.3.RELEASE.jar
junit-4.10.jar
1.1 springmvc + junit
在maven
中如下即可(版本管理略):
<!--spring + springmvc 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<!-- junit单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
注意:上述排除了spring
自带的common-logging
,使用了log4j。关于log4j,需要桥接过来。
spring-core
依赖的日志为common-logging
,将其排除后,要使用common-logging接口,将其引入其他接口。
(1)对于jcl转log4j,有以下过程
Component
|
| log to Apache Commons Logging
V
(1)jcl-over-slf4j.jar
— (redirect) —> SLF4j —>
(2)slf4j-log4j12-version.jar
—>
(3)log4j.jar
—> 输出日志
使用log4j依赖(1.x版本下示例)
<!-- 日志,属性文件放置可识别目录 -->
<!-- (1)jcl->slf4j (2)slf4j->log4j (3)log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--end log-->
测试代码如下:
@ContextConfiguration("classpath:spring-context.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SomeTest {
private static final Logger LOGGER = LoggerFactory.getLogger(XXX.class);//(1)
@Resource
private ***Service ***Service;
@Test
public void fun(){
Set<String> set = new HashSet<String>();
set.add("whb1");
set.add("whb2");
String g** = "gg";
...
**Service.needTestfun(set, groupCode, g**,..);
//在无返回时可以打印到logger
LOGGER.info("--whb success--");
}
}
总结:
@ContextConfiguration
基本spring
配置文件路径
@RunWith(SpringJUnit4ClassRunner.class)
此次使用的配置环境
@Test
标注这是一个Test方法
Assert
类可以用于断言,非空、相等等等。在无返回时也可以打印到log。
说明:
(1) LoggerFactory.getLogger
private static final Logger LOGGER = LoggerFactory.getLogger(com.XXX.class);
中XXX.class
用于绑定类名,在调试的时候会显示,如:
LOGGER.debug("日志信息");
会显示:
com...XXX: 日志信息
3. web项目配置
web.xml 中指出 spring
配置文件,log
配置文件(固定名称按序加载,略)放在资源目录下自动加载即可。
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>pro</display-name>
<context-param>
<param-name>webPro</param-name>
<param-value>whbweb.root</param-value>
</context-param>
<!-- Spring MVC 转发Servlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring-context.xml
</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<servlet-name>springmvc</servlet-name>
</filter-mapping>
</web-app>
注意:关于日志的属性文件不需要在初始化时配置。上述classpath
处只写spring-context.xml
即可。(由spring
容器加载)
4. spring-context.xml 及 log4j.properties
均放在资源目录下即可。值得一提的是,log4j属性文件不需要额外指出,直接放在资源目录下即可!
(1)spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="cn.whbing.pro.web.*"/>
<mvc:annotation-driven enable-matrix-variables="true" />
</beans>
(2)log4j.properties(1.x版本以后不再介绍)
### 设置###
#log4j.rootLogger = debug,stdout,D,E
log4j.rootLogger = info,stdout,D,E
#log4j.rootLogger = error,stdout,D,E
### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
### 输出DEBUG 级别以上的日志到=/home/**/logs/debug.log ###
### 相对目录是相对于整个项目而言的
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = logs/debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 输出ERROR 级别以上的日志到=/home/admin/logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
目录结构如下:(说明:log4j 1版本以log4j.properties
名称即可)
src
|-main
|-java
|-resources
|- log4j.properties
|-webapp
|-WEB-INF
|-web.xml
工具:
在
pom
文件处,在idea
中通过右键Diagrams
查看依赖关系图如下:
通过diagrams可以查看是否存在重复的包对一个接口实现等。在maven project中查看dependency可以查看包中的包含关系,以便排除多余的依赖,或者减少已经引入进来的依赖。如:spring-context依赖中实际上已经有了spring-core
上述log4j 1.x在spring中使用配置完毕。
【springweb+log4j中注意点】
web.xml中初始化classpath中不要写log配置文件路径!
log4j.xml等配置文件不用指明,自动识别。
5. log4j2详解
log4j与log4j2
1.x与2.x坐标和名称都有所变化
log4j:(不再维护)
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
log42:(主要依赖有两个core与api)
<!-- log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.5</version>
</dependency>
关于slf4j、log4j、log4j2
由于java日志框架众多(common-logging,log4j,slf4j,logback等),引入jar包的时候,就要为其添加对应的日志实现。。
不同的jar包,可能用了不同的日志框架,那引用了之后就得给不同的日志框架添加配置,这个是比较麻烦的。
slf4j
就是为了解决这个麻烦事的。
slf4j
全称为Simple Logging Facade for JAVA
,java简单日志门面。类似于Apache Common-Logging
,是对不同日志框架提供的一个门面封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。但是,他在编译时静态绑定真正的Log库。使用SLF4J时,如果你需要使用某一种日志实现,那么你必须选择正确的SLF4J的jar包的集合(各种桥接包)。
【slj4j常用依赖】
jcl-over-slf4j.jar --> (jcl -> slf4j) 将Jakarta Commons Logging日志框架到 slf4j 的桥接
log4j-over-slf4j.jar--> (log4j -> slf4j) 将log4j 的日志,桥接到slf4j,所以这个包不能和log4j-over-slf4j.jar同时用,否则会死循环!!
slf4j-log4j12.jar --> (slf4j -> log4j) slf4j 转接到 log4j,所以这个包不能和log4j-over-slf4j.jar同时用,否则会死循环!!
slf4j-api.jar --> slf4j 的api接口jar包
slf4j-ext.jar --> 扩展功能
slf4j-jcl.jar --> (slf4j -> jcl) slf4j 转接到 Jakarta Commons Logging日志输出框架
其他依赖包:
jul-to-slf4j.jar --> (juc -> slf4j) 将java.util.logging的日志桥接到 slf4j
osgi-over-slf4j.jar --> (osgi -> slf4j) 将osgi环境下的日志,桥接到slf4j
slf4j-android.jar --> (android-> slf4j) 将android环境下的日志,桥接到slf4j
slf4j-jdk14.jar --> (slf4j -> jul ) slf4j 转接到 java.util.logging,所以这个包不能和jul-to-slf4j.jar同时用,否则会死循环!!
slf4j-migrator.jar --> 一个GUI工具,支持将项目代码中 JCL,log4j,java.util.logging的日志API转换为slf4j的写法
slf4j-nop.jar --> (slf4j -> null) slf4j的空接口输出绑定,丢弃所有日志输出
slf4j-simple.jar --> (slf4j -> slf4j-simple ) slf4j的自带的简单日志输出接口
说明:上述有部分包,如相互引用,将出现死循环。
【log4j2常用依赖】
一般还会有log4j和log4j2混用的问题。
因为log4j项目已经停止更新了,官方建议用log4j2。
log4j2
里也提供了对各类log
的桥接支持,这里就只列举相关的几个jar包说明。
log4j-1.2-api.jar --> (log4j -> log4j2) 将log4j 的日志转接到log4j2日志框架
log4j-api.jar --> log4j2的api接口jar包
log4j-core.jar --> (log4j2 ) log4j2的日志输出核心jar包
log4j-slf4j-impl.jar--> (slf4j -> log4j2) slf4j 转接到 log4j2 的日志输出框架 (不能和 log4j-to-slf4j同时用)
log4j-to-slf4j.jar --> (log4j2 -> slf4j) 将 log4j2的日志桥接到 slf4j (不能和 log4j-slf4j-impl 同时用)
【说明】
slf4j转log4j2时,
slf4j
包中提供了slf4j-log4j12.jar
将slf4j 转接到 log4j2;同时log4j
包中也提供了log4j-slf4j-impl.jar
将slf4j 转接到 log4j2。两个依赖不能同时出现,否则报重复bindings错误:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/didi/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.5/log4j-slf4j-impl-2.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/didi/.m2/repository/org/slf4j/slf4j-log4j12/1.7.10/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
因此在spring中排除掉common-logging后,jcl->slf4j->log4j2
过程如下
<!-- 日志,属性文件放置可识别目录 -->
<!-- (1)jcl->slf4j (2)slf4j->log4j (3)log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${dependency.version.slf4j}</version>
</dependency>
<!--任选其一-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.5</version>
</dependency>
<!--任选其一-->
<!--<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.10</version>
</dependency>-->
log4j2依赖:
<!-- log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.5</version>
</dependency>
<!-- end log4j2 -->
【关于几种log包的总结】
【log4j】
这个最简单,单独使用jar包就一个,已经停止更新,当前最新版是 log4j-1.2.17.jar
只有输出功能,没有转接功能。
【log4j2】
同时有日志输出和转接功能。
单独使用时,jar包是 log4j-api-2.x.x.jar 和 log4j-core-2.x.x.jar
配置文件在资源目录下即可。
【log4j -> log4j2 桥接】
官网
去掉 log4j 1.x jar,添加log4j-1.2-api.jar,配合 log4j-api-2.x.x.jar 和 log4j-core-2.x.x.jar 即可,依赖如下
log4j-1.2-api.jar
log4j-api-2.x.x.jar
log4j-core-2.x.x.jar
【log4j2 -> log4j 桥接】
不建议。
本来log4j在2015年停止更新后,就建议转向log4j2,并提供了到log4j2的桥接接口。
所以反过来log4j2到log4j是不建议这么做的,log4j2也没有提供直接支持。
但理清了上面的jar包作用,就会发现,可以通过 log4j2 -> slf4j -> log4j 的方式来实现。
需要的jar包,根据依赖关系分别是:
log4j-api-2.x.x.jar
log4j-to-slf4j.jar
slf4j-api-x.x.x.jar
slf4j-log4j12-x.x.x.jar
log4j-1.2.17.jar
【slf4j】
同时有日志输出和转接功能。
核心jar包是 slf4j-api-x.x.x.jar
因为一般slf4j 只作为桥接用,如果要搭配 slf4j 自带的简单日志输出,那么就加上 slf4j-simple.jar
【log4j -> slf4j】
将代码中的log4j日志桥接到 slf4j,需要如下jar包
log4j-api-2.x.x.jar
log4j-to-slf4j-2.x.x.jar
slf4j-api-x.x.x.jar
【slf4j -> log4j】
将slf4j日志,采用log4j实现进行输出,需要如下jar包
slf4j-api-x.x.x.jar
slf4j-log4j12.jar
log4j-1.2.17.jar
【slf4j -> log4j2】
将slf4j日志,采用log4j2实现进行输出,需要如下jar包
slf4j-api-x.x.x.jar
log4j-slf4j-impl.jar
log4j-api.jar
log4j-core.jar
slf4j的代理绑定和输出组合起来,就实现了从一种日志框架,转到另一种日志实现框架的效果。
建议在这三种日志混用的情况下,采用如下方案
log4j -> log4j2
slf4j -> log4j2
配置文件结构:
src
|-main
|-java
|-resources
|- log4j2.xml
|-webapp
|-WEB-INF
|-web.xml
使用:
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
private static final Logger LOGGER = LoggerFactory.getLogger(ReadSpecialLineServiceImpl.class);
log4j2.xml配置
xml方式log4j配置
可以通过以下四种方式配置log4j2
- 通过一个格式为xml或json的配置文件
- 以编程方式,通过创建一个ConfigurationFactory工厂和Configuration实现
- 以编程方式,通过api暴露在配置界面添加组件的博人方式
- 以编程方式,通过调用Logger内部类上的方法
配置文件优先级及存放位置
log4j2不在支持log2j1中的.properties后缀的文件配置方式。log4j2版本配置文件后缀只能为xml、json或jsn。
系统选择配置的文件的优先级如下:
- classpath下的名为log4j2-test.json或者log4j-test.jsn文件
- classpath下名为log4j2-test.xml的文件
- classpath下名为log4j2.json或者log4j2.jsn的文件
- classpath下名为log4j2.xml的文件
我们一般默认使用log4j2.xml进行命名。如果本地测试,可以先把log4j2-test.xml放到classpath,而正式环境使用log4j2.xml,则在打包部署的时候不要打包log4j2-test.xml
简单配置如下,详解参考:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="info">
<appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</appenders>
<loggers>
<root level="error">
<appender-ref ref="Console"/>
</root>
</loggers>
</configuration>
如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="off" monitorInterval="1800">
<!--
status : 这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,会看到log4j2内部各种详细输出 ,默认OFF
monitorInterval : Log4j能够自动检测修改配置文件和重新配置本身, 设置间隔秒数。
%d{yyyy-MM-dd HH:mm:ss, SSS} : 日志生产时间
%p : 日志输出格式
%c : logger的名称
%m : 日志内容,即 logger.info("message")
%n : 换行符
%C : Java类名
%L : 日志输出所在行数
%M : 日志输出所在方法名
%t:表示当前线程名称
%-5level:输出日志级别,-5表示左对齐并且固定输出5个字符
%l:输出语句所在的包名、类名、函数名、行数
hostName : 本地机器名 hostAddress : 本地ip地址
<Async>属性用于是否异步,暂时忽略
-->
<Properties>
<!-- 配置日志文件输出目录,此配置将日志输出到tomcat根目录下的指定文件夹 -->
<!--<Property name="LOG_HOME">log4j2-test</Property>-->
<Property name="LOG_HOME">log4j2-pro2</Property>
</Properties>
<appenders>
<!--这个输出控制台的配置 -->
<Console name="Console" target="SYSTEM_OUT">
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)-->
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
<!--<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>-->
<PatternLayout pattern="%d{DEFAULT} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<!-- INFO级别日志 -->
<RollingRandomAccessFile name="info_appender"
immediateFlush="true" fileName="${LOG_HOME}/info/info.log"
filePattern="${LOG_HOME}/info/info - %d{yyyy-MM-dd HH_mm_ss}.log.gz">
<PatternLayout>
<pattern>%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</pattern>
</PatternLayout>
<Policies>
<!-- 日志压缩触发条件 -->
<!-- 每次日志超过size配置的大小,按filePattern的配置的格式进行压缩-->
<SizeBasedTriggeringPolicy size="25MB"/>
<!-- 此处是按时间进行压缩 根据filePattern中的时间格式 每隔一个单位进行压缩 此处是按天压缩-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<Filters>
<!--如果是warn级别拒绝-->
<ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/>
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</RollingRandomAccessFile>
<!-- ERROR级别日志 -->
<RollingRandomAccessFile name="error_appender"
immediateFlush="true" fileName="${LOG_HOME}/error/error.log"
filePattern="${LOG_HOME}/error/error - %d{yyyy-MM-dd HH_mm_ss}.log.gz">
<PatternLayout>
<pattern>%5p [%t] %d{yyyy-MM-dd HH:mm:ss} (%F:%L) %m%n</pattern>
</PatternLayout>
<Policies>
<!-- 日志压缩触发条件 -->
<!-- 每次日志超过size配置的大小,按filePattern的配置的格式进行压缩-->
<SizeBasedTriggeringPolicy size="25MB"/>
<!-- 此处是按时间进行压缩 根据filePattern中的时间格式 每隔一个单位进行压缩 此处是按天压缩-->
<TimeBasedTriggeringPolicy interval="1" modulate="true"/>
</Policies>
<Filters>
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
</Filters>
</RollingRandomAccessFile>
</appenders>
<!--定义logger,只有定义了logger并引入的appender,appender才会生效 -->
<loggers>
<root level="info">
<appender-ref ref="Console"/>
<appender-ref ref="info_appender"/>
</root>
</loggers>
</configuration>