View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.handler.codec.http2;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufUtil;
20  import io.netty.channel.ChannelHandlerAdapter;
21  import io.netty.channel.ChannelHandlerContext;
22  import io.netty.handler.logging.LogLevel;
23  import io.netty.util.internal.StringUtil;
24  import io.netty.util.internal.UnstableApi;
25  import io.netty.util.internal.logging.InternalLogLevel;
26  import io.netty.util.internal.logging.InternalLogger;
27  import io.netty.util.internal.logging.InternalLoggerFactory;
28  
29  import static io.netty.util.internal.ObjectUtil.checkNotNull;
30  
31  /**
32   * Logs HTTP2 frames for debugging purposes.
33   */
34  @UnstableApi
35  public class Http2FrameLogger extends ChannelHandlerAdapter {
36  
37      public enum Direction {
38          INBOUND,
39          OUTBOUND
40      }
41  
42      private static final int BUFFER_LENGTH_THRESHOLD = 64;
43      private final InternalLogger logger;
44      private final InternalLogLevel level;
45  
46      public Http2FrameLogger(LogLevel level) {
47          this(level.toInternalLevel(), InternalLoggerFactory.getInstance(Http2FrameLogger.class));
48      }
49  
50      public Http2FrameLogger(LogLevel level, String name) {
51          this(level.toInternalLevel(), InternalLoggerFactory.getInstance(name));
52      }
53  
54      public Http2FrameLogger(LogLevel level, Class<?> clazz) {
55          this(level.toInternalLevel(), InternalLoggerFactory.getInstance(clazz));
56      }
57  
58      private Http2FrameLogger(InternalLogLevel level, InternalLogger logger) {
59          this.level = checkNotNull(level, "level");
60          this.logger = checkNotNull(logger, "logger");
61      }
62  
63      public void logData(Direction direction, ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
64              boolean endStream) {
65          logger.log(level, "{} {} DATA: streamId={} padding={} endStream={} length={} bytes={}", ctx.channel(),
66                  direction.name(), streamId, padding, endStream, data.readableBytes(), toString(data));
67      }
68  
69      public void logHeaders(Direction direction, ChannelHandlerContext ctx, int streamId, Http2Headers headers,
70              int padding, boolean endStream) {
71          logger.log(level, "{} {} HEADERS: streamId={} headers={} padding={} endStream={}", ctx.channel(),
72                  direction.name(), streamId, headers, padding, endStream);
73      }
74  
75      public void logHeaders(Direction direction, ChannelHandlerContext ctx, int streamId, Http2Headers headers,
76              int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) {
77          logger.log(level, "{} {} HEADERS: streamId={} headers={} streamDependency={} weight={} exclusive={} " +
78                          "padding={} endStream={}", ctx.channel(),
79                  direction.name(), streamId, headers, streamDependency, weight, exclusive, padding, endStream);
80      }
81  
82      public void logPriority(Direction direction, ChannelHandlerContext ctx, int streamId, int streamDependency,
83              short weight, boolean exclusive) {
84          logger.log(level, "{} {} PRIORITY: streamId={} streamDependency={} weight={} exclusive={}", ctx.channel(),
85                  direction.name(), streamId, streamDependency, weight, exclusive);
86      }
87  
88      public void logRstStream(Direction direction, ChannelHandlerContext ctx, int streamId, long errorCode) {
89          logger.log(level, "{} {} RST_STREAM: streamId={} errorCode={}", ctx.channel(),
90                  direction.name(), streamId, errorCode);
91      }
92  
93      public void logSettingsAck(Direction direction, ChannelHandlerContext ctx) {
94          logger.log(level, "{} {} SETTINGS: ack=true", ctx.channel(), direction.name());
95      }
96  
97      public void logSettings(Direction direction, ChannelHandlerContext ctx, Http2Settings settings) {
98          logger.log(level, "{} {} SETTINGS: ack=false settings={}", ctx.channel(), direction.name(), settings);
99      }
100 
101     public void logPing(Direction direction, ChannelHandlerContext ctx, ByteBuf data) {
102         logger.log(level, "{} {} PING: ack=false length={} bytes={}", ctx.channel(),
103                 direction.name(), data.readableBytes(), toString(data));
104     }
105 
106     public void logPingAck(Direction direction, ChannelHandlerContext ctx, ByteBuf data) {
107         logger.log(level, "{} {} PING: ack=true length={} bytes={}", ctx.channel(),
108                 direction.name(), data.readableBytes(), toString(data));
109     }
110 
111     public void logPushPromise(Direction direction, ChannelHandlerContext ctx, int streamId, int promisedStreamId,
112             Http2Headers headers, int padding) {
113         logger.log(level, "{} {} PUSH_PROMISE: streamId={} promisedStreamId={} headers={} padding={}", ctx.channel(),
114                 direction.name(), streamId, promisedStreamId, headers, padding);
115     }
116 
117     public void logGoAway(Direction direction, ChannelHandlerContext ctx, int lastStreamId, long errorCode,
118             ByteBuf debugData) {
119         logger.log(level, "{} {} GO_AWAY: lastStreamId={} errorCode={} length={} bytes={}", ctx.channel(),
120                 direction.name(), lastStreamId, errorCode, debugData.readableBytes(), toString(debugData));
121     }
122 
123     public void logWindowsUpdate(Direction direction, ChannelHandlerContext ctx, int streamId,
124             int windowSizeIncrement) {
125         logger.log(level, "{} {} WINDOW_UPDATE: streamId={} windowSizeIncrement={}", ctx.channel(),
126                 direction.name(), streamId, windowSizeIncrement);
127     }
128 
129     public void logUnknownFrame(Direction direction, ChannelHandlerContext ctx, byte frameType, int streamId,
130             Http2Flags flags, ByteBuf data) {
131         logger.log(level, "{} {} UNKNOWN: frameType={} streamId={} flags={} length={} bytes={}", ctx.channel(),
132                 direction.name(), frameType & 0xFF, streamId, flags.value(), data.readableBytes(), toString(data));
133     }
134 
135     private String toString(ByteBuf buf) {
136         if (!logger.isEnabled(level)) {
137             return StringUtil.EMPTY_STRING;
138         }
139 
140         if (level == InternalLogLevel.TRACE || buf.readableBytes() <= BUFFER_LENGTH_THRESHOLD) {
141             // Log the entire buffer.
142             return ByteBufUtil.hexDump(buf);
143         }
144 
145         // Otherwise just log the first 64 bytes.
146         int length = Math.min(buf.readableBytes(), BUFFER_LENGTH_THRESHOLD);
147         return ByteBufUtil.hexDump(buf, buf.readerIndex(), length) + "...";
148     }
149 }