Using logback-access with Google App Engine (GAE)

2019-04-10 03:54发布

问题:

I am working on a project that uses GAE as the backend for a mobile app. We want to have really good logging implemented in the project. I have spent a lot of time reading about log4j, logback-classic, logback-access, java.util.logging (JUL) and slf4j.

My conclusion is that I want to use logback-access since it has some nice features when it comes to logging http related stuff (e.g. logging full-request-data upon errors etc.).

Since GAE only supports the log levels for JUL and logback-access does not support slf4j, I thought that I should just install logback-access and make sure it writes all logs through JUL to GAE.

Is this possible? Has anyone done this and can guide me when it comes to the configuration files for logback-access and JUL? Can logback-access communicate directly through JUL, whithout me having to add a custom Appender (I am thinking about the connection to ch.qos.logback.access.jetty.RequestLogImpl that can be added to the configuration according to the docs)? Or does the logback-access configuration for Jetty not apply for GAE? Have I gotten something wrong?

Any tips are welcome!

回答1:

We did that and stayed with logback for two years. The short story is that you will end up mixing JUL and logback settings, since your application will be using logback and Google classes will be using JUL directly (and you cannot redirect JUL into logback in GAE).

After two years we switched to a slf4j + JUL configuration, it's easier and a single point of configuration. It is not easy, though (warning: tons of code follow):

logging.properties:

.level = INFO
handlers = com.acme.log.InfoHandler,com.acme.log.ErrorHandler

# these should work, but they don't. See 
# http://code.google.com/p/googleappengine/issues/detail?id=7467
com.acme.log.InfoHandler.level=ALL
com.acme.log.ErrorHandler.level=WARNING

# Example of log level setup for a single class
# workaround http://code.google.com/p/google-guice/issues/detail?id=488
com.google.inject.internal.util.level = WARNING

InfoHandler.java:

/**
 * Logs {@link Level#INFO} to {@link System#out}.
 * This class is inspired by {@link ConsoleHandler}
 */
public class InfoHandler extends StreamHandler {

    public InfoHandler() {
        setOutputStream(System.out);
        setFilter(new MaxLevelFilter(Level.WARNING));
    }

    @Override
    public void publish(LogRecord record) {
        super.publish(record);  
        flush();
    }

    @Override
    public void close() {
        flush();
    }

}

ErrorHandler.java:

public class ErrorHandler extends StreamHandler {

    public ErrorHandler() {
        setOutputStream(System.err);
    }

    @Override
    public void publish(LogRecord record) {
        super.publish(record);  
        flush();
    }

    @Override
    public void close() {
        flush();
    }

}

MaxLevel.java:

public class MaxLevelFilter implements Filter {

    private final Level maxLevel;

    public MaxLevelFilter(Level level) {
        this.maxLevel = level;
    }

    @Override
    public boolean isLoggable(LogRecord record) {
        return maxLevel.intValue() > record.getLevel().intValue();
    }

}

You should also apply the workaround explained here at some application listener when the server starts.



回答2:

With recent updations you will be able to use the slfj logging for google app engine stack driver.

You will have to use pom dependency

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-jdk14</artifactId>
</dependency>

Note: Make sure you avoid any other slf4j dependency.

And you can use the slf4j logging api as

@Slf4j
public class MyClass {
    ...
    log.info("Info log with param = {}", param);
    ...
}

You can find more details here : https://medium.com/@RasAlhague/logging-with-app-engine-spring-boot-2-and-slf4j-b2cb4d9234f9

Official link to spring boot application api - app engine: https://github.com/GoogleCloudPlatform/getting-started-java/tree/master/appengine-standard-java8/springboot-appengine-standard#exclude-jul-to-slf4j-bridge

I think its a nice and clean solution to this problem.