1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.apache.log4j;
24
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InterruptedIOException;
29 import java.net.URLConnection;
30 import java.util.Enumeration;
31 import java.util.Hashtable;
32 import java.util.Properties;
33 import java.util.StringTokenizer;
34 import java.util.Vector;
35 import java.util.Iterator;
36 import java.util.Map;
37
38 import org.apache.log4j.config.PropertySetter;
39 import org.apache.log4j.helpers.FileWatchdog;
40 import org.apache.log4j.helpers.LogLog;
41 import org.apache.log4j.helpers.OptionConverter;
42 import org.apache.log4j.or.RendererMap;
43 import org.apache.log4j.spi.Configurator;
44 import org.apache.log4j.spi.Filter;
45 import org.apache.log4j.spi.LoggerFactory;
46 import org.apache.log4j.spi.LoggerRepository;
47 import org.apache.log4j.spi.OptionHandler;
48 import org.apache.log4j.spi.RendererSupport;
49 import org.apache.log4j.spi.ThrowableRenderer;
50 import org.apache.log4j.spi.ThrowableRendererSupport;
51 import org.apache.log4j.spi.ErrorHandler;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93 public class PropertyConfigurator implements Configurator {
94
95
96
97
98 protected Hashtable registry = new Hashtable(11);
99 private LoggerRepository repository;
100 protected LoggerFactory loggerFactory = new DefaultCategoryFactory();
101
102 static final String CATEGORY_PREFIX = "log4j.category.";
103 static final String LOGGER_PREFIX = "log4j.logger.";
104 static final String FACTORY_PREFIX = "log4j.factory";
105 static final String ADDITIVITY_PREFIX = "log4j.additivity.";
106 static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
107 static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
108 static final String APPENDER_PREFIX = "log4j.appender.";
109 static final String RENDERER_PREFIX = "log4j.renderer.";
110 static final String THRESHOLD_PREFIX = "log4j.threshold";
111 private static final String THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer";
112 private static final String LOGGER_REF = "logger-ref";
113 private static final String ROOT_REF = "root-ref";
114 private static final String APPENDER_REF_TAG = "appender-ref";
115
116
117
118
119 public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
120
121
122
123
124 private static final String RESET_KEY = "log4j.reset";
125
126 static final private String INTERNAL_ROOT_NAME = "root";
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367 public
368 void doConfigure(String configFileName, LoggerRepository hierarchy) {
369 Properties props = new Properties();
370 FileInputStream istream = null;
371 try {
372 istream = new FileInputStream(configFileName);
373 props.load(istream);
374 istream.close();
375 }
376 catch (Exception e) {
377 if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
378 Thread.currentThread().interrupt();
379 }
380 LogLog.error("Could not read configuration file ["+configFileName+"].", e);
381 LogLog.error("Ignoring configuration file [" + configFileName+"].");
382 return;
383 } finally {
384 if(istream != null) {
385 try {
386 istream.close();
387 } catch(InterruptedIOException ignore) {
388 Thread.currentThread().interrupt();
389 } catch(Throwable ignore) {
390 }
391
392 }
393 }
394
395 doConfigure(props, hierarchy);
396 }
397
398
399
400 static
401 public
402 void configure(String configFilename) {
403 new PropertyConfigurator().doConfigure(configFilename,
404 LogManager.getLoggerRepository());
405 }
406
407
408
409
410
411
412 public
413 static
414 void configure(java.net.URL configURL) {
415 new PropertyConfigurator().doConfigure(configURL,
416 LogManager.getLoggerRepository());
417 }
418
419
420
421
422
423
424
425 static
426 public
427 void configure(Properties properties) {
428 new PropertyConfigurator().doConfigure(properties,
429 LogManager.getLoggerRepository());
430 }
431
432
433
434
435
436
437
438
439
440 static
441 public
442 void configureAndWatch(String configFilename) {
443 configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
444 }
445
446
447
448
449
450
451
452
453
454
455
456
457
458 static
459 public
460 void configureAndWatch(String configFilename, long delay) {
461 PropertyWatchdog pdog = new PropertyWatchdog(configFilename);
462 pdog.setDelay(delay);
463 pdog.start();
464 }
465
466
467
468
469
470
471
472 public
473 void doConfigure(Properties properties, LoggerRepository hierarchy) {
474 repository = hierarchy;
475 String value = properties.getProperty(LogLog.DEBUG_KEY);
476 if(value == null) {
477 value = properties.getProperty("log4j.configDebug");
478 if(value != null)
479 LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
480 }
481
482 if(value != null) {
483 LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
484 }
485
486
487
488
489 String reset = properties.getProperty(RESET_KEY);
490 if (reset != null && OptionConverter.toBoolean(reset, false)) {
491 hierarchy.resetConfiguration();
492 }
493
494 String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX,
495 properties);
496 if(thresholdStr != null) {
497 hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr,
498 (Level) Level.ALL));
499 LogLog.debug("Hierarchy threshold set to ["+hierarchy.getThreshold()+"].");
500 }
501
502 configureRootCategory(properties, hierarchy);
503 configureLoggerFactory(properties);
504 parseCatsAndRenderers(properties, hierarchy);
505
506 LogLog.debug("Finished configuring.");
507
508
509 registry.clear();
510 }
511
512
513
514
515 public
516 void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) {
517 Properties props = new Properties();
518 LogLog.debug("Reading configuration from URL " + configURL);
519 InputStream istream = null;
520 URLConnection uConn = null;
521 try {
522 uConn = configURL.openConnection();
523 uConn.setUseCaches(false);
524 istream = uConn.getInputStream();
525 props.load(istream);
526 }
527 catch (Exception e) {
528 if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
529 Thread.currentThread().interrupt();
530 }
531 LogLog.error("Could not read configuration file from URL [" + configURL
532 + "].", e);
533 LogLog.error("Ignoring configuration file [" + configURL +"].");
534 return;
535 }
536 finally {
537 if (istream != null) {
538 try {
539 istream.close();
540 } catch(InterruptedIOException ignore) {
541 Thread.currentThread().interrupt();
542 } catch(IOException ignore) {
543 } catch(RuntimeException ignore) {
544 }
545 }
546 }
547 doConfigure(props, hierarchy);
548 }
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565 protected void configureLoggerFactory(Properties props) {
566 String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY,
567 props);
568 if(factoryClassName != null) {
569 LogLog.debug("Setting category factory to ["+factoryClassName+"].");
570 loggerFactory = (LoggerFactory)
571 OptionConverter.instantiateByClassName(factoryClassName,
572 LoggerFactory.class,
573 loggerFactory);
574 PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX + ".");
575 }
576 }
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601 void configureRootCategory(Properties props, LoggerRepository hierarchy) {
602 String effectiveFrefix = ROOT_LOGGER_PREFIX;
603 String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
604
605 if(value == null) {
606 value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
607 effectiveFrefix = ROOT_CATEGORY_PREFIX;
608 }
609
610 if(value == null)
611 LogLog.debug("Could not find root logger information. Is this OK?");
612 else {
613 Logger root = hierarchy.getRootLogger();
614 synchronized(root) {
615 parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
616 }
617 }
618 }
619
620
621
622
623
624 protected
625 void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {
626 Enumeration enumeration = props.propertyNames();
627 while(enumeration.hasMoreElements()) {
628 String key = (String) enumeration.nextElement();
629 if(key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
630 String loggerName = null;
631 if(key.startsWith(CATEGORY_PREFIX)) {
632 loggerName = key.substring(CATEGORY_PREFIX.length());
633 } else if(key.startsWith(LOGGER_PREFIX)) {
634 loggerName = key.substring(LOGGER_PREFIX.length());
635 }
636 String value = OptionConverter.findAndSubst(key, props);
637 Logger logger = hierarchy.getLogger(loggerName, loggerFactory);
638 synchronized(logger) {
639 parseCategory(props, logger, key, loggerName, value);
640 parseAdditivityForLogger(props, logger, loggerName);
641 }
642 } else if(key.startsWith(RENDERER_PREFIX)) {
643 String renderedClass = key.substring(RENDERER_PREFIX.length());
644 String renderingClass = OptionConverter.findAndSubst(key, props);
645 if(hierarchy instanceof RendererSupport) {
646 RendererMap.addRenderer((RendererSupport) hierarchy, renderedClass,
647 renderingClass);
648 }
649 } else if (key.equals(THROWABLE_RENDERER_PREFIX)) {
650 if (hierarchy instanceof ThrowableRendererSupport) {
651 ThrowableRenderer tr = (ThrowableRenderer)
652 OptionConverter.instantiateByKey(props,
653 THROWABLE_RENDERER_PREFIX,
654 org.apache.log4j.spi.ThrowableRenderer.class,
655 null);
656 if(tr == null) {
657 LogLog.error(
658 "Could not instantiate throwableRenderer.");
659 } else {
660 PropertySetter setter = new PropertySetter(tr);
661 setter.setProperties(props, THROWABLE_RENDERER_PREFIX + ".");
662 ((ThrowableRendererSupport) hierarchy).setThrowableRenderer(tr);
663
664 }
665 }
666 }
667 }
668 }
669
670
671
672
673 void parseAdditivityForLogger(Properties props, Logger cat,
674 String loggerName) {
675 String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName,
676 props);
677 LogLog.debug("Handling "+ADDITIVITY_PREFIX + loggerName+"=["+value+"]");
678
679 if((value != null) && (!value.equals(""))) {
680 boolean additivity = OptionConverter.toBoolean(value, true);
681 LogLog.debug("Setting additivity for \""+loggerName+"\" to "+
682 additivity);
683 cat.setAdditivity(additivity);
684 }
685 }
686
687
688
689
690 void parseCategory(Properties props, Logger logger, String optionKey,
691 String loggerName, String value) {
692
693 LogLog.debug("Parsing for [" +loggerName +"] with value=[" + value+"].");
694
695 StringTokenizer st = new StringTokenizer(value, ",");
696
697
698
699
700 if(!(value.startsWith(",") || value.equals(""))) {
701
702
703 if(!st.hasMoreTokens())
704 return;
705
706 String levelStr = st.nextToken();
707 LogLog.debug("Level token is [" + levelStr + "].");
708
709
710
711
712 if(INHERITED.equalsIgnoreCase(levelStr) ||
713 NULL.equalsIgnoreCase(levelStr)) {
714 if(loggerName.equals(INTERNAL_ROOT_NAME)) {
715 LogLog.warn("The root logger cannot be set to null.");
716 } else {
717 logger.setLevel(null);
718 }
719 } else {
720 logger.setLevel(OptionConverter.toLevel(levelStr, (Level) Level.DEBUG));
721 }
722 LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
723 }
724
725
726 logger.removeAllAppenders();
727
728 Appender appender;
729 String appenderName;
730 while(st.hasMoreTokens()) {
731 appenderName = st.nextToken().trim();
732 if(appenderName == null || appenderName.equals(","))
733 continue;
734 LogLog.debug("Parsing appender named \"" + appenderName +"\".");
735 appender = parseAppender(props, appenderName);
736 if(appender != null) {
737 logger.addAppender(appender);
738 }
739 }
740 }
741
742 Appender parseAppender(Properties props, String appenderName) {
743 Appender appender = registryGet(appenderName);
744 if((appender != null)) {
745 LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
746 return appender;
747 }
748
749 String prefix = APPENDER_PREFIX + appenderName;
750 String layoutPrefix = prefix + ".layout";
751
752 appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
753 org.apache.log4j.Appender.class,
754 null);
755 if(appender == null) {
756 LogLog.error(
757 "Could not instantiate appender named \"" + appenderName+"\".");
758 return null;
759 }
760 appender.setName(appenderName);
761
762 if(appender instanceof OptionHandler) {
763 if(appender.requiresLayout()) {
764 Layout layout = (Layout) OptionConverter.instantiateByKey(props,
765 layoutPrefix,
766 Layout.class,
767 null);
768 if(layout != null) {
769 appender.setLayout(layout);
770 LogLog.debug("Parsing layout options for \"" + appenderName +"\".");
771
772 PropertySetter.setProperties(layout, props, layoutPrefix + ".");
773 LogLog.debug("End of parsing for \"" + appenderName +"\".");
774 }
775 }
776 final String errorHandlerPrefix = prefix + ".errorhandler";
777 String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
778 if (errorHandlerClass != null) {
779 ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(props,
780 errorHandlerPrefix,
781 ErrorHandler.class,
782 null);
783 if (eh != null) {
784 appender.setErrorHandler(eh);
785 LogLog.debug("Parsing errorhandler options for \"" + appenderName +"\".");
786 parseErrorHandler(eh, errorHandlerPrefix, props, repository);
787 final Properties edited = new Properties();
788 final String[] keys = new String[] {
789 errorHandlerPrefix + "." + ROOT_REF,
790 errorHandlerPrefix + "." + LOGGER_REF,
791 errorHandlerPrefix + "." + APPENDER_REF_TAG
792 };
793 for(Iterator iter = props.entrySet().iterator();iter.hasNext();) {
794 Map.Entry entry = (Map.Entry) iter.next();
795 int i = 0;
796 for(; i < keys.length; i++) {
797 if(keys[i].equals(entry.getKey())) break;
798 }
799 if (i == keys.length) {
800 edited.put(entry.getKey(), entry.getValue());
801 }
802 }
803 PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
804 LogLog.debug("End of errorhandler parsing for \"" + appenderName +"\".");
805 }
806
807 }
808
809 PropertySetter.setProperties(appender, props, prefix + ".");
810 LogLog.debug("Parsed \"" + appenderName +"\" options.");
811 }
812 parseAppenderFilters(props, appenderName, appender);
813 registryPut(appender);
814 return appender;
815 }
816
817 private void parseErrorHandler(
818 final ErrorHandler eh,
819 final String errorHandlerPrefix,
820 final Properties props,
821 final LoggerRepository hierarchy) {
822 boolean rootRef = OptionConverter.toBoolean(
823 OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false);
824 if (rootRef) {
825 eh.setLogger(hierarchy.getRootLogger());
826 }
827 String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF , props);
828 if (loggerName != null) {
829 Logger logger = (loggerFactory == null) ? hierarchy.getLogger(loggerName)
830 : hierarchy.getLogger(loggerName, loggerFactory);
831 eh.setLogger(logger);
832 }
833 String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props);
834 if (appenderName != null) {
835 Appender backup = parseAppender(props, appenderName);
836 if (backup != null) {
837 eh.setBackupAppender(backup);
838 }
839 }
840 }
841
842
843 void parseAppenderFilters(Properties props, String appenderName, Appender appender) {
844
845
846
847 final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
848 int fIdx = filterPrefix.length();
849 Hashtable filters = new Hashtable();
850 Enumeration e = props.keys();
851 String name = "";
852 while (e.hasMoreElements()) {
853 String key = (String) e.nextElement();
854 if (key.startsWith(filterPrefix)) {
855 int dotIdx = key.indexOf('.', fIdx);
856 String filterKey = key;
857 if (dotIdx != -1) {
858 filterKey = key.substring(0, dotIdx);
859 name = key.substring(dotIdx+1);
860 }
861 Vector filterOpts = (Vector) filters.get(filterKey);
862 if (filterOpts == null) {
863 filterOpts = new Vector();
864 filters.put(filterKey, filterOpts);
865 }
866 if (dotIdx != -1) {
867 String value = OptionConverter.findAndSubst(key, props);
868 filterOpts.add(new NameValue(name, value));
869 }
870 }
871 }
872
873
874
875 Enumeration g = new SortedKeyEnumeration(filters);
876 while (g.hasMoreElements()) {
877 String key = (String) g.nextElement();
878 String clazz = props.getProperty(key);
879 if (clazz != null) {
880 LogLog.debug("Filter key: ["+key+"] class: ["+props.getProperty(key) +"] props: "+filters.get(key));
881 Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null);
882 if (filter != null) {
883 PropertySetter propSetter = new PropertySetter(filter);
884 Vector v = (Vector)filters.get(key);
885 Enumeration filterProps = v.elements();
886 while (filterProps.hasMoreElements()) {
887 NameValue kv = (NameValue)filterProps.nextElement();
888 propSetter.setProperty(kv.key, kv.value);
889 }
890 propSetter.activate();
891 LogLog.debug("Adding filter of type ["+filter.getClass()
892 +"] to appender named ["+appender.getName()+"].");
893 appender.addFilter(filter);
894 }
895 } else {
896 LogLog.warn("Missing class definition for filter: ["+key+"]");
897 }
898 }
899 }
900
901
902 void registryPut(Appender appender) {
903 registry.put(appender.getName(), appender);
904 }
905
906 Appender registryGet(String name) {
907 return (Appender) registry.get(name);
908 }
909 }
910
911 class PropertyWatchdog extends FileWatchdog {
912
913 PropertyWatchdog(String filename) {
914 super(filename);
915 }
916
917
918
919
920 public
921 void doOnChange() {
922 new PropertyConfigurator().doConfigure(filename,
923 LogManager.getLoggerRepository());
924 }
925 }
926
927 class NameValue {
928 String key, value;
929 public NameValue(String key, String value) {
930 this.key = key;
931 this.value = value;
932 }
933 public String toString() {
934 return key + "=" + value;
935 }
936 }
937
938 class SortedKeyEnumeration implements Enumeration {
939
940 private Enumeration e;
941
942 public SortedKeyEnumeration(Hashtable ht) {
943 Enumeration f = ht.keys();
944 Vector keys = new Vector(ht.size());
945 for (int i, last = 0; f.hasMoreElements(); ++last) {
946 String key = (String) f.nextElement();
947 for (i = 0; i < last; ++i) {
948 String s = (String) keys.get(i);
949 if (key.compareTo(s) <= 0) break;
950 }
951 keys.add(i, key);
952 }
953 e = keys.elements();
954 }
955
956 public boolean hasMoreElements() {
957 return e.hasMoreElements();
958 }
959
960 public Object nextElement() {
961 return e.nextElement();
962 }
963 }