0
# SPI and Extension Points
1
2
The Service Provider Interface (SPI) classes provide extension points for custom functionality including LoggerRepository, ErrorHandler, Filter, and other pluggable components for advanced logging system customization.
3
4
## Core SPI Interfaces
5
6
### LoggerRepository Interface
7
8
```java { .api }
9
public interface LoggerRepository {
10
// Logger management
11
Logger getLogger(String name);
12
Logger getLogger(String name, LoggerFactory factory);
13
Logger getRootLogger();
14
Logger exists(String name);
15
16
// Repository state
17
Enumeration getCurrentLoggers();
18
Enumeration getCurrentCategories();
19
20
// Configuration
21
void shutdown();
22
void resetConfiguration();
23
boolean isDisabled(int level);
24
void setThreshold(Level level);
25
void setThreshold(String val);
26
Level getThreshold();
27
28
// Error handling
29
void emitNoAppenderWarning(Category cat);
30
31
// Hierarchy management
32
void setLoggerFactory(LoggerFactory factory);
33
LoggerFactory getLoggerFactory();
34
}
35
```
36
37
**Parameters:**
38
- `name` - String logger name
39
- `factory` - LoggerFactory for logger creation
40
- `level` - Integer level to check or Level to set
41
- `val` - String threshold value
42
- `cat` - Category for warning
43
44
**Returns:**
45
- `Logger` instances
46
- `Enumeration` of loggers/categories
47
- `boolean` for disabled checks
48
- `Level` current threshold
49
- `LoggerFactory` current factory
50
51
### LoggerFactory Interface
52
53
```java { .api }
54
public interface LoggerFactory {
55
// Create logger instance
56
Logger makeNewLoggerInstance(String name);
57
}
58
```
59
60
**Parameters:**
61
- `name` - String name for the new logger
62
63
**Returns:**
64
- `Logger` newly created logger instance
65
66
### ErrorHandler Interface
67
68
```java { .api }
69
public interface ErrorHandler {
70
// Error handling methods
71
void error(String message);
72
void error(String message, Exception ex, int errorCode);
73
void error(String message, Exception ex, int errorCode, LoggingEvent event);
74
75
// Appender association
76
void setAppender(Appender appender);
77
void setBackupAppender(Appender appender);
78
void setLogger(Logger logger);
79
80
// Activation
81
void activateOptions();
82
}
83
```
84
85
**Parameters:**
86
- `message` - String error message
87
- `ex` - Exception that occurred
88
- `errorCode` - Integer error code
89
- `event` - LoggingEvent being processed
90
- `appender` - Appender for association
91
- `logger` - Logger for association
92
93
### Filter Interface
94
95
```java { .api }
96
public interface Filter {
97
// Filter results
98
int DENY = -1;
99
int NEUTRAL = 0;
100
int ACCEPT = 1;
101
102
// Decision method
103
int decide(LoggingEvent event);
104
105
// Filter chaining
106
Filter getNext();
107
void setNext(Filter next);
108
}
109
```
110
111
**Parameters:**
112
- `event` - LoggingEvent to evaluate
113
- `next` - Filter next filter in chain
114
115
**Returns:**
116
- `int` decision (DENY, NEUTRAL, or ACCEPT)
117
- `Filter` next filter in chain
118
119
## Logger SPI Classes
120
121
### DefaultCategoryFactory
122
123
```java { .api }
124
public class DefaultCategoryFactory implements LoggerFactory {
125
// Constructor
126
public DefaultCategoryFactory();
127
128
// Factory method implementation
129
@Override
130
public Logger makeNewLoggerInstance(String name);
131
}
132
```
133
134
**Parameters:**
135
- `name` - String logger name
136
137
**Returns:**
138
- `Logger` new logger instance
139
140
### Hierarchy
141
142
```java { .api }
143
public class Hierarchy implements LoggerRepository {
144
// Constructor
145
public Hierarchy(Logger root);
146
147
// Logger operations
148
@Override
149
public Logger getLogger(String name);
150
@Override
151
public Logger getLogger(String name, LoggerFactory factory);
152
@Override
153
public Logger getRootLogger();
154
@Override
155
public Logger exists(String name);
156
157
// Enumeration
158
@Override
159
public Enumeration getCurrentLoggers();
160
@Override
161
public Enumeration getCurrentCategories();
162
163
// Configuration
164
@Override
165
public void shutdown();
166
@Override
167
public void resetConfiguration();
168
@Override
169
public boolean isDisabled(int level);
170
@Override
171
public void setThreshold(Level level);
172
@Override
173
public Level getThreshold();
174
175
// Factory management
176
@Override
177
public void setLoggerFactory(LoggerFactory factory);
178
@Override
179
public LoggerFactory getLoggerFactory();
180
181
// Hierarchy-specific methods
182
public void clear();
183
public void overrideAsNeeded(String override);
184
}
185
```
186
187
**Parameters:**
188
- `root` - Logger root logger for hierarchy
189
- `name` - String logger name
190
- `factory` - LoggerFactory for logger creation
191
- `level` - Level or integer threshold
192
- `override` - String override configuration
193
194
**Returns:**
195
- `Logger`, `Enumeration`, `Level`, `LoggerFactory` as appropriate
196
- `boolean` for threshold checks
197
198
## Error Handler Implementations
199
200
### OnlyOnceErrorHandler
201
202
```java { .api }
203
public class OnlyOnceErrorHandler implements ErrorHandler {
204
// Constants
205
public static final String WARN_PREFIX = "log4j warning: ";
206
public static final String ERROR_PREFIX = "log4j error: ";
207
208
// Constructor
209
public OnlyOnceErrorHandler();
210
211
// Error handling
212
@Override
213
public void error(String message);
214
@Override
215
public void error(String message, Exception ex, int errorCode);
216
@Override
217
public void error(String message, Exception ex, int errorCode, LoggingEvent event);
218
219
// Configuration
220
@Override
221
public void setAppender(Appender appender);
222
@Override
223
public void setBackupAppender(Appender appender);
224
@Override
225
public void setLogger(Logger logger);
226
@Override
227
public void activateOptions();
228
}
229
```
230
231
**Parameters:**
232
- `message` - String error message
233
- `ex` - Exception that occurred
234
- `errorCode` - Integer error code
235
- `event` - LoggingEvent being processed
236
- `appender` - Appender for configuration
237
- `logger` - Logger for configuration
238
239
## Filter Implementations
240
241
### LevelRangeFilter
242
243
```java { .api }
244
public class LevelRangeFilter implements Filter {
245
// Configuration
246
public void setLevelMin(Level levelMin);
247
public Level getLevelMin();
248
public void setLevelMax(Level levelMax);
249
public Level getLevelMax();
250
public void setAcceptOnMatch(boolean acceptOnMatch);
251
public boolean getAcceptOnMatch();
252
253
// Filter implementation
254
@Override
255
public int decide(LoggingEvent event);
256
@Override
257
public Filter getNext();
258
@Override
259
public void setNext(Filter next);
260
261
// Lifecycle
262
public void activateOptions();
263
}
264
```
265
266
**Parameters:**
267
- `levelMin` - Level minimum level in range
268
- `levelMax` - Level maximum level in range
269
- `acceptOnMatch` - Boolean accept or deny on match
270
- `event` - LoggingEvent to evaluate
271
- `next` - Filter next filter in chain
272
273
**Returns:**
274
- `Level` min/max levels
275
- `boolean` accept on match setting
276
- `int` filter decision
277
- `Filter` next filter
278
279
### LevelMatchFilter
280
281
```java { .api }
282
public class LevelMatchFilter implements Filter {
283
// Configuration
284
public void setLevel(Level level);
285
public Level getLevel();
286
public void setAcceptOnMatch(boolean acceptOnMatch);
287
public boolean getAcceptOnMatch();
288
289
// Filter implementation
290
@Override
291
public int decide(LoggingEvent event);
292
@Override
293
public Filter getNext();
294
@Override
295
public void setNext(Filter next);
296
297
// Lifecycle
298
public void activateOptions();
299
}
300
```
301
302
**Parameters:**
303
- `level` - Level to match against
304
- `acceptOnMatch` - Boolean accept or deny on match
305
- `event` - LoggingEvent to evaluate
306
- `next` - Filter next filter in chain
307
308
**Returns:**
309
- `Level` match level
310
- `boolean` accept on match setting
311
- `int` filter decision
312
- `Filter` next filter
313
314
### StringMatchFilter
315
316
```java { .api }
317
public class StringMatchFilter implements Filter {
318
// Configuration
319
public void setStringToMatch(String s);
320
public String getStringToMatch();
321
public void setAcceptOnMatch(boolean acceptOnMatch);
322
public boolean getAcceptOnMatch();
323
324
// Filter implementation
325
@Override
326
public int decide(LoggingEvent event);
327
@Override
328
public Filter getNext();
329
@Override
330
public void setNext(Filter next);
331
332
// Lifecycle
333
public void activateOptions();
334
}
335
```
336
337
**Parameters:**
338
- `s` - String to match in log messages
339
- `acceptOnMatch` - Boolean accept or deny on match
340
- `event` - LoggingEvent to evaluate
341
- `next` - Filter next filter in chain
342
343
**Returns:**
344
- `String` match string
345
- `boolean` accept on match setting
346
- `int` filter decision
347
- `Filter` next filter
348
349
## Configuration SPI
350
351
### OptionHandler Interface
352
353
```java { .api }
354
public interface OptionHandler {
355
// Activate configured options
356
void activateOptions();
357
}
358
```
359
360
### Configurator Interface
361
362
```java { .api }
363
public interface Configurator {
364
// Configure from URL
365
void doConfigure(URL url, LoggerRepository repository);
366
367
// Configure from InputStream
368
void doConfigure(InputStream inputStream, LoggerRepository repository);
369
}
370
```
371
372
**Parameters:**
373
- `url` - URL pointing to configuration
374
- `inputStream` - InputStream containing configuration
375
- `repository` - LoggerRepository to configure
376
377
## Advanced SPI Components
378
379
### LoggingEvent
380
381
```java { .api }
382
public class LoggingEvent implements Serializable {
383
// Event properties
384
public String getLoggerName();
385
public Logger getLogger();
386
public Level getLevel();
387
public Object getMessage();
388
public String getRenderedMessage();
389
public String[] getThrowableStrRep();
390
public ThrowableInformation getThrowableInformation();
391
public long getTimeStamp();
392
public String getThreadName();
393
public LocationInfo getLocationInformation();
394
public String getFQNOfLoggerClass();
395
396
// MDC access
397
public Object getMDC(String key);
398
public void getMDCCopy();
399
400
// NDC access
401
public String getNDC();
402
403
// Properties
404
public Set getPropertyKeySet();
405
public String getProperty(String key);
406
}
407
```
408
409
**Parameters:**
410
- `key` - String key for MDC or property access
411
412
**Returns:**
413
- Various return types depending on method (String, Level, Object, etc.)
414
415
### LocationInfo
416
417
```java { .api }
418
public class LocationInfo implements Serializable {
419
// Location constants
420
public static final String NA = "?";
421
422
// Location information
423
public String getClassName();
424
public String getFileName();
425
public String getLineNumber();
426
public String getMethodName();
427
public String fullInfo();
428
}
429
```
430
431
**Returns:**
432
- `String` location information components
433
434
### ThrowableInformation
435
436
```java { .api }
437
public class ThrowableInformation implements Serializable {
438
// Constructor
439
public ThrowableInformation(Throwable throwable);
440
public ThrowableInformation(String[] rep);
441
442
// Throwable access
443
public Throwable getThrowable();
444
public String[] getThrowableStrRep();
445
}
446
```
447
448
**Parameters:**
449
- `throwable` - Throwable exception
450
- `rep` - String array representation
451
452
**Returns:**
453
- `Throwable` original exception
454
- `String[]` string representation
455
456
## Usage Examples
457
458
### Custom Logger Factory
459
```java
460
import org.apache.log4j.spi.LoggerFactory;
461
import org.apache.log4j.Logger;
462
463
public class CustomLoggerFactory implements LoggerFactory {
464
465
@Override
466
public Logger makeNewLoggerInstance(String name) {
467
// Create custom logger with enhanced functionality
468
return new CustomLogger(name);
469
}
470
471
// Custom logger implementation
472
public static class CustomLogger extends Logger {
473
public CustomLogger(String name) {
474
super(name);
475
}
476
477
// Add custom logging methods
478
public void audit(Object message) {
479
// Custom audit logging
480
super.info("AUDIT: " + message);
481
}
482
483
public void security(Object message) {
484
// Custom security logging
485
super.warn("SECURITY: " + message);
486
}
487
}
488
}
489
```
490
491
### Custom Error Handler
492
```java
493
import org.apache.log4j.spi.ErrorHandler;
494
import org.apache.log4j.spi.LoggingEvent;
495
import org.apache.log4j.*;
496
497
public class CustomErrorHandler implements ErrorHandler {
498
private Appender appender;
499
private Appender backupAppender;
500
private Logger logger;
501
502
@Override
503
public void error(String message) {
504
System.err.println("Custom Error Handler: " + message);
505
}
506
507
@Override
508
public void error(String message, Exception ex, int errorCode) {
509
System.err.println("Custom Error Handler [" + errorCode + "]: " + message);
510
if (ex != null) {
511
ex.printStackTrace();
512
}
513
}
514
515
@Override
516
public void error(String message, Exception ex, int errorCode, LoggingEvent event) {
517
error(message, ex, errorCode);
518
519
// Try backup appender if available
520
if (backupAppender != null && event != null) {
521
try {
522
backupAppender.doAppend(event);
523
} catch (Exception backupEx) {
524
System.err.println("Backup appender also failed: " + backupEx.getMessage());
525
}
526
}
527
}
528
529
@Override
530
public void setAppender(Appender appender) {
531
this.appender = appender;
532
}
533
534
@Override
535
public void setBackupAppender(Appender appender) {
536
this.backupAppender = appender;
537
}
538
539
@Override
540
public void setLogger(Logger logger) {
541
this.logger = logger;
542
}
543
544
@Override
545
public void activateOptions() {
546
// Initialize any resources
547
}
548
}
549
```
550
551
### Custom Filter Implementation
552
```java
553
import org.apache.log4j.spi.Filter;
554
import org.apache.log4j.spi.LoggingEvent;
555
556
public class CustomMessageFilter implements Filter {
557
private String[] forbiddenWords;
558
private Filter next;
559
560
public CustomMessageFilter(String[] forbiddenWords) {
561
this.forbiddenWords = forbiddenWords;
562
}
563
564
@Override
565
public int decide(LoggingEvent event) {
566
String message = event.getRenderedMessage();
567
568
if (message != null) {
569
for (String forbidden : forbiddenWords) {
570
if (message.toLowerCase().contains(forbidden.toLowerCase())) {
571
return Filter.DENY; // Block messages with forbidden words
572
}
573
}
574
}
575
576
return Filter.NEUTRAL; // Let other filters decide
577
}
578
579
@Override
580
public Filter getNext() {
581
return next;
582
}
583
584
@Override
585
public void setNext(Filter next) {
586
this.next = next;
587
}
588
589
// Usage example
590
public static void setupFilteredLogging() {
591
// Create filter to block sensitive information
592
String[] sensitiveWords = {"password", "ssn", "credit card"};
593
CustomMessageFilter filter = new CustomMessageFilter(sensitiveWords);
594
595
// Create appender with filter
596
ConsoleAppender appender = new ConsoleAppender(
597
new PatternLayout("%-5p %c - %m%n")
598
);
599
appender.addFilter(filter);
600
601
// Configure logger
602
Logger logger = Logger.getLogger("sensitive");
603
logger.addAppender(appender);
604
605
// Test filtering
606
logger.info("User login successful"); // Will appear
607
logger.info("User password is secret"); // Will be blocked
608
logger.info("Processing credit card transaction"); // Will be blocked
609
}
610
}
611
```
612
613
### Custom LoggerRepository
614
```java
615
import org.apache.log4j.spi.*;
616
import org.apache.log4j.*;
617
import java.util.*;
618
import java.util.concurrent.ConcurrentHashMap;
619
620
public class CustomLoggerRepository implements LoggerRepository {
621
private final Map<String, Logger> loggers = new ConcurrentHashMap<>();
622
private final Logger rootLogger;
623
private LoggerFactory loggerFactory = new DefaultCategoryFactory();
624
private Level threshold = Level.ALL;
625
626
public CustomLoggerRepository() {
627
this.rootLogger = new Logger("root");
628
this.rootLogger.setLevel(Level.DEBUG);
629
loggers.put("root", rootLogger);
630
}
631
632
@Override
633
public Logger getLogger(String name) {
634
return getLogger(name, loggerFactory);
635
}
636
637
@Override
638
public Logger getLogger(String name, LoggerFactory factory) {
639
Logger logger = loggers.get(name);
640
if (logger == null) {
641
logger = factory.makeNewLoggerInstance(name);
642
loggers.put(name, logger);
643
644
// Set up parent-child relationships
645
setupParentChild(logger, name);
646
}
647
return logger;
648
}
649
650
@Override
651
public Logger getRootLogger() {
652
return rootLogger;
653
}
654
655
@Override
656
public Logger exists(String name) {
657
return loggers.get(name);
658
}
659
660
@Override
661
public Enumeration getCurrentLoggers() {
662
return Collections.enumeration(
663
loggers.values().stream()
664
.filter(logger -> !logger.getName().equals("root"))
665
.collect(java.util.stream.Collectors.toList())
666
);
667
}
668
669
@Override
670
public Enumeration getCurrentCategories() {
671
return getCurrentLoggers();
672
}
673
674
@Override
675
public void shutdown() {
676
// Close all appenders
677
for (Logger logger : loggers.values()) {
678
Enumeration appenders = logger.getAllAppenders();
679
while (appenders.hasMoreElements()) {
680
Appender appender = (Appender) appenders.nextElement();
681
appender.close();
682
}
683
}
684
}
685
686
@Override
687
public void resetConfiguration() {
688
// Remove all appenders and reset levels
689
for (Logger logger : loggers.values()) {
690
logger.removeAllAppenders();
691
logger.setLevel(null);
692
}
693
rootLogger.setLevel(Level.DEBUG);
694
}
695
696
@Override
697
public boolean isDisabled(int level) {
698
return threshold.toInt() > level;
699
}
700
701
@Override
702
public void setThreshold(Level level) {
703
this.threshold = level;
704
}
705
706
@Override
707
public void setThreshold(String val) {
708
setThreshold(Level.toLevel(val));
709
}
710
711
@Override
712
public Level getThreshold() {
713
return threshold;
714
}
715
716
@Override
717
public void emitNoAppenderWarning(Category cat) {
718
System.err.println("No appenders could be found for logger (" +
719
cat.getName() + ").");
720
}
721
722
@Override
723
public void setLoggerFactory(LoggerFactory factory) {
724
this.loggerFactory = factory;
725
}
726
727
@Override
728
public LoggerFactory getLoggerFactory() {
729
return loggerFactory;
730
}
731
732
private void setupParentChild(Logger logger, String name) {
733
// Simplified parent-child setup
734
int lastDot = name.lastIndexOf('.');
735
if (lastDot > 0) {
736
String parentName = name.substring(0, lastDot);
737
Logger parent = getLogger(parentName);
738
// Set up parent relationship (simplified)
739
}
740
}
741
}
742
```
743
744
### Advanced Filter Chain
745
```java
746
import org.apache.log4j.spi.Filter;
747
import org.apache.log4j.spi.LoggingEvent;
748
import org.apache.log4j.*;
749
750
public class FilterChainExample {
751
752
public static void setupAdvancedFiltering() {
753
// Create multiple filters
754
LevelMatchFilter infoFilter = new LevelMatchFilter();
755
infoFilter.setLevel(Level.INFO);
756
infoFilter.setAcceptOnMatch(true);
757
758
LevelRangeFilter errorFilter = new LevelRangeFilter();
759
errorFilter.setLevelMin(Level.ERROR);
760
errorFilter.setLevelMax(Level.FATAL);
761
errorFilter.setAcceptOnMatch(true);
762
763
StringMatchFilter sensitiveFilter = new StringMatchFilter();
764
sensitiveFilter.setStringToMatch("password");
765
sensitiveFilter.setAcceptOnMatch(false); // Deny
766
767
// Chain filters
768
infoFilter.setNext(errorFilter);
769
errorFilter.setNext(sensitiveFilter);
770
771
// Create appender with filter chain
772
ConsoleAppender appender = new ConsoleAppender(
773
new PatternLayout("%-5p %c - %m%n")
774
);
775
appender.addFilter(infoFilter);
776
777
// Configure logger
778
Logger logger = Logger.getLogger("filtered");
779
logger.addAppender(appender);
780
logger.setLevel(Level.DEBUG);
781
782
// Test filter chain
783
logger.debug("Debug message"); // Filtered out (not INFO or ERROR+)
784
logger.info("Info message"); // Passes (INFO level)
785
logger.info("User password changed"); // Blocked (contains "password")
786
logger.error("Error occurred"); // Passes (ERROR level)
787
}
788
}
789
```
790
791
## Error Handler Implementations
792
793
### FallbackErrorHandler
794
795
An error handler that provides failover capability by switching to a backup appender when the primary appender fails.
796
797
```java { .api }
798
public class FallbackErrorHandler implements ErrorHandler {
799
// Constructors
800
public FallbackErrorHandler();
801
802
// ErrorHandler interface methods
803
public void error(String message);
804
public void error(String message, Exception e, int errorCode);
805
public void error(String message, Exception e, int errorCode, LoggingEvent event);
806
public void setAppender(Appender primary);
807
public void setBackupAppender(Appender backup);
808
public void setLogger(Logger logger);
809
810
// Activation
811
public void activateOptions();
812
}
813
```
814
815
**Parameters:**
816
- `message` - Error message describing the failure
817
- `e` - Exception that caused the error
818
- `errorCode` - Numeric error code from ErrorCode constants
819
- `event` - LoggingEvent that failed to be processed
820
- `primary` - Primary appender that failed
821
- `backup` - Backup appender to use for failover
822
- `logger` - Logger to update during failover
823
824
**Usage Example:**
825
```java
826
import org.apache.log4j.*;
827
import org.apache.log4j.varia.FallbackErrorHandler;
828
829
public class FallbackErrorHandlerExample {
830
public void setupFallback() {
831
// Create primary appender (might fail)
832
FileAppender primaryAppender = new FileAppender(
833
new PatternLayout("%d %-5p %c - %m%n"),
834
"/tmp/primary.log",
835
true
836
);
837
primaryAppender.setName("primary");
838
839
// Create backup appender (console)
840
ConsoleAppender backupAppender = new ConsoleAppender(
841
new PatternLayout("FALLBACK: %d %-5p %c - %m%n")
842
);
843
backupAppender.setName("backup");
844
845
// Create fallback error handler
846
FallbackErrorHandler errorHandler = new FallbackErrorHandler();
847
errorHandler.setAppender(primaryAppender);
848
errorHandler.setBackupAppender(backupAppender);
849
errorHandler.setLogger(Logger.getLogger("com.myapp"));
850
errorHandler.activateOptions();
851
852
// Set error handler on primary appender
853
primaryAppender.setErrorHandler(errorHandler);
854
855
// Configure logger
856
Logger logger = Logger.getLogger("com.myapp");
857
logger.addAppender(primaryAppender);
858
}
859
}
860
```
861
862
## Rewrite Policies
863
864
### RewritePolicy Interface
865
866
Base interface for implementing logging event rewrite strategies used with RewriteAppender.
867
868
```java { .api }
869
public interface RewritePolicy {
870
/**
871
* Rewrite a logging event
872
* @param source original logging event
873
* @return rewritten event, original event, or null to suppress
874
*/
875
LoggingEvent rewrite(LoggingEvent source);
876
}
877
```
878
879
### MapRewritePolicy
880
881
A rewrite policy that processes events where the message implements java.util.Map, combining message properties with the event's existing properties.
882
883
```java { .api }
884
public class MapRewritePolicy implements RewritePolicy {
885
// Constructor
886
public MapRewritePolicy();
887
888
// RewritePolicy implementation
889
public LoggingEvent rewrite(LoggingEvent source);
890
}
891
```
892
893
**Usage Example:**
894
```java
895
import org.apache.log4j.*;
896
import org.apache.log4j.rewrite.MapRewritePolicy;
897
import java.util.HashMap;
898
import java.util.Map;
899
900
public class RewritePolicyExample {
901
public void setupMapRewrite() {
902
// Create rewrite policy
903
MapRewritePolicy rewritePolicy = new MapRewritePolicy();
904
905
// Create RewriteAppender (would need to be implemented)
906
// RewriteAppender rewriteAppender = new RewriteAppender();
907
// rewriteAppender.setRewritePolicy(rewritePolicy);
908
909
// Example of map-based logging
910
Logger logger = Logger.getLogger("com.myapp");
911
912
Map<String, Object> logMap = new HashMap<>();
913
logMap.put("message", "User action performed");
914
logMap.put("userId", "12345");
915
logMap.put("action", "login");
916
logMap.put("timestamp", System.currentTimeMillis());
917
918
// Log the map - will be processed by MapRewritePolicy
919
logger.info(logMap);
920
}
921
}
922
```
923
924
## Object Renderers
925
926
### ObjectRenderer Interface
927
928
Interface for custom object-to-string conversion in log messages.
929
930
```java { .api }
931
public interface ObjectRenderer {
932
/**
933
* Render the object as a String
934
* @param o object to render
935
* @return String representation of the object
936
*/
937
String doRender(Object o);
938
}
939
```
940
941
### RendererMap
942
943
Manages mapping between classes and their corresponding ObjectRenderer implementations.
944
945
```java { .api }
946
public class RendererMap {
947
// Constructor
948
public RendererMap();
949
950
// Static methods
951
public static void addRenderer(RendererSupport repository, String renderedClassName, String renderingClassName);
952
953
// Instance methods
954
public ObjectRenderer get(Object o);
955
public ObjectRenderer get(Class clazz);
956
public void put(Class clazz, ObjectRenderer renderer);
957
public void clear();
958
}
959
```
960
961
**Parameters:**
962
- `repository` - RendererSupport instance (typically LoggerRepository)
963
- `renderedClassName` - Class name to be rendered
964
- `renderingClassName` - ObjectRenderer implementation class name
965
- `o` - Object instance to find renderer for
966
- `clazz` - Class to find renderer for
967
- `renderer` - ObjectRenderer to associate with class
968
969
**Returns:**
970
- `get()` methods return ObjectRenderer instance or default renderer
971
- `put()` and `clear()` methods return void
972
973
**Usage Example:**
974
```java
975
import org.apache.log4j.*;
976
import org.apache.log4j.or.ObjectRenderer;
977
import org.apache.log4j.or.RendererMap;
978
979
public class ObjectRendererExample {
980
981
// Custom renderer for User objects
982
public static class UserRenderer implements ObjectRenderer {
983
public String doRender(Object o) {
984
if (o instanceof User) {
985
User user = (User) o;
986
return "User[id=" + user.getId() + ", name=" + user.getName() + "]";
987
}
988
return o.toString();
989
}
990
}
991
992
public void setupCustomRenderer() {
993
// Get logger repository that supports renderers
994
LoggerRepository repository = LogManager.getLoggerRepository();
995
996
// Add custom renderer for User class
997
RendererMap.addRenderer(
998
(org.apache.log4j.spi.RendererSupport) repository,
999
"com.myapp.User",
1000
"com.myapp.UserRenderer"
1001
);
1002
1003
// Now User objects will be rendered using UserRenderer
1004
Logger logger = Logger.getLogger("com.myapp");
1005
User user = new User(123, "John Doe");
1006
logger.info("Created user: " + user); // Uses custom renderer
1007
}
1008
}
1009
```
1010
1011
## JMX Support (Deprecated)
1012
1013
Log4j 1.x compatibility API includes deprecated JMX management classes for backward compatibility. These classes are primarily for legacy applications and new code should use Log4j 2.x JMX features directly.
1014
1015
### Key JMX Classes
1016
1017
```java { .api }
1018
// Main JMX agent (deprecated)
1019
@Deprecated
1020
public class Agent {
1021
public static Object createServer(int port);
1022
public static void start(Object server);
1023
}
1024
1025
// MBean implementations (deprecated)
1026
@Deprecated
1027
public class HierarchyDynamicMBean extends AbstractDynamicMBean;
1028
1029
@Deprecated
1030
public class LoggerDynamicMBean extends AbstractDynamicMBean;
1031
1032
@Deprecated
1033
public class AppenderDynamicMBean extends AbstractDynamicMBean;
1034
```
1035
1036
**Note:** These JMX classes are deprecated and maintained only for compatibility. For new applications, use Log4j 2.x's built-in JMX support instead.