Spring Boot jetty / tomcat嵌入式访问日志配置

时间:2013-12-13 20:01:39

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

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

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

8 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:6)

您必须在服务器容器中包含相关功能。例如。对于Tomcat,在LogbackValve bean中添加EmbeddedServletContainerCustomizerTomcatEmbeddedServletContainerFactory为此目的使用addContextValves方法。

答案 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项目中有一个pull请求可以让它再次运行。

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

上面的pull请求中还提到了一个问题的修复,可以在它被排序之前使用。

创建一个添加缺少接口的类,并使用它而不是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 Configuration @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,所以我可以将所有日志配置保存在同一个文件中。

祝你好运。

答案 3 :(得分:1)

这个版本的编程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 :(得分: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过滤器,可以通过@ServletComponentScan @Configuration上的@WebFilter + implements javax.servlet.Filter或{bean}上的@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; } 轻松在Spring Boot应用中注册配置:

GenericFilterBean

我建议至少使用OncePerRequestFilter或更好org.springframework.web.filter。 Spring Web已在AbstractRequestLoggingFilter包中提供了一些日志记录过滤器:

  • CommonsRequestLoggingFilter
  • ServletContextRequestLoggingFilter
  • OncePerRequestFilter

我基于 var stats; var camera, controls, scene, renderer; init(); render(); function init() { scene = new THREE.Scene(); var ambient = new THREE.AmbientLight( 0xFFFFFF ); scene.add( ambient ); var container = document.getElementById('container'); renderer = new THREE.WebGLRenderer({antialias: 1 }); renderer.shadowMap.enabled = true; renderer.setPixelRatio( window.devicePixelRatio ); renderer.setSize( window.innerWidth, window.innerHeight); renderer.setClearColor(0x013A65); container.appendChild( renderer.domElement ); camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5000 ); camera.position.set(0,0,0); controls = new THREE.OrbitControls( camera, renderer.domElement ); controls.addEventListener( 'change', render ); controls.enableKeys = false; controls.enableZoom = true; controls.minDistance = 2000; controls.maxDistance = 3500; controls.maxPolarAngle = Math.PI/2; // world var onError = function ( xhr ) { }; THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() ); var mtlLoader = new THREE.MTLLoader(); mtlLoader.setPath( './assets/3d/' ); mtlLoader.load( 'A.mtl', function( materials ) { materials.preload(); var objLoader = new THREE.OBJLoader(); objLoader.setMaterials( materials ); mtlLoader.setPath( './assets/3d/' ); objLoader.load( './assets/3d/A.obj', function ( object ) { object.position.x = 0; object.position.y = 0; object.position.z = 0; scene.add( object ); }, onError ); }); //Text var loader = new THREE.FontLoader(); loader.load( './fonts/Open_Sans_Regular.json', function ( font ) { var textGeometry = new THREE.TextGeometry( "Test", {font: font, size: 22, height: 3, curveSegments: 1});var textMaterial = new THREE.MeshPhongMaterial({ color: 0xFFFFFF, specular: 0xFFFFFF });var mesh1 = new THREE.Mesh( textGeometry, textMaterial );mesh1.position.x = -200;mesh1.position.y = 250;mesh1.position.z = 725;scene.add( mesh1);}); // lights light = new THREE.DirectionalLight( 0xFFFFFF ); light.position.set( 1, 1, 1 ); scene.add( light ); window.addEventListener( 'resize', onWindowResize, true ); } function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize( window.innerWidth, window.innerHeight ); } function animate() { requestAnimationFrame( animate ); stats.update(); render(); } function render() { renderer.render( scene, camera ); } 定义自己的实现,用IP地址和其他信息填充Slf4j MDC上下文......

答案 5 :(得分:0)

currently accepted answer的实施,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 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>

答案 6 :(得分:0)

在我们的案例中,我们将Jetty v9.x +随附的项目从SpringBoot 1.3.0更新为1.5.16.RELEASE,该项目打破了该线程先前所报告的logback访问日志的生成。要解决此问题,我们按照以下步骤进行了操作-

  1. 添加了logback-access-spring-boot-starter依赖项,以及经典的logback和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 {
}         

答案 7 :(得分: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>