From 60cb5fd35c67d194ab78b3d4703faa946408ea6e Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 16 Dec 2013 17:18:28 +0000 Subject: [PATCH] Add log4j starter and some documentation As discussed in gh-162 --- docs/howto.md | 84 +++++++++++++++++ spring-boot-samples/pom.xml | 1 + .../README.md | 21 +++++ .../spring-boot-sample-actuator-log4j/pom.xml | 44 +++++++++ .../actuator/log4j/HelloWorldService.java | 32 +++++++ .../log4j/SampleActuatorApplication.java | 35 +++++++ .../actuator/log4j/SampleController.java | 45 +++++++++ .../actuator/log4j/ServiceProperties.java | 36 ++++++++ .../src/main/resources/application.properties | 17 ++++ .../src/main/resources/log4j.properties | 14 +++ .../log4j/SampleActuatorApplicationTests.java | 92 +++++++++++++++++++ .../spring-boot-sample-actuator/pom.xml | 24 ++--- spring-boot-starters/pom.xml | 1 + .../spring-boot-starter-actuator/pom.xml | 4 - .../spring-boot-starter-log4j/pom.xml | 33 +++++++ .../main/resources/META-INF/spring.provides | 1 + .../spring-boot-starter-logging/pom.xml | 4 + .../spring-boot-starter-parent/pom.xml | 5 + 18 files changed, 473 insertions(+), 20 deletions(-) create mode 100644 spring-boot-samples/spring-boot-sample-actuator-log4j/README.md create mode 100644 spring-boot-samples/spring-boot-sample-actuator-log4j/pom.xml create mode 100644 spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/HelloWorldService.java create mode 100644 spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/SampleActuatorApplication.java create mode 100644 spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/SampleController.java create mode 100644 spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/ServiceProperties.java create mode 100644 spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/resources/application.properties create mode 100644 spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/resources/log4j.properties create mode 100644 spring-boot-samples/spring-boot-sample-actuator-log4j/src/test/java/org/springframework/boot/sample/actuator/log4j/SampleActuatorApplicationTests.java create mode 100644 spring-boot-starters/spring-boot-starter-log4j/pom.xml create mode 100644 spring-boot-starters/spring-boot-starter-log4j/src/main/resources/META-INF/spring.provides diff --git a/docs/howto.md b/docs/howto.md index 06d444956a2..1a304c6df6b 100644 --- a/docs/howto.md +++ b/docs/howto.md @@ -437,6 +437,90 @@ server.port: ${port:8080} > Spring can bind to capitalized synonyms for `Environment` > properties. +## Configure Logback for Logging + +Spring Boot has no mandatory logging dependence, except for the +`commons-logging` API, of which there are many implementations to +choose from. To use [Logback](http://logback.qos.ch) you need to +include it, and some bindings for `commons-logging` on the classpath. +The simplest way to do that is through the starter poms which all +depend on `spring-boot-start-logging`. For a web application you only +need the web starter since it depends transitively on the logging +starter. E.g. in Maven: + +```xml + + org.springframework.boot + spring-boot-starter-web + +``` + +Spring Boot has a `LoggingSystem` abstraction that attempts to select +a system depending on the contents of the classpath. If Logback is +available it is the first choice. So if you put a `logback.xml` in the +root of your classpath it will be picked up from there. Spring Boot +provides a default base configuration that you can include if you just +want to set levels, e.g. + +```xml + + + + + +``` + +If you look at the default `logback.xml` in the spring-boot JAR you +will see that it uses some useful System properties which the +`LoggingSystem` takes care of creating for you. These are: + +* `${PID}` the current process ID +* `${LOG_FILE}` if `logging.file` was set in Boot's external configuration +* `${LOG_PATH` if `logging.path` was set (representing a directory for + log files to live in) + +Spring Boot also provides some nice ANSI colour terminal output on a +console (but not in a log file) using a custom Logback converter. See +the default `base.xml` configuration for details. + +If Groovy is on the classpath you should be able to configure Logback +with `logback.groovy` as well (it will be given preference if +present). + +## Configure Log4j for Logging + +Spring Boot supports [Log4j](http://logging.apache.org/log4j/1.x/) for +logging configuration, but it has to be on the classpath. If you are +using the starter poms for assembling dependencies that means you have +to exclude logback and then include log4j back. If you aren't using +the starter poms then you need to provide `commons-logging` (at least) +in addition to Log4j. + +The simplest path to using Log4j is probably through the starter poms, +even though it requires some jiggling with excludes, e.g. in Maven: + +```xml + + org.springframework.boot + spring-boot-starter-web + + + ${project.groupId} + spring-boot-starter-logging + + + + + org.springframework.boot + spring-boot-starter-log4j + +``` + +Note the use of the log4j starter to gather together the dependencies +for common logging requirements (e.g. including having Tomcat use +`java.util.logging` but configure the output using Log4j). See the +[Actuator Log4j Sample]() for more detail and to see it in action. + ## Test a Spring Boot Application A Spring Boot application is just a Spring `ApplicationContext` so diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml index a63b80ea82c..d1dd8b0b1e7 100644 --- a/spring-boot-samples/pom.xml +++ b/spring-boot-samples/pom.xml @@ -15,6 +15,7 @@ spring-boot-sample-actuator + spring-boot-sample-actuator-log4j spring-boot-sample-actuator-ui spring-boot-sample-amqp spring-boot-sample-aop diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j/README.md b/spring-boot-samples/spring-boot-sample-actuator-log4j/README.md new file mode 100644 index 00000000000..5818a1e87ec --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j/README.md @@ -0,0 +1,21 @@ +# Spring Boot Actuator Sample + +You can build this sample using Maven (>3) or Gradle (1.6). + +With Maven: + +``` +$ mvn package +$ java -jar target/*.jar +``` + +Then access the app via a browser (or curl) on http://localhost:8080 (the user name is "user" and look at the INFO log output for the password to login). + +With gradle: + +``` +$ gradle build +$ java -jar build/libs/*.jar +``` + +The gradle build contains an intentionally odd configuration to exclude the security dependencies from the executable JAR. So the app run like this behaves differently than the one run from the Maven-built JAR file. See comments in the `build.gradle` for details. \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j/pom.xml b/spring-boot-samples/spring-boot-sample-actuator-log4j/pom.xml new file mode 100644 index 00000000000..513d8c4c379 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j/pom.xml @@ -0,0 +1,44 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-samples + 0.5.0.BUILD-SNAPSHOT + + spring-boot-sample-actuator-log4j + jar + + ${basedir}/../.. + + + + ${project.groupId} + spring-boot-starter-actuator + + + ${project.groupId} + spring-boot-starter-logging + + + + + ${project.groupId} + spring-boot-starter-web + + + ${project.groupId} + spring-boot-starter-log4j + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/HelloWorldService.java b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/HelloWorldService.java new file mode 100644 index 00000000000..e0d7f442a24 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/HelloWorldService.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.sample.actuator.log4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class HelloWorldService { + + @Autowired + private ServiceProperties configuration; + + public String getHelloMessage() { + return "Hello " + this.configuration.getName(); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/SampleActuatorApplication.java b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/SampleActuatorApplication.java new file mode 100644 index 00000000000..30faf9b935c --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/SampleActuatorApplication.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.sample.actuator.log4j; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableAutoConfiguration +@EnableConfigurationProperties +@ComponentScan +public class SampleActuatorApplication { + + public static void main(String[] args) throws Exception { + SpringApplication.run(SampleActuatorApplication.class, args); + } + +} diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/SampleController.java b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/SampleController.java new file mode 100644 index 00000000000..65494c6db0e --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/SampleController.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.sample.actuator.log4j; + +import java.util.Collections; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class SampleController { + + @Autowired + private HelloWorldService helloWorldService; + + @RequestMapping("/") + @ResponseBody + public Map helloWorld() { + return Collections.singletonMap("message", + this.helloWorldService.getHelloMessage()); + } + + @RequestMapping("/foo") + @ResponseBody + public String foo() { + throw new IllegalArgumentException("Server error"); + } +} diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/ServiceProperties.java b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/ServiceProperties.java new file mode 100644 index 00000000000..94f6f179f61 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/java/org/springframework/boot/sample/actuator/log4j/ServiceProperties.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.sample.actuator.log4j; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@ConfigurationProperties(name = "service", ignoreUnknownFields = false) +@Component +public class ServiceProperties { + + private String name = "World"; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/resources/application.properties new file mode 100644 index 00000000000..66fe1fc2269 --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/resources/application.properties @@ -0,0 +1,17 @@ +logging.file: /tmp/logs/app.log +management.port: 8080 +management.address: 127.0.0.1 +endpoints.shutdown.enabled: true +server.port: 8080 +server.tomcat.basedir: target/tomcat +server.tomcat.access_log_pattern: %h %t "%r" %s %b +security.require_ssl: false +service.name: Phil +shell.ssh.enabled: true +shell.ssh.port: 2222 +#shell.telnet.enabled: false +#shell.telnet.port: 1111 +shell.auth: spring +#shell.auth: key +#shell.auth.key.path: ${user.home}/test/id_rsa.pub.pem +#shell.auth: simple \ No newline at end of file diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/resources/log4j.properties b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/resources/log4j.properties new file mode 100644 index 00000000000..6070be6142a --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/main/resources/log4j.properties @@ -0,0 +1,14 @@ +log4j.rootCategory=INFO, CONSOLE + +PID=???? +LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] log4j%X{context} - ${PID} %5p [%t] --- %c{1}: %m%n + +# CONSOLE is set to be a ConsoleAppender using a PatternLayout. +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=${LOG_PATTERN} + +log4j.category.org.hibernate.validator.internal.util.Version=WARN +log4j.category.org.apache.coyote.http11.Http11NioProtocol=WARN +log4j.category.org.apache.tomcat.util.net.NioSelectorPool=WARN +log4j.category.org.apache.catalina.startup.DigesterFactory=ERROR diff --git a/spring-boot-samples/spring-boot-sample-actuator-log4j/src/test/java/org/springframework/boot/sample/actuator/log4j/SampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/test/java/org/springframework/boot/sample/actuator/log4j/SampleActuatorApplicationTests.java new file mode 100644 index 00000000000..ad24a4c7cfe --- /dev/null +++ b/spring-boot-samples/spring-boot-sample-actuator-log4j/src/test/java/org/springframework/boot/sample/actuator/log4j/SampleActuatorApplicationTests.java @@ -0,0 +1,92 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.sample.actuator.log4j; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.ClientHttpResponse; +import org.springframework.web.client.DefaultResponseErrorHandler; +import org.springframework.web.client.RestTemplate; + +/** + * Basic integration tests for service demo application. + * + * @author Dave Syer + */ +public class SampleActuatorApplicationTests { + + private static ConfigurableApplicationContext context; + + @BeforeClass + public static void start() throws Exception { + Future future = Executors + .newSingleThreadExecutor().submit( + new Callable() { + @Override + public ConfigurableApplicationContext call() throws Exception { + return SpringApplication + .run(SampleActuatorApplication.class); + } + }); + context = future.get(60, TimeUnit.SECONDS); + } + + @AfterClass + public static void stop() { + if (context != null) { + context.close(); + } + } + + @Test + public void testHome() throws Exception { + @SuppressWarnings("rawtypes") + ResponseEntity entity = getRestTemplate().getForEntity( + "http://localhost:8080", Map.class); + assertEquals(HttpStatus.OK, entity.getStatusCode()); + @SuppressWarnings("unchecked") + Map body = entity.getBody(); + assertEquals("Hello Phil", body.get("message")); + } + + + private RestTemplate getRestTemplate() { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { + @Override + public void handleError(ClientHttpResponse response) throws IOException { + } + }); + return restTemplate; + + } + +} diff --git a/spring-boot-samples/spring-boot-sample-actuator/pom.xml b/spring-boot-samples/spring-boot-sample-actuator/pom.xml index e4c0da3829a..423dbdd3efe 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/pom.xml +++ b/spring-boot-samples/spring-boot-sample-actuator/pom.xml @@ -19,8 +19,8 @@ spring-boot-starter-actuator - ch.qos.logback - logback-classic + ${project.groupId} + spring-boot-starter-logging @@ -73,12 +73,8 @@ - org.apache.tomcat.embed - tomcat-embed-core - - - org.apache.tomcat.embed - tomcat-embed-logging-juli + org.springframework.boot + spring-boot-starter-tomcat @@ -117,12 +113,8 @@ - org.slf4j - slf4j-log4j12 - - - log4j - log4j + org.springframework.boot + spring-boot-starter-log4j @@ -133,8 +125,8 @@ - ch.qos.logback - logback-classic + org.springframework.boot + spring-boot-starter-logging diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index 06949189a95..d32601ba30b 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -23,6 +23,7 @@ spring-boot-starter-jdbc spring-boot-starter-jetty spring-boot-starter-logging + spring-boot-starter-log4j spring-boot-starter-mobile spring-boot-starter-actuator spring-boot-starter-parent diff --git a/spring-boot-starters/spring-boot-starter-actuator/pom.xml b/spring-boot-starters/spring-boot-starter-actuator/pom.xml index 97bb7fc4e50..609aa23813a 100644 --- a/spring-boot-starters/spring-boot-starter-actuator/pom.xml +++ b/spring-boot-starters/spring-boot-starter-actuator/pom.xml @@ -23,9 +23,5 @@ spring-boot-actuator ${project.version} - - org.slf4j - log4j-over-slf4j - diff --git a/spring-boot-starters/spring-boot-starter-log4j/pom.xml b/spring-boot-starters/spring-boot-starter-log4j/pom.xml new file mode 100644 index 00000000000..12d11c8fa39 --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-log4j/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starters + 0.5.0.BUILD-SNAPSHOT + + spring-boot-starter-log4j + jar + + ${basedir}/../.. + + + + org.slf4j + jcl-over-slf4j + + + org.slf4j + jul-to-slf4j + + + org.slf4j + slf4j-log4j12 + + + log4j + log4j + + + diff --git a/spring-boot-starters/spring-boot-starter-log4j/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-log4j/src/main/resources/META-INF/spring.provides new file mode 100644 index 00000000000..10484626f8a --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-log4j/src/main/resources/META-INF/spring.provides @@ -0,0 +1 @@ +provides: logback-classic,jcl-over-slf4j,jul-to-slf4j \ No newline at end of file diff --git a/spring-boot-starters/spring-boot-starter-logging/pom.xml b/spring-boot-starters/spring-boot-starter-logging/pom.xml index 2c897d257f7..712c4a74d42 100644 --- a/spring-boot-starters/spring-boot-starter-logging/pom.xml +++ b/spring-boot-starters/spring-boot-starter-logging/pom.xml @@ -21,6 +21,10 @@ org.slf4j jul-to-slf4j + + org.slf4j + log4j-over-slf4j + ch.qos.logback logback-classic diff --git a/spring-boot-starters/spring-boot-starter-parent/pom.xml b/spring-boot-starters/spring-boot-starter-parent/pom.xml index deb72bc63bd..6caf2f1c43b 100644 --- a/spring-boot-starters/spring-boot-starter-parent/pom.xml +++ b/spring-boot-starters/spring-boot-starter-parent/pom.xml @@ -94,6 +94,11 @@ spring-boot-starter-logging ${spring-boot.version} + + org.springframework.boot + spring-boot-starter-log4j + ${spring-boot.version} + org.springframework.boot spring-boot-starter-mobile