001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.core.logging;
028:
029: import org.apache.log4j.Appender;
030: import org.apache.log4j.AppenderSkeleton;
031: import org.apache.log4j.Level;
032: import org.apache.log4j.Logger;
033: import org.apache.log4j.spi.LoggingEvent;
034: import org.apache.log4j.spi.ThrowableInformation;
035:
036: import org.cougaar.core.component.Component;
037: import org.cougaar.core.component.ServiceBroker;
038: import org.cougaar.core.service.SuicideService;
039: import org.cougaar.util.GenericStateModelAdapter;
040: import org.cougaar.util.log.log4j.ShoutPriority;
041:
042: /**
043: * This component calls {@link SuicideService.die} if any logging
044: * client logs an ERROR or FATAL string.
045: * <p>
046: * This optional component is useful in deployed or automated
047: * environments, where any error should kill the node. For example,
048: * an exception thrown during a component's load may result in a
049: * partially configured (broken) node, in which case a nightly
050: * testing script would likely want a quick "System.exit" instead
051: * of waiting forever.
052: * <p>
053: * To load, add the following XML to your node definition:<pre>
054: * <component
055: * class='org.cougaar.core.logging.DieOnErrorComponent'
056: * priority='HIGH'
057: * insertionpoint='Node.AgentManager.Agent.Component'/>
058: * </pre>
059: * To enable the "System.exit(..)", which is disabled by
060: * default, add the following system property:<pre>
061: * <vm_parameter>
062: * -Dorg.cougaar.core.service.SuicideService.enable=true
063: * </vm_parameter>
064: * </pre>
065: * <p>
066: * Note that the optional ACME {@link
067: * org.cougaar.util.log.log4j.SocketAppender} will redirect {@link
068: * java.lang.System#err} output to an ERROR log, so users may
069: * want to change this to WARN logging by setting:<pre>
070: * <vm_parameter>
071: * -Dorg.cougaar.util.log.log4j.stderrLogLevel=WARN
072: * </vm_parameter>
073: * </pre>
074: * <p>
075: * Some of the alternate design options that were considered:
076: * <ul>
077: * <li>A binder around LoggingService clients, but this would miss
078: * the static logger clients.</li>
079: * <li>A Log4J appender defined in the "loggingConfig.conf" properties
080: * file. However, it would be awkward to set the SuicideService
081: * in the appender, which would likely require assistance from a
082: * Cougaar component. The advantage of the appender-based design
083: * would be the ability to filter out ERRORs for specific log
084: * categories (e.g. ignore errors from "org.foo").</li>
085: * </ul>
086: */
087: public final class DieOnErrorComponent extends GenericStateModelAdapter
088: implements Component {
089: // we use a ThreadLocal to avoid logging recursion by the
090: // SuicideService (e.g. "log.error" calls "die", which calls
091: // "log.error", etc).
092: private static final Object RECURSION_MARKER = new Object();
093: private static final ThreadLocal RECURSION_DETECTOR = new ThreadLocal();
094:
095: private static final Level SHOUT = ShoutPriority.toLevel("SHOUT",
096: null);
097:
098: private ServiceBroker sb;
099:
100: private Appender appender;
101:
102: private SuicideService suicideService;
103:
104: public void setServiceBroker(ServiceBroker sb) {
105: this .sb = sb;
106: }
107:
108: public void load() {
109: super .load();
110:
111: // get the suicide service
112: suicideService = (SuicideService) sb.getService(this ,
113: SuicideService.class, null);
114: if (suicideService == null) {
115: throw new RuntimeException(
116: "Unable to obtain SuicideService");
117: }
118:
119: // attach our log4j appender to observe error logging
120: Logger cat = Logger.getRootLogger();
121: AppenderSkeleton a = new AppenderSkeleton() {
122: protected void append(LoggingEvent event) {
123: handleError(event);
124: }
125:
126: public boolean requiresLayout() {
127: return false;
128: }
129:
130: public void close() {
131: }
132: };
133: a.setThreshold(Level.ERROR);
134: appender = a;
135: cat.addAppender(appender);
136: }
137:
138: public void unload() {
139: if (appender != null) {
140: Logger cat = Logger.getRootLogger();
141: cat.removeAppender(appender);
142: appender = null;
143: }
144: if (suicideService != null) {
145: sb.releaseService(this , SuicideService.class,
146: suicideService);
147: suicideService = null;
148: }
149: super .unload();
150: }
151:
152: private void handleError(LoggingEvent event) {
153: if (SHOUT.equals(event.getLevel())) {
154: // ignore SHOUT; it's not an error
155: return;
156: }
157:
158: try {
159: // we use a ThreadLocal to ignore our own SuicideService
160: // calls, which use the logger
161: if (RECURSION_DETECTOR.get() != null) {
162: return;
163: }
164: RECURSION_DETECTOR.set(RECURSION_MARKER);
165:
166: // another concern is that the SuicideService implementation
167: // may attempt to acquire locks (e.g. obtain services, etc),
168: // which risks deadlock. This would require us to do our
169: // work in a separate Thread. The standard SuicideService
170: // is well behaved, so we ignore this potential issue.
171:
172: // decide what to kill (null=node, string=agentName)
173: Object target = null;
174:
175: // include optional reason
176: ThrowableInformation ti = event.getThrowableInformation();
177: Throwable cause = new RuntimeException("Observed Log4j "
178: + event.getLevel() + ": " + event.getMessage(),
179: (ti == null ? null : ti.getThrowable()));
180:
181: // call "die", which will likely call "System.exit"
182: suicideService.die(target, cause);
183: } finally {
184: RECURSION_DETECTOR.set(null);
185: }
186: }
187: }
|