1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  package io.netty5.handler.logging;
17  
18  import io.netty5.buffer.BufferUtil;
19  import io.netty5.buffer.api.Buffer;
20  import io.netty5.channel.ChannelHandler;
21  import io.netty5.channel.ChannelHandlerContext;
22  import io.netty5.util.concurrent.Future;
23  import io.netty5.util.internal.logging.InternalLogLevel;
24  import io.netty5.util.internal.logging.InternalLogger;
25  import io.netty5.util.internal.logging.InternalLoggerFactory;
26  
27  import java.net.SocketAddress;
28  
29  import static io.netty5.util.internal.StringUtil.NEWLINE;
30  import static java.util.Objects.requireNonNull;
31  
32  
33  
34  
35  
36  @SuppressWarnings("StringBufferReplaceableByString")
37  public class LoggingHandler implements ChannelHandler {
38  
39      private static final LogLevel DEFAULT_LEVEL = LogLevel.DEBUG;
40  
41      protected final InternalLogger logger;
42      protected final InternalLogLevel internalLevel;
43  
44      private final LogLevel level;
45      private final BufferFormat bufferFormat;
46  
47      
48  
49  
50  
51      public LoggingHandler() {
52          this(DEFAULT_LEVEL);
53      }
54      
55  
56  
57  
58  
59  
60      public LoggingHandler(BufferFormat format) {
61          this(DEFAULT_LEVEL, format);
62      }
63  
64      
65  
66  
67  
68  
69  
70      public LoggingHandler(LogLevel level) {
71          this(level, BufferFormat.HEX_DUMP);
72      }
73  
74      
75  
76  
77  
78  
79  
80  
81      public LoggingHandler(LogLevel level, BufferFormat bufferFormat) {
82          this.level = requireNonNull(level, "level");
83          this.bufferFormat = requireNonNull(bufferFormat, "bufferFormat");
84          logger = InternalLoggerFactory.getInstance(getClass());
85          internalLevel = level.toInternalLevel();
86      }
87  
88      
89  
90  
91  
92  
93  
94      public LoggingHandler(Class<?> clazz) {
95          this(clazz, DEFAULT_LEVEL);
96      }
97  
98      
99  
100 
101 
102 
103 
104     public LoggingHandler(Class<?> clazz, LogLevel level) {
105         this(clazz, level, BufferFormat.HEX_DUMP);
106     }
107 
108     
109 
110 
111 
112 
113 
114 
115     public LoggingHandler(Class<?> clazz, LogLevel level, BufferFormat bufferFormat) {
116         requireNonNull(clazz, "clazz");
117         this.level = requireNonNull(level, "level");
118         this.bufferFormat = requireNonNull(bufferFormat, "bufferFormat");
119         logger = InternalLoggerFactory.getInstance(clazz);
120         internalLevel = level.toInternalLevel();
121     }
122 
123     
124 
125 
126 
127 
128     public LoggingHandler(String name) {
129         this(name, DEFAULT_LEVEL);
130     }
131 
132     
133 
134 
135 
136 
137 
138     public LoggingHandler(String name, LogLevel level) {
139         this(name, level, BufferFormat.HEX_DUMP);
140     }
141 
142     
143 
144 
145 
146 
147 
148 
149     public LoggingHandler(String name, LogLevel level, BufferFormat bufferFormat) {
150         requireNonNull(name, "name");
151 
152         this.level = requireNonNull(level, "level");
153         this.bufferFormat = requireNonNull(bufferFormat, "bufferFormat");
154         logger = InternalLoggerFactory.getInstance(name);
155         internalLevel = level.toInternalLevel();
156     }
157 
158     @Override
159     public boolean isSharable() {
160         return true;
161     }
162 
163     
164 
165 
166     public LogLevel level() {
167         return level;
168     }
169 
170     
171 
172 
173     public BufferFormat bufferFormat() {
174         return bufferFormat;
175     }
176 
177     @Override
178     public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
179         if (logger.isEnabled(internalLevel)) {
180             logger.log(internalLevel, format(ctx, "REGISTERED"));
181         }
182         ctx.fireChannelRegistered();
183     }
184 
185     @Override
186     public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
187         if (logger.isEnabled(internalLevel)) {
188             logger.log(internalLevel, format(ctx, "UNREGISTERED"));
189         }
190         ctx.fireChannelUnregistered();
191     }
192 
193     @Override
194     public void channelActive(ChannelHandlerContext ctx) throws Exception {
195         if (logger.isEnabled(internalLevel)) {
196             logger.log(internalLevel, format(ctx, "ACTIVE"));
197         }
198         ctx.fireChannelActive();
199     }
200 
201     @Override
202     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
203         if (logger.isEnabled(internalLevel)) {
204             logger.log(internalLevel, format(ctx, "INACTIVE"));
205         }
206         ctx.fireChannelInactive();
207     }
208 
209     @Override
210     public void channelExceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
211         if (logger.isEnabled(internalLevel)) {
212             logger.log(internalLevel, format(ctx, "EXCEPTION", cause), cause);
213         }
214         ctx.fireChannelExceptionCaught(cause);
215     }
216 
217     @Override
218     public void channelInboundEvent(ChannelHandlerContext ctx, Object evt) throws Exception {
219         if (logger.isEnabled(internalLevel)) {
220             logger.log(internalLevel, format(ctx, "USER_EVENT", evt));
221         }
222         ctx.fireChannelInboundEvent(evt);
223     }
224 
225     @Override
226     public Future<Void> bind(ChannelHandlerContext ctx, SocketAddress localAddress) {
227         if (logger.isEnabled(internalLevel)) {
228             logger.log(internalLevel, format(ctx, "BIND", localAddress));
229         }
230         return ctx.bind(localAddress);
231     }
232 
233     @Override
234     public Future<Void> connect(
235             ChannelHandlerContext ctx,
236             SocketAddress remoteAddress, SocketAddress localAddress) {
237         if (logger.isEnabled(internalLevel)) {
238             logger.log(internalLevel, format(ctx, "CONNECT", remoteAddress, localAddress));
239         }
240         return ctx.connect(remoteAddress, localAddress);
241     }
242 
243     @Override
244     public Future<Void> disconnect(ChannelHandlerContext ctx) {
245         if (logger.isEnabled(internalLevel)) {
246             logger.log(internalLevel, format(ctx, "DISCONNECT"));
247         }
248         return ctx.disconnect();
249     }
250 
251     @Override
252     public Future<Void> close(ChannelHandlerContext ctx) {
253         if (logger.isEnabled(internalLevel)) {
254             logger.log(internalLevel, format(ctx, "CLOSE"));
255         }
256         return ctx.close();
257     }
258 
259     @Override
260     public Future<Void> deregister(ChannelHandlerContext ctx) {
261         if (logger.isEnabled(internalLevel)) {
262             logger.log(internalLevel, format(ctx, "DEREGISTER"));
263         }
264         return ctx.deregister();
265     }
266 
267     @Override
268     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
269         if (logger.isEnabled(internalLevel)) {
270             logger.log(internalLevel, format(ctx, "READ COMPLETE"));
271         }
272         ctx.fireChannelReadComplete();
273     }
274 
275     @Override
276     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
277         if (logger.isEnabled(internalLevel)) {
278             logger.log(internalLevel, format(ctx, "READ", msg));
279         }
280         ctx.fireChannelRead(msg);
281     }
282 
283     @Override
284     public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
285         if (logger.isEnabled(internalLevel)) {
286             logger.log(internalLevel, format(ctx, "WRITE", msg));
287         }
288         return ctx.write(msg);
289     }
290 
291     @Override
292     public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
293         if (logger.isEnabled(internalLevel)) {
294             logger.log(internalLevel, format(ctx, "WRITABILITY CHANGED"));
295         }
296         ctx.fireChannelWritabilityChanged();
297     }
298 
299     @Override
300     public void flush(ChannelHandlerContext ctx) {
301         if (logger.isEnabled(internalLevel)) {
302             logger.log(internalLevel, format(ctx, "FLUSH"));
303         }
304         ctx.flush();
305     }
306 
307     
308 
309 
310 
311 
312     protected String format(ChannelHandlerContext ctx, String eventName) {
313         String chStr = ctx.channel().toString();
314         return new StringBuilder(chStr.length() + 1 + eventName.length())
315             .append(chStr)
316             .append(' ')
317             .append(eventName)
318             .toString();
319     }
320 
321     
322 
323 
324 
325 
326 
327     protected String format(ChannelHandlerContext ctx, String eventName, Object arg) {
328         if (arg instanceof Buffer) {
329             return formatBuffer(ctx, eventName, (Buffer) arg);
330         } else {
331             return formatSimple(ctx, eventName, arg);
332         }
333     }
334 
335     
336 
337 
338 
339 
340 
341 
342 
343     protected String format(ChannelHandlerContext ctx, String eventName, Object firstArg, Object secondArg) {
344         if (secondArg == null) {
345             return formatSimple(ctx, eventName, firstArg);
346         }
347 
348         String chStr = ctx.channel().toString();
349         String arg1Str = String.valueOf(firstArg);
350         String arg2Str = secondArg.toString();
351         StringBuilder buf = new StringBuilder(
352                 chStr.length() + 1 + eventName.length() + 2 + arg1Str.length() + 2 + arg2Str.length());
353         buf.append(chStr).append(' ').append(eventName).append(": ").append(arg1Str).append(", ").append(arg2Str);
354         return buf.toString();
355     }
356 
357     
358 
359 
360     private String formatBuffer(ChannelHandlerContext ctx, String eventName, Buffer msg) {
361         String chStr = ctx.channel().toString();
362         int length = msg.readableBytes();
363         if (length == 0) {
364             StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 4);
365             buf.append(chStr).append(' ').append(eventName).append(": 0B");
366             return buf.toString();
367         } else {
368             int outputLength = chStr.length() + 1 + eventName.length() + 2 + 10 + 1;
369             if (bufferFormat == BufferFormat.HEX_DUMP) {
370                 int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4;
371                 int hexDumpLength = 2 + rows * 80;
372                 outputLength += hexDumpLength;
373             }
374             StringBuilder buf = new StringBuilder(outputLength);
375             buf.append(chStr).append(' ').append(eventName).append(": ").append(length).append('B');
376             if (bufferFormat == BufferFormat.HEX_DUMP) {
377                 buf.append(NEWLINE);
378                 BufferUtil.appendPrettyHexDump(buf, msg);
379             }
380 
381             return buf.toString();
382         }
383     }
384 
385     
386 
387 
388     private static String formatSimple(ChannelHandlerContext ctx, String eventName, Object msg) {
389         String chStr = ctx.channel().toString();
390         String msgStr = String.valueOf(msg);
391         StringBuilder buf = new StringBuilder(chStr.length() + 1 + eventName.length() + 2 + msgStr.length());
392         return buf.append(chStr).append(' ').append(eventName).append(": ").append(msgStr).toString();
393     }
394 }