Spring Bootで組み込みTomcatのアクセスログを標準出力に出力する

## 概要

Spring Bootの組み込みTomcatでは、アクセスログを自動でファイルに出力することができます。
本記事ではこれを標準出力に出力させる方法を紹介します。

## 環境

- Java8
- Gradle 5.6.2
- Spring Boot 2.2.1
- logback-access 1.2.3
- logback-access-spring-boot-starter 2.7.1

## まずはアクセスログを出力する

てきとーなGETとPOSTを受けるRestControllerを書きます。

```java
@RequestMapping("hello")
@RestController
public class HelloController {

    @GetMapping("")
    public String hello() {
        return "Hello, world!";
    }

    @PostMapping("")
    public String message(@RequestBody String message) {
        return "Hello, " + message + "!";
    }
}
```

application.propertiesに以下のような設定を記述することでアクセスログを出力させることができます。

```
server.tomcat.accesslog.enabled=true
server.tomcat.basedir=/path/to/dir
server.tomcat.accesslog.directory=logs
server.tomcat.accesslog.suffix=.log
server.tomcat.accesslog.prefix=access_log
server.tomcat.accesslog.file-date-format=.yyyy-MM-dd
```

実際にローカルで叩いてみると、/path/to/dir/logs/access_log.yyyy-MM-dd.logにアクセスログが出力されました。

```
$ curl localhost:8080/hello
Hello, world!
curl -H "Content-Type: text/plain" localhost:8080/hello -d "mito"
Hello, mito!
$ ls /path/to/dir/logs
access_log.2019-11-08.log
$ cat /path/to/dir/logs/access_log.2019-11-08.log
0:0:0:0:0:0:0:1 - - [08/Nov/2019:17:59:56 +0900] "GET /hello HTTP/1.1" 200 13
0:0:0:0:0:0:0:1 - - [08/Nov/2019:18:00:01 +0900] "POST /hello HTTP/1.1" 200 16
```

## アクセスログを標準出力に出力させる

[Twelve-Factor Appでも触れられている](https://12factor.net/ja/logs)通り、モダンなアプリケーションではログは標準出力に出力する方が一般的です。

標準出力に出力させるにはいくつかの方法があります。

### /dev/stdoutに出力させる

設定を工夫して、出力先のファイルを/dev/stdoutにすることで標準出力に出力できます。

```
server.tomcat.accesslog.enabled=true
server.tomcat.basedir=/dev
server.tomcat.accesslog.directory=stdout
server.tomcat.accesslog.suffix=
server.tomcat.accesslog.prefix=
server.tomcat.accesslog.file-date-format=
```

### logback-accessを利用する

/dev/stdoutを直で触るのはできれば避けたいケースもあります。 
[logback-access](http://logback.qos.ch/access.html)を利用することで出力先を標準出力に設定できます。

まず、`src/main/resources/conf/`にlogback-access.xmlを配置します。
logback-access.xmlはstdoutに出力させるよう、以下のように記述します。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <!--  combined log format-->
      <pattern>combined</pattern>
    </encoder>
  </appender>

  <appender-ref ref="STDOUT" />

</configuration>

```

さらにlogback-accessの依存を追加し(以下はgradleの場合)、

```diff
dependencies {
  ...
+	implementation group: 'ch.qos.logback', name: 'logback-access', version: '1.2.3'
}
```

TomcatServletWebSErverFactoryのBeanを作成することで、組み込みtomcatに設定を反映させます。

```java
@Configuration
public class TomcatLoggingConfig {
    @Bean
    public TomcatServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory();
        // LogbackValveはresources以下を参照するため、これでlogback-access.xmlの内容が反映される
        tomcatServletWebServerFactory.addContextValves(new LogbackValve());
        return tomcatServletWebServerFactory;
    }
}

```

起動して実際にリクエストを行ってみると、以下のようなログが出力されることが確認できます。

```
0:0:0:0:0:0:0:1 - - [08/11/2019:18:38:29 +0900] "GET /hello HTTP/1.1" 200 13 "-" "curl/7.54.0"
0:0:0:0:0:0:0:1 - - [08/11/2019:18:38:20 +0900] "POST /hello HTTP/1.1" 200 16 "-" "curl/7.54.0"
```

application.propertiesの設定も不要です。

### logback-access-spring-boot-starterを利用する

[logback-access-spring-boot-starter](https://github.com/akihyro/logback-access-spring-boot-starter)を利用することで、上記のlogback-accessの設定を自動で反映してくれます。

依存を追加して、

```diff
dependencies {
  ...
+ implementation group: 'net.rakugakibox.spring.boot', name: 'logback-access-spring-boot-starter', version: '2.7.1'
}
```

`src/main/resources`直下に先程のlogback-access.xmlを配置すれば、アクセスログが標準出力に出力されます。便利...!



## 参考
https://www.baeldung.com/spring-boot-embedded-tomcat-logs
http://logback.qos.ch/access.html
https://stackoverflow.com/questions/36780680/how-do-you-tell-spring-boot-to-send-the-embedded-tomcats-access-logs-to-stdout?answertab=votes#tab-top
https://github.com/akihyro/logback-access-spring-boot-starter