如何通过GELF将Spring Boot访问日志发送到远程服务器?

时间:2017-03-22 07:25:05

标签: spring tomcat spring-boot gelf

对于我们的Spring启动应用程序,我们使用logback + GELF将应用程序日志发送到我们可以分析它们的中央日志服务器。是否可以使用spring boot的访问日志执行相同的操作?

如果没有,是否有任何其他建议或最佳做法可以从中央服务器上的多个Spring启动应用程序中收集访问日志?

1 个答案:

答案 0 :(得分:4)

好的我经过一些研究后发现了。您需要向tomcat添加自定义阀门:

@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
    TomcatEmbeddedServletContainerFactory factory 
        = new TomcatEmbeddedServletContainerFactory();
    GelfAccessLogValve gelfAccessLogValve = new GelfAccessLogValve();
    gelfAccessLogValve.setPattern("%h %m %U %I %l %u %t "%r" %s %b");
    factory.addContextValves(gelfAccessLogValve);
    return factory;
}

我写的阀门基于paluch.biz's valve,但不同之处在于它仅依赖于graylog的gelf客户端。因此,您需要将此依赖项添加到您的pom:

<dependency>
    <groupId>org.graylog2</groupId>
    <artifactId>gelfclient</artifactId>
    <version>1.4.0</version>
</dependency>

这是阀门代码:

public class GelfAccessLogValve extends AccessLogValve {

    private final static Map<Class, String> names = Collections.unmodifiableMap(new HashMap<Class, String>() {
        {
            put(HeaderElement.class, "Header");
            put(CookieElement.class, "Cookie");
            put(ResponseHeaderElement.class, "ResponseHeader");
            put(SessionAttributeElement.class, "SessionAttribute");
            put(RemoteAddrElement.class, "RemoteAddr");
            put(LocalAddrElement.class, "LocalAddr");
            put(ByteSentElement.class, "ByteSent");
            put(ElapsedTimeElement.class, "ElapsedTime");
            put(HostElement.class, "Host");
            put(ProtocolElement.class, "Protocol");
            put(MethodElement.class, "Method");
            put(PortElement.class, "LocalPort");
            put(QueryElement.class, "Query");
            put(RequestElement.class, "Request");
            put(FirstByteTimeElement.class, "FirstByteTime");
            put(HttpStatusCodeElement.class, "HttpStatusCode");
            put(SessionIdElement.class, "SessionId");
            put(DateAndTimeElement.class, "DateAndTime");
            put(UserElement.class, "User");
            put(RequestURIElement.class, "RequestURI");
            put(LocalServerNameElement.class, "LocalServerName");
            put(ThreadNameElement.class, "ThreadName");
        }
    });

    private String host = "localhost";
    private int port = 1234;
    private GelfTransport gelfSender;

    @Override
    public void log(Request request, Response response, long time) {

        if (gelfSender == null || !getState().isAvailable() || !getEnabled() || logElements == null || condition != null
                && null != request.getRequest().getAttribute(condition) || conditionIf != null
                && null == request.getRequest().getAttribute(conditionIf)) {
            return;
        }

        /**
         * XXX This is a bit silly, but we want to have start and stop time and duration consistent. It would be better to keep
         * start and stop simply in the request and/or response object and remove time (duration) from the interface.
         */
        long start = request.getCoyoteRequest().getStartTime();
        Date date = new Date(start + time);

        GelfMessage message = new GelfMessage(request.getMethod() + " " + request.getRequestURI());
        message.addAdditionalField("facility", getClass().getSimpleName());
        message.setFullMessage(request.getMethod() + " " + request.getRequestURI());
        message.setTimestamp(start + time);
        message.setLevel(GelfMessageLevel.INFO);

        for (int i = 0; i < logElements.length; i++) {

            String name = names.get(logElements[i].getClass());
            if (name == null) {
                continue;
            }

            CharArrayWriter result = new CharArrayWriter(128);
            logElements[i].addElement(result, date, request, response, time);
            message.addAdditionalField(name, result.toString());
        }

        try {
            gelfSender.send(message);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void createSender() {
        GelfConfiguration configuration = new GelfConfiguration(host, port);
        gelfSender = GelfTransports.create(configuration);
    }

    @Override
    protected synchronized void startInternal() throws LifecycleException {
        createSender();

        super.startInternal();
    }

    @Override
    protected synchronized void stopInternal() throws LifecycleException {
        if (gelfSender != null) {
            gelfSender.stop();
            gelfSender = null;
        }
        super.stopInternal();
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
}
相关问题