Spring Boot嵌入式Jetty/Tomcat访问日志配置

14

我配置了logback.xml,它完美地工作了,但是logback-access.xml没有工作。

在Maven的pom.xml文件中。

   <dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
  </dependency>

src/main/resource

logback.xml
logback-access.xml

有没有方法可以配置访问日志?


根据此元讨论,我已将此问题的答案编辑掉,并放置到一个新的社区维基回答中,因为答案应该属于答案部分。如果您想自己提供它作为答案,您可以在社区维基回答中留下评论,我会将其删除。 - eis
9个回答

9

对于嵌入式Jetty,您还可以将其作为Spring Boot配置的一部分编写:

@Bean
public EmbeddedServletContainerFactory jettyConfigBean() {
    JettyEmbeddedServletContainerFactory jef = new JettyEmbeddedServletContainerFactory();
    jef.addServerCustomizers(new JettyServerCustomizer() {
        public void customize(Server server) {
            HandlerCollection handlers = new HandlerCollection();
            for (Handler handler : server.getHandlers()) {
                handlers.addHandler(handler);
            }
            RequestLogHandler reqLogs = new RequestLogHandler();
            NCSARequestLog reqLogImpl = new NCSARequestLog("./logs/access-yyyy_mm_dd.log");
            reqLogImpl.setRetainDays(30);
            reqLogImpl.setAppend(true);
            reqLogImpl.setExtended(false);
            reqLogImpl.setLogTimeZone("GMT");
            reqLogs.setRequestLog(reqLogImpl);
            handlers.addHandler(reqLogs);
            server.setHandler(handlers);

            // For Jetty 9.3+, use the following
            //RequestLogHandler reqLogs = new RequestLogHandler();
            //reqLogs.setServer(server);
            //RequestLogImpl rli = new RequestLogImpl();
            //rli.setResource("/logback-access.xml");
            //rli.setQuiet(false);
            //rli.start();
            //reqLogs.setRequestLog(rli);
            //handlers.addHandler(reqLogs);
            //server.setHandler(handlers);
        }
    });
    return jef;
}

你知道如何配置这个,以便只有滚动的文件在文件名中带有日期吗?即当前日志文件称为access.log,而滚动的文件现在称为access-2016-01-01.log? - chrismacp
这段代码适用于Logback:RequestLogHandler reqLogs = new RequestLogHandler(); reqLogs.setServer(server); RequestLogImpl rli = new RequestLogImpl(); rli.setResource("/logback-access.xml"); rli.setQuiet(true); reqLogs.setRequestLog(rli); handlers.addHandler(reqLogs); server.setHandler(handlers); 此外,您需要在类路径上拥有一个 logback-access.xml 配置文件。请查看文档获取更多信息。 - albogdano
一定是我的jar或者classpath有问题,因为日志文件没有被生成。我在logback.xml旁边的/resources目录下有logback-access.xml。我正在使用spring-boot-maven-plugin重新打包。 - chrismacp
1
我要创建一个新的问题,因为我真的不确定为什么这不起作用。我可以测试资源是否存在。没有日志/错误,所以很难知道出了什么问题。 - chrismacp
我解决了这个问题 - 真的已经超过5个小时了 :( 请看我的下面/上面的答案。 - chrismacp
显示剩余2条评论

6

经过多个小时的尝试,我终于找到了一个适用于SpringBoot 1.4 + Jetty + Logback-access的解决方案。Jetty的API接口在v9.3中发生了变化,导致Logback-access无法正常工作。

http://shibboleth.1660669.n2.nabble.com/Jetty-9-3-access-logging-recommended-configuration-td7620755.html

Logback项目已经有一个拉取请求来修复此问题。

https://github.com/qos-ch/logback/pull/269

上述拉取请求中提到了几种解决方案。

选项1

使用org.eclipse.jetty.server.Slf4jRequestLog实现将日志配置路由回经典的Logback。

JettyConfiguration @Bean

RequestLogHandler requestLogsHandler = new RequestLogHandler();
requestLogsHandler.setServer(server);
Slf4jRequestLog log = new Slf4jRequestLog();
log.setLoggerName("com.example.accesslog");
requestLogsHandler.setRequestLog(log);
handlers.addHandler(requestLogsHandler);
server.setHandler(handlers);

logback.xml

<appender name="FILE-ACCESS" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>${LOG_PATH}/main.log</file>
    <encoder>
        <!-- You'll have to work this out -->
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>${LOG_PATH}/main.%d{yyyy-MM-dd}-%i.log
        </fileNamePattern>
        <timeBasedFileNamingAndTriggeringPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
            <maxFileSize>20MB</maxFileSize>
        </timeBasedFileNamingAndTriggeringPolicy>
        <maxHistory>14</maxHistory>
    </rollingPolicy>
</appender>

<logger name="com.example.accesslog">
    <appender-ref ref="FILE-ACCESS" />
</logger>

这种方法可行,但你将失去Logback-access中自定义PatternLayout的所有访问日志特定参数。你可能需要编写自己的模式类。

我原以为这种方法可以实现,但事实并非如此(或者我没有正确操作)。

<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
        <layout class="ch.qos.logback.access.PatternLayout">
            <pattern>%h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"</pattern>
        </layout>
</encoder>

选项2

上述拉取请求中也提到了一个解决问题的修复方法,可以在排序之前使用。

创建一个类,添加缺失的接口,并使用该类来替代RequestLogImpl。

新建类。

package com.example.ch.qos.logback.access.jetty;

import ch.qos.logback.access.jetty.RequestLogImpl;
import org.eclipse.jetty.util.component.LifeCycle;

public class LogbackAccessRequestLogImplFix1052 extends RequestLogImpl implements LifeCycle {

Jetty配置@Bean

RequestLogHandler requestLogs = new RequestLogHandler();
requestLogs.setServer(server);
LogbackAccessRequestLogImplFix1052 rli = new LogbackAccessRequestLogImplFix1052();
rli.setResource("/logback-access.xml");
rli.setQuiet(false);
requestLogs.setRequestLog(rli);
handlers.addHandler(requestLogs);
server.setHandler(handlers);

我尝试了两种方法,最终选择了第二种,因为我已经花了太长时间在这个问题上。不过,我更喜欢第一种方法,因为可以将所有的日志配置保存在同一个文件中。

祝你好运。


非常感谢您写下这篇文章!我刚刚意识到我最近升级到Jetty 9.3,也可能受到了影响。希望这个问题能尽快得到解决。 - albogdano
使用编码器模式 <pattern>%msg</pattern>。调用slf4jRequestLog.setExtended(true)以获取扩展格式。如果您想要标准格式,则无需自己修改模式。 - ccleve

6

您需要在服务器容器中包含相关功能。例如,对于Tomcat,请在EmbeddedServletContainerCustomizer bean 中添加 LogbackValve。 为此,TomcatEmbeddedServletContainerFactory 提供了一个 addContextValves 方法。


1
Dave Sayer:我知道这是一篇旧帖子,但当我在寻找配置我的应用程序的访问日志时,我偶然发现了这篇文章。我想知道,为什么不使用这里提到的# EMBEDDED SERVER CONFIGURATION(ServerProperties)https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html 。就像在你的spring-boot微服务的application.yml文件中放置server.tomcat.accesslog.enabled: true一样。这两种方法之间有什么区别呢? - Amrut
这只是启用访问日志的标志,不是吗?OP想要配置日志输出的格式。 - Dave Syer
是的,但你也可以在application.yml中进行配置。这个server.tomcat.accesslog.pattern: "%h %l %u %t '%r' %s %b %D"会起到作用,除非我漏掉了什么。你也可以指定旋转策略、日志文件的名称和位置等。抱歉,戴夫,我并不是说你的方法是错误的,只是想理解什么时候使用其中的某一个方法。 - Amrut
@Amrut 如果它可行,你应该添加另一个回答来解释它。 - eis
使用logback可以使应用程序具备将日志发送到远程数据源(如ELK或TCP日志服务器)的能力。如果您有许多在线服务器,如果日志仅存在于本地目录中,则很难找到错误。 - cedricliang
有人能提供配置 LogbackValve 到 Spring Boot 2 的模板代码吗? - das

1
此程序化添加Tomcat valve以支持logback-access的版本对作者原始解决方案进行了一些改进。
感谢wacai。这是我的版本,它:
  • ${logback.access.config.path:}中删除了尾随的:
  • 假定src/main/resources/logback-access.xml
  • 删除了更改logback-access.xml名称的配置选项
  • 适用于Spring Boot 1.3.3
注意:您需要logback-access 1.1.6才能从资源加载配置-自动搜索logback-access.xml
import ch.qos.logback.access.tomcat.LogbackValve;
import org.apache.catalina.Context;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LogbackAccessEventConfiguration {

    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {

        return new EmbeddedServletContainerCustomizer() {

            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {
                if (container instanceof TomcatEmbeddedServletContainerFactory) {
                    ((TomcatEmbeddedServletContainerFactory) container)
                            .addContextCustomizers(new TomcatContextCustomizer() {

                                @Override
                                public void customize(Context context) {
                                    LogbackValve logbackValve = new LogbackValve();
                                    logbackValve.setFilename("logback-access.xml");
                                    context.getPipeline().addValve(logbackValve);
                                }
                            });
                }
            }
        };
    }

}

0

实现目前被接受的答案,由Ego Slayer提供,作为社区维基发布:


我从http://spring.io/guides/gs/rest-service/开始入门。

只需在此处创建文件src/main/java/hello/MyConfig.java

package hello;

import org.apache.catalina.valves.AccessLogValve;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import ch.qos.logback.access.tomcat.LogbackValve;

@Configuration
public class MyConfig {

    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer(){
        return new EmbeddedServletContainerCustomizer() {
            @Override
            public void customize(ConfigurableEmbeddedServletContainerFactory factory) {

                if(factory instanceof TomcatEmbeddedServletContainerFactory){
                    TomcatEmbeddedServletContainerFactory containerFactory = (TomcatEmbeddedServletContainerFactory) factory;

                    LogbackValve  logbackValve = new LogbackValve();
                    logbackValve.setFilename("src/main/resources/logback-access.xml");
                    containerFactory.addContextValves(logbackValve);


                }

            }
        };
    }
}

在maven的pom.xml中添加logback-access

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-rest-service</artifactId>
    <version>0.1.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>0.5.0.M6</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-access</artifactId>
            <version>1.0.13</version>
        </dependency>
    </dependencies>

    <properties>
        <start-class>hello.Application</start-class>
    </properties>

    <build>
        <plugins>
            <plugin> 
                <artifactId>maven-compiler-plugin</artifactId> 
                <version>2.3.2</version> 
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-snapshots</id>
            <url>http://repo.spring.io/libs-snapshot</url>
            <snapshots><enabled>true</enabled></snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-snapshots</id>
            <url>http://repo.spring.io/libs-snapshot</url>
            <snapshots><enabled>true</enabled></snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>

0
如果有帮助的话:
除了像这样配置LogbackValve之外
@Bean
public TomcatServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    tomcat.addContextValves(new LogbackValve());

    return tomcat;
}

我还必须将logback-access.xml放置在src/main/resources/conf下。如果我将其放置在src/main/resources中,则不会自动加载。

这是使用Spring Boot v2.5.2和logback-access v1.2.5。


0
在我们的情况下,我们将一个项目从SpringBoot 1.3.0更新到1.5.16.RELEASE,其中包含Jetty v9.x+,这导致记录访问日志的logback生成出现问题,就像之前在这个线程中报告的那样。为了解决这个问题,我们采取了以下步骤 -
  1. 在依赖项中添加logback-access-spring-boot-starter,以及logback-classic和logback-core。
  2. 在我的JettyConfig中添加以下代码
   @Bean
RequestLog makeRequestLog() {
    RequestLog requestLog = new Jetty93RequestLogImpl()
    requestLog.resource = '/logback-access.xml'
    requestLog
}
// Jetty 9.x
private static class Jetty93RequestLogImpl extends RequestLogImpl implements LifeCycle {
}         


0

一个使用Spring Boot 2(2.1.4.RELEASE)的例子。对我来说运行良好。

@Component
public class JettyCustomizationConfig implements WebServerFactoryCustomizer<ConfigurableJettyWebServerFactory> {

    @Override
    public void customize(ConfigurableJettyWebServerFactory server) {
        server.addServerCustomizers(customJettyServer());
    }

    private JettyServerCustomizer customJettyServer() {
        return server -> {
            HandlerCollection handlers = new HandlerCollection();
            RequestLogHandler requestLogHandler = new RequestLogHandler();
            requestLogHandler.setServer(server);
            RequestLogImpl requestLog = new RequestLogImpl();
            requestLog.setResource("/logback-access.xml");
            requestLog.setQuiet(false);
            requestLog.start();
            requestLogHandler.setRequestLog(requestLog);
            handlers.addHandler(server.getHandler());
            handlers.addHandler(requestLogHandler);
            server.setHandler(handlers);
        };
    }
}

logback-access.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
    <property name="log.path" value="logs" />

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/requests/seastar_request_%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
    </appender>

    <appender-ref ref="FILE"/>
</configuration>

您是否想要更新RequestLogImpl的详细信息? - Georgios Syngouroglou

0

另一种方法是注册Servlet过滤器并写入常规日志。

为了防止访问事件与其他事件混合,禁用additivity

<appender name="ACCESS-LOG"
          class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>./log/evilAccess.log</file>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
        <Pattern>
            %d{yyyy-MM-dd HH:mm:ss} %msg ip=%mdc{ip} session=%mdc{session} user=%mdc{user}%n
        </Pattern>
    </encoder>
    <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
        <fileNamePattern>./log/evilAccess-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <maxFileSize>5MB</maxFileSize>
        <maxHistory>30</maxHistory>
    </rollingPolicy>
</appender>

<logger name="com.evil.web.log.MyAccessLogFilter" level="debug" additivity="false">
    <appender-ref ref="ACCESS-LOG" />
</logger>

<logger name="com.evil.web.log.MyAccessLogFilter" level="debug" additivity="false">
    <appender-ref ref="ACCESS-LOG" />
</logger>

LoggingFilter 是一个常规的 Servlet 过滤器,可以通过在 @Configuration 类上使用 @ServletComponentScan 和在实现了 javax.servlet.Filter 接口的类上使用 @WebFilter,或者通过 bean 配置轻松地在 Spring Boot 应用程序中注册。

@Bean
myAccessLogFilter myAccessLogFilter() {
    SaAccessLogFilter filter = new MyAccessLogFilter();
    // filter.setMaxPayloadLength(100);
    return filter;
}

@Bean
FilterRegistrationBean registration() {
    FilterRegistrationBean registration = new FilterRegistrationBean(myAccessLogFilter());
    registration.setOrder(1);
    registration.setEnabled(true);
    return registration;
}

我建议至少使用 GenericFilterBean 或者更好的是 OncePerRequestFilter。Spring Web 已经提供了一些记录过滤器,在 org.springframework.web.filter 包内:

  • AbstractRequestLoggingFilter
  • CommonsRequestLoggingFilter
  • ServletContextRequestLoggingFilter

我基于 OncePerRequestFilter 定义自己的实现,通过 IP 地址和其他信息填充 Slf4j MDC 上下文...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接