1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.UnstableApi;
24 import io.netty.util.internal.logging.InternalLogLevel;
25 import io.netty.util.internal.logging.InternalLogger;
26 import io.netty.util.internal.logging.InternalLoggerFactory;
27
28 import static io.netty.util.internal.ObjectUtil.checkNotNull;
29
30
31
32
33 @UnstableApi
34 public class Http2FrameLogger extends ChannelHandlerAdapter {
35
36 public enum Direction {
37 INBOUND,
38 OUTBOUND
39 }
40
41 private static final int BUFFER_LENGTH_THRESHOLD = 64;
42 private final InternalLogger logger;
43 private final InternalLogLevel level;
44
45 public Http2FrameLogger(LogLevel level) {
46 this(checkAndConvertLevel(level), InternalLoggerFactory.getInstance(Http2FrameLogger.class));
47 }
48
49 public Http2FrameLogger(LogLevel level, String name) {
50 this(checkAndConvertLevel(level), InternalLoggerFactory.getInstance(checkNotNull(name, "name")));
51 }
52
53 public Http2FrameLogger(LogLevel level, Class<?> clazz) {
54 this(checkAndConvertLevel(level), InternalLoggerFactory.getInstance(checkNotNull(clazz, "clazz")));
55 }
56
57 private Http2FrameLogger(InternalLogLevel level, InternalLogger logger) {
58 this.level = level;
59 this.logger = logger;
60 }
61
62 private static InternalLogLevel checkAndConvertLevel(LogLevel level) {
63 return checkNotNull(level, "level").toInternalLevel();
64 }
65
66 public boolean isEnabled() {
67 return logger.isEnabled(level);
68 }
69
70 public void logData(Direction direction, ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
71 boolean endStream) {
72 if (isEnabled()) {
73 logger.log(level, "{} {} DATA: streamId={} padding={} endStream={} length={} bytes={}", ctx.channel(),
74 direction.name(), streamId, padding, endStream, data.readableBytes(), toString(data));
75 }
76 }
77
78 public void logHeaders(Direction direction, ChannelHandlerContext ctx, int streamId, Http2Headers headers,
79 int padding, boolean endStream) {
80 if (isEnabled()) {
81 logger.log(level, "{} {} HEADERS: streamId={} headers={} padding={} endStream={}", ctx.channel(),
82 direction.name(), streamId, headers, padding, endStream);
83 }
84 }
85
86 public void logHeaders(Direction direction, ChannelHandlerContext ctx, int streamId, Http2Headers headers,
87 int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) {
88 if (isEnabled()) {
89 logger.log(level, "{} {} HEADERS: streamId={} headers={} streamDependency={} weight={} exclusive={} " +
90 "padding={} endStream={}", ctx.channel(),
91 direction.name(), streamId, headers, streamDependency, weight, exclusive, padding, endStream);
92 }
93 }
94
95 public void logPriority(Direction direction, ChannelHandlerContext ctx, int streamId, int streamDependency,
96 short weight, boolean exclusive) {
97 if (isEnabled()) {
98 logger.log(level, "{} {} PRIORITY: streamId={} streamDependency={} weight={} exclusive={}", ctx.channel(),
99 direction.name(), streamId, streamDependency, weight, exclusive);
100 }
101 }
102
103 public void logRstStream(Direction direction, ChannelHandlerContext ctx, int streamId, long errorCode) {
104 if (isEnabled()) {
105 logger.log(level, "{} {} RST_STREAM: streamId={} errorCode={}", ctx.channel(),
106 direction.name(), streamId, errorCode);
107 }
108 }
109
110 public void logSettingsAck(Direction direction, ChannelHandlerContext ctx) {
111 logger.log(level, "{} {} SETTINGS: ack=true", ctx.channel(), direction.name());
112 }
113
114 public void logSettings(Direction direction, ChannelHandlerContext ctx, Http2Settings settings) {
115 if (isEnabled()) {
116 logger.log(level, "{} {} SETTINGS: ack=false settings={}", ctx.channel(), direction.name(), settings);
117 }
118 }
119
120 public void logPing(Direction direction, ChannelHandlerContext ctx, long data) {
121 if (isEnabled()) {
122 logger.log(level, "{} {} PING: ack=false bytes={}", ctx.channel(),
123 direction.name(), data);
124 }
125 }
126
127 public void logPingAck(Direction direction, ChannelHandlerContext ctx, long data) {
128 if (isEnabled()) {
129 logger.log(level, "{} {} PING: ack=true bytes={}", ctx.channel(),
130 direction.name(), data);
131 }
132 }
133
134 public void logPushPromise(Direction direction, ChannelHandlerContext ctx, int streamId, int promisedStreamId,
135 Http2Headers headers, int padding) {
136 if (isEnabled()) {
137 logger.log(level, "{} {} PUSH_PROMISE: streamId={} promisedStreamId={} headers={} padding={}",
138 ctx.channel(), direction.name(), streamId, promisedStreamId, headers, padding);
139 }
140 }
141
142 public void logGoAway(Direction direction, ChannelHandlerContext ctx, int lastStreamId, long errorCode,
143 ByteBuf debugData) {
144 if (isEnabled()) {
145 logger.log(level, "{} {} GO_AWAY: lastStreamId={} errorCode={} length={} bytes={}", ctx.channel(),
146 direction.name(), lastStreamId, errorCode, debugData.readableBytes(), toString(debugData));
147 }
148 }
149
150 public void logWindowsUpdate(Direction direction, ChannelHandlerContext ctx, int streamId,
151 int windowSizeIncrement) {
152 if (isEnabled()) {
153 logger.log(level, "{} {} WINDOW_UPDATE: streamId={} windowSizeIncrement={}", ctx.channel(),
154 direction.name(), streamId, windowSizeIncrement);
155 }
156 }
157
158 public void logUnknownFrame(Direction direction, ChannelHandlerContext ctx, byte frameType, int streamId,
159 Http2Flags flags, ByteBuf data) {
160 if (isEnabled()) {
161 logger.log(level, "{} {} UNKNOWN: frameType={} streamId={} flags={} length={} bytes={}", ctx.channel(),
162 direction.name(), frameType & 0xFF, streamId, flags.value(), data.readableBytes(), toString(data));
163 }
164 }
165
166 private String toString(ByteBuf buf) {
167 if (level == InternalLogLevel.TRACE || buf.readableBytes() <= BUFFER_LENGTH_THRESHOLD) {
168
169 return ByteBufUtil.hexDump(buf);
170 }
171
172
173 int length = Math.min(buf.readableBytes(), BUFFER_LENGTH_THRESHOLD);
174 return ByteBufUtil.hexDump(buf, buf.readerIndex(), length) + "...";
175 }
176 }