Optimize and clean up LoggingHandler
- Use ': ' instead of '(...)' for simpler string concatenation and prettier presentation - Optimize the overall performance of format*() methods - All format*() methods are now expected to encode the channel information by themselves so that StringBuilder instances are created less often. - Use a look-up table for generating per-row prefixes - Hid formatByteBuf(), formatByteBufHolder(), and formatNonByteBuf() from user because a user can always override format(ctx, eventName, arg). For example, to disable hexdump: protected void format(ChannelHandlerContext ctx, String eventName, Object arg) { if (arg instanceof ByteBuf) { super.format(ctx, eventName, arg.toString()); } else { super.format(ctx, eventName, arg); } }
This commit is contained in:
parent
f6ffc5c1c7
commit
835b4443f3
@ -44,6 +44,7 @@ public class LoggingHandler extends ChannelDuplexHandler {
|
||||
private static final String[] HEXPADDING = new String[16];
|
||||
private static final String[] BYTEPADDING = new String[16];
|
||||
private static final char[] BYTE2CHAR = new char[256];
|
||||
private static final String[] HEXDUMP_ROWPREFIXES = new String[65536 >>> 4];
|
||||
|
||||
static {
|
||||
int i;
|
||||
@ -81,6 +82,16 @@ public class LoggingHandler extends ChannelDuplexHandler {
|
||||
BYTE2CHAR[i] = (char) i;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the lookup table for the start-offset header in each row (up to 64KiB).
|
||||
for (i = 0; i < HEXDUMP_ROWPREFIXES.length; i ++) {
|
||||
StringBuilder buf = new StringBuilder(12);
|
||||
buf.append(NEWLINE);
|
||||
buf.append(Long.toHexString(i << 4 & 0xFFFFFFFFL | 0x100000000L));
|
||||
buf.setCharAt(buf.length() - 9, '|');
|
||||
buf.append('|');
|
||||
HEXDUMP_ROWPREFIXES[i] = buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected final InternalLogger logger;
|
||||
@ -168,22 +179,13 @@ public class LoggingHandler extends ChannelDuplexHandler {
|
||||
return level;
|
||||
}
|
||||
|
||||
protected String format(ChannelHandlerContext ctx, String message) {
|
||||
String chStr = ctx.channel().toString();
|
||||
StringBuilder buf = new StringBuilder(chStr.length() + message.length() + 1);
|
||||
buf.append(chStr);
|
||||
buf.append(' ');
|
||||
buf.append(message);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRegistered(ChannelHandlerContext ctx)
|
||||
throws Exception {
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, "REGISTERED"));
|
||||
}
|
||||
super.channelRegistered(ctx);
|
||||
ctx.fireChannelRegistered();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -201,7 +203,7 @@ public class LoggingHandler extends ChannelDuplexHandler {
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, "ACTIVE"));
|
||||
}
|
||||
super.channelActive(ctx);
|
||||
ctx.fireChannelActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -210,34 +212,34 @@ public class LoggingHandler extends ChannelDuplexHandler {
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, "INACTIVE"));
|
||||
}
|
||||
super.channelInactive(ctx);
|
||||
ctx.fireChannelInactive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx,
|
||||
Throwable cause) throws Exception {
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, "EXCEPTION: " + cause), cause);
|
||||
logger.log(internalLevel, format(ctx, "EXCEPTION", cause), cause);
|
||||
}
|
||||
super.exceptionCaught(ctx, 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));
|
||||
logger.log(internalLevel, format(ctx, "USER_EVENT", evt));
|
||||
}
|
||||
super.userEventTriggered(ctx, 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 + ')'));
|
||||
logger.log(internalLevel, format(ctx, "BIND", localAddress));
|
||||
}
|
||||
super.bind(ctx, localAddress, promise);
|
||||
ctx.bind(localAddress, promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -245,47 +247,51 @@ public class LoggingHandler extends ChannelDuplexHandler {
|
||||
SocketAddress remoteAddress, SocketAddress localAddress,
|
||||
ChannelPromise promise) throws Exception {
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, "CONNECT(" + remoteAddress + ", " + localAddress + ')'));
|
||||
logger.log(internalLevel, format(ctx, "CONNECT", remoteAddress, localAddress));
|
||||
}
|
||||
super.connect(ctx, remoteAddress, localAddress, promise);
|
||||
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()"));
|
||||
logger.log(internalLevel, format(ctx, "DISCONNECT"));
|
||||
}
|
||||
super.disconnect(ctx, promise);
|
||||
ctx.disconnect(promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(ChannelHandlerContext ctx,
|
||||
ChannelPromise promise) throws Exception {
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, "CLOSE()"));
|
||||
logger.log(internalLevel, format(ctx, "CLOSE"));
|
||||
}
|
||||
super.close(ctx, promise);
|
||||
ctx.close(promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deregister(ChannelHandlerContext ctx,
|
||||
ChannelPromise promise) throws Exception {
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, "DEREGISTER()"));
|
||||
logger.log(internalLevel, format(ctx, "DEREGISTER"));
|
||||
}
|
||||
super.deregister(ctx, promise);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
|
||||
logMessage(ctx, "RECEIVED", msg);
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, "RECEIVED", msg));
|
||||
}
|
||||
ctx.fireChannelRead(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
|
||||
logMessage(ctx, "WRITE", msg);
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, "WRITE", msg));
|
||||
}
|
||||
ctx.write(msg, promise);
|
||||
}
|
||||
|
||||
@ -297,90 +303,183 @@ public class LoggingHandler extends ChannelDuplexHandler {
|
||||
ctx.flush();
|
||||
}
|
||||
|
||||
private void logMessage(ChannelHandlerContext ctx, String eventName, Object msg) {
|
||||
if (logger.isEnabled(internalLevel)) {
|
||||
logger.log(internalLevel, format(ctx, formatMessage(eventName, msg)));
|
||||
}
|
||||
/**
|
||||
* 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();
|
||||
StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length());
|
||||
buf.append(chStr);
|
||||
buf.append(' ');
|
||||
buf.append(eventName);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
protected String formatMessage(String eventName, Object msg) {
|
||||
if (msg instanceof ByteBuf) {
|
||||
return formatByteBuf(eventName, (ByteBuf) msg);
|
||||
} else if (msg instanceof ByteBufHolder) {
|
||||
return formatByteBufHolder(eventName, (ByteBufHolder) msg);
|
||||
/**
|
||||
* 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 formatNonByteBuf(eventName, msg);
|
||||
return formatUserMessage(ctx, eventName, arg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String which contains all details to log the {@link ByteBuf}
|
||||
* Formats an event and returns the formatted message.
|
||||
*
|
||||
* @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 formatByteBuf(String eventName, ByteBuf buf) {
|
||||
int length = buf.readableBytes();
|
||||
int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
|
||||
StringBuilder dump = new StringBuilder(rows * 80 + eventName.length() + 16);
|
||||
protected String format(ChannelHandlerContext ctx, String eventName, Object firstArg, Object secondArg) {
|
||||
String chStr = ctx.channel().toString();
|
||||
String arg1Str = String.valueOf(firstArg);
|
||||
if (secondArg == null) {
|
||||
StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName + 2 + arg1Str.length());
|
||||
buf.append(chStr).append(' ').append(eventName).append(": ").append(arg1Str);
|
||||
return buf.toString();
|
||||
} else {
|
||||
String arg2Str = secondArg.toString();
|
||||
StringBuilder buf = new StringBuilder(
|
||||
chStr.length() + 1 + eventName + 2 + arg1Str.length() + 2 + arg2Str.length());
|
||||
buf.append(chStr).append(' ').append(eventName).append(": ").append(arg1Str).append(", ").append(arg2Str);
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
dump.append(eventName).append('(').append(length).append('B').append(')');
|
||||
/**
|
||||
* 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');
|
||||
appendHexDump(buf, msg);
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(": ");
|
||||
buf.append(msgStr).append(", ").append(length).append('B');
|
||||
appendHexDump(buf, content);
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
||||
private static void appendHexDump(StringBuilder dump, ByteBuf buf) {
|
||||
dump.append(
|
||||
NEWLINE + " +-------------------------------------------------+" +
|
||||
NEWLINE + " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |" +
|
||||
NEWLINE + "+--------+-------------------------------------------------+----------------+");
|
||||
NEWLINE + " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |" +
|
||||
NEWLINE + "+--------+-------------------------------------------------+----------------+");
|
||||
|
||||
final int startIndex = buf.readerIndex();
|
||||
final int endIndex = buf.writerIndex();
|
||||
final int length = endIndex - startIndex;
|
||||
final int fullRows = length >>> 4;
|
||||
final int remainder = length & 0xF;
|
||||
|
||||
int i;
|
||||
for (i = startIndex; i < endIndex; i ++) {
|
||||
int relIdx = i - startIndex;
|
||||
int relIdxMod16 = relIdx & 15;
|
||||
if (relIdxMod16 == 0) {
|
||||
dump.append(NEWLINE);
|
||||
dump.append(Long.toHexString(relIdx & 0xFFFFFFFFL | 0x100000000L));
|
||||
dump.setCharAt(dump.length() - 9, '|');
|
||||
dump.append('|');
|
||||
// Dump the rows which have 16 bytes.
|
||||
for (int row = 0; row < fullRows; row ++) {
|
||||
int rowStartIndex = row << 4;
|
||||
|
||||
// Per-row prefix.
|
||||
appendHexDumpRowPrefix(dump, row, rowStartIndex);
|
||||
|
||||
// Hex dump
|
||||
int rowEndIndex = rowStartIndex + 16;
|
||||
for (int j = rowStartIndex; j < rowEndIndex; j ++) {
|
||||
dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
|
||||
}
|
||||
dump.append(BYTE2HEX[buf.getUnsignedByte(i)]);
|
||||
if (relIdxMod16 == 15) {
|
||||
dump.append(" |");
|
||||
for (int j = i - 15; j <= i; j ++) {
|
||||
dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
|
||||
}
|
||||
dump.append('|');
|
||||
dump.append(" |");
|
||||
|
||||
// ASCII dump
|
||||
for (int j = rowStartIndex; j < rowEndIndex; j ++) {
|
||||
dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
|
||||
}
|
||||
dump.append('|');
|
||||
}
|
||||
|
||||
if ((i - startIndex & 15) != 0) {
|
||||
int remainder = length & 15;
|
||||
// Dump the last row which has less than 16 bytes.
|
||||
if (remainder != 0) {
|
||||
int rowStartIndex = fullRows << 4;
|
||||
appendHexDumpRowPrefix(dump, fullRows, rowStartIndex);
|
||||
|
||||
// Hex dump
|
||||
int rowEndIndex = rowStartIndex + remainder;
|
||||
for (int j = rowStartIndex; j < rowEndIndex; j ++) {
|
||||
dump.append(BYTE2HEX[buf.getUnsignedByte(j)]);
|
||||
}
|
||||
dump.append(HEXPADDING[remainder]);
|
||||
dump.append(" |");
|
||||
for (int j = i - remainder; j < i; j ++) {
|
||||
|
||||
// Ascii dump
|
||||
for (int j = rowStartIndex; j < rowEndIndex; j ++) {
|
||||
dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]);
|
||||
}
|
||||
dump.append(BYTEPADDING[remainder]);
|
||||
dump.append('|');
|
||||
}
|
||||
|
||||
dump.append(
|
||||
NEWLINE + "+--------+-------------------------------------------------+----------------+");
|
||||
|
||||
return dump.toString();
|
||||
dump.append(NEWLINE + "+--------+-------------------------------------------------+----------------+");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String which contains all details to log the {@link Object}
|
||||
* Appends the prefix of each hex dump row. Uses the look-up table for the buffer <= 64 KiB.
|
||||
*/
|
||||
protected String formatNonByteBuf(String eventName, Object msg) {
|
||||
return eventName + ": " + msg;
|
||||
private static void appendHexDumpRowPrefix(StringBuilder dump, int row, int rowStartIndex) {
|
||||
if (row < HEXDUMP_ROWPREFIXES.length) {
|
||||
dump.append(HEXDUMP_ROWPREFIXES[row]);
|
||||
} else {
|
||||
dump.append(NEWLINE);
|
||||
dump.append(Long.toHexString(rowStartIndex & 0xFFFFFFFFL | 0x100000000L));
|
||||
dump.setCharAt(dump.length() - 9, '|');
|
||||
dump.append('|');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a String which contains all details to log the {@link ByteBufHolder}.
|
||||
*
|
||||
* By default this method just delegates to {@link #formatByteBuf(String, ByteBuf)},
|
||||
* using the content of the {@link ByteBufHolder}. Sub-classes may override this.
|
||||
* Generates the default log message of the specified event whose argument is an arbitrary object.
|
||||
*/
|
||||
protected String formatByteBufHolder(String eventName, ByteBufHolder msg) {
|
||||
return formatByteBuf(eventName, msg.content());
|
||||
private static String formatUserMessage(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();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,304 @@
|
||||
/*
|
||||
* 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 ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.Appender;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufHolder;
|
||||
import io.netty.buffer.DefaultByteBufHolder;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelMetadata;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import org.easymock.IArgumentMatcher;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import static org.easymock.EasyMock.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Verifies the correct functionality of the {@link LoggingHandler}.
|
||||
*/
|
||||
public class LoggingHandlerTest {
|
||||
|
||||
private static final Logger root = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
|
||||
private static final List<Appender<ILoggingEvent>> oldAppenders = new ArrayList<Appender<ILoggingEvent>>();
|
||||
/**
|
||||
* Custom logback appender which gets used to match on log messages.
|
||||
*/
|
||||
private Appender<ILoggingEvent> appender;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
for (Iterator<Appender<ILoggingEvent>> i = root.iteratorForAppenders(); i.hasNext();) {
|
||||
Appender<ILoggingEvent> a = i.next();
|
||||
oldAppenders.add(a);
|
||||
root.detachAppender(a);
|
||||
}
|
||||
|
||||
Unpooled.buffer();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClass() {
|
||||
for (Appender<ILoggingEvent> a: oldAppenders) {
|
||||
root.addAppender(a);
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setup() {
|
||||
appender = createNiceMock(Appender.class);
|
||||
root.addAppender(appender);
|
||||
}
|
||||
|
||||
@After
|
||||
public void teardown() {
|
||||
root.detachAppender(appender);
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void shouldNotAcceptNullLogLevel() {
|
||||
LogLevel level = null;
|
||||
new LoggingHandler(level);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldApplyCustomLogLevel() {
|
||||
LoggingHandler handler = new LoggingHandler("LoggingHandlerTest", LogLevel.INFO);
|
||||
assertEquals(LogLevel.INFO, handler.level());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogChannelActive() {
|
||||
appender.doAppend(matchesLog(".+ACTIVE$"));
|
||||
replay(appender);
|
||||
new EmbeddedChannel(new LoggingHandler());
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogChannelRegistered() {
|
||||
appender.doAppend(matchesLog(".+REGISTERED$"));
|
||||
replay(appender);
|
||||
new EmbeddedChannel(new LoggingHandler());
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogChannelClose() throws Exception {
|
||||
appender.doAppend(matchesLog(".+CLOSE$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.close().await();
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogChannelConnect() throws Exception {
|
||||
appender.doAppend(matchesLog(".+CONNECT: 0.0.0.0/0.0.0.0:80$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.connect(new InetSocketAddress(80)).await();
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogChannelConnectWithLocalAddress() throws Exception {
|
||||
appender.doAppend(matchesLog(".+CONNECT: 0.0.0.0/0.0.0.0:80, 0.0.0.0/0.0.0.0:81$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.connect(new InetSocketAddress(80), new InetSocketAddress(81)).await();
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogChannelDisconnect() throws Exception {
|
||||
appender.doAppend(matchesLog(".+DISCONNECT$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new DisconnectingEmbeddedChannel(new LoggingHandler());
|
||||
channel.connect(new InetSocketAddress(80)).await();
|
||||
channel.disconnect().await();
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogChannelInactive() throws Exception {
|
||||
appender.doAppend(matchesLog(".+INACTIVE$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.pipeline().fireChannelInactive();
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogChannelBind() throws Exception {
|
||||
appender.doAppend(matchesLog(".+BIND: 0.0.0.0/0.0.0.0:80$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.bind(new InetSocketAddress(80));
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("RedundantStringConstructorCall")
|
||||
public void shouldLogChannelUserEvent() throws Exception {
|
||||
String userTriggered = "iAmCustom!";
|
||||
appender.doAppend(matchesLog(".+USER_EVENT: " + userTriggered + '$'));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.pipeline().fireUserEventTriggered(new String(userTriggered));
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogChannelException() throws Exception {
|
||||
String msg = "illegalState";
|
||||
Throwable cause = new IllegalStateException(msg);
|
||||
appender.doAppend(matchesLog(".+EXCEPTION: " + cause.getClass().getCanonicalName() + ": " + msg + '$'));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.pipeline().fireExceptionCaught(cause);
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogDataWritten() throws Exception {
|
||||
String msg = "hello";
|
||||
appender.doAppend(matchesLog(".+WRITE: " + msg + '$'));
|
||||
appender.doAppend(matchesLog(".+FLUSH$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.writeOutbound(msg);
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogNonByteBufDataRead() throws Exception {
|
||||
String msg = "hello";
|
||||
appender.doAppend(matchesLog(".+RECEIVED: " + msg + '$'));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.writeInbound(msg);
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogByteBufDataRead() throws Exception {
|
||||
ByteBuf msg = Unpooled.copiedBuffer("hello", CharsetUtil.UTF_8);
|
||||
appender.doAppend(matchesLog(".+RECEIVED: " + msg.readableBytes() + "B$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.writeInbound(msg);
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogEmptyByteBufDataRead() throws Exception {
|
||||
ByteBuf msg = Unpooled.EMPTY_BUFFER;
|
||||
appender.doAppend(matchesLog(".+RECEIVED: 0B$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.writeInbound(msg);
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLogByteBufHolderDataRead() throws Exception {
|
||||
ByteBufHolder msg = new DefaultByteBufHolder(Unpooled.copiedBuffer("hello", CharsetUtil.UTF_8)) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "foobar";
|
||||
}
|
||||
};
|
||||
|
||||
appender.doAppend(matchesLog(".+RECEIVED: foobar, 5B$"));
|
||||
replay(appender);
|
||||
EmbeddedChannel channel = new EmbeddedChannel(new LoggingHandler());
|
||||
channel.writeInbound(msg);
|
||||
verify(appender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Static helper method for matching Logback messages.
|
||||
*
|
||||
* @param toMatch the regex to match.
|
||||
* @return a mocked event to pass into the {@link Appender#doAppend(Object)} method.
|
||||
*/
|
||||
private static ILoggingEvent matchesLog(String toMatch) {
|
||||
reportMatcher(new RegexLogMatcher(toMatch));
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A custom EasyMock matcher that matches on Logback messages.
|
||||
*/
|
||||
private static class RegexLogMatcher implements IArgumentMatcher {
|
||||
|
||||
private final String expected;
|
||||
private String actualMsg;
|
||||
|
||||
public RegexLogMatcher(String expected) {
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("DynamicRegexReplaceableByCompiledPattern")
|
||||
public boolean matches(Object actual) {
|
||||
if (!(actual instanceof ILoggingEvent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Match only the first line to skip the validation of hex-dump format.
|
||||
actualMsg = ((ILoggingEvent) actual).getMessage().split("(?s)[\\r\\n]+")[0];
|
||||
return actualMsg.matches(expected);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTo(StringBuffer buffer) {
|
||||
buffer.append("matchesLog(");
|
||||
buffer.append("expected: \"" + expected);
|
||||
buffer.append("\", got: \"" + actualMsg);
|
||||
buffer.append("\")");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DisconnectingEmbeddedChannel extends EmbeddedChannel {
|
||||
|
||||
private DisconnectingEmbeddedChannel(ChannelHandler... handlers) {
|
||||
super(handlers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMetadata metadata() {
|
||||
return new ChannelMetadata(true);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user