1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.log4j;
19
20 import org.apache.log4j.helpers.LogLog;
21 import org.apache.log4j.or.ObjectRenderer;
22 import org.apache.log4j.or.RendererMap;
23 import org.apache.log4j.plugins.Plugin;
24 import org.apache.log4j.plugins.PluginRegistry;
25 import org.apache.log4j.scheduler.Scheduler;
26 import org.apache.log4j.spi.ErrorItem;
27 import org.apache.log4j.spi.HierarchyEventListener;
28 import org.apache.log4j.spi.LoggerEventListener;
29 import org.apache.log4j.spi.LoggerFactory;
30 import org.apache.log4j.spi.LoggerRepository;
31 import org.apache.log4j.spi.LoggerRepositoryEventListener;
32 import org.apache.log4j.spi.LoggerRepositoryEx;
33 import org.apache.log4j.spi.RendererSupport;
34 import org.apache.log4j.xml.UnrecognizedElementHandler;
35 import org.apache.log4j.xml.DOMConfigurator;
36 import org.w3c.dom.Element;
37
38 import java.util.ArrayList;
39 import java.util.Enumeration;
40 import java.util.HashMap;
41 import java.util.Hashtable;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.Properties;
45 import java.util.Vector;
46
47
48 /**
49 * This class implements LoggerRepositoryEx by
50 * wrapping an existing LoggerRepository implementation
51 * and implementing the newly added capabilities.
52 */
53 public final class LoggerRepositoryExImpl
54 implements LoggerRepositoryEx,
55 RendererSupport,
56 UnrecognizedElementHandler {
57
58 /**
59 * Wrapped logger repository.
60 */
61 private final LoggerRepository repo;
62
63 /**
64 * Logger factory. Does not affect class of logger
65 * created by underlying repository.
66 */
67 private LoggerFactory loggerFactory;
68
69 /**
70 * Renderer support.
71 */
72 private final RendererSupport rendererSupport;
73
74 /**
75 * List of repository event listeners.
76 */
77 private final ArrayList repositoryEventListeners = new ArrayList();
78 /**
79 * Map of HierarchyEventListener keyed by LoggingEventListener.
80 */
81 private final Map loggerEventListeners = new HashMap();
82 /**
83 * Name of hierarchy.
84 */
85 private String name;
86 /**
87 * Plug in registry.
88 */
89 private PluginRegistry pluginRegistry;
90 /**
91 * Properties.
92 */
93 private final Map properties = new Hashtable();
94 /**
95 * Scheduler.
96 */
97 private Scheduler scheduler;
98
99 /** The repository can also be used as an object store
100 * for various objects used by log4j components.
101 */
102 private Map objectMap = new HashMap();
103
104
105 /**
106 * Error list.
107 */
108 private List errorList = new Vector();
109
110 /**
111 * True if hierarchy has not been modified.
112 */
113 private boolean pristine = true;
114
115 /**
116 Constructs a new logger hierarchy.
117
118 @param repository Base implementation of repository.
119
120 */
121 public LoggerRepositoryExImpl(final LoggerRepository repository) {
122 super();
123 if (repository == null) {
124 throw new NullPointerException("repository");
125 }
126 repo = repository;
127 if (repository instanceof RendererSupport) {
128 rendererSupport = (RendererSupport) repository;
129 } else {
130 rendererSupport = new RendererSupportImpl();
131 }
132 }
133
134
135 /**
136 Add a {@link LoggerRepositoryEventListener} to the repository. The
137 listener will be called when repository events occur.
138 @param listener listener
139 */
140 public void addLoggerRepositoryEventListener(
141 final LoggerRepositoryEventListener listener) {
142 synchronized (repositoryEventListeners) {
143 if (repositoryEventListeners.contains(listener)) {
144 LogLog.warn(
145 "Ignoring attempt to add a previously "
146 + "registered LoggerRepositoryEventListener.");
147 } else {
148 repositoryEventListeners.add(listener);
149 }
150 }
151 }
152
153
154 /**
155 Remove a {@link LoggerRepositoryEventListener} from the repository.
156 @param listener listener
157 */
158 public void removeLoggerRepositoryEventListener(
159 final LoggerRepositoryEventListener listener) {
160 synchronized (repositoryEventListeners) {
161 if (!repositoryEventListeners.contains(listener)) {
162 LogLog.warn(
163 "Ignoring attempt to remove a "
164 + "non-registered LoggerRepositoryEventListener.");
165 } else {
166 repositoryEventListeners.remove(listener);
167 }
168 }
169 }
170
171 /**
172 Add a {@link LoggerEventListener} to the repository. The listener
173 will be called when repository events occur.
174 @param listener listener
175 */
176 public void addLoggerEventListener(final LoggerEventListener listener) {
177 synchronized (loggerEventListeners) {
178 if (loggerEventListeners.get(listener) != null) {
179 LogLog.warn(
180 "Ignoring attempt to add a previously registerd LoggerEventListener.");
181 } else {
182 HierarchyEventListenerProxy proxy =
183 new HierarchyEventListenerProxy(listener);
184 loggerEventListeners.put(listener, proxy);
185 repo.addHierarchyEventListener(proxy);
186 }
187 }
188 }
189
190 /**
191 Add a {@link org.apache.log4j.spi.HierarchyEventListener}
192 event to the repository.
193 @param listener listener
194 @deprecated Superceded by addLoggerEventListener
195 */
196 public
197 void addHierarchyEventListener(final HierarchyEventListener listener) {
198 repo.addHierarchyEventListener(listener);
199 }
200
201
202 /**
203 Remove a {@link LoggerEventListener} from the repository.
204 @param listener listener to be removed
205 */
206 public void removeLoggerEventListener(final LoggerEventListener listener) {
207 synchronized (loggerEventListeners) {
208 HierarchyEventListenerProxy proxy =
209 (HierarchyEventListenerProxy) loggerEventListeners.get(listener);
210 if (proxy == null) {
211 LogLog.warn(
212 "Ignoring attempt to remove a non-registered LoggerEventListener.");
213 } else {
214 loggerEventListeners.remove(listener);
215 proxy.disable();
216 }
217 }
218 }
219
220 /**
221 * Issue warning that there are no appenders in hierarchy.
222 * @param cat logger, not currently used.
223 */
224 public void emitNoAppenderWarning(final Category cat) {
225 repo.emitNoAppenderWarning(cat);
226 }
227
228 /**
229 Check if the named logger exists in the hierarchy. If so return
230 its reference, otherwise returns <code>null</code>.
231
232 @param loggerName The name of the logger to search for.
233 @return true if logger exists.
234 */
235 public Logger exists(final String loggerName) {
236 return repo.exists(loggerName);
237 }
238
239 /**
240 * Return the name of this hierarchy.
241 * @return name of hierarchy
242 */
243 public String getName() {
244 return name;
245 }
246
247 /**
248 * Set the name of this repository.
249 *
250 * Note that once named, a repository cannot be rerenamed.
251 * @param repoName name of hierarchy
252 */
253 public void setName(final String repoName) {
254 if (name == null) {
255 name = repoName;
256 } else if (!name.equals(repoName)) {
257 throw new IllegalStateException(
258 "Repository [" + name + "] cannot be renamed as [" + repoName + "].");
259 }
260 }
261
262 /**
263 * {@inheritDoc}
264 */
265 public Map getProperties() {
266 return properties;
267 }
268
269 /**
270 * {@inheritDoc}
271 */
272 public String getProperty(final String key) {
273 return (String) properties.get(key);
274 }
275
276 /**
277 * Set a property by key and value. The property will be shared by all
278 * events in this repository.
279 * @param key property name
280 * @param value property value
281 */
282 public void setProperty(final String key,
283 final String value) {
284 properties.put(key, value);
285 }
286
287 /**
288 The string form of {@link #setThreshold(Level)}.
289 @param levelStr symbolic name for level
290 */
291 public void setThreshold(final String levelStr) {
292 repo.setThreshold(levelStr);
293 }
294
295 /**
296 Enable logging for logging requests with level <code>l</code> or
297 higher. By default all levels are enabled.
298
299 @param l The minimum level for which logging requests are sent to
300 their appenders. */
301 public void setThreshold(final Level l) {
302 repo.setThreshold(l);
303 }
304
305 /**
306 * {@inheritDoc}
307 */
308 public PluginRegistry getPluginRegistry() {
309 if (pluginRegistry == null) {
310 pluginRegistry = new PluginRegistry(this);
311 }
312 return pluginRegistry;
313 }
314
315
316 /**
317 Requests that a appender added event be sent to any registered
318 {@link LoggerEventListener}.
319 @param logger The logger to which the appender was added.
320 @param appender The appender added to the logger.
321 */
322 public void fireAddAppenderEvent(final Category logger,
323 final Appender appender) {
324 repo.fireAddAppenderEvent(logger, appender);
325 }
326
327
328 /**
329 Requests that a appender removed event be sent to any registered
330 {@link LoggerEventListener}.
331 @param logger The logger from which the appender was removed.
332 @param appender The appender removed from the logger.
333 */
334 public void fireRemoveAppenderEvent(final Category logger,
335 final Appender appender) {
336 if (repo instanceof Hierarchy) {
337 ((Hierarchy) repo).fireRemoveAppenderEvent(logger, appender);
338 }
339 }
340
341
342 /**
343 Requests that a level changed event be sent to any registered
344 {@link LoggerEventListener}.
345 @param logger The logger which changed levels.
346 */
347 public void fireLevelChangedEvent(final Logger logger) {
348 }
349
350 /**
351 *
352 * Requests that a configuration changed event be sent to any registered
353 * {@link LoggerRepositoryEventListener}.
354 *
355 */
356 public void fireConfigurationChangedEvent() {
357 }
358
359
360 /**
361 Returns the current threshold.
362 @return current threshold level
363
364 @since 1.2 */
365 public Level getThreshold() {
366 return repo.getThreshold();
367 }
368
369
370 /**
371 Return a new logger instance named as the first parameter using
372 the default factory.
373
374 <p>If a logger of that name already exists, then it will be
375 returned. Otherwise, a new logger will be instantiated and
376 then linked with its existing ancestors as well as children.
377
378 @param loggerName The name of the logger to retrieve.
379 @return logger
380
381 */
382 public Logger getLogger(final String loggerName) {
383 return repo.getLogger(loggerName);
384 }
385
386 /**
387 Return a new logger instance named as the first parameter using
388 <code>factory</code>.
389
390 <p>If a logger of that name already exists, then it will be
391 returned. Otherwise, a new logger will be instantiated by the
392 <code>factory</code> parameter and linked with its existing
393 ancestors as well as children.
394
395 @param loggerName The name of the logger to retrieve.
396 @param factory The factory that will make the new logger instance.
397 @return logger
398
399 */
400 public Logger getLogger(final String loggerName,
401 final LoggerFactory factory) {
402 return repo.getLogger(loggerName, factory);
403 }
404
405 /**
406 Returns all the currently defined categories in this hierarchy as
407 an {@link java.util.Enumeration Enumeration}.
408
409 <p>The root logger is <em>not</em> included in the returned
410 {@link Enumeration}.
411 @return enumerator of current loggers
412 */
413 public Enumeration getCurrentLoggers() {
414 return repo.getCurrentLoggers();
415 }
416
417 /**
418 * Return the the list of previously encoutered {@link ErrorItem error items}.
419 * @return list of errors
420 */
421 public List getErrorList() {
422 return errorList;
423 }
424
425 /**
426 * Add an error item to the list of previously encountered errors.
427 * @param errorItem error to add to list of errors.
428 */
429 public void addErrorItem(final ErrorItem errorItem) {
430 getErrorList().add(errorItem);
431 }
432
433 /**
434 * Get enumerator over current loggers.
435 * @return enumerator over current loggers
436 @deprecated Please use {@link #getCurrentLoggers} instead.
437 */
438 public Enumeration getCurrentCategories() {
439 return repo.getCurrentCategories();
440 }
441
442 /**
443 Get the renderer map for this hierarchy.
444 @return renderer map
445 */
446 public RendererMap getRendererMap() {
447 return rendererSupport.getRendererMap();
448 }
449
450 /**
451 Get the root of this hierarchy.
452
453 @since 0.9.0
454 @return root of hierarchy
455 */
456 public Logger getRootLogger() {
457 return repo.getRootLogger();
458 }
459
460 /**
461 This method will return <code>true</code> if this repository is
462 disabled for <code>level</code> value passed as parameter and
463 <code>false</code> otherwise. See also the {@link
464 #setThreshold(Level) threshold} method.
465 @param level numeric value for level.
466 @return true if disabled for specified level
467 */
468 public boolean isDisabled(final int level) {
469 return repo.isDisabled(level);
470 }
471
472 /**
473 Reset all values contained in this hierarchy instance to their
474 default. This removes all appenders from all categories, sets
475 the level of all non-root categories to <code>null</code>,
476 sets their additivity flag to <code>true</code> and sets the level
477 of the root logger to DEBUG. Moreover,
478 message disabling is set its default "off" value.
479
480 <p>Existing categories are not removed. They are just reset.
481
482 <p>This method should be used sparingly and with care as it will
483 block all logging until it is completed.</p>
484
485 @since 0.8.5 */
486 public void resetConfiguration() {
487 repo.resetConfiguration();
488 }
489
490 /**
491 Used by subclasses to add a renderer to the hierarchy passed as parameter.
492 @param renderedClass class
493 @param renderer object used to render class.
494 */
495 public void setRenderer(final Class renderedClass,
496 final ObjectRenderer renderer) {
497 rendererSupport.setRenderer(renderedClass, renderer);
498 }
499
500 /**
501 * {@inheritDoc}
502 */
503 public boolean isPristine() {
504 return pristine;
505 }
506
507 /**
508 * {@inheritDoc}
509 */
510 public void setPristine(final boolean state) {
511 pristine = state;
512 }
513
514 /**
515 Shutting down a hierarchy will <em>safely</em> close and remove
516 all appenders in all categories including the root logger.
517
518 <p>Some appenders such as org.apache.log4j.net.SocketAppender
519 and AsyncAppender need to be closed before the
520 application exists. Otherwise, pending logging events might be
521 lost.
522
523 <p>The <code>shutdown</code> method is careful to close nested
524 appenders before closing regular appenders. This is allows
525 configurations where a regular appender is attached to a logger
526 and again to a nested appender.
527
528 @since 1.0 */
529 public void shutdown() {
530 repo.shutdown();
531 }
532
533
534 /**
535 * Return this repository's own scheduler.
536 * The scheduler is lazily instantiated.
537 * @return this repository's own scheduler.
538 */
539 public Scheduler getScheduler() {
540 if (scheduler == null) {
541 scheduler = new Scheduler();
542 scheduler.setDaemon(true);
543 scheduler.start();
544 }
545 return scheduler;
546 }
547
548 /**
549 * Puts object by key.
550 * @param key key, may not be null.
551 * @param value object to associate with key.
552 */
553 public void putObject(final String key,
554 final Object value) {
555 objectMap.put(key, value);
556 }
557
558 /**
559 * Get object by key.
560 * @param key key, may not be null.
561 * @return object associated with key or null.
562 */
563 public Object getObject(final String key) {
564 return objectMap.get(key);
565 }
566
567 /**
568 * Set logger factory.
569 * @param factory logger factory.
570 */
571 public void setLoggerFactory(final LoggerFactory factory) {
572 if (factory == null) {
573 throw new NullPointerException();
574 }
575 this.loggerFactory = factory;
576 }
577
578 /**
579 * Get logger factory.
580 * @return logger factory.
581 */
582 public LoggerFactory getLoggerFactory() {
583 return loggerFactory;
584 }
585
586 /** {@inheritDoc} */
587 public boolean parseUnrecognizedElement(
588 final Element element,
589 final Properties props) throws Exception {
590 if ("plugin".equals(element.getNodeName())) {
591 Object instance =
592 DOMConfigurator.parseElement(element, props, Plugin.class);
593 if (instance instanceof Plugin) {
594 Plugin plugin = (Plugin) instance;
595 String pluginName = DOMConfigurator.subst(element.getAttribute("name"), props);
596 if (pluginName.length() > 0) {
597 plugin.setName(pluginName);
598 }
599 getPluginRegistry().addPlugin(plugin);
600 plugin.setLoggerRepository(this);
601
602 LogLog.debug("Pushing plugin on to the object stack.");
603 plugin.activateOptions();
604 return true;
605 }
606 }
607 return false;
608 }
609
610
611
612 /**
613 * Implementation of RendererSupportImpl if not
614 * provided by LoggerRepository.
615 */
616 private static final class RendererSupportImpl implements RendererSupport {
617 /**
618 * Renderer map.
619 */
620 private final RendererMap renderers = new RendererMap();
621
622 /**
623 * Create new instance.
624 */
625 public RendererSupportImpl() {
626 super();
627 }
628
629 /** {@inheritDoc} */
630 public RendererMap getRendererMap() {
631 return renderers;
632 }
633
634 /** {@inheritDoc} */
635 public void setRenderer(final Class renderedClass,
636 final ObjectRenderer renderer) {
637 renderers.put(renderedClass, renderer);
638 }
639 }
640
641 /**
642 * Proxy that implements HierarchyEventListener
643 * and delegates to LoggerEventListener.
644 */
645 private static final class HierarchyEventListenerProxy
646 implements HierarchyEventListener {
647 /**
648 * Wrapper listener.
649 */
650 private LoggerEventListener listener;
651
652 /**
653 * Creates new instance.
654 * @param l listener
655 */
656 public HierarchyEventListenerProxy(final LoggerEventListener l) {
657 super();
658 if (l == null) {
659 throw new NullPointerException("l");
660 }
661 listener = l;
662 }
663
664 /** {@inheritDoc} */
665 public void addAppenderEvent(final Category cat,
666 final Appender appender) {
667 if (isEnabled() && cat instanceof Logger) {
668 listener.appenderAddedEvent((Logger) cat, appender);
669 }
670 }
671
672 /** {@inheritDoc} */
673 public void removeAppenderEvent(final Category cat,
674 final Appender appender) {
675 if (isEnabled() && cat instanceof Logger) {
676 listener.appenderRemovedEvent((Logger) cat, appender);
677 }
678 }
679
680 /**
681 * Disable forwarding of notifications to
682 * simulate removal of listener.
683 */
684 public synchronized void disable() {
685 listener = null;
686 }
687
688 /**
689 * Gets whether proxy is enabled.
690 * @return true if proxy is enabled.
691 */
692 private synchronized boolean isEnabled() {
693 return listener != null;
694 }
695 }
696
697 }