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 }