/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you 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 io.netty.handler.logging;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPromise;
import io.netty.util.internal.logging.InternalLogLevel;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import java.net.SocketAddress;

import static io.netty.buffer.ByteBufUtil.appendPrettyHexDump;
import static io.netty.util.internal.StringUtil.NEWLINE;

A ChannelHandler that logs all events using a logging framework. By default, all events are logged at DEBUG level.
/** * A {@link ChannelHandler} that logs all events using a logging framework. * By default, all events are logged at <tt>DEBUG</tt> level. */
@Sharable @SuppressWarnings({ "StringConcatenationInsideStringBufferAppend", "StringBufferReplaceableByString" }) public class LoggingHandler extends ChannelDuplexHandler { private static final LogLevel DEFAULT_LEVEL = LogLevel.DEBUG; protected final InternalLogger logger; protected final InternalLogLevel internalLevel; private final LogLevel level;
Creates a new instance whose logger name is the fully qualified class name of the instance with hex dump enabled.
/** * Creates a new instance whose logger name is the fully qualified class * name of the instance with hex dump enabled. */
public LoggingHandler() { this(DEFAULT_LEVEL); }
Creates a new instance whose logger name is the fully qualified class name of the instance.
Params:
  • level – the log level
/** * Creates a new instance whose logger name is the fully qualified class * name of the instance. * * @param level the log level */
public LoggingHandler(LogLevel level) { if (level == null) { throw new NullPointerException("level"); } logger = InternalLoggerFactory.getInstance(getClass()); this.level = level; internalLevel = level.toInternalLevel(); }
Creates a new instance with the specified logger name and with hex dump enabled.
Params:
  • clazz – the class type to generate the logger for
/** * Creates a new instance with the specified logger name and with hex dump * enabled. * * @param clazz the class type to generate the logger for */
public LoggingHandler(Class<?> clazz) { this(clazz, DEFAULT_LEVEL); }
Creates a new instance with the specified logger name.
Params:
  • clazz – the class type to generate the logger for
  • level – the log level
/** * Creates a new instance with the specified logger name. * * @param clazz the class type to generate the logger for * @param level the log level */
public LoggingHandler(Class<?> clazz, LogLevel level) { if (clazz == null) { throw new NullPointerException("clazz"); } if (level == null) { throw new NullPointerException("level"); } logger = InternalLoggerFactory.getInstance(clazz); this.level = level; internalLevel = level.toInternalLevel(); }
Creates a new instance with the specified logger name using the default log level.
Params:
  • name – the name of the class to use for the logger
/** * Creates a new instance with the specified logger name using the default log level. * * @param name the name of the class to use for the logger */
public LoggingHandler(String name) { this(name, DEFAULT_LEVEL); }
Creates a new instance with the specified logger name.
Params:
  • name – the name of the class to use for the logger
  • level – the log level
/** * Creates a new instance with the specified logger name. * * @param name the name of the class to use for the logger * @param level the log level */
public LoggingHandler(String name, LogLevel level) { if (name == null) { throw new NullPointerException("name"); } if (level == null) { throw new NullPointerException("level"); } logger = InternalLoggerFactory.getInstance(name); this.level = level; internalLevel = level.toInternalLevel(); }
Returns the LogLevel that this handler uses to log
/** * Returns the {@link LogLevel} that this handler uses to log */
public LogLevel level() { return level; } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "REGISTERED")); } ctx.fireChannelRegistered(); } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "UNREGISTERED")); } ctx.fireChannelUnregistered(); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "ACTIVE")); } ctx.fireChannelActive(); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "INACTIVE")); } ctx.fireChannelInactive(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "EXCEPTION", cause), cause); } ctx.fireExceptionCaught(cause); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "USER_EVENT", evt)); } ctx.fireUserEventTriggered(evt); } @Override public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "BIND", localAddress)); } ctx.bind(localAddress, promise); } @Override public void connect( ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "CONNECT", remoteAddress, localAddress)); } ctx.connect(remoteAddress, localAddress, promise); } @Override public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "DISCONNECT")); } ctx.disconnect(promise); } @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "CLOSE")); } ctx.close(promise); } @Override public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "DEREGISTER")); } ctx.deregister(promise); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "READ COMPLETE")); } ctx.fireChannelReadComplete(); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "READ", msg)); } ctx.fireChannelRead(msg); } @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "WRITE", msg)); } ctx.write(msg, promise); } @Override public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "WRITABILITY CHANGED")); } ctx.fireChannelWritabilityChanged(); } @Override public void flush(ChannelHandlerContext ctx) throws Exception { if (logger.isEnabled(internalLevel)) { logger.log(internalLevel, format(ctx, "FLUSH")); } ctx.flush(); }
Formats an event and returns the formatted message.
Params:
  • eventName – the name of the event
/** * Formats an event and returns the formatted message. * * @param eventName the name of the event */
protected String format(ChannelHandlerContext ctx, String eventName) { String chStr = ctx.channel().toString(); return new StringBuilder(chStr.length() + 1 + eventName.length()) .append(chStr) .append(' ') .append(eventName) .toString(); }
Formats an event and returns the formatted message.
Params:
  • eventName – the name of the event
  • arg – the argument of the event
/** * Formats an event and returns the formatted message. * * @param eventName the name of the event * @param arg the argument of the event */
protected String format(ChannelHandlerContext ctx, String eventName, Object arg) { if (arg instanceof ByteBuf) { return formatByteBuf(ctx, eventName, (ByteBuf) arg); } else if (arg instanceof ByteBufHolder) { return formatByteBufHolder(ctx, eventName, (ByteBufHolder) arg); } else { return formatSimple(ctx, eventName, arg); } }
Formats an event and returns the formatted message. This method is currently only used for formatting ChannelOutboundHandler.connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise).
Params:
  • eventName – the name of the event
  • firstArg – the first argument of the event
  • secondArg – the second argument of the event
/** * Formats an event and returns the formatted message. This method is currently only used for formatting * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)}. * * @param eventName the name of the event * @param firstArg the first argument of the event * @param secondArg the second argument of the event */
protected String format(ChannelHandlerContext ctx, String eventName, Object firstArg, Object secondArg) { if (secondArg == null) { return formatSimple(ctx, eventName, firstArg); } String chStr = ctx.channel().toString(); String arg1Str = String.valueOf(firstArg); String arg2Str = secondArg.toString(); StringBuilder buf = new StringBuilder( chStr.length() + 1 + eventName.length() + 2 + arg1Str.length() + 2 + arg2Str.length()); buf.append(chStr).append(' ').append(eventName).append(": ").append(arg1Str).append(", ").append(arg2Str); return buf.toString(); }
Generates the default log message of the specified event whose argument is a ByteBuf.
/** * Generates the default log message of the specified event whose argument is a {@link ByteBuf}. */
private static String formatByteBuf(ChannelHandlerContext ctx, String eventName, ByteBuf msg) { String chStr = ctx.channel().toString(); int length = msg.readableBytes(); if (length == 0) { StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 4); buf.append(chStr).append(' ').append(eventName).append(": 0B"); return buf.toString(); } else { int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4; StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + 10 + 1 + 2 + rows * 80); buf.append(chStr).append(' ').append(eventName).append(": ").append(length).append('B').append(NEWLINE); appendPrettyHexDump(buf, msg); return buf.toString(); } }
Generates the default log message of the specified event whose argument is a ByteBufHolder.
/** * Generates the default log message of the specified event whose argument is a {@link ByteBufHolder}. */
private static String formatByteBufHolder(ChannelHandlerContext ctx, String eventName, ByteBufHolder msg) { String chStr = ctx.channel().toString(); String msgStr = msg.toString(); ByteBuf content = msg.content(); int length = content.readableBytes(); if (length == 0) { StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 4); buf.append(chStr).append(' ').append(eventName).append(", ").append(msgStr).append(", 0B"); return buf.toString(); } else { int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4; StringBuilder buf = new StringBuilder( chStr.length() + 1 + eventName.length() + 2 + msgStr.length() + 2 + 10 + 1 + 2 + rows * 80); buf.append(chStr).append(' ').append(eventName).append(": ") .append(msgStr).append(", ").append(length).append('B').append(NEWLINE); appendPrettyHexDump(buf, content); return buf.toString(); } }
Generates the default log message of the specified event whose argument is an arbitrary object.
/** * Generates the default log message of the specified event whose argument is an arbitrary object. */
private static String formatSimple(ChannelHandlerContext ctx, String eventName, Object msg) { String chStr = ctx.channel().toString(); String msgStr = String.valueOf(msg); StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length()); return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString(); } }