1
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
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
56 public LoggingHandler() {
57 this(DEFAULT_LEVEL);
58 }
59
60
66 public LoggingHandler(LogLevel level) {
67 this(level, ByteBufFormat.HEX_DUMP);
68 }
69
70
77 public LoggingHandler(LogLevel level, ByteBufFormat byteBufFormat) {
78 this.level = ObjectUtil.checkNotNull(level, "level");
79 this.byteBufFormat = ObjectUtil.checkNotNull(byteBufFormat, "byteBufFormat");
80 logger = InternalLoggerFactory.getInstance(getClass());
81 internalLevel = level.toInternalLevel();
82 }
83
84
90 public LoggingHandler(Class<?> clazz) {
91 this(clazz, DEFAULT_LEVEL);
92 }
93
94
100 public LoggingHandler(Class<?> clazz, LogLevel level) {
101 this(clazz, level, ByteBufFormat.HEX_DUMP);
102 }
103
104
111 public LoggingHandler(Class<?> clazz, LogLevel level, ByteBufFormat byteBufFormat) {
112 ObjectUtil.checkNotNull(clazz, "clazz");
113 this.level = ObjectUtil.checkNotNull(level, "level");
114 this.byteBufFormat = ObjectUtil.checkNotNull(byteBufFormat, "byteBufFormat");
115 logger = InternalLoggerFactory.getInstance(clazz);
116 internalLevel = level.toInternalLevel();
117 }
118
119
124 public LoggingHandler(String name) {
125 this(name, DEFAULT_LEVEL);
126 }
127
128
134 public LoggingHandler(String name, LogLevel level) {
135 this(name, level, ByteBufFormat.HEX_DUMP);
136 }
137
138
145 public LoggingHandler(String name, LogLevel level, ByteBufFormat byteBufFormat) {
146 ObjectUtil.checkNotNull(name, "name");
147
148 this.level = ObjectUtil.checkNotNull(level, "level");
149 this.byteBufFormat = ObjectUtil.checkNotNull(byteBufFormat, "byteBufFormat");
150 logger = InternalLoggerFactory.getInstance(name);
151 internalLevel = level.toInternalLevel();
152 }
153
154
157 public LogLevel level() {
158 return level;
159 }
160
161
164 public ByteBufFormat byteBufFormat() {
165 return byteBufFormat;
166 }
167
168 @Override
169 public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
170 if (logger.isEnabled(internalLevel)) {
171 logger.log(internalLevel, format(ctx, "REGISTERED"));
172 }
173 ctx.fireChannelRegistered();
174 }
175
176 @Override
177 public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
178 if (logger.isEnabled(internalLevel)) {
179 logger.log(internalLevel, format(ctx, "UNREGISTERED"));
180 }
181 ctx.fireChannelUnregistered();
182 }
183
184 @Override
185 public void channelActive(ChannelHandlerContext ctx) throws Exception {
186 if (logger.isEnabled(internalLevel)) {
187 logger.log(internalLevel, format(ctx, "ACTIVE"));
188 }
189 ctx.fireChannelActive();
190 }
191
192 @Override
193 public void channelInactive(ChannelHandlerContext ctx) throws Exception {
194 if (logger.isEnabled(internalLevel)) {
195 logger.log(internalLevel, format(ctx, "INACTIVE"));
196 }
197 ctx.fireChannelInactive();
198 }
199
200 @Override
201 public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
202 if (logger.isEnabled(internalLevel)) {
203 logger.log(internalLevel, format(ctx, "EXCEPTION", cause), cause);
204 }
205 ctx.fireExceptionCaught(cause);
206 }
207
208 @Override
209 public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
210 if (logger.isEnabled(internalLevel)) {
211 logger.log(internalLevel, format(ctx, "USER_EVENT", evt));
212 }
213 ctx.fireUserEventTriggered(evt);
214 }
215
216 @Override
217 public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
218 if (logger.isEnabled(internalLevel)) {
219 logger.log(internalLevel, format(ctx, "BIND", localAddress));
220 }
221 ctx.bind(localAddress, promise);
222 }
223
224 @Override
225 public void connect(
226 ChannelHandlerContext ctx,
227 SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
228 if (logger.isEnabled(internalLevel)) {
229 logger.log(internalLevel, format(ctx, "CONNECT", remoteAddress, localAddress));
230 }
231 ctx.connect(remoteAddress, localAddress, promise);
232 }
233
234 @Override
235 public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
236 if (logger.isEnabled(internalLevel)) {
237 logger.log(internalLevel, format(ctx, "DISCONNECT"));
238 }
239 ctx.disconnect(promise);
240 }
241
242 @Override
243 public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
244 if (logger.isEnabled(internalLevel)) {
245 logger.log(internalLevel, format(ctx, "CLOSE"));
246 }
247 ctx.close(promise);
248 }
249
250 @Override
251 public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
252 if (logger.isEnabled(internalLevel)) {
253 logger.log(internalLevel, format(ctx, "DEREGISTER"));
254 }
255 ctx.deregister(promise);
256 }
257
258 @Override
259 public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
260 if (logger.isEnabled(internalLevel)) {
261 logger.log(internalLevel, format(ctx, "READ COMPLETE"));
262 }
263 ctx.fireChannelReadComplete();
264 }
265
266 @Override
267 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
268 if (logger.isEnabled(internalLevel)) {
269 logger.log(internalLevel, format(ctx, "READ", msg));
270 }
271 ctx.fireChannelRead(msg);
272 }
273
274 @Override
275 public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
276 if (logger.isEnabled(internalLevel)) {
277 logger.log(internalLevel, format(ctx, "WRITE", msg));
278 }
279 ctx.write(msg, promise);
280 }
281
282 @Override
283 public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
284 if (logger.isEnabled(internalLevel)) {
285 logger.log(internalLevel, format(ctx, "WRITABILITY CHANGED"));
286 }
287 ctx.fireChannelWritabilityChanged();
288 }
289
290 @Override
291 public void flush(ChannelHandlerContext ctx) throws Exception {
292 if (logger.isEnabled(internalLevel)) {
293 logger.log(internalLevel, format(ctx, "FLUSH"));
294 }
295 ctx.flush();
296 }
297
298
303 protected String format(ChannelHandlerContext ctx, String eventName) {
304 String chStr = ctx.channel().toString();
305 return new StringBuilder(chStr.length() + 1 + eventName.length())
306 .append(chStr)
307 .append(' ')
308 .append(eventName)
309 .toString();
310 }
311
312
318 protected String format(ChannelHandlerContext ctx, String eventName, Object arg) {
319 if (arg instanceof ByteBuf) {
320 return formatByteBuf(ctx, eventName, (ByteBuf) arg);
321 } else if (arg instanceof ByteBufHolder) {
322 return formatByteBufHolder(ctx, eventName, (ByteBufHolder) arg);
323 } else {
324 return formatSimple(ctx, eventName, arg);
325 }
326 }
327
328
336 protected String format(ChannelHandlerContext ctx, String eventName, Object firstArg, Object secondArg) {
337 if (secondArg == null) {
338 return formatSimple(ctx, eventName, firstArg);
339 }
340
341 String chStr = ctx.channel().toString();
342 String arg1Str = String.valueOf(firstArg);
343 String arg2Str = secondArg.toString();
344 StringBuilder buf = new StringBuilder(
345 chStr.length() + 1 + eventName.length() + 2 + arg1Str.length() + 2 + arg2Str.length());
346 buf.append(chStr).append(' ').append(eventName).append(": ").append(arg1Str).append(", ").append(arg2Str);
347 return buf.toString();
348 }
349
350
353 private String formatByteBuf(ChannelHandlerContext ctx, String eventName, ByteBuf msg) {
354 String chStr = ctx.channel().toString();
355 int length = msg.readableBytes();
356 if (length == 0) {
357 StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 4);
358 buf.append(chStr).append(' ').append(eventName).append(": 0B");
359 return buf.toString();
360 } else {
361 int outputLength = chStr.length() + 1 + eventName.length() + 2 + 10 + 1;
362 if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
363 int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
364 int hexDumpLength = 2 + rows * 80;
365 outputLength += hexDumpLength;
366 }
367 StringBuilder buf = new StringBuilder(outputLength);
368 buf.append(chStr).append(' ').append(eventName).append(": ").append(length).append('B');
369 if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
370 buf.append(NEWLINE);
371 appendPrettyHexDump(buf, msg);
372 }
373
374 return buf.toString();
375 }
376 }
377
378
381 private String formatByteBufHolder(ChannelHandlerContext ctx, String eventName, ByteBufHolder msg) {
382 String chStr = ctx.channel().toString();
383 String msgStr = msg.toString();
384 ByteBuf content = msg.content();
385 int length = content.readableBytes();
386 if (length == 0) {
387 StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 4);
388 buf.append(chStr).append(' ').append(eventName).append(", ").append(msgStr).append(", 0B");
389 return buf.toString();
390 } else {
391 int outputLength = chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 2 + 10 + 1;
392 if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
393 int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
394 int hexDumpLength = 2 + rows * 80;
395 outputLength += hexDumpLength;
396 }
397 StringBuilder buf = new StringBuilder(outputLength);
398 buf.append(chStr).append(' ').append(eventName).append(": ")
399 .append(msgStr).append(", ").append(length).append('B');
400 if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
401 buf.append(NEWLINE);
402 appendPrettyHexDump(buf, content);
403 }
404
405 return buf.toString();
406 }
407 }
408
409
412 private static String formatSimple(ChannelHandlerContext ctx, String eventName, Object msg) {
413 String chStr = ctx.channel().toString();
414 String msgStr = String.valueOf(msg);
415 StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length());
416 return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString();
417 }
418 }
419