英文:
@SLF4J : How to configure lombok supported slf4j
问题
我有一个使用Lombok的内置@Slf4j记录日志的Maven项目。目前我的日志在控制台中可见,如下所示:
06-Apr-2020 17:42:38.217 INFO [RMI TCP Connection(2)-127.0.0.1] org.apache.jasper.servlet.TldScanner.scanJars 至少扫描了一个JAR以寻找TLD,但未包含任何TLD。为此记录器启用调试日志以获取扫描过的JAR的完整列表,但在其中没有找到TLD。在扫描期间跳过不需要的JAR可以提高启动时间和JSP编译时间。
[2020-04-06 05:42:40,701] Artifact Tarkshala-Scholars-Engine:war exploded: 已成功部署工件
[2020-04-06 05:42:40,701] Artifact Tarkshala-Scholars-Engine:war exploded: 部署耗时4,521毫秒
[http-nio-8080-exec-1] INFO com.tarkshala.scholars.engine.webservices.AuthenticationService - 欢迎使用Tarkshala Scholar APIs
[http-nio-8080-exec-3] INFO com.tarkshala.scholars.engine.webservices.AuthenticationService - 欢迎使用Tarkshala Scholar APIs
06-Apr-2020 17:42:45.808 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory 正在部署Web应用程序目录[/Users/kuldeep/Work/apps/apache-tomcat-9/webapps/manager]
06-Apr-2020 17:42:45.843 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Web应用程序目录[/Users/kuldeep/Work/apps/apache-tomcat-9/webapps/manager]的部署已在[35]毫秒内完成
在中间的一对行是由slf4j记录的。
resources/log4j.properties文件如下所示:
# Root logger option
log4j.rootLogger=DEBUG, stdout, file
# Redirect log messages to console
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=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# Redirect log messages to a log file, support file rolling.
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=/Users/kuldeep/Work/repos/Tarkshala-Scholars-APIs/logs/application.log
log4j.appender.file.MaxFileSize=5MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
pom.xml文件如下所示:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
Java类如下所示:
@Slf4j
@Path("/authentication")
@Authorization
public class AuthenticationService extends SpringApplication {
@Path("/greet")
@GET
@Produces(MediaType.TEXT_PLAIN)
@PubliclyAllowed
public Response greet(){
log.info("欢迎使用Tarkshala Scholar APIs");
return getBean(AuthenticationServiceHandler.class).greet();
}
}
我遇到了一些问题:
- 语句格式,它应该包含方括号中的时间。
- 日志未添加到配置中指定的文件中,实际上我认为配置未从文件中获取。
不知道我到底缺了什么。任何帮助/提示都将不胜感激。
<details>
<summary>英文:</summary>
I have a maven project which logs using Lombok's inbuilt @Slf4j. Currently my logs are visible in console as below
06-Apr-2020 17:42:38.217 INFO [RMI TCP Connection(2)-127.0.0.1] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
[2020-04-06 05:42:40,701] Artifact Tarkshala-Scholars-Engine:war exploded: Artifact is deployed successfully
[2020-04-06 05:42:40,701] Artifact Tarkshala-Scholars-Engine:war exploded: Deploy took 4,521 milliseconds
[http-nio-8080-exec-1] INFO com.tarkshala.scholars.engine.webservices.AuthenticationService - Welcome to Tarkshala Scholar APIs
[http-nio-8080-exec-3] INFO com.tarkshala.scholars.engine.webservices.AuthenticationService - Welcome to Tarkshala Scholar APIs
06-Apr-2020 17:42:45.808 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/Users/kuldeep/Work/apps/apache-tomcat-9/webapps/manager]
06-Apr-2020 17:42:45.843 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/Users/kuldeep/Work/apps/apache-tomcat-9/webapps/manager] has finished in [35] ms
Pair of lines in the middle are the one logged by slf4j.
resources/log4j.properties looks like below
# Root logger option
log4j.rootLogger=DEBUG, stdout, file
# Redirect log messages to console
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=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
# Redirect log messages to a log file, support file rolling.
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=/Users/kuldeep/Work/repos/Tarkshala-Scholars-APIs/logs/application.log
log4j.appender.file.MaxFileSize=5MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
pom.xml looks like below:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
Java class looks like this:
@Slf4j
@Path("/authentication")
@Authorization
public class AuthenticationService extends SpringApplication {
@Path("/greet")
@GET
@Produces(MediaType.TEXT_PLAIN)
@PubliclyAllowed
public Response greet(){
log.info("Welcome to Tarkshala Scholar APIs");
return getBean(AuthenticationServiceHandler.class).greet();
}
}
I have few problems:
- Statement formatting, it should contain time in square brackets[].
- logs are not added to file given in configuration, infact I believe the configuration is not getting picked from the file.
No idea what exactly am I missing. Any help/hint is appreciated.
</details>
# 答案1
**得分**: 5
我在查阅了官方的[Apache手册页面][1]后自行解决了问题。
我将整个答案分成以下两个部分:
1. 用于Jersey2 Maven Web应用程序的Log4j2设置
-------------------------------------
在pom.xml中添加以下依赖项:
```xml
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.13.1</version>
</dependency>
在web.xml中添加以下过滤器:
<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>
</filter-mapping>
配置文件log4j2.xml。必须按照名称log4j2.xml,因为在类路径中扫描配置会自动选择它,否则需要在web.xml中添加更多行来设置配置文件路径。而且不仅仅是名称,配置文件必须放在WEB-INF目录中,就像web.xml也在那里一样。
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="LogToConsole" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<RollingRandomAccessFile name="LogToRollingRandomAccessFile" fileName="logs/TSE-app.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="1 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<!-- 避免重复记录日志,通过additivity=false -->
<Logger name="com.tarkshala.scholars" level="debug" additivity="false">
<AppenderRef ref="LogToConsole"/>
<AppenderRef ref="LogToRollingRandomAccessFile"/>
</Logger>
<Root level="error">
<AppenderRef ref="LogToRollingRandomAccessFile"/>
<AppenderRef ref="LogToConsole"/>
</Root>
</Loggers>
</Configuration>
上述配置包含两种类型的记录器,一种用于控制台,另一种用于文件。还可以为各个记录器指定日志级别。有关更多信息,例如日志模式等,请参阅Apache的手册[页面][2]。
在Java类中使用日志记录器的最后一点是,可以将日志记录器注入到Java类中,如下所示:
@Path("/authentication")
public class AuthenticationService extends SpringApplication {
private Logger logger = LogManager.getLogger(AuthenticationService.class);
@Path("/greet")
@GET
@Produces(MediaType.TEXT_PLAIN)
public Response greet(){
logger.info("Welcome to Tarkshala Scholar APIs");
logger.error("This is error log");
logger.debug("This is debug log");
return getBean(AuthenticationServiceHandler.class).greet();
}
}
这么做的甜蜜结果如下图所示:
![logs][3]
这个方法运行良好,但在每个类中通过初始化实例来注入Logger很烦人,为了节省一些工作,可以使用Slf4j来帮助。Slf4j只是一个抽象层的外观(abstract layer),在其后面工作的是一个实际的日志记录框架(如Log4j、Java的内置util.Logger和Logback等)。因此,如果将来出现了更好的日志记录框架,我们就不需要触及已实例化Logger的Java类,只需根据新的需求来处理依赖项和配置。
- 除了log4j2之外还使用Slf4j和lombok
除了第1部分中的所有工作之外,我们还需要添加一些附加的依赖项。
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.1</version>
</dependency>
使用Lombok注解@Slf4j
很容易注入Logger。
@Path("/authentication")
@Slf4j
public class AuthenticationService extends SpringApplication {
private Logger logger = LogManager.getLogger(AuthenticationService.class);
@Path("/greet")
@GET
@Produces(MediaType.TEXT_PLAIN)
public Response greet(){
log.info("Welcome to Tarkshala Scholar APIs");
log.error("This is error log");
log.debug("This is debug log");
return getBean(AuthenticationServiceHandler.class).greet();
}
}
Lombok会负责初始化并注入日志记录器实例(`log`对象)到类中。
**就是这样。乐意帮助 :)**
[1]: https://logging.apache.org/log4j/2.x/manual/webapp.html
[2]: https://logging.apache.org/log4j/2.x/manual/appenders.html
[3]: https://i.stack.imgur.com/OfHRX.png
<details>
<summary>英文:</summary>
Figured it out myself after going through official [apache manual page.][1]
I will break whole answer into following two sections
1. Log4j2 setup for Jersey2 Maven Webapp
-------------------------------------
Add following dependencies into pom.xml
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.13.1</version>
</dependency>
Add following filter into web.xml
<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>
</filter-mapping>
A configuration file log4j2.xml. It is required to follow the name log4j2.xml as scanning for configuration in classpath automatically picks it up or otherwise few more lines in web.xml will be needed to set the configuration file path. And not just the name, the configuration file must be put in WEB-INF directory where web.xml also resides.
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="DEBUG">
<Appenders>
<Console name="LogToConsole" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
<RollingRandomAccessFile name="LogToRollingRandomAccessFile" fileName="logs/TSE-app.log"
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout>
<Pattern>%d %p %c{1.} [%t] %m%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy/>
<SizeBasedTriggeringPolicy size="1 MB"/>
</Policies>
<DefaultRolloverStrategy max="10"/>
</RollingRandomAccessFile>
</Appenders>
<Loggers>
<!-- avoid duplicated logs with additivity=false -->
<Logger name="com.tarkshala.scholars" level="debug" additivity="false">
<AppenderRef ref="LogToConsole"/>
<AppenderRef ref="LogToRollingRandomAccessFile"/>
</Logger>
<Root level="error">
<AppenderRef ref="LogToRollingRandomAccessFile"/>
<AppenderRef ref="LogToConsole"/>
</Root>
</Loggers>
</Configuration>
The configuration given above contains two types of loggers, first for console and second for file. Also log levels can be specified for individual loggers. For more information like log pattern etc follow apache's manual [page][2]
And the last point is use of logger in java class. Logger can be injected into a java class as following:
@Path("/authentication")
public class AuthenticationService extends SpringApplication {
private Logger logger = LogManager.getLogger(AuthenticationService.class);
@Path("/greet")
@GET
@Produces(MediaType.TEXT_PLAIN)
public Response greet(){
logger.info("Welcome to Tarkshala Scholar APIs");
logger.error("THis is error log");
logger.debug("THis is debug log");
return getBean(AuthenticationServiceHandler.class).greet();
}
}
and the sweet result of doing so much looks like this
[![logs][3]][3]
----------
This works fine but it is annoying injecting Logger by initializing instance in every class, so to save a bit of work Slf4j can help. Slf4j is just a facade(abstract layer) behind which an actual logging framework(like Log4j, Java's inbuilt util.Logger and Logback etc) works. So in future if a better logging framework surfaces out then we won't have to touch java classes where logger have been instantiated, it is just the dependencies and config that will be taken care as per the new needs.
2. Slf4j and lombok in addition to log4j2
-------------------------------------
We need to add few more dependencies in addition to all the work we did in case 1.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.1</version>
</dependency>
It is very easy to inject logger using Lombok annotation `@Slf4j`.
@Path("/authentication")
@Slf4j
public class AuthenticationService extends SpringApplication {
private Logger logger = LogManager.getLogger(AuthenticationService.class);
@Path("/greet")
@GET
@Produces(MediaType.TEXT_PLAIN)
public Response greet(){
log.info("Welcome to Tarkshala Scholar APIs");
log.error("THis is error log");
log.debug("THis is debug log");
return getBean(AuthenticationServiceHandler.class).greet();
}
}
Lombok takes care of initialising and injecting the logger instance(`log` object) into class.
**And that is it. Happy to help :)**
[1]: https://logging.apache.org/log4j/2.x/manual/webapp.html
[2]: https://logging.apache.org/log4j/2.x/manual/appenders.html
[3]: https://i.stack.imgur.com/OfHRX.png
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论