001: /*
002: * <copyright>
003: *
004: * Copyright 2004 InfoEther
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.util.log.log4j;
028:
029: import java.io.IOException;
030: import java.io.PrintWriter;
031: import java.net.Socket;
032:
033: import org.apache.log4j.AppenderSkeleton;
034: import org.apache.log4j.Priority;
035: import org.apache.log4j.spi.LoggingEvent;
036:
037: import org.cougaar.bootstrap.SystemProperties;
038:
039: /**
040: * A log4j appender that sends "EVENT.*" logs (representing Cougaar
041: * EventService operations) to a remote socket, typically where
042: * ACME is listening.
043: * <p>
044: * To use, modify your "$CIP/configs/common/loggingConfig.conf" to
045: * add:<pre>
046: * log4j.rootCategory=WARN, .. ,EVENT
047: * log4j.category.EVENT=DEBUG
048: * log4j.appender.EVENT=org.cougaar.util.log.log4j.SocketAppender
049: * log4j.appender.EVENT.layout.ConversionPattern=%d{ABSOLUTE} %-5p - %c{1} - %m%n
050: * </pre>
051: * and in your Java "-D"s, specify the listener address:<pre>
052: * -Dorg.cougaar.event.host=<i>hostname</i>
053: * -Dorg.cougaar.event.port=<i>port</i>
054: * </pre>
055: *
056: * @property org.cougaar.event.stdio
057: * If set to </em>true</em> (the default) then the {@link
058: * StreamCapture} will redirect STDOUT/STDERR to the logger.
059: *
060: * @property org.cougaar.event.host
061: * Host name of the socket to which we'll send events.
062: *
063: * @property org.cougaar.event.port
064: * Port of the socket to which we'll send events.
065: */
066: public class SocketAppender extends AppenderSkeleton {
067:
068: private static final int SHOUT_INT = Priority.ERROR_INT + 1;
069:
070: private boolean hasOptHost = false;
071: private boolean hasOptPort = false;
072: private String optHost = "127.0.0.1";
073: private int optPort = 2000;
074:
075: private Socket connection;
076: private PrintWriter pw;
077: private boolean checkedOnce = false;
078:
079: private StreamCapture stdOutCapture;
080: private StreamCapture stdErrCapture;
081:
082: public boolean requiresLayout() {
083: return false;
084: }
085:
086: private boolean checkConnection() {
087: if (connection != null || checkedOnce) {
088: // already connected
089: return (connection != null && connection.isConnected());
090: }
091:
092: // only perform this connection check once
093: checkedOnce = true;
094:
095: String host = null;
096: int port = 0;
097: String sysHost = SystemProperties
098: .getProperty("org.cougaar.event.host");
099: String sysPort = SystemProperties
100: .getProperty("org.cougaar.event.port");
101: if (sysHost != null && sysPort != null) {
102: host = sysHost;
103: try {
104: port = Integer.valueOf(sysPort).intValue();
105: } catch (NumberFormatException ex) {
106: ex.printStackTrace(System.err);
107: }
108: } else if (hasOptHost && hasOptPort) {
109: host = optHost;
110: port = optPort;
111: }
112:
113: boolean connect = (host != null && port > 0);
114: if (connect) {
115: try {
116: connection = new Socket(host, port);
117: initPrintWriter();
118: } catch (IOException ex) {
119: ex.printStackTrace(System.err);
120: } catch (NumberFormatException ex) {
121: ex.printStackTrace(System.err);
122: }
123: }
124:
125: //capture the StdOut and StdErr streams to Log4J
126: boolean eatStdio = SystemProperties.getBoolean(
127: "org.cougaar.event.stdio", true);
128: SecurityException se = null;
129: if (eatStdio) {
130: try {
131: stdOutCapture = StreamCapture.captureStdOut();
132: stdErrCapture = StreamCapture.captureStdErr();
133: } catch (SecurityException x) {
134: se = x;
135: }
136: } else {
137: System.out
138: .println("stdout and stderr will not be redirected");
139: }
140:
141: if (connect) {
142: try {
143: String msg = "Std0ut and Stderr "
144: + (eatStdio ? (se == null ? "redirected"
145: : "redirect failed: " + se)
146: : "will not be redirected");
147: pw.println("<CougaarEvent type=\"STATUS\">" + msg
148: + "</CougaarEvent>");
149: pw.flush();
150: } catch (Exception ex) {
151: ex.printStackTrace(System.err);
152: }
153: }
154:
155: //System.err.print("Log4J SocketAppender connected");
156:
157: return (connection != null && connection.isConnected());
158: }
159:
160: private void initPrintWriter() {
161: try {
162: pw = new PrintWriter(connection.getOutputStream());
163:
164: String nodeName = SystemProperties
165: .getProperty("org.cougaar.node.name");
166:
167: String experimentName = SystemProperties
168: .getProperty("org.cougaar.event.experiment");
169: if (experimentName == null) {
170: experimentName = "";
171: }
172:
173: pw.println("<CougaarEvents Node=\"" + nodeName
174: + "\" experiment=\"" + experimentName + "\">");
175: pw.flush();
176: } catch (Exception e) {
177: e.printStackTrace();
178: }
179: }
180:
181: /**
182: * @param clusterIdentifier
183: * @param component
184: * @param eventText
185: * @param encoded
186: * @return
187: */
188: private String generateEventString(String clusterIdentifier,
189: String component, String eventText, boolean encoded) {
190: StringBuffer event = new StringBuffer();
191: event.append("<CougaarEvent type=\"").append("STATUS");
192: if (clusterIdentifier != null)
193: event.append("\" clusterIdentifier=\"").append(
194: clusterIdentifier);
195: if (component != null)
196: event.append("\" component=\"").append(component);
197: event.append("\">");
198: if (encoded) {
199: event.append(eventText);
200: } else {
201: event.append(encodeXML(eventText));
202: }
203: event.append("</CougaarEvent>");
204: return event.toString();
205: }
206:
207: public static String encodeXML(String text) {
208: text = text.replaceAll("&", "&");
209: return text.replaceAll("<", "<");
210: }
211:
212: protected void append(LoggingEvent event) {
213: if (!checkConnection()) {
214: return;
215: }
216:
217: String loggerName = event.getLoggerName();
218: if (!loggerName.startsWith("EVENT")) {
219: return;
220: }
221:
222: String componentId = loggerName.substring(loggerName
223: .lastIndexOf(".") + 1);
224: String clusterId = event.getRenderedMessage();
225: clusterId = clusterId.substring(0, clusterId.indexOf(":"));
226: String msg = generateEventString(clusterId, componentId, event
227: .getRenderedMessage(), false);
228: try {
229: pw.println(msg);
230: pw.flush();
231: } catch (Exception ex) {
232: ex.printStackTrace();
233: }
234: }
235:
236: public void close() {
237: if (stdOutCapture != null) {
238: stdOutCapture.closeStream();
239: }
240: if (stdErrCapture != null) {
241: stdErrCapture.closeStream();
242: }
243:
244: if (connection != null && connection.isConnected()) {
245: try {
246: pw.println("</CougaarEvents>");
247: connection.close();
248: } catch (Exception ex) {
249: ex.printStackTrace();
250: }
251: }
252: }
253:
254: public void activateOptions() {
255: // create connection early if options were set by the log4j
256: // PropertyConfigurator
257: checkConnection();
258: }
259:
260: public void setHostName(String hostName) {
261: optHost = hostName;
262: hasOptHost = true;
263: }
264:
265: public void setPort(int port) {
266: optPort = port;
267: hasOptPort = true;
268: }
269:
270: }
|