【问题标题】:Spring Boot jetty/tomcat embedded access log configurationSpring Boot jetty/tomcat 内嵌访问日志配置
【发布时间】:2014-01-01 17:19:25
【问题描述】:

我配置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

有没有办法配置访问日志?

【问题讨论】:

  • 我已经根据this meta discussion 编辑了这个问题的答案,并将其放入new community wiki answer,因为答案属于答案部分。如果您想自己提供答案,可以在社区 wiki 答案中发表评论,我会删除它。

标签: spring logging logback access-log spring-boot


【解决方案1】:

您必须在服务器容器中包含相关功能。例如。对于 Tomcat,在 EmbeddedServletContainerCustomizer bean 中添加 LogbackValveTomcatEmbeddedServletContainerFactory 有一个用于此目的的 addContextValves 方法。

【讨论】:

  • Dave Sayer:我知道它的旧帖子,但我在为我的应用程序配置访问日志时遇到了这个问题。只是想知道,为什么不使用这里提到的# EMBEDDED SERVER CONFIGURATION (ServerProperties) docs.spring.io/spring-boot/docs/current/reference/html/… 。就像将 server.tomcat.accesslog.enabled: true 放在 spring-boot 微服务的 application.yml 文件中一样。这两种方法有什么区别。
  • 那只是启用访问日志的标志,不是吗? OP想要配置日志输出的格式。
  • 是的,但您也可以在 application.yml 中进行配置。这个 server.tomcat.accesslog.pattern: "%h %l %u %t '%r' %s %b %D" 可以解决问题,除非我遗漏了什么。您还可以指定轮换策略、日志文件的名称和位置等。抱歉,戴夫,我并不是说您的方法是错误的,只是试图了解何时使用这两种方法。
  • @Amrut 如果可行,您可能应该添加另一个解释它的答案
  • @Amrut 使用 logback 使应用程序能够将日志发送到远程数据源,如 ELK 或 TCP 日志服务器。如果你有很多服务器在线,如果日志只存在于本地目录中,很难发现错误。
【解决方案2】:

对于嵌入式 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;
}

【讨论】:

  • 你知道如何配置它以便只有滚动文件的文件名中有日期吗? IE。当前的日志文件称为 access.log,滚动的文件现在是 access-2016-01-01.log?
  • 这适用于 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 配置文件。检查文档。
  • 我实际上只是在尝试,但似乎对我不起作用,如果您确认它有效,我会再试一次
  • 必须与我的 jar 或类路径有关,因为只是没有生成日志文件。我的 logback.xml 旁边的 /resources 中有 logback-access.xml。我正在使用 spring-boot-maven-plugin 重新打包。
  • 要创建一个新问题,因为真的不确定为什么这不起作用。我可以测试资源是否存在。没有日志/错误,很难知道发生了什么。
【解决方案3】:

这个版本的程序化adding a Tomcat valve for 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);
                                }
                            });
                }
            }
        };
    }

}

【讨论】:

    【解决方案4】:

    经过数小时试图找到一个与 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 {
    

    }

    码头配置@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);
    

    我尝试了这两种方法,最终选择了选项 2,因为我已经在这方面花费了太久。不过,我更喜欢选项 1,因此我可以将所有日志记录配置保存在同一个文件中。

    祝你好运。

    【讨论】:

    • 非常感谢您写这篇文章!我刚刚意识到,当我最近升级到 Jetty 9.3 时,我也可能会受到影响。希望这很快得到修复。
    • 使用编码器模式%msg。调用 slf4jRequestLog.setExtended(true) 获取扩展格式。如果您想要标准格式,则无需摆弄自己的模式。
    【解决方案5】:

    另一种方法是注册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 过滤器,可以通过 @ServletComponentScan on @Configuration class + @WebFilter on @WebFilter on @WebFilter 或通过 bean config 在 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上下文......

    【讨论】:

      【解决方案6】:

      currently accepted answer 的实现,由Ego Slayer 提供,发布为社区 wiki:


      我从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 中添加logback-access pom.xml

      <?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>
      

      【讨论】:

        【解决方案7】:

        在我们的例子中,我们将一个项目从 SpringBoot 1.3.0 更新到 Jetty v9.x+ 附带的 1.5.16.RELEASE,这破坏了该线程前面报告的 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 {
        }         
        
        

        【讨论】:

          【解决方案8】:

          使用 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 的详细信息吗?
          【解决方案9】:

          如果它对任何人有帮助:

          除了像这样配置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。

          【讨论】:

            猜你喜欢
            • 2014-06-29
            • 2020-03-30
            • 2017-09-25
            • 2015-04-13
            • 2018-06-27
            • 1970-01-01
            • 1970-01-01
            • 2019-08-25
            • 2017-10-26
            相关资源
            最近更新 更多