How do you tell Spring Boot to send the embedded Tomcat's access logs to stdout?
Asked Answered
E

5

27

In a standalone Spring Boot web application (executable jar), how do you tell Spring Boot that we want the embedded Tomcat instance's HTTP access logs to be sent to stdout?

Emiliaemiliaromagna answered 21/4, 2016 at 21:18 Comment(3)
I think you would just need to set the logging level to debug because spring and tomcat logs are set to info by default. The logs would have the fully qualified class name so you'd know the source of the log.Reasoned
@RahulSharma the logging level has no impact at all on the appender output location (e.g. file, stdout, URL, etc). We need all output (including Tomcat access logs) to go to stdout. Everything exception Tomcat is easy, it is the Tomcat logs that are hard.Emiliaemiliaromagna
Sorry I thought you wanted to see 100% of the logs. And from the docs spring boot writes logs to console by default so I don't know why you don't see them. Also, are you using embedded tomcat? If yes, have a look at the embedded server properties hereReasoned
D
18

If you use Logback, you can use logback-access for this.

Add dependency ch.qos.logback:logback-access

Optional Javaconfig to add TeeFilter (request & response logging):

@Bean(name = "TeeFilter")
public Filter teeFilter() {
    return new ch.qos.logback.access.servlet.TeeFilter();
}

Javaconfig for embedded tomcat:

@Bean
public EmbeddedServletContainerFactory servletContainer() {
    TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();

    // put logback-access.xml in src/main/resources/conf
    tomcat.addContextValves(new LogbackValve());

    return tomcat;
}

Contents for logback-access.xml (save in src/main/resources/conf)

<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <Pattern>combined</Pattern>
      <Pattern>%fullRequest%n%n%fullResponse</Pattern>
    </encoder>
  </appender>

  <appender-ref ref="STDOUT" />

</configuration>
Decrescendo answered 22/4, 2016 at 9:55 Comment(3)
We do indeed use Logback and already have used the console appender :) The missing piece was the TomcatEmbeddedServletContainerFactory bean config. Great answer!Emiliaemiliaromagna
In Spring Boot, it will attempt to find the valve filename as a classpath resource - if "src/main/resources" is in your filename literal, it won't be able to find it.Baro
A better way is to put the logback-access.xml file under src/main/resources/conf. From the docs: By default, LogbackValve looks for a configuration file called logback-access.xml, in the same folder where server.xml is located, that is in $TOMCAT_HOME/conf/.Decrescendo
A
40

Update 2019.02.11:

These inks should be useful to map which properties you should set in application.properties:


@acohen answer is slightly correct. If you provide the empty double quotes it won't work. I will extend his answer because I think it's important for people who don't want to mess with adding dependencies or modifying code:

config/application.properties

# here we say that we want to enable accesslog
server.tomcat.accesslog.enabled=true

# it is important to understand what the options means:
# 'directory/prefix + suffix + file-date-format' will be
# the file that tomcat will try to open.
# /dev/stdout is standard output, so we want tomcat
# to write to that fd. Then, we need to play with
# directory, prefix, suffix and file-date-format to match our desired path.
server.tomcat.accesslog.directory=/dev
server.tomcat.accesslog.prefix=stdout
server.tomcat.accesslog.buffered=false

# Don't use empty double quotes, see below
server.tomcat.accesslog.suffix=
server.tomcat.accesslog.file-date-format=

Notes

  1. If you set file-date-format and suffix to be double quotes, you will have this error:
java.io.FileNotFoundException: /dev/stdout"""" (Permission denied)

  1. If you don't include them in the config file, you will then be using defaults values and this error:
java.io.FileNotFoundException: /dev/stdout.2019-02-07.log (Permission denied)
  1. If you leave them empty, then it will work.
Aron answered 7/2, 2019 at 5:9 Comment(11)
I tried for undertow, it still says Failed to create access log directory 'logs'. i used server.undertow.accesslog.directory=/devExorcist
@Exorcist I'm not familiar with undertow, maybe guessing this doc the option for undertow is dir (because method is getDir()) instead of directory. It's just a guess, tell me how did that went :)Aron
I'm running the app inside a docker container so I wrote to the directory /proc/1/fd with prefix "1" but for some reason it is adding "management_" before it. Failed to open access log file [/proc/1/fd/management_1]Trike
@Trike I also run this inside docker. Not sure why management_ is being prefixed. Sounds like a logger configuration. Do you have a working example?Aron
@Aron It showed that error but afterwards everything was working as expected. Although it would be nice not to see that error. I think the cause is that we are using the "management.*" configurations of the embedded server.Trike
I can confirm that the management was happening because of the management.* configurations in the embedded server. It has all the access logs of the management endpoints (mainly /actuator/health)Trike
Just a note: this is not cross-platform compatible and is Linux-specific. @JohanB's answer (using the newer TomcatServletWebServerFactory instead) works on all platforms.Emiliaemiliaromagna
@LesHazlewood then stop using windows for serving APIs :)Aron
@Aron I don't lol, but it's good to know it's not platform-independent like most Java solutions ;)Emiliaemiliaromagna
not sure about the last statement. If you ask out there, there's only a few devs/engineers that uses Java just because portability. Most of them uses java because is the only thing they know. And now, with docker you also eliminate that thing. API portability layer is: docker.Aron
If you are looking for server.xml solution <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/dev" prefix="stdout" suffix="" pattern="%h %l %u %t &quot;%r&quot; %s %b" buffered="false" fileDateFormat=""/> This is xml equivalent to this answer.Chemoreceptor
D
18

If you use Logback, you can use logback-access for this.

Add dependency ch.qos.logback:logback-access

Optional Javaconfig to add TeeFilter (request & response logging):

@Bean(name = "TeeFilter")
public Filter teeFilter() {
    return new ch.qos.logback.access.servlet.TeeFilter();
}

Javaconfig for embedded tomcat:

@Bean
public EmbeddedServletContainerFactory servletContainer() {
    TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();

    // put logback-access.xml in src/main/resources/conf
    tomcat.addContextValves(new LogbackValve());

    return tomcat;
}

Contents for logback-access.xml (save in src/main/resources/conf)

<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <Pattern>combined</Pattern>
      <Pattern>%fullRequest%n%n%fullResponse</Pattern>
    </encoder>
  </appender>

  <appender-ref ref="STDOUT" />

</configuration>
Decrescendo answered 22/4, 2016 at 9:55 Comment(3)
We do indeed use Logback and already have used the console appender :) The missing piece was the TomcatEmbeddedServletContainerFactory bean config. Great answer!Emiliaemiliaromagna
In Spring Boot, it will attempt to find the valve filename as a classpath resource - if "src/main/resources" is in your filename literal, it won't be able to find it.Baro
A better way is to put the logback-access.xml file under src/main/resources/conf. From the docs: By default, LogbackValve looks for a configuration file called logback-access.xml, in the same folder where server.xml is located, that is in $TOMCAT_HOME/conf/.Decrescendo
A
9

This did it for me on Spring Boot 2.x:

server.tomcat.accesslog.enabled=true
server.tomcat.accesslog.directory=/dev
server.tomcat.accesslog.prefix=stdout
server.tomcat.accesslog.buffered=false
server.tomcat.accesslog.suffix=""
server.tomcat.accesslog.file-date-format=""
Adama answered 7/12, 2018 at 17:50 Comment(2)
are you running this on pivotal/ cf?Culbert
Full example with application.yml: https://mcmap.net/q/382278/-spring-boot-enable-http-requests-logging-access-logsTeaching
J
4

Here's the followup up on the great answer from JohanB, for Spring Boot 2.0.0+.

In Spring Boot 2.0.0, the EmbeddedServletContainerFactory was replaced with TomcatServletWebServerFactory. All other aspects of JohanB's answer still works correctly the factory bean creation just needs to be modified:

@Bean
public TomcatServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    // put logback-access.xml in src/main/resources/conf
    tomcat.addContextValves(new LogbackValve());
    return tomcat;
}
Jargonize answered 19/6, 2018 at 19:52 Comment(2)
How to use this? i.e., where I put it?Seanseana
You have to put it in a Java class, with @Configuration annotated on the class so Spring loads the beans defined within itJargonize
C
3

JohanB's solution works, but if you don't want to write code, someone did it better and wrapped Server access logs in a nice Spring Boot starter. It covers Tomcat, Jetty and Undertow.

Just add the dependency:

<dependency>
    <groupId>net.rakugakibox.spring.boot</groupId>
    <artifactId>logback-access-spring-boot-starter</artifactId>
    <version>2.7.1</version>
</dependency>

And a logback-access.xml file at the classpath root:

<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>common</pattern>
        </encoder>
    </appender>
    <appender-ref ref="CONSOLE" />
</configuration>

and the access logs are printed to stdout:

127.0.0.1 - - [08/févr./2019:11:23:30 +0100] "GET /password HTTP/1.1" 200 32

At this point you will need to create the TeeFilter Bean on your own if you want to print the full HTTP request & response for debugging.

Caul answered 8/2, 2019 at 10:25 Comment(1)
Link does not work anymoreEucken

© 2022 - 2024 — McMap. All rights reserved.