View Javadoc

    1   /*
    2    * Copyright 2012 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 org.jboss.netty.handler.logging;
    17
    18  import org.jboss.netty.buffer.ChannelBuffer;
    19  import org.jboss.netty.channel.ChannelDownstreamHandler;
    20  import org.jboss.netty.channel.ChannelEvent;
    21  import org.jboss.netty.channel.ChannelHandler;
    22  import org.jboss.netty.channel.ChannelHandler.Sharable;
    23  import org.jboss.netty.channel.ChannelHandlerContext;
    24  import org.jboss.netty.channel.ChannelUpstreamHandler;
    25  import org.jboss.netty.channel.ExceptionEvent;
    26  import org.jboss.netty.channel.MessageEvent;
    27  import org.jboss.netty.logging.InternalLogLevel;
    28  import org.jboss.netty.logging.InternalLogger;
    29  import org.jboss.netty.logging.InternalLoggerFactory;
    30
    31  /**
    32   * A {@link ChannelHandler} that logs all events via {@link InternalLogger}.
    33   * By default, all events are logged at <tt>DEBUG</tt> level.  You can extend
    34   * this class and override {@link #log(ChannelEvent)} to change the default
    35   * behavior.
    36   * @apiviz.landmark
    37   */
    38  @Sharable
    39  public class LoggingHandler implements ChannelUpstreamHandler, ChannelDownstreamHandler {
    40
    41      private static final InternalLogLevel DEFAULT_LEVEL = InternalLogLevel.DEBUG;
    42      private static final String NEWLINE = String.format("%n");
    43
    44      private static final String[] BYTE2HEX = new String[256];
    45      private static final String[] HEXPADDING = new String[16];
    46      private static final String[] BYTEPADDING = new String[16];
    47      private static final char[] BYTE2CHAR = new char[256];
    48
    49      static {
    50          int i;
    51
    52          // Generate the lookup table for byte-to-hex-dump conversion
    53          for (i = 0; i < 10; i ++) {
    54              StringBuilder buf = new StringBuilder(3);
    55              buf.append(" 0");
    56              buf.append(i);
    57              BYTE2HEX[i] = buf.toString();
    58          }
    59          for (; i < 16; i ++) {
    60              StringBuilder buf = new StringBuilder(3);
    61              buf.append(" 0");
    62              buf.append((char) ('a' + i - 10));
    63              BYTE2HEX[i] = buf.toString();
    64          }
    65          for (; i < BYTE2HEX.length; i ++) {
    66              StringBuilder buf = new StringBuilder(3);
    67              buf.append(' ');
    68              buf.append(Integer.toHexString(i));
    69              BYTE2HEX[i] = buf.toString();
    70          }
    71
    72          // Generate the lookup table for hex dump paddings
    73          for (i = 0; i < HEXPADDING.length; i ++) {
    74              int padding = HEXPADDING.length - i;
    75              StringBuilder buf = new StringBuilder(padding * 3);
    76              for (int j = 0; j < padding; j ++) {
    77                  buf.append("   ");
    78              }
    79              HEXPADDING[i] = buf.toString();
    80          }
    81
    82          // Generate the lookup table for byte dump paddings
    83          for (i = 0; i < BYTEPADDING.length; i ++) {
    84              int padding = BYTEPADDING.length - i;
    85              StringBuilder buf = new StringBuilder(padding);
    86              for (int j = 0; j < padding; j ++) {
    87                  buf.append(' ');
    88              }
    89              BYTEPADDING[i] = buf.toString();
    90          }
    91
    92          // Generate the lookup table for byte-to-char conversion
    93          for (i = 0; i < BYTE2CHAR.length; i ++) {
    94              if (i <= 0x1f || i >= 0x7f) {
    95                  BYTE2CHAR[i] = '.';
    96              } else {
    97                  BYTE2CHAR[i] = (char) i;
    98              }
    99          }
    100     }
    101
    102
    103     private final InternalLogger logger;
    104     private final InternalLogLevel level;
    105     private final boolean hexDump;
    106
    107     /**
    108      * Creates a new instance whose logger name is the fully qualified class
    109      * name of the instance with hex dump enabled.
    110      */
    111     public LoggingHandler() {
    112         this(true);
    113     }
    114
    115     /**
    116      * Creates a new instance whose logger name is the fully qualified class
    117      * name of the instance.
    118      *
    119      * @param level   the log level
    120      */
    121     public LoggingHandler(InternalLogLevel level) {
    122         this(level, true);
    123     }
    124
    125     /**
    126      * Creates a new instance whose logger name is the fully qualified class
    127      * name of the instance.
    128      *
    129      * @param hexDump {@code true} if and only if the hex dump of the received
    130      *                message is logged
    131      */
    132     public LoggingHandler(boolean hexDump) {
    133         this(DEFAULT_LEVEL, hexDump);
    134     }
    135
    136     /**
    137      * Creates a new instance whose logger name is the fully qualified class
    138      * name of the instance.
    139      *
    140      * @param level   the log level
    141      * @param hexDump {@code true} if and only if the hex dump of the received
    142      *                message is logged
    143      */
    144     public LoggingHandler(InternalLogLevel level, boolean hexDump) {
    145         if (level == null) {
    146             throw new NullPointerException("level");
    147         }
    148
    149         logger = InternalLoggerFactory.getInstance(getClass());
    150         this.level = level;
    151         this.hexDump = hexDump;
    152     }
    153
    154     /**
    155      * Creates a new instance with the specified logger name and with hex dump
    156      * enabled.
    157      */
    158     public LoggingHandler(Class<?> clazz) {
    159         this(clazz, true);
    160     }
    161
    162     /**
    163      * Creates a new instance with the specified logger name.
    164      *
    165      * @param hexDump {@code true} if and only if the hex dump of the received
    166      *                message is logged
    167      */
    168     public LoggingHandler(Class<?> clazz, boolean hexDump) {
    169         this(clazz, DEFAULT_LEVEL, hexDump);
    170     }
    171
    172     /**
    173      * Creates a new instance with the specified logger name.
    174      *
    175      * @param level   the log level
    176      */
    177     public LoggingHandler(Class<?> clazz, InternalLogLevel level) {
    178         this(clazz, level, true);
    179     }
    180
    181     /**
    182      * Creates a new instance with the specified logger name.
    183      *
    184      * @param level   the log level
    185      * @param hexDump {@code true} if and only if the hex dump of the received
    186      *                message is logged
    187      */
    188     public LoggingHandler(Class<?> clazz, InternalLogLevel level, boolean hexDump) {
    189         if (clazz == null) {
    190             throw new NullPointerException("clazz");
    191         }
    192         if (level == null) {
    193             throw new NullPointerException("level");
    194         }
    195         logger = InternalLoggerFactory.getInstance(clazz);
    196         this.level = level;
    197         this.hexDump = hexDump;
    198     }
    199
    200     /**
    201      * Creates a new instance with the specified logger name and with hex dump
    202      * enabled.
    203      */
    204     public LoggingHandler(String name) {
    205         this(name, true);
    206     }
    207
    208     /**
    209      * Creates a new instance with the specified logger name.
    210      *
    211      * @param hexDump {@code true} if and only if the hex dump of the received
    212      *                message is logged
    213      */
    214     public LoggingHandler(String name, boolean hexDump) {
    215         this(name, DEFAULT_LEVEL, hexDump);
    216     }
    217
    218     /**
    219      * Creates a new instance with the specified logger name.
    220      *
    221      * @param level   the log level
    222      * @param hexDump {@code true} if and only if the hex dump of the received
    223      *                message is logged
    224      */
    225     public LoggingHandler(String name, InternalLogLevel level, boolean hexDump) {
    226         if (name == null) {
    227             throw new NullPointerException("name");
    228         }
    229         if (level == null) {
    230             throw new NullPointerException("level");
    231         }
    232         logger = InternalLoggerFactory.getInstance(name);
    233         this.level = level;
    234         this.hexDump = hexDump;
    235     }
    236
    237     /**
    238      * Returns the {@link InternalLogger} that this handler uses to log
    239      * a {@link ChannelEvent}.
    240      */
    241     public InternalLogger getLogger() {
    242         return logger;
    243     }
    244
    245     /**
    246      * Returns the {@link InternalLogLevel} that this handler uses to log
    247      * a {@link ChannelEvent}.
    248      */
    249     public InternalLogLevel getLevel() {
    250         return level;
    251     }
    252
    253     /**
    254      * Logs the specified event to the {@link InternalLogger} returned by
    255      * {@link #getLogger()}. If hex dump has been enabled for this handler,
    256      * the hex dump of the {@link ChannelBuffer} in a {@link MessageEvent} will
    257      * be logged together.
    258      */
    259     public void log(ChannelEvent e) {
    260         if (getLogger().isEnabled(level)) {
    261             String msg = e.toString();
    262
    263             // Append hex dump if necessary.
    264             if (hexDump && e instanceof MessageEvent) {
    265                 MessageEvent me = (MessageEvent) e;
    266                 if (me.getMessage() instanceof ChannelBuffer) {
    267                     msg += formatBuffer((ChannelBuffer) me.getMessage());
    268                 }
    269             }
    270
    271             // Log the message (and exception if available.)
    272             if (e instanceof ExceptionEvent) {
    273                 getLogger().log(level, msg, ((ExceptionEvent) e).getCause());
    274             } else {
    275                 getLogger().log(level, msg);
    276             }
    277         }
    278     }
    279
    280     private static String formatBuffer(ChannelBuffer buf) {
    281         int length = buf.readableBytes();
    282         int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
    283         StringBuilder dump = new StringBuilder(rows * 80);
    284
    285         dump.append(
    286                 NEWLINE + "         +-------------------------------------------------+" +
    287                 NEWLINE + "         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |" +
    288                 NEWLINE + "+--------+-------------------------------------------------+----------------+");
    289
    290         final int startIndex = buf.readerIndex();
    291         final int endIndex = buf.writerIndex();
    292
    293         int i;
    294         for (i = startIndex; i < endIndex; i ++) {
    295             int relIdx = i - startIndex;
    296             int relIdxMod16 = relIdx & 15;
    297             if (relIdxMod16 == 0) {
    298                 dump.append(NEWLINE);
    299                 dump.append(Long.toHexString(relIdx & 0xFFFFFFFFL | 0x100000000L));
    300                 dump.setCharAt(dump.length() - 9, '|');
    301                 dump.append('|');
    302             }
    303             dump.append(BYTE2HEX[buf.getUnsignedByte(i)]);
    304             if (relIdxMod16 == 15) {
    305                 dump.append(" |");
    306                 for (int j = i - 15; j <= i; j ++) {
    307                     dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
    308                 }
    309                 dump.append('|');
    310             }
    311         }
    312
    313         if ((i - startIndex & 15) != 0) {
    314             int remainder = length & 15;
    315             dump.append(HEXPADDING[remainder]);
    316             dump.append(" |");
    317             for (int j = i - remainder; j < i; j ++) {
    318                 dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
    319             }
    320             dump.append(BYTEPADDING[remainder]);
    321             dump.append('|');
    322         }
    323
    324         dump.append(
    325                 NEWLINE + "+--------+-------------------------------------------------+----------------+");
    326
    327         return dump.toString();
    328     }
    329
    330     public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
    331             throws Exception {
    332         log(e);
    333         ctx.sendUpstream(e);
    334     }
    335
    336     public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e)
    337             throws Exception {
    338         log(e);
    339         ctx.sendDownstream(e);
    340     }
    341 }