@Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

      Channel channel = new NettyChannel(ctx);

      final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(channel);
      LOGGER.warn("CLIENT : exceptionCaught {}", remoteAddress);
      LOGGER.warn("CLIENT : exceptionCaught exception.", cause);
      closeChannel(channel);
      if (channelEventListener != null) {
        putRemotingEvent(new RemotingEvent(RemotingEventType.EXCEPTION, remoteAddress, channel));
      }
    }
    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
      Channel channel = new NettyChannel(ctx);

      final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(channel);
      LOGGER.info("CLIENT : CLOSE {}", remoteAddress);
      closeChannel(channel);
      super.close(ctx, promise);

      if (channelEventListener != null) {
        putRemotingEvent(new RemotingEvent(RemotingEventType.CLOSE, remoteAddress, channel));
      }
    }
    @Override
    public void connect(
        ChannelHandlerContext ctx,
        SocketAddress remoteAddress,
        SocketAddress localAddress,
        ChannelPromise promise)
        throws Exception {
      final String local = localAddress == null ? "UNKNOW" : localAddress.toString();
      final String remote = remoteAddress == null ? "UNKNOW" : remoteAddress.toString();
      LOGGER.info("CLIENT : CONNECT  {} => {}", local, remote);
      super.connect(ctx, remoteAddress, localAddress, promise);

      if (channelEventListener != null) {
        assert remoteAddress != null;
        putRemotingEvent(
            new RemotingEvent(
                RemotingEventType.CONNECT, remoteAddress.toString(), new NettyChannel(ctx)));
      }
    }
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
      if (evt instanceof IdleStateEvent) {
        IdleStateEvent event = (IdleStateEvent) evt;

        Channel channel = new NettyChannel(ctx);

        final String remoteAddress = RemotingHelper.parseChannelRemoteAddr(channel);

        if (event.state().equals(io.netty.handler.timeout.IdleState.ALL_IDLE)) {
          LOGGER.warn("CLIENT : IDLE [{}]", remoteAddress);
          closeChannel(channel);
        }

        if (channelEventListener != null) {
          RemotingEventType remotingEventType = RemotingEventType.valueOf(event.state().name());
          putRemotingEvent(new RemotingEvent(remotingEventType, remoteAddress, channel));
        }
      }

      ctx.fireUserEventTriggered(evt);
    }