All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.netty.common.accesslog.AccessLogChannelHandler Maven / Gradle / Ivy

There is a newer version: 2.5.13
Show newest version
/*
 * Copyright 2018 Netflix, Inc.
 *
 *      Licensed under the Apache License, Version 2.0 (the "License");
 *      you may not use this file except in compliance with the License.
 *      You may obtain a copy of the License at
 *
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 *      Unless required by applicable law or agreed to in writing, software
 *      distributed under the License is distributed on an "AS IS" BASIS,
 *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *      See the License for the specific language governing permissions and
 *      limitations under the License.
 */

package com.netflix.netty.common.accesslog;

import com.netflix.netty.common.HttpLifecycleChannelHandler;
import com.netflix.netty.common.SourceAddressChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.util.AttributeKey;
import java.time.LocalDateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * User: [email protected]
 * Date: 4/14/16
 * Time: 3:51 PM
 */
public final class AccessLogChannelHandler {
    private static final AttributeKey ATTR_REQ_STATE =
            AttributeKey.newInstance("_accesslog_requeststate");

    private static final Logger LOG = LoggerFactory.getLogger(AccessLogChannelHandler.class);

    public static final class AccessLogInboundChannelHandler extends ChannelInboundHandlerAdapter {
        private final AccessLogPublisher publisher;

        public AccessLogInboundChannelHandler(AccessLogPublisher publisher) {
            this.publisher = publisher;
        }

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            if (msg instanceof HttpRequest) {
                RequestState state = new RequestState();
                state.request = (HttpRequest) msg;
                state.startTimeNs = System.nanoTime();
                state.requestBodySize = 0;
                ctx.channel().attr(ATTR_REQ_STATE).set(state);
            }

            if (msg instanceof HttpContent) {
                RequestState state = ctx.channel().attr(ATTR_REQ_STATE).get();
                if (state != null) {
                    state.requestBodySize += ((HttpContent) msg).content().readableBytes();
                }
            }

            super.channelRead(ctx, msg);
        }

        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            if (evt instanceof HttpLifecycleChannelHandler.CompleteEvent) {
                // Get the stored request, and remove the attr from channel to cleanup.
                RequestState state = ctx.channel().attr(ATTR_REQ_STATE).get();
                ctx.channel().attr(ATTR_REQ_STATE).set(null);

                // Response complete, so now write to access log.
                long durationNs = System.nanoTime() - state.startTimeNs;
                String remoteIp = ctx.channel()
                        .attr(SourceAddressChannelHandler.ATTR_SOURCE_ADDRESS)
                        .get();
                Integer localPort = ctx.channel()
                        .attr(SourceAddressChannelHandler.ATTR_SERVER_LOCAL_PORT)
                        .get();

                if (state.response == null) {
                    LOG.debug(
                            "Response null in AccessLog, Complete reason={}, duration={}, url={}, method={}",
                            ((HttpLifecycleChannelHandler.CompleteEvent) evt).getReason(),
                            durationNs / (1000 * 1000),
                            state.request != null ? state.request.uri() : "-",
                            state.request != null ? state.request.method() : "-");
                }

                publisher.log(
                        ctx.channel(),
                        state.request,
                        state.response,
                        state.dateTime,
                        localPort,
                        remoteIp,
                        durationNs,
                        state.requestBodySize,
                        state.responseBodySize);
            }

            super.userEventTriggered(ctx, evt);
        }
    }

    public static final class AccessLogOutboundChannelHandler extends ChannelOutboundHandlerAdapter {
        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            RequestState state = ctx.channel().attr(ATTR_REQ_STATE).get();

            if (msg instanceof HttpResponse) {
                state.response = (HttpResponse) msg;
                state.responseBodySize = 0;
            }

            if (msg instanceof HttpContent) {
                state.responseBodySize += ((HttpContent) msg).content().readableBytes();
            }

            super.write(ctx, msg, promise);
        }
    }

    private static class RequestState {
        LocalDateTime dateTime = LocalDateTime.now();
        HttpRequest request;
        HttpResponse response;
        long startTimeNs;
        int requestBodySize = 0;
        int responseBodySize = 0;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy