0
# Plugin System
1
2
Log4j Core provides an extensible plugin architecture that allows custom appenders, layouts, filters, and other components to be seamlessly integrated into the logging framework. The plugin system uses annotation-based registration and factory methods for component creation.
3
4
## Capabilities
5
6
### Plugin Annotations
7
8
Core annotations for creating plugins that integrate with Log4j Core.
9
10
#### @Plugin
11
12
```java { .api }
13
/**
14
* Marks a class as a Log4j plugin
15
*/
16
@Retention(RetentionPolicy.RUNTIME)
17
@Target(ElementType.TYPE)
18
public @interface Plugin {
19
/**
20
* Plugin name used in configuration
21
* @return Plugin name
22
*/
23
String name();
24
25
/**
26
* Plugin category (e.g., "Core", "Lookup", "Converter")
27
* @return Plugin category
28
*/
29
String category();
30
31
/**
32
* Element type for configuration (e.g., "appender", "layout", "filter")
33
* @return Element type
34
*/
35
String elementType() default "";
36
37
/**
38
* Whether to print object representation
39
* @return Print object flag
40
*/
41
boolean printObject() default false;
42
43
/**
44
* Whether to defer children processing
45
* @return Defer children flag
46
*/
47
boolean deferChildren() default false;
48
}
49
```
50
51
#### @PluginFactory
52
53
```java { .api }
54
/**
55
* Marks a static method as the factory method for creating plugin instances
56
*/
57
@Retention(RetentionPolicy.RUNTIME)
58
@Target(ElementType.METHOD)
59
public @interface PluginFactory {
60
// No attributes - just marks the factory method
61
}
62
```
63
64
#### @PluginAttribute
65
66
```java { .api }
67
/**
68
* Marks a parameter as receiving an attribute value from configuration
69
*/
70
@Retention(RetentionPolicy.RUNTIME)
71
@Target(ElementType.PARAMETER)
72
public @interface PluginAttribute {
73
/**
74
* Attribute name in configuration
75
* @return Attribute name (defaults to parameter name)
76
*/
77
String value() default "";
78
79
/**
80
* Default value if attribute not specified
81
* @return Default value
82
*/
83
String defaultValue() default "";
84
85
/**
86
* Whether this attribute contains sensitive information
87
* @return Sensitive flag
88
*/
89
boolean sensitive() default false;
90
}
91
```
92
93
#### @PluginElement
94
95
```java { .api }
96
/**
97
* Marks a parameter as receiving a child element from configuration
98
*/
99
@Retention(RetentionPolicy.RUNTIME)
100
@Target(ElementType.PARAMETER)
101
public @interface PluginElement {
102
/**
103
* Element name in configuration
104
* @return Element name (defaults to parameter name)
105
*/
106
String value() default "";
107
}
108
```
109
110
#### @PluginConfiguration
111
112
```java { .api }
113
/**
114
* Marks a parameter to receive the current Configuration instance
115
*/
116
@Retention(RetentionPolicy.RUNTIME)
117
@Target(ElementType.PARAMETER)
118
public @interface PluginConfiguration {
119
// No attributes - injects current configuration
120
}
121
```
122
123
#### @PluginNode
124
125
```java { .api }
126
/**
127
* Marks a parameter to receive the configuration Node
128
*/
129
@Retention(RetentionPolicy.RUNTIME)
130
@Target(ElementType.PARAMETER)
131
public @interface PluginNode {
132
// No attributes - injects configuration node
133
}
134
```
135
136
#### @PluginLoggerContext
137
138
```java { .api }
139
/**
140
* Marks a parameter to receive the LoggerContext instance
141
*/
142
@Retention(RetentionPolicy.RUNTIME)
143
@Target(ElementType.PARAMETER)
144
public @interface PluginLoggerContext {
145
// No attributes - injects logger context
146
}
147
```
148
149
### Plugin Utilities
150
151
Utility classes for plugin management and discovery.
152
153
#### PluginManager
154
155
```java { .api }
156
/**
157
* Manager for plugin discovery and instantiation
158
*/
159
public class PluginManager {
160
/**
161
* Create PluginManager for specific category
162
* @param category Plugin category to manage
163
*/
164
public PluginManager(String category);
165
166
/**
167
* Get plugin type by name
168
* @param name Plugin name
169
* @return PluginType instance or null
170
*/
171
public PluginType<?> getPluginType(String name);
172
173
/**
174
* Get all plugin types in this category
175
* @return Map of plugin names to types
176
*/
177
public Map<String, PluginType<?>> getPlugins();
178
179
/**
180
* Create plugin instance
181
* @param name Plugin name
182
* @param elementType Expected element type
183
* @param node Configuration node
184
* @param configuration Current configuration
185
* @return Plugin instance or null
186
*/
187
public Object createPluginObject(String name, String elementType, Node node, Configuration configuration);
188
}
189
```
190
191
#### PluginType
192
193
```java { .api }
194
/**
195
* Represents a plugin type with metadata
196
* @param <T> Plugin class type
197
*/
198
public class PluginType<T> {
199
/**
200
* Get plugin class
201
* @return Plugin class
202
*/
203
public Class<T> getPluginClass();
204
205
/**
206
* Get plugin name
207
* @return Plugin name
208
*/
209
public String getKey();
210
211
/**
212
* Get element type
213
* @return Element type
214
*/
215
public String getElementType();
216
217
/**
218
* Get plugin category
219
* @return Plugin category
220
*/
221
public String getCategory();
222
223
/**
224
* Check if object should be printed
225
* @return Print object flag
226
*/
227
public boolean isObjectPrintable();
228
229
/**
230
* Check if children should be deferred
231
* @return Defer children flag
232
*/
233
public boolean isDeferChildren();
234
}
235
```
236
237
## Creating Custom Plugins
238
239
### Custom Appender Example
240
241
```java { .api }
242
/**
243
* Example custom appender plugin
244
*/
245
@Plugin(name = "MyCustom", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE)
246
public class MyCustomAppender extends AbstractAppender {
247
248
/**
249
* Private constructor - use factory method
250
*/
251
private MyCustomAppender(String name, Filter filter, Layout<? extends Serializable> layout,
252
boolean ignoreExceptions, String customAttribute) {
253
super(name, filter, layout, ignoreExceptions, Property.EMPTY_ARRAY);
254
// Initialize custom appender
255
}
256
257
/**
258
* Append log event to custom destination
259
* @param event LogEvent to append
260
*/
261
@Override
262
public void append(LogEvent event) {
263
// Custom append logic
264
byte[] data = getLayout().toByteArray(event);
265
// Write data to custom destination
266
}
267
268
/**
269
* Factory method for creating appender instances
270
* @param name Appender name from configuration
271
* @param layout Layout component from configuration
272
* @param filter Filter component from configuration
273
* @param customAttribute Custom attribute value
274
* @param ignoreExceptions Whether to ignore exceptions
275
* @param configuration Current configuration
276
* @return MyCustomAppender instance
277
*/
278
@PluginFactory
279
public static MyCustomAppender createAppender(
280
@PluginAttribute("name") String name,
281
@PluginElement("Layout") Layout<? extends Serializable> layout,
282
@PluginElement("Filter") Filter filter,
283
@PluginAttribute("customAttribute") String customAttribute,
284
@PluginAttribute(value = "ignoreExceptions", defaultValue = "true") boolean ignoreExceptions,
285
@PluginConfiguration Configuration configuration) {
286
287
if (name == null) {
288
LOGGER.error("No name provided for MyCustomAppender");
289
return null;
290
}
291
292
if (layout == null) {
293
layout = PatternLayout.createDefaultLayout();
294
}
295
296
return new MyCustomAppender(name, filter, layout, ignoreExceptions, customAttribute);
297
}
298
}
299
```
300
301
### Custom Layout Example
302
303
```java { .api }
304
/**
305
* Example custom layout plugin
306
*/
307
@Plugin(name = "MyCustomLayout", category = Core.CATEGORY_NAME, elementType = Layout.ELEMENT_TYPE)
308
public class MyCustomLayout extends AbstractStringLayout {
309
310
private final String prefix;
311
private final String suffix;
312
313
/**
314
* Private constructor - use factory method
315
*/
316
private MyCustomLayout(Charset charset, String prefix, String suffix) {
317
super(charset);
318
this.prefix = prefix != null ? prefix : "";
319
this.suffix = suffix != null ? suffix : "";
320
}
321
322
/**
323
* Format log event to string
324
* @param event LogEvent to format
325
* @return Formatted string
326
*/
327
@Override
328
public String toSerializable(LogEvent event) {
329
StringBuilder sb = new StringBuilder();
330
sb.append(prefix);
331
sb.append(event.getTimeMillis()).append(" | ");
332
sb.append(event.getLevel()).append(" | ");
333
sb.append(event.getLoggerName()).append(" | ");
334
sb.append(event.getMessage().getFormattedMessage());
335
sb.append(suffix);
336
sb.append(System.lineSeparator());
337
return sb.toString();
338
}
339
340
/**
341
* Factory method for creating layout instances
342
* @param charset Character encoding
343
* @param prefix Prefix string for each log entry
344
* @param suffix Suffix string for each log entry
345
* @return MyCustomLayout instance
346
*/
347
@PluginFactory
348
public static MyCustomLayout createLayout(
349
@PluginAttribute(value = "charset", defaultValue = "UTF-8") Charset charset,
350
@PluginAttribute("prefix") String prefix,
351
@PluginAttribute("suffix") String suffix) {
352
353
return new MyCustomLayout(charset, prefix, suffix);
354
}
355
}
356
```
357
358
### Custom Filter Example
359
360
```java { .api }
361
/**
362
* Example custom filter plugin
363
*/
364
@Plugin(name = "MyCustomFilter", category = Core.CATEGORY_NAME, elementType = Filter.ELEMENT_TYPE)
365
public class MyCustomFilter extends AbstractFilter {
366
367
private final String requiredProperty;
368
369
/**
370
* Private constructor - use factory method
371
*/
372
private MyCustomFilter(String requiredProperty, Result onMatch, Result onMismatch) {
373
super(onMatch, onMismatch);
374
this.requiredProperty = requiredProperty;
375
}
376
377
/**
378
* Filter log event based on custom criteria
379
* @param event LogEvent to filter
380
* @return Filter result
381
*/
382
@Override
383
public Result filter(LogEvent event) {
384
// Custom filtering logic
385
String propertyValue = event.getContextData().getValue(requiredProperty);
386
if (propertyValue != null && !propertyValue.isEmpty()) {
387
return onMatch;
388
}
389
return onMismatch;
390
}
391
392
/**
393
* Factory method for creating filter instances
394
* @param requiredProperty Property that must be present
395
* @param match Result when filter matches
396
* @param mismatch Result when filter doesn't match
397
* @return MyCustomFilter instance
398
*/
399
@PluginFactory
400
public static MyCustomFilter createFilter(
401
@PluginAttribute("requiredProperty") String requiredProperty,
402
@PluginAttribute(value = "onMatch", defaultValue = "NEUTRAL") Result match,
403
@PluginAttribute(value = "onMismatch", defaultValue = "DENY") Result mismatch) {
404
405
if (requiredProperty == null) {
406
LOGGER.error("requiredProperty is required for MyCustomFilter");
407
return null;
408
}
409
410
return new MyCustomFilter(requiredProperty, match, mismatch);
411
}
412
}
413
```
414
415
### Custom Lookup Example
416
417
```java { .api }
418
/**
419
* Example custom lookup plugin for variable substitution
420
*/
421
@Plugin(name = "mycustom", category = StrLookup.CATEGORY)
422
public class MyCustomLookup implements StrLookup {
423
424
private static final Map<String, String> customData = new HashMap<>();
425
426
static {
427
customData.put("appname", "MyApplication");
428
customData.put("version", "1.0.0");
429
customData.put("environment", "production");
430
}
431
432
/**
433
* Look up value by key
434
* @param key Lookup key
435
* @return Value for key or null
436
*/
437
@Override
438
public String lookup(String key) {
439
return customData.get(key);
440
}
441
442
/**
443
* Look up value with LogEvent context
444
* @param event LogEvent for context
445
* @param key Lookup key
446
* @return Value for key or null
447
*/
448
@Override
449
public String lookup(LogEvent event, String key) {
450
// Can use log event for context-specific lookups
451
return lookup(key);
452
}
453
454
/**
455
* Factory method for creating lookup instances
456
* @return MyCustomLookup instance
457
*/
458
@PluginFactory
459
public static MyCustomLookup createLookup() {
460
return new MyCustomLookup();
461
}
462
}
463
```
464
465
## Plugin Configuration Usage
466
467
Once plugins are created, they can be used in configuration files:
468
469
### XML Configuration
470
```xml
471
<Configuration status="WARN" packages="com.example.plugins">
472
<Appenders>
473
<MyCustom name="Custom" customAttribute="value">
474
<MyCustomLayout prefix="[CUSTOM] " suffix=" [END]"/>
475
<MyCustomFilter requiredProperty="userId" onMatch="ACCEPT" onMismatch="DENY"/>
476
</MyCustom>
477
</Appenders>
478
<Loggers>
479
<Root level="INFO">
480
<AppenderRef ref="Custom"/>
481
</Root>
482
</Loggers>
483
</Configuration>
484
```
485
486
### Using Custom Lookup
487
```xml
488
<Configuration status="WARN">
489
<Properties>
490
<Property name="filename">logs/${mycustom:appname}-${mycustom:environment}.log</Property>
491
</Properties>
492
<Appenders>
493
<File name="File" fileName="${filename}">
494
<PatternLayout pattern="%d %level %logger - %msg%n"/>
495
</File>
496
</Appenders>
497
</Configuration>
498
```
499
500
## Plugin Discovery and Loading
501
502
### Automatic Discovery
503
Log4j automatically discovers plugins in:
504
- `org.apache.logging.log4j.core` package (built-in plugins)
505
- Packages specified in `packages` attribute of Configuration
506
- Packages specified via system property `log4j2.packages`
507
508
### Manual Plugin Loading
509
```java
510
// Programmatically add plugin packages
511
System.setProperty("log4j2.packages", "com.example.plugins,com.mycompany.logging");
512
513
// Or in configuration
514
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
515
builder.setPackages("com.example.plugins");
516
```
517
518
## Plugin Categories
519
520
Standard plugin categories in Log4j Core:
521
522
- **"Core"**: Appenders, layouts, filters, lookups
523
- **"Converter"**: Pattern layout converters
524
- **"Lookup"**: Variable lookup providers
525
- **"TypeConverter"**: Type conversion plugins
526
- **"ConfigurationFactory"**: Configuration file parsers