I tried to add Spring and Maven to one of my existing project, and I find that no matter how I configure, the logging seems to be out of my control.
I tried putting the log4j.properties in src/main/java and src/main/resources(Actually I am not sure where to put).
But when I use Log4j to log, the log displays in the console only, though I configure it into a file.
My log4j.properties is like:
log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.FileAppender
log4j.appender.A1.encoding=utf-8
log4j.appender.A1.File=E:\Programminglog\debug.log
log4j.appender.A1.Threshold = DEBUG
log4j.appender.A1.Append=true
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
I am not sure if I miss something or Spring overrides some settings, since I am new to Maven and Spring.
PS: Before I add dependencies of Log4j in pom.xml,no compile errors though I use org.apache.log4j.Logger
This is how my application.java looks like:
#Configuration
#EnableAutoConfiguration
#ComponentScan({"hello","wodinow.weixin.jaskey"})
public class Application extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
System.out.println("Let's inspect the beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
System.out.println(beanName);
}
LogUtil.info("Application Boots!");// here this line does not show in the file
}
#Bean
public CommandService commandService(){
return CommandService.getInstance();
}
}
If you are using log4j with spring-boot then you have to add dependency with "exclusions" in your pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>1.3.3.RELEASE</version>
**<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>**
</dependency>
**<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.2.5.RELEASE</version>
</dependency>**
Please follow this. It will resolve your problem.
http://www.atechref.com/blog/maven/spring-boot-using-log4j-logging/
UPDATE
By default, If you use the ‘Starter POMs’, Logback will be used for logging
(From: Spring Boot Reference, Chapter 25 Logging)
So either you configure your logging via logback logback.xml or you include the log4j libs. (when you need more help with including the lib then please post your pom.xml)
I reccomend to use logback (and slf4j)
OLD:
put the log4j.properties file in src\main\resources (not in ...\java)
make sure that it is named log4j.properties (in your question you named the file log4j.propertie)
add this lines to your web.xml
web.xml:
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
(#see Initializing Log4J with Spring?)
I ran into this exact problem, it turned out that spring boot includes Spring Boot Starter Logging and will ignore anything you add while that is still there. I was able to fix it by double clicking on my pom and clicking on Spring-Boot-Starter-Logging and then selecting "edit starters" then removing the spring boot starter logging.
If you are using some other kind of dependency management system the idea is the same, just carefully review anything that spring boot included in your project and make sure that log4J is the only logging system included.
Related
I have a log4j.xml with a customized appender like:
<appender name="console" class="com.example.MyAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%m (%c{1}:%L)"/>
</layout>
</appender>
Recently I upgraded log4j dependency to log4j2, but still using this log4j.xml and it works.
Now, I add a Spring Boot module in my project. Following Spring doc, I set my pom.xml as
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<version>2.6.4</version>
</dependency>
And I also add arguments -Dlogging.config=log4j.xml -Dlog4j.configuration=log4j.xml -Dlog4j1.compatibility=true for it.
But my Spring application shows the error and no log output:
ERROR StatusLogger Unknown object "logger" of type org.apache.logging.log4j.core.config.LoggerConfig is ignored: try nesting it inside one of: ["Appenders", "Loggers", "Properties", "Scripts", "CustomLevels"].
Seems log4j2 lib cannot recognize log4j.xml, which means -Dlog4j1.compatibility=true does not work for Spring Boot I think.
Any related config can be utilized or any workaround? Thanks.
TL;DR: The problem is that Log4j2 has two XML configuration factories (for the Log4j 1.x and Log4j 2.x formats), with the 2.x format having higher priority. You need to explicitly set the ConfigurationFactory to use:
-Dlog4j2.configurationFactory=org.apache.log4j.xml.XmlConfigurationFactory
When a Spring Boot application starts Log4j2 is configured twice:
at the very beginning using Log4j2 automatic configuration. For this round you just need to set -Dlog4j1.compatibility=true and call the config file log4j.xml or call the file differently and set -Dlog4j.configuration.
when Spring's environment is ready, Spring reconfigures Log4j2 programmatically using only a subset of Log4j2 automatic configuration. That is why this phase requires many manual settings:
-Dlogging.config=log4j.xml: Spring does not look for a file named log4j.xml,
-Dlog4j1.compatibility=true to activate the Log4j 1.x configuration factories,
-Dlog4j2.configurationFactory=org.apache.log4j.xml.XmlConfigurationFactory to increase the priority of the Log4j 1.x XML configuration factory.
Remark: Using a native Log4j 1.x custom appender exposes you to all the problems (synchronization and performance) of the original Log4j 1.x. For example Log4j 1.x looses events during reconfiguration (as the one performed by Spring Boot), whereas Log4j 2.x does not.
I have to write a small project, which is not runnable by itself, but it is used by some other ("main") project as dependency to do some database operations and some simple business logic. So there will be no actual main class in my project, only some API, which will be used by the main project. Now I was wondering, if it is possible, to use Spring Boot with Spring Data JPA in my small project for Database operations. So there will be no #SpringBootApplication annotated class. (And the main project is also not a Spring Boot application)
So I actually modified my pom.xml by adding this:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
(...)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Also I added an application.properties file in resources folder:
spring.jpa.hibernate.ddl-auto=none
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.show_sql=false
spring.jpa.properties.hibernate.format_sql=false
And finally, I added a simple config file:
#Configuration
#ComponentScan
#EnableJpaRepositories("package-of-the-JPA-repositories")
//#EnableAutoConfiguration
#EntityScan("package-of-the-entities")
#PropertySource("classpath:/application.properties")
public class SpringConfig {
}
Well, this configuration done, I tried to run the main project, but it sais, it cannot find the entityManagerFactory bean. As I understood, Spring Boot should generate this bean from the specified properties. So my question would be: 1) Is the thing I am tring to do generally possible? Means, is it possible to use Spring Boot without having an #SpringBootApplication annotated class? Or should I rather get rid of Spring Boot and use only Spring Data and Hibernate? And if it is possible, what am I possibly doing wrong? Any ideas?
A Spring web application is just a normal web application using servlet API 3.0.
In servlet API 3.0 web.xml file is optional (most of the time).
I' ve tried not to include web.xml inside my Spring aplication,but somehow it seems to be required even using Servlet 3.0
The only difference between a Spring applications and web apps I've run successfully without writing a web.xml file, is that in Spring filters and servlets are defined inside jars in the lib folder.
Any clues why this happens ?
You can't just exclude the web.xml, as you (at least) need to define which version of the Servlet API are you using.
At the minimum, you'd need this in your web.xml:
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
Everything else (servlets, filters, etc.) can be configured with annotations.
Fortunately, you can create completely XML free Spring applications nowadays.
There are various different options for such configs with or without Spring Boot.
With pure Spring you can use AbstractAnnotationConfigDispatcherServletInitializer
With Spring Boot, you can have SpringBootServletInitializer.
This is my Github repository with bunch of projects based on both approaches.
Version of servlets is defined by Servlet API you have on your classpath. E.g.:
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.servlet</artifactId>
<version>3.0</version>
<scope>provided</scope>
</dependency>
or
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
You also don't specify servlets, as Spring has own implementation of servlet DispatcherServlet.
Filters are registered as beans into Spring context.
The stack I am using at the moment is:
log4j2 rc1
spring 3.2 core and web
tomcat 7.0.47
java 1.6.0_45
Windows 7
I don't have the ability to alter the tomcat version or java version and I would prefer not to alter the log4j version and spring version.
Essentially, when I undeploy my webapp I receieve a SEVERE warning saying:
SEVERE: The web application [/MyApp] appears to have started a thread named
[AsyncAppender-AsyncFile] but has failed to stop it. This is very likely to
create a memory leak
I can confirm that it does create a memory leak and this is what I am trying to fix.
So far I have attempted to create a custom ServletContextListener which contains the following code:
#Override
public void contextDestroyed(ServletContextEvent event) {
System.out.println("Shutting down logger");
try {
((Log4jWebSupport) event.getServletContext().getAttribute(
Log4jWebSupport.SUPPORT_ATTRIBUTE)).clearLoggerContext();
((LifeCycle) LogManager.getContext()).stop();
} catch (Exception e) {
}
}
None of these two lines seems to fix the problem, however I can confirm that this code is being executed due to my sysout statement appearing in the tomcat console.
I am using log4j2 through an interceptor which I am setting up using Spring
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<ref bean="LoggingInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
<bean id="LoggingInterceptor" class="MyLoggerClass">
The interceptor works correctly and the logs appear where I expect them to. My implementation of the logger is:
private static Logger log = LogManager.getLogger("MetricLogger");
public void log(LogPayload payload) {
if(payload != null){
log.info(payload.get());
}
}
Where LogPayload.get() returns a String.
As I am using the logging facility across multiple webapps I have created a separate jar file containing this and the classes recording the measurements. I have included this using maven and I compile it into the final war file I deploy to tomcat. This war file is included on a per app basis and is not included in the global tomcat/lib folder.
Does anyone have any insight as to why I get my memory leak issue and what the possible solutions are to fixing this?
Many thanks for your help and please let me know if you need further information.
The solution to this that I have so far found is that I need to include the following snippet in the web.xml.
<listener>
<listener-class>org.apache.logging.log4j.core.web.Log4jServletContextListener</listener-class>
</listener>
<filter>
<filter-name>log4jServletFilter</filter-name>
<filter-class>org.apache.logging.log4j.core.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>
</filter-mapping>
This is specific to servlet spec 2.5. This seems to resolve the memory leak issue.
In case you are facing this issue recently you should note the following:
If you are using servlet 3.0+ and tomcat > 7.0.43 or Tomcat 8 there are no configuration needed, you just have to provide the correct maven dependencies to make log4j2 aware of running in a webcontainer like the following (in addition we use slf4j):
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-web</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.11.0</version>
</dependency>
Since in our case the log4j-web artifact was missing we faced the same warning which said "... started a thread named [AsyncAppender-AsyncFile] but has failed to stop it ..."
After providing the mentioned dependencies the warning is gone.
you can read more about requirements here
Adding the below snippet worked for me to get rid of memory leak due to Async-Appender threads not getting shut down even after the application shutdown.
Thank you #kipper_t.
<listener>
<listener-class>org.apache.logging.log4j.core.web.Log4jServletContextListener</listener-class>
</listener>
<filter>
<filter-name>log4jServletFilter</filter-name>
<filter-class>org.apache.logging.log4j.core.web.Log4jServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>log4jServletFilter</filter-name>
<url-pattern>/*</url-pattern>
I recently switched to Apache log4j2, and still can not find a way to configure hibernate logging using log4j2.xml.
Because I can not find a way around this problem I still use log4j.properties file explicitly for hibernate. This is not the best solution since my log4j2.xml uses JPA appender (writes logs to db). I do not want to write separate logic for hibernate.
Is there a way to configure hibernate logging using log4j2?
As suggested in
https://issues.apache.org/jira/browse/LOG4J2-172
you can add system property to force hibernate use slf4j
-Dorg.jboss.logging.provider=slf4j
also log4j-slf4j-impl should be added to classpath
My custom solution:
with Spring you can place
org.jboss.logging.provider=slf4j
in property file
(envConfigLocation is file url)
<bean id="propertyConfigurer" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" ref="envConfigLocation" />
<property name="order" value="1"/>
</bean>
I found an answer to this question at: How to redirect all logs from hibernate and spring to log4j2?
Basically log4j2 doesn't work with Hibernate so you have to use log4j. But you still use your log4j2 configuration. You need the following dependencies and then the magic happens in the background.
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-1.2-api</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<!--HIBERNATE LOGGER (log4j)-->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.6</version>
</dependency>
It is possible to redirect calls to the log4j-1.x API to the log4j-2.0 implementation. The FAQ about which jars to include explains how to do this. You probably need to remove the old log4j-1.x jar from the classpath when you do this.