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 public
425 static
426 void configure(InputStream inputStream) {
427 new PropertyConfigurator().doConfigure(inputStream,
428 LogManager.getLoggerRepository());
429 }
430
431
432
433
434
435
436
437 static
438 public
439 void configure(Properties properties) {
440 new PropertyConfigurator().doConfigure(properties,
441 LogManager.getLoggerRepository());
442 }
443
444
445
446
447
448
449
450
451
452 static
453 public
454 void configureAndWatch(String configFilename) {
455 configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
456 }
457
458
459
460
461
462
463
464
465
466
467
468
469
470 static
471 public
472 void configureAndWatch(String configFilename, long delay) {
473 PropertyWatchdog pdog = new PropertyWatchdog(configFilename);
474 pdog.setDelay(delay);
475 pdog.start();
476 }
477
478
479
480
481
482
483
484 public
485 void doConfigure(Properties properties, LoggerRepository hierarchy) {
486 repository = hierarchy;
487 String value = properties.getProperty(LogLog.DEBUG_KEY);
488 if(value == null) {
489 value = properties.getProperty("log4j.configDebug");
490 if(value != null)
491 LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
492 }
493
494 if(value != null) {
495 LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
496 }
497
498
499
500
501 String reset = properties.getProperty(RESET_KEY);
502 if (reset != null && OptionConverter.toBoolean(reset, false)) {
503 hierarchy.resetConfiguration();
504 }
505
506 String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX,
507 properties);
508 if(thresholdStr != null) {
509 hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr,
510 (Level) Level.ALL));
511 LogLog.debug("Hierarchy threshold set to ["+hierarchy.getThreshold()+"].");
512 }
513
514 configureRootCategory(properties, hierarchy);
515 configureLoggerFactory(properties);
516 parseCatsAndRenderers(properties, hierarchy);
517
518 LogLog.debug("Finished configuring.");
519
520
521 registry.clear();
522 }
523
524
525
526
527
528
529 public void doConfigure(InputStream inputStream, LoggerRepository hierarchy) {
530 Properties props = new Properties();
531 try {
532 props.load(inputStream);
533 } catch (IOException e) {
534 if (e instanceof InterruptedIOException) {
535 Thread.currentThread().interrupt();
536 }
537 LogLog.error("Could not read configuration file from InputStream [" + inputStream
538 + "].", e);
539 LogLog.error("Ignoring configuration InputStream [" + inputStream +"].");
540 return;
541 }
542 this.doConfigure(props, hierarchy);
543 }
544
545
546
547
548 public
549 void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) {
550 Properties props = new Properties();
551 LogLog.debug("Reading configuration from URL " + configURL);
552 InputStream istream = null;
553 URLConnection uConn = null;
554 try {
555 uConn = configURL.openConnection();
556 uConn.setUseCaches(false);
557 istream = uConn.getInputStream();
558 props.load(istream);
559 }
560 catch (Exception e) {
561 if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
562 Thread.currentThread().interrupt();
563 }
564 LogLog.error("Could not read configuration file from URL [" + configURL
565 + "].", e);
566 LogLog.error("Ignoring configuration file [" + configURL +"].");
567 return;
568 }
569 finally {
570 if (istream != null) {
571 try {
572 istream.close();
573 } catch(InterruptedIOException ignore) {
574 Thread.currentThread().interrupt();
575 } catch(IOException ignore) {
576 } catch(RuntimeException ignore) {
577 }
578 }
579 }
580 doConfigure(props, hierarchy);
581 }
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598 protected void configureLoggerFactory(Properties props) {
599 String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY,
600 props);
601 if(factoryClassName != null) {
602 LogLog.debug("Setting category factory to ["+factoryClassName+"].");
603 loggerFactory = (LoggerFactory)
604 OptionConverter.instantiateByClassName(factoryClassName,
605 LoggerFactory.class,
606 loggerFactory);
607 PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX + ".");
608 }
609 }
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634 void configureRootCategory(Properties props, LoggerRepository hierarchy) {
635 String effectiveFrefix = ROOT_LOGGER_PREFIX;
636 String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
637
638 if(value == null) {
639 value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
640 effectiveFrefix = ROOT_CATEGORY_PREFIX;
641 }
642
643 if(value == null)
644 LogLog.debug("Could not find root logger information. Is this OK?");
645 else {
646 Logger root = hierarchy.getRootLogger();
647 synchronized(root) {
648 parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
649 }
650 }
651 }
652
653
654
655
656
657 protected
658 void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {
659 Enumeration enumeration = props.propertyNames();
660 while(enumeration.hasMoreElements()) {
661 String key = (String) enumeration.nextElement();
662 if(key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
663 String loggerName = null;
664 if(key.startsWith(CATEGORY_PREFIX)) {
665 loggerName = key.substring(CATEGORY_PREFIX.length());
666 } else if(key.startsWith(LOGGER_PREFIX)) {
667 loggerName = key.substring(LOGGER_PREFIX.length());
668 }
669 String value = OptionConverter.findAndSubst(key, props);
670 Logger logger = hierarchy.getLogger(loggerName, loggerFactory);
671 synchronized(logger) {
672 parseCategory(props, logger, key, loggerName, value);
673 parseAdditivityForLogger(props, logger, loggerName);
674 }
675 } else if(key.startsWith(RENDERER_PREFIX)) {
676 String renderedClass = key.substring(RENDERER_PREFIX.length());
677 String renderingClass = OptionConverter.findAndSubst(key, props);
678 if(hierarchy instanceof RendererSupport) {
679 RendererMap.addRenderer((RendererSupport) hierarchy, renderedClass,
680 renderingClass);
681 }
682 } else if (key.equals(THROWABLE_RENDERER_PREFIX)) {
683 if (hierarchy instanceof ThrowableRendererSupport) {
684 ThrowableRenderer tr = (ThrowableRenderer)
685 OptionConverter.instantiateByKey(props,
686 THROWABLE_RENDERER_PREFIX,
687 org.apache.log4j.spi.ThrowableRenderer.class,
688 null);
689 if(tr == null) {
690 LogLog.error(
691 "Could not instantiate throwableRenderer.");
692 } else {
693 PropertySetter setter = new PropertySetter(tr);
694 setter.setProperties(props, THROWABLE_RENDERER_PREFIX + ".");
695 ((ThrowableRendererSupport) hierarchy).setThrowableRenderer(tr);
696
697 }
698 }
699 }
700 }
701 }
702
703
704
705
706 void parseAdditivityForLogger(Properties props, Logger cat,
707 String loggerName) {
708 String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName,
709 props);
710 LogLog.debug("Handling "+ADDITIVITY_PREFIX + loggerName+"=["+value+"]");
711
712 if((value != null) && (!value.equals(""))) {
713 boolean additivity = OptionConverter.toBoolean(value, true);
714 LogLog.debug("Setting additivity for \""+loggerName+"\" to "+
715 additivity);
716 cat.setAdditivity(additivity);
717 }
718 }
719
720
721
722
723 void parseCategory(Properties props, Logger logger, String optionKey,
724 String loggerName, String value) {
725
726 LogLog.debug("Parsing for [" +loggerName +"] with value=[" + value+"].");
727
728 StringTokenizer st = new StringTokenizer(value, ",");
729
730
731
732
733 if(!(value.startsWith(",") || value.equals(""))) {
734
735
736 if(!st.hasMoreTokens())
737 return;
738
739 String levelStr = st.nextToken();
740 LogLog.debug("Level token is [" + levelStr + "].");
741
742
743
744
745 if(INHERITED.equalsIgnoreCase(levelStr) ||
746 NULL.equalsIgnoreCase(levelStr)) {
747 if(loggerName.equals(INTERNAL_ROOT_NAME)) {
748 LogLog.warn("The root logger cannot be set to null.");
749 } else {
750 logger.setLevel(null);
751 }
752 } else {
753 logger.setLevel(OptionConverter.toLevel(levelStr, (Level) Level.DEBUG));
754 }
755 LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
756 }
757
758
759 logger.removeAllAppenders();
760
761 Appender appender;
762 String appenderName;
763 while(st.hasMoreTokens()) {
764 appenderName = st.nextToken().trim();
765 if(appenderName == null || appenderName.equals(","))
766 continue;
767 LogLog.debug("Parsing appender named \"" + appenderName +"\".");
768 appender = parseAppender(props, appenderName);
769 if(appender != null) {
770 logger.addAppender(appender);
771 }
772 }
773 }
774
775 Appender parseAppender(Properties props, String appenderName) {
776 Appender appender = registryGet(appenderName);
777 if((appender != null)) {
778 LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
779 return appender;
780 }
781
782 String prefix = APPENDER_PREFIX + appenderName;
783 String layoutPrefix = prefix + ".layout";
784
785 appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
786 org.apache.log4j.Appender.class,
787 null);
788 if(appender == null) {
789 LogLog.error(
790 "Could not instantiate appender named \"" + appenderName+"\".");
791 return null;
792 }
793 appender.setName(appenderName);
794
795 if(appender instanceof OptionHandler) {
796 if(appender.requiresLayout()) {
797 Layout layout = (Layout) OptionConverter.instantiateByKey(props,
798 layoutPrefix,
799 Layout.class,
800 null);
801 if(layout != null) {
802 appender.setLayout(layout);
803 LogLog.debug("Parsing layout options for \"" + appenderName +"\".");
804
805 PropertySetter.setProperties(layout, props, layoutPrefix + ".");
806 LogLog.debug("End of parsing for \"" + appenderName +"\".");
807 }
808 }
809 final String errorHandlerPrefix = prefix + ".errorhandler";
810 String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
811 if (errorHandlerClass != null) {
812 ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(props,
813 errorHandlerPrefix,
814 ErrorHandler.class,
815 null);
816 if (eh != null) {
817 appender.setErrorHandler(eh);
818 LogLog.debug("Parsing errorhandler options for \"" + appenderName +"\".");
819 parseErrorHandler(eh, errorHandlerPrefix, props, repository);
820 final Properties edited = new Properties();
821 final String[] keys = new String[] {
822 errorHandlerPrefix + "." + ROOT_REF,
823 errorHandlerPrefix + "." + LOGGER_REF,
824 errorHandlerPrefix + "." + APPENDER_REF_TAG
825 };
826 for(Iterator iter = props.entrySet().iterator();iter.hasNext();) {
827 Map.Entry entry = (Map.Entry) iter.next();
828 int i = 0;
829 for(; i < keys.length; i++) {
830 if(keys[i].equals(entry.getKey())) break;
831 }
832 if (i == keys.length) {
833 edited.put(entry.getKey(), entry.getValue());
834 }
835 }
836 PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
837 LogLog.debug("End of errorhandler parsing for \"" + appenderName +"\".");
838 }
839
840 }
841
842 PropertySetter.setProperties(appender, props, prefix + ".");
843 LogLog.debug("Parsed \"" + appenderName +"\" options.");
844 }
845 parseAppenderFilters(props, appenderName, appender);
846 registryPut(appender);
847 return appender;
848 }
849
850 private void parseErrorHandler(
851 final ErrorHandler eh,
852 final String errorHandlerPrefix,
853 final Properties props,
854 final LoggerRepository hierarchy) {
855 boolean rootRef = OptionConverter.toBoolean(
856 OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false);
857 if (rootRef) {
858 eh.setLogger(hierarchy.getRootLogger());
859 }
860 String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF , props);
861 if (loggerName != null) {
862 Logger logger = (loggerFactory == null) ? hierarchy.getLogger(loggerName)
863 : hierarchy.getLogger(loggerName, loggerFactory);
864 eh.setLogger(logger);
865 }
866 String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props);
867 if (appenderName != null) {
868 Appender backup = parseAppender(props, appenderName);
869 if (backup != null) {
870 eh.setBackupAppender(backup);
871 }
872 }
873 }
874
875
876 void parseAppenderFilters(Properties props, String appenderName, Appender appender) {
877
878
879
880 final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
881 int fIdx = filterPrefix.length();
882 Hashtable filters = new Hashtable();
883 Enumeration e = props.keys();
884 String name = "";
885 while (e.hasMoreElements()) {
886 String key = (String) e.nextElement();
887 if (key.startsWith(filterPrefix)) {
888 int dotIdx = key.indexOf('.', fIdx);
889 String filterKey = key;
890 if (dotIdx != -1) {
891 filterKey = key.substring(0, dotIdx);
892 name = key.substring(dotIdx+1);
893 }
894 Vector filterOpts = (Vector) filters.get(filterKey);
895 if (filterOpts == null) {
896 filterOpts = new Vector();
897 filters.put(filterKey, filterOpts);
898 }
899 if (dotIdx != -1) {
900 String value = OptionConverter.findAndSubst(key, props);
901 filterOpts.add(new NameValue(name, value));
902 }
903 }
904 }
905
906
907
908 Enumeration g = new SortedKeyEnumeration(filters);
909 while (g.hasMoreElements()) {
910 String key = (String) g.nextElement();
911 String clazz = props.getProperty(key);
912 if (clazz != null) {
913 LogLog.debug("Filter key: ["+key+"] class: ["+props.getProperty(key) +"] props: "+filters.get(key));
914 Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null);
915 if (filter != null) {
916 PropertySetter propSetter = new PropertySetter(filter);
917 Vector v = (Vector)filters.get(key);
918 Enumeration filterProps = v.elements();
919 while (filterProps.hasMoreElements()) {
920 NameValue kv = (NameValue)filterProps.nextElement();
921 propSetter.setProperty(kv.key, kv.value);
922 }
923 propSetter.activate();
924 LogLog.debug("Adding filter of type ["+filter.getClass()
925 +"] to appender named ["+appender.getName()+"].");
926 appender.addFilter(filter);
927 }
928 } else {
929 LogLog.warn("Missing class definition for filter: ["+key+"]");
930 }
931 }
932 }
933
934
935 void registryPut(Appender appender) {
936 registry.put(appender.getName(), appender);
937 }
938
939 Appender registryGet(String name) {
940 return (Appender) registry.get(name);
941 }
942 }
943
944 class PropertyWatchdog extends FileWatchdog {
945
946 PropertyWatchdog(String filename) {
947 super(filename);
948 }
949
950
951
952
953 public
954 void doOnChange() {
955 new PropertyConfigurator().doConfigure(filename,
956 LogManager.getLoggerRepository());
957 }
958 }
959
960 class NameValue {
961 String key, value;
962 public NameValue(String key, String value) {
963 this.key = key;
964 this.value = value;
965 }
966 public String toString() {
967 return key + "=" + value;
968 }
969 }
970
971 class SortedKeyEnumeration implements Enumeration {
972
973 private Enumeration e;
974
975 public SortedKeyEnumeration(Hashtable ht) {
976 Enumeration f = ht.keys();
977 Vector keys = new Vector(ht.size());
978 for (int i, last = 0; f.hasMoreElements(); ++last) {
979 String key = (String) f.nextElement();
980 for (i = 0; i < last; ++i) {
981 String s = (String) keys.get(i);
982 if (key.compareTo(s) <= 0) break;
983 }
984 keys.add(i, key);
985 }
986 e = keys.elements();
987 }
988
989 public boolean hasMoreElements() {
990 return e.hasMoreElements();
991 }
992
993 public Object nextElement() {
994 return e.nextElement();
995 }
996 }