1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.handler.logging;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.buffer.ByteBufHolder;
20 import io.netty.channel.ChannelDuplexHandler;
21 import io.netty.channel.ChannelHandler;
22 import io.netty.channel.ChannelHandler.Sharable;
23 import io.netty.channel.ChannelHandlerContext;
24 import io.netty.channel.ChannelOutboundHandler;
25 import io.netty.channel.ChannelPromise;
26 import io.netty.util.internal.ObjectUtil;
27 import io.netty.util.internal.logging.InternalLogLevel;
28 import io.netty.util.internal.logging.InternalLogger;
29 import io.netty.util.internal.logging.InternalLoggerFactory;
30
31 import java.net.SocketAddress;
32
33 import static io.netty.buffer.ByteBufUtil.appendPrettyHexDump;
34 import static io.netty.util.internal.StringUtil.NEWLINE;
35
36
37
38
39
40 @Sharable
41 @SuppressWarnings({ "StringConcatenationInsideStringBufferAppend", "StringBufferReplaceableByString" })
42 public class LoggingHandler extends ChannelDuplexHandler {
43
44 private static final LogLevel DEFAULT_LEVEL = LogLevel.DEBUG;
45
46 protected final InternalLogger logger;
47 protected final InternalLogLevel internalLevel;
48
49 private final LogLevel level;
50 private final ByteBufFormat byteBufFormat;
51
52
53
54
55
56 public LoggingHandler() {
57 this(DEFAULT_LEVEL);
58 }
59
60
61
62
63
64
65 public LoggingHandler(ByteBufFormat format) {
66 this(DEFAULT_LEVEL, format);
67 }
68
69
70
71
72
73
74
75 public LoggingHandler(LogLevel level) {
76 this(level, ByteBufFormat.HEX_DUMP);
77 }
78
79
80
81
82
83
84
85
86 public LoggingHandler(LogLevel level, ByteBufFormat byteBufFormat) {
87 this.level = ObjectUtil.checkNotNull(level, "level");
88 this.byteBufFormat = ObjectUtil.checkNotNull(byteBufFormat, "byteBufFormat");
89 logger = InternalLoggerFactory.getInstance(getClass());
90 internalLevel = level.toInternalLevel();
91 }
92
93
94
95
96
97
98
99 public LoggingHandler(Class<?> clazz) {
100 this(clazz, DEFAULT_LEVEL);
101 }
102
103
104
105
106
107
108
109 public LoggingHandler(Class<?> clazz, LogLevel level) {
110 this(clazz, level, ByteBufFormat.HEX_DUMP);
111 }
112
113
114
115
116
117
118
119
120 public LoggingHandler(Class<?> clazz, LogLevel level, ByteBufFormat byteBufFormat) {
121 ObjectUtil.checkNotNull(clazz, "clazz");
122 this.level = ObjectUtil.checkNotNull(level, "level");
123 this.byteBufFormat = ObjectUtil.checkNotNull(byteBufFormat, "byteBufFormat");
124 logger = InternalLoggerFactory.getInstance(clazz);
125 internalLevel = level.toInternalLevel();
126 }
127
128
129
130
131
132
133 public LoggingHandler(String name) {
134 this(name, DEFAULT_LEVEL);
135 }
136
137
138
139
140
141
142
143 public LoggingHandler(String name, LogLevel level) {
144 this(name, level, ByteBufFormat.HEX_DUMP);
145 }
146
147
148
149
150
151
152
153
154 public LoggingHandler(String name, LogLevel level, ByteBufFormat byteBufFormat) {
155 ObjectUtil.checkNotNull(name, "name");
156
157 this.level = ObjectUtil.checkNotNull(level, "level");
158 this.byteBufFormat = ObjectUtil.checkNotNull(byteBufFormat, "byteBufFormat");
159 logger = InternalLoggerFactory.getInstance(name);
160 internalLevel = level.toInternalLevel();
161 }
162
163
164
165
166 public LogLevel level() {
167 return level;
168 }
169
170
171
172
173 public ByteBufFormat byteBufFormat() {
174 return byteBufFormat;
175 }
176
177 @Override
178 public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
179 if (logger.isEnabled(internalLevel)) {
180 logger.log(internalLevel, format(ctx, "REGISTERED"));
181 }
182 ctx.fireChannelRegistered();
183 }
184
185 @Override
186 public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
187 if (logger.isEnabled(internalLevel)) {
188 logger.log(internalLevel, format(ctx, "UNREGISTERED"));
189 }
190 ctx.fireChannelUnregistered();
191 }
192
193 @Override
194 public void channelActive(ChannelHandlerContext ctx) throws Exception {
195 if (logger.isEnabled(internalLevel)) {
196 logger.log(internalLevel, format(ctx, "ACTIVE"));
197 }
198 ctx.fireChannelActive();
199 }
200
201 @Override
202 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
203 if (logger.isEnabled(internalLevel)) {
204 logger.log(internalLevel, format(ctx, "INACTIVE"));
205 }
206 ctx.fireChannelInactive();
207 }
208
209 @Override
210 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
211 if (logger.isEnabled(internalLevel)) {
212 logger.log(internalLevel, format(ctx, "EXCEPTION", cause), cause);
213 }
214 ctx.fireExceptionCaught(cause);
215 }
216
217 @Override
218 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
219 if (logger.isEnabled(internalLevel)) {
220 logger.log(internalLevel, format(ctx, "USER_EVENT", evt));
221 }
222 ctx.fireUserEventTriggered(evt);
223 }
224
225 @Override
226 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
227 if (logger.isEnabled(internalLevel)) {
228 logger.log(internalLevel, format(ctx, "BIND", localAddress));
229 }
230 ctx.bind(localAddress, promise);
231 }
232
233 @Override
234 public void connect(
235 ChannelHandlerContext ctx,
236 SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
237 if (logger.isEnabled(internalLevel)) {
238 logger.log(internalLevel, format(ctx, "CONNECT", remoteAddress, localAddress));
239 }
240 ctx.connect(remoteAddress, localAddress, promise);
241 }
242
243 @Override
244 public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
245 if (logger.isEnabled(internalLevel)) {
246 logger.log(internalLevel, format(ctx, "DISCONNECT"));
247 }
248 ctx.disconnect(promise);
249 }
250
251 @Override
252 public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
253 if (logger.isEnabled(internalLevel)) {
254 logger.log(internalLevel, format(ctx, "CLOSE"));
255 }
256 ctx.close(promise);
257 }
258
259 @Override
260 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
261 if (logger.isEnabled(internalLevel)) {
262 logger.log(internalLevel, format(ctx, "DEREGISTER"));
263 }
264 ctx.deregister(promise);
265 }
266
267 @Override
268 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
269 if (logger.isEnabled(internalLevel)) {
270 logger.log(internalLevel, format(ctx, "READ COMPLETE"));
271 }
272 ctx.fireChannelReadComplete();
273 }
274
275 @Override
276 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
277 if (logger.isEnabled(internalLevel)) {
278 logger.log(internalLevel, format(ctx, "READ", msg));
279 }
280 ctx.fireChannelRead(msg);
281 }
282
283 @Override
284 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
285 if (logger.isEnabled(internalLevel)) {
286 logger.log(internalLevel, format(ctx, "WRITE", msg));
287 }
288 ctx.write(msg, promise);
289 }
290
291 @Override
292 public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
293 if (logger.isEnabled(internalLevel)) {
294 logger.log(internalLevel, format(ctx, "WRITABILITY CHANGED"));
295 }
296 ctx.fireChannelWritabilityChanged();
297 }
298
299 @Override
300 public void flush(ChannelHandlerContext ctx) throws Exception {
301 if (logger.isEnabled(internalLevel)) {
302 logger.log(internalLevel, format(ctx, "FLUSH"));
303 }
304 ctx.flush();
305 }
306
307
308
309
310
311
312 protected String format(ChannelHandlerContext ctx, String eventName) {
313 String chStr = ctx.channel().toString();
314 return new StringBuilder(chStr.length() + 1 + eventName.length())
315 .append(chStr)
316 .append(' ')
317 .append(eventName)
318 .toString();
319 }
320
321
322
323
324
325
326
327 protected String format(ChannelHandlerContext ctx, String eventName, Object arg) {
328 if (arg instanceof ByteBuf) {
329 return formatByteBuf(ctx, eventName, (ByteBuf) arg);
330 } else if (arg instanceof ByteBufHolder) {
331 return formatByteBufHolder(ctx, eventName, (ByteBufHolder) arg);
332 } else {
333 return formatSimple(ctx, eventName, arg);
334 }
335 }
336
337
338
339
340
341
342
343
344
345 protected String format(ChannelHandlerContext ctx, String eventName, Object firstArg, Object secondArg) {
346 if (secondArg == null) {
347 return formatSimple(ctx, eventName, firstArg);
348 }
349
350 String chStr = ctx.channel().toString();
351 String arg1Str = String.valueOf(firstArg);
352 String arg2Str = secondArg.toString();
353 StringBuilder buf = new StringBuilder(
354 chStr.length() + 1 + eventName.length() + 2 + arg1Str.length() + 2 + arg2Str.length());
355 buf.append(chStr).append(' ').append(eventName).append(": ").append(arg1Str).append(", ").append(arg2Str);
356 return buf.toString();
357 }
358
359
360
361
362 private String formatByteBuf(ChannelHandlerContext ctx, String eventName, ByteBuf msg) {
363 String chStr = ctx.channel().toString();
364 int length = msg.readableBytes();
365 if (length == 0) {
366 StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 4);
367 buf.append(chStr).append(' ').append(eventName).append(": 0B");
368 return buf.toString();
369 } else {
370 int outputLength = chStr.length() + 1 + eventName.length() + 2 + 10 + 1;
371 if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
372 int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
373 int hexDumpLength = 2 + rows * 80;
374 outputLength += hexDumpLength;
375 }
376 StringBuilder buf = new StringBuilder(outputLength);
377 buf.append(chStr).append(' ').append(eventName).append(": ").append(length).append('B');
378 if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
379 buf.append(NEWLINE);
380 appendPrettyHexDump(buf, msg);
381 }
382
383 return buf.toString();
384 }
385 }
386
387
388
389
390 private String formatByteBufHolder(ChannelHandlerContext ctx, String eventName, ByteBufHolder msg) {
391 String chStr = ctx.channel().toString();
392 String msgStr = msg.toString();
393 ByteBuf content = msg.content();
394 int length = content.readableBytes();
395 if (length == 0) {
396 StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 4);
397 buf.append(chStr).append(' ').append(eventName).append(", ").append(msgStr).append(", 0B");
398 return buf.toString();
399 } else {
400 int outputLength = chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 2 + 10 + 1;
401 if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
402 int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
403 int hexDumpLength = 2 + rows * 80;
404 outputLength += hexDumpLength;
405 }
406 StringBuilder buf = new StringBuilder(outputLength);
407 buf.append(chStr).append(' ').append(eventName).append(": ")
408 .append(msgStr).append(", ").append(length).append('B');
409 if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
410 buf.append(NEWLINE);
411 appendPrettyHexDump(buf, content);
412 }
413
414 return buf.toString();
415 }
416 }
417
418
419
420
421 private static String formatSimple(ChannelHandlerContext ctx, String eventName, Object msg) {
422 String chStr = ctx.channel().toString();
423 String msgStr = String.valueOf(msg);
424 StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length());
425 return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString();
426 }
427 }