我是靠谱客的博主 花痴钢笔,最近开发中收集的这篇文章主要介绍Netty源码解析-LoggingHandler,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

前言:

在最开始我们创建HelloServer的示例中,我们为ServerBootStrap指定了LoggingHandler,当时的代码是这样写的

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
    // 指定处理channel
    .channel(NioServerSocketChannel.class)
    // 设置属性值
    .option(ChannelOption.SO_BACKLOG, 100)
    // 指定server处理Handler
    .handler(new LoggingHandler(LogLevel.INFO))
    ...

这个Handler有什么作用呢?我们是如何使用到这个Handler的呢。这篇文章就来详细介绍下LoggingHandler

1.io.netty.handler.logging.LoggingHandler构造

public class LoggingHandler extends ChannelDuplexHandler {

    // 默认日志级别为DEBUG
    private static final LogLevel DEFAULT_LEVEL = LogLevel.DEBUG;

    // 这里做了一个日志适配器,兼容各种日志模板
    protected final InternalLogger logger;
    // 貌似跟LogLevel是一样的 
    protected final InternalLogLevel internalLevel;
 	// 指定日志级别
    private final LogLevel level;
    // 暂时不知道作用,后面我们通过代码来看
    private final ByteBufFormat byteBufFormat;
}

LoggingHandler继承了ChannelDuplexHandler,那么无论是inbound事件还是outbound事件都会经过LoggingHandler。

1.1 构造方法

	// 默认debug级别展示
	public LoggingHandler() {
        this(DEFAULT_LEVEL);
    }

    public LoggingHandler(LogLevel level) {
        this(level, ByteBufFormat.HEX_DUMP);
    }

	// 最终在这里设置各种参数
    public LoggingHandler(LogLevel level, ByteBufFormat byteBufFormat) {
        this.level = ObjectUtil.checkNotNull(level, "level");
        this.byteBufFormat = ObjectUtil.checkNotNull(byteBufFormat, "byteBufFormat");
        logger = InternalLoggerFactory.getInstance(getClass());
        internalLevel = level.toInternalLevel();
    }

    /**
     * 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);
    }

    public LoggingHandler(Class<?> clazz, LogLevel level) {
        this(clazz, level, ByteBufFormat.HEX_DUMP);
    }

	// 与上面不带class的LoggingHandler区别就是logger对象的创建
    public LoggingHandler(Class<?> clazz, LogLevel level, ByteBufFormat byteBufFormat) {
        ObjectUtil.checkNotNull(clazz, "clazz");
        this.level = ObjectUtil.checkNotNull(level, "level");
        this.byteBufFormat = ObjectUtil.checkNotNull(byteBufFormat, "byteBufFormat");
        logger = InternalLoggerFactory.getInstance(clazz);
        internalLevel = level.toInternalLevel();
    }

    public LoggingHandler(String name) {
        this(name, DEFAULT_LEVEL);
    }

    public LoggingHandler(String name, LogLevel level) {
        this(name, level, ByteBufFormat.HEX_DUMP);
    }

    public LoggingHandler(String name, LogLevel level, ByteBufFormat byteBufFormat) {
        ObjectUtil.checkNotNull(name, "name");

        this.level = ObjectUtil.checkNotNull(level, "level");
        this.byteBufFormat = ObjectUtil.checkNotNull(byteBufFormat, "byteBufFormat");
        // 可以指定name
        logger = InternalLoggerFactory.getInstance(name);
        internalLevel = level.toInternalLevel();
    }

LoggingHandler的构造方法不少,方便我们在构造时灵活创建,主要就是Level和logger的创建。

2 常规方法的日志打印

我们可以看一个典型,channelRegistered方法

public class LoggingHandler extends ChannelDuplexHandler {	
	public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        // 如果适配当前level
        if (logger.isEnabled(internalLevel)) {
            // 这里就是将format后的内容以internalLevel级别打印出来,重点在format,具体在2.1
            logger.log(internalLevel, format(ctx, "REGISTERED"));
        }
        // 事件传递到下一个Handler
        ctx.fireChannelRegistered();
    }
}

其他常规方法也是与registered方法类似,不再展示

2.1 format(ChannelHandlerContext ctx, String eventName)打印

protected String format(ChannelHandlerContext ctx, String eventName) {
    // 获取当前channel信息
    String chStr = ctx.channel().toString();
    // 将channel信息及事件类型打印出来
    return new StringBuilder(chStr.length() + 1 + eventName.length())
        .append(chStr)
        .append(' ')
        .append(eventName)
        .toString();
}
// 笔者启动HelloServer时打印出来的(channelRegistered)结果如下
22:28:52.627 [nioEventLoopGroup-2-1] INFO  i.n.handler.logging.LoggingHandler - [id: 0xffa14504] REGISTERED

3.读写类方法日志打印

读写类的方法,因为有ByteBuf参数的传递,所以,打印方式有所不同。这里可以选择打印出16进制数据,具体方法如下:

public class LoggingHandler extends ChannelDuplexHandler {	
	protected String format(ChannelHandlerContext ctx, String eventName, Object arg) {
        if (arg instanceof ByteBuf) {
            // 将ByteBuf中的字节信息打印出来
            return formatByteBuf(ctx, eventName, (ByteBuf) arg);
        } else if (arg instanceof ByteBufHolder) {
            return formatByteBufHolder(ctx, eventName, (ByteBufHolder) arg);
        } else {
            // 这个与2.1中的format方法类似
            return formatSimple(ctx, eventName, arg);
        }
    }
}

3.1 LoggingHandler.formatByteBuf()

public class LoggingHandler extends ChannelDuplexHandler {	
	private 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 outputLength = chStr.length() + 1 + eventName.length() + 2 + 10 + 1;
            if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
                // 一堆计算StringBuilder的总大小
                int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
                int hexDumpLength = 2 + rows * 80;
                outputLength += hexDumpLength;
            }
            StringBuilder buf = new StringBuilder(outputLength);
            buf.append(chStr).append(' ').append(eventName).append(": ").append(length).append('B');
            if (byteBufFormat == ByteBufFormat.HEX_DUMP) {
                buf.append(NEWLINE);
                // 具体在这里打印,具体不再深入
                appendPrettyHexDump(buf, msg);
            }

            return buf.toString();
        }
    }
}
// 看一个服务端读取到客户端发送过来的信息(jack),所打印出来的结果
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 6a 61 63 6b                                     |jack            |
+--------+-------------------------------------------------+----------------+

注意:HelloServer的代码需要改造下

// 指定client处理Handler
.childHandler(new ChannelInitializer<SocketChannel>() {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast("frame", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()));
        // 需要放在这里,否则后续经过StringDecoder的处理后,ByteBuf已经变成了String类型了。
        pipeline.addLast("logger", new LoggingHandler(LogLevel.INFO));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new HelloServerHandler());
    }
});

另外formatByteBufHolder()的打印方法与该方法类似,不再细聊。

总结:

LoggingHandler在我们排查问题时还是比较有用的,我们可以在Netty开发中适当引用。

最后

以上就是花痴钢笔为你收集整理的Netty源码解析-LoggingHandler的全部内容,希望文章能够帮你解决Netty源码解析-LoggingHandler所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(173)

评论列表共有 0 条评论

立即
投稿
返回
顶部