0
# Encoders and Layouts
1
2
Event encoding and transformation system for converting log events into formatted byte arrays or strings. Encoders are the modern approach for formatting log output, while layouts provide the traditional string-based formatting interface.
3
4
## Capabilities
5
6
### Encoder Interface
7
8
The primary interface for transforming log events into byte arrays, supporting headers, footers, and character encoding.
9
10
```java { .api }
11
/**
12
* Interface for transforming events into byte arrays.
13
* Encoders are the recommended approach for formatting log output.
14
*/
15
public interface Encoder<E> extends ContextAware, LifeCycle {
16
/**
17
* Get header bytes to write at the beginning of the output.
18
* @return header bytes, or null if no header
19
*/
20
byte[] headerBytes();
21
22
/**
23
* Encode a single event into a byte array.
24
* @param event the event to encode
25
* @return encoded bytes for the event
26
*/
27
byte[] encode(E event);
28
29
/**
30
* Get footer bytes to write at the end of the output.
31
* @return footer bytes, or null if no footer
32
*/
33
byte[] footerBytes();
34
}
35
```
36
37
### EncoderBase
38
39
Base implementation providing common encoder functionality and lifecycle management.
40
41
```java { .api }
42
/**
43
* Base encoder implementation with lifecycle management.
44
*/
45
public abstract class EncoderBase<E> extends ContextAwareBase implements Encoder<E> {
46
private boolean started = false;
47
48
/**
49
* Check if the encoder is started.
50
* @return true if started
51
*/
52
public boolean isStarted();
53
54
/**
55
* Start the encoder. Subclasses should override for initialization.
56
*/
57
public void start();
58
59
/**
60
* Stop the encoder. Subclasses should override for cleanup.
61
*/
62
public void stop();
63
64
/**
65
* Default implementation returns null (no header).
66
* @return null
67
*/
68
public byte[] headerBytes();
69
70
/**
71
* Default implementation returns null (no footer).
72
* @return null
73
*/
74
public byte[] footerBytes();
75
}
76
```
77
78
### LayoutWrappingEncoder
79
80
Adapter that wraps a Layout to work with the Encoder interface, providing charset conversion and immediate flush control.
81
82
```java { .api }
83
/**
84
* Encoder that wraps a Layout, converting string output to bytes.
85
* Provides the bridge between the old Layout interface and modern Encoder interface.
86
*/
87
public class LayoutWrappingEncoder<E> extends EncoderBase<E> {
88
/**
89
* Set the layout to wrap.
90
* @param layout the layout to use for formatting
91
*/
92
public void setLayout(Layout<E> layout);
93
94
/**
95
* Get the current layout.
96
* @return the wrapped layout
97
*/
98
public Layout<E> getLayout();
99
100
/**
101
* Set the character encoding for string-to-bytes conversion.
102
* @param charset the charset to use (default: UTF-8)
103
*/
104
public void setCharset(Charset charset);
105
106
/**
107
* Get the current character encoding.
108
* @return the charset being used
109
*/
110
public Charset getCharset();
111
112
/**
113
* Control immediate flush behavior (used by appenders).
114
* @param immediateFlush true to flush immediately after each event
115
*/
116
public void setImmediateFlush(boolean immediateFlush);
117
118
/**
119
* Check if immediate flush is enabled.
120
* @return true if immediate flush is enabled
121
*/
122
public boolean isImmediateFlush();
123
124
/**
125
* Encode event by calling layout.doLayout() and converting to bytes.
126
* @param event the event to encode
127
* @return UTF-8 encoded bytes of the formatted event
128
*/
129
public byte[] encode(E event);
130
}
131
```
132
133
### Layout Interface
134
135
Traditional string-based formatting interface for transforming events into formatted strings.
136
137
```java { .api }
138
/**
139
* Interface for transforming events into formatted strings.
140
* Layouts are wrapped by LayoutWrappingEncoder for modern usage.
141
*/
142
public interface Layout<E> extends ContextAware, LifeCycle {
143
/**
144
* Format an event as a string.
145
* @param event the event to format
146
* @return formatted string representation
147
*/
148
String doLayout(E event);
149
150
/**
151
* Get header string for file output.
152
* @return header string, or null if no header
153
*/
154
String getFileHeader();
155
156
/**
157
* Get footer string for file output.
158
* @return footer string, or null if no footer
159
*/
160
String getFileFooter();
161
162
/**
163
* Get header string for presentation (e.g., HTML table header).
164
* @return presentation header, or null if none
165
*/
166
String getPresentationHeader();
167
168
/**
169
* Get footer string for presentation (e.g., HTML table footer).
170
* @return presentation footer, or null if none
171
*/
172
String getPresentationFooter();
173
174
/**
175
* Get the content type of the formatted output.
176
* @return content type (e.g., "text/plain", "text/html")
177
*/
178
String getContentType();
179
}
180
```
181
182
### LayoutBase
183
184
Base implementation for layouts providing lifecycle management and default header/footer behavior.
185
186
```java { .api }
187
/**
188
* Base layout implementation with lifecycle support.
189
*/
190
public abstract class LayoutBase<E> extends ContextAwareBase implements Layout<E> {
191
private boolean started = false;
192
193
/**
194
* Check if the layout is started.
195
* @return true if started
196
*/
197
public boolean isStarted();
198
199
/**
200
* Start the layout.
201
*/
202
public void start();
203
204
/**
205
* Stop the layout.
206
*/
207
public void stop();
208
209
/**
210
* Subclasses must implement this method to format events.
211
* @param event the event to format
212
* @return formatted string
213
*/
214
public abstract String doLayout(E event);
215
216
/**
217
* Default implementation returns null (no file header).
218
* @return null
219
*/
220
public String getFileHeader();
221
222
/**
223
* Default implementation returns null (no file footer).
224
* @return null
225
*/
226
public String getFileFooter();
227
228
/**
229
* Default implementation returns null (no presentation header).
230
* @return null
231
*/
232
public String getPresentationHeader();
233
234
/**
235
* Default implementation returns null (no presentation footer).
236
* @return null
237
*/
238
public String getPresentationFooter();
239
240
/**
241
* Default implementation returns "text/plain".
242
* @return "text/plain"
243
*/
244
public String getContentType();
245
}
246
```
247
248
### EchoLayout
249
250
Simple layout that returns the event's string representation.
251
252
```java { .api }
253
/**
254
* Simple layout that returns event.toString().
255
* Useful for testing and simple output scenarios.
256
*/
257
public class EchoLayout<E> extends LayoutBase<E> {
258
/**
259
* Format event using its toString() method.
260
* @param event the event to format
261
* @return event.toString()
262
*/
263
public String doLayout(E event);
264
}
265
```
266
267
## Utility Classes
268
269
### ByteArrayUtil
270
271
Utility methods for byte array operations used in encoding.
272
273
```java { .api }
274
/**
275
* Utility methods for byte array operations.
276
*/
277
public class ByteArrayUtil {
278
/**
279
* Convert hex string to byte array at specified offset.
280
* @param hexString hex string to convert
281
* @param ba target byte array
282
* @param offset offset in target array
283
*/
284
public static void hexStringToByteArray(String hexString, byte[] ba, int offset);
285
286
/**
287
* Convert byte array to hex string.
288
* @param ba byte array to convert
289
* @return hex string representation
290
*/
291
public static String toHexString(byte[] ba);
292
}
293
```
294
295
### NonClosableInputStream
296
297
InputStream wrapper that prevents closing, useful for protecting system streams.
298
299
```java { .api }
300
/**
301
* InputStream wrapper that prevents the close() method from actually closing the stream.
302
* Useful for protecting System.in and other shared streams.
303
*/
304
public class NonClosableInputStream extends FilterInputStream {
305
/**
306
* Create a non-closable wrapper around an InputStream.
307
* @param is the input stream to wrap
308
*/
309
public NonClosableInputStream(InputStream is);
310
311
/**
312
* Override close() to do nothing, preventing actual stream closure.
313
*/
314
@Override
315
public void close();
316
}
317
```
318
319
## Usage Examples
320
321
### Basic Layout Wrapping Encoder
322
323
```java
324
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
325
import ch.qos.logback.core.layout.EchoLayout;
326
import java.nio.charset.StandardCharsets;
327
328
// Create and configure encoder with layout
329
LayoutWrappingEncoder<Object> encoder = new LayoutWrappingEncoder<>();
330
encoder.setContext(context);
331
332
// Set layout
333
EchoLayout<Object> layout = new EchoLayout<>();
334
layout.setContext(context);
335
layout.start();
336
337
encoder.setLayout(layout);
338
encoder.setCharset(StandardCharsets.UTF_8);
339
encoder.setImmediateFlush(true);
340
encoder.start();
341
342
// Use with appender
343
appender.setEncoder(encoder);
344
```
345
346
### Custom Layout Implementation
347
348
```java
349
import ch.qos.logback.core.LayoutBase;
350
import java.time.LocalDateTime;
351
import java.time.format.DateTimeFormatter;
352
353
public class TimestampLayout<E> extends LayoutBase<E> {
354
private DateTimeFormatter formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
355
private String prefix = "";
356
357
@Override
358
public String doLayout(E event) {
359
StringBuilder sb = new StringBuilder();
360
sb.append('[').append(LocalDateTime.now().format(formatter)).append(']');
361
if (!prefix.isEmpty()) {
362
sb.append(' ').append(prefix);
363
}
364
sb.append(' ').append(event.toString());
365
sb.append(System.lineSeparator());
366
return sb.toString();
367
}
368
369
public void setPrefix(String prefix) {
370
this.prefix = prefix;
371
}
372
373
public String getPrefix() {
374
return prefix;
375
}
376
377
public void setDatePattern(String pattern) {
378
this.formatter = DateTimeFormatter.ofPattern(pattern);
379
}
380
381
@Override
382
public String getContentType() {
383
return "text/plain";
384
}
385
}
386
```
387
388
### Custom Encoder Implementation
389
390
```java
391
import ch.qos.logback.core.encoder.EncoderBase;
392
import java.nio.charset.StandardCharsets;
393
394
public class JsonEncoder<E> extends EncoderBase<E> {
395
private boolean includeTimestamp = true;
396
397
@Override
398
public byte[] headerBytes() {
399
return "[".getBytes(StandardCharsets.UTF_8);
400
}
401
402
@Override
403
public byte[] encode(E event) {
404
StringBuilder json = new StringBuilder();
405
json.append("{");
406
407
if (includeTimestamp) {
408
json.append("\"timestamp\":\"")
409
.append(System.currentTimeMillis())
410
.append("\",");
411
}
412
413
json.append("\"message\":\"")
414
.append(event.toString().replace("\"", "\\\""))
415
.append("\"}");
416
417
return json.toString().getBytes(StandardCharsets.UTF_8);
418
}
419
420
@Override
421
public byte[] footerBytes() {
422
return "]".getBytes(StandardCharsets.UTF_8);
423
}
424
425
public void setIncludeTimestamp(boolean includeTimestamp) {
426
this.includeTimestamp = includeTimestamp;
427
}
428
429
public boolean isIncludeTimestamp() {
430
return includeTimestamp;
431
}
432
}
433
```
434
435
### Using Encoder with File Headers and Footers
436
437
```java
438
import ch.qos.logback.core.FileAppender;
439
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
440
441
// Create encoder that produces headers and footers for XML files
442
public class XmlEncoder<E> extends EncoderBase<E> {
443
@Override
444
public byte[] headerBytes() {
445
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<events>\n"
446
.getBytes(StandardCharsets.UTF_8);
447
}
448
449
@Override
450
public byte[] encode(E event) {
451
return String.format(" <event>%s</event>\n", event.toString())
452
.getBytes(StandardCharsets.UTF_8);
453
}
454
455
@Override
456
public byte[] footerBytes() {
457
return "</events>\n".getBytes(StandardCharsets.UTF_8);
458
}
459
}
460
461
// Use with file appender
462
FileAppender<Object> fileAppender = new FileAppender<>();
463
fileAppender.setContext(context);
464
fileAppender.setFile("events.xml");
465
466
XmlEncoder<Object> xmlEncoder = new XmlEncoder<>();
467
xmlEncoder.setContext(context);
468
xmlEncoder.start();
469
470
fileAppender.setEncoder(xmlEncoder);
471
fileAppender.start();
472
```
473
474
## Character Encoding Considerations
475
476
When working with encoders and layouts, character encoding is crucial:
477
478
- **Default Encoding**: LayoutWrappingEncoder uses UTF-8 by default
479
- **Platform Encoding**: Avoid relying on platform default encoding
480
- **Explicit Configuration**: Always set charset explicitly for predictable behavior
481
- **Byte Array Handling**: Encoders work with byte arrays, eliminating encoding ambiguity
482
483
## Migration from Layouts to Encoders
484
485
For modern logback usage, prefer encoders over layouts:
486
487
1. **Replace** `layout` configuration with `encoder`
488
2. **Wrap** existing layouts with `LayoutWrappingEncoder`
489
3. **Implement** `Encoder<E>` for new custom formatters
490
4. **Set** explicit charset on `LayoutWrappingEncoder`
491
492
The encoder approach provides better control over output format, character encoding, and integration with modern appender implementations.