001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.services.loggingmonitor;
023:
024: import java.util.Timer;
025: import java.util.TimerTask;
026:
027: import javax.management.MalformedObjectNameException;
028:
029: import org.apache.log4j.Appender;
030: import org.apache.log4j.Level;
031: import org.apache.log4j.Logger;
032: import org.apache.log4j.PatternLayout;
033: import org.jboss.logging.appender.DailyRollingFileAppender;
034: import org.jboss.system.ServiceMBeanSupport;
035: import org.w3c.dom.DOMException;
036: import org.w3c.dom.Element;
037: import org.w3c.dom.Node;
038: import org.w3c.dom.NodeList;
039: import org.w3c.dom.Text;
040:
041: /**
042: * This class implements the LoggingMonitor service which provides the ability
043: * to create monitoring logs for various MBeans and their attributes.
044: *
045: * @author <a href="mailto:jimmy.wilson@acxiom.com">James Wilson</a>
046: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
047: * @version $Revision: 57210 $
048: */
049: public class LoggingMonitor extends ServiceMBeanSupport implements
050: LoggingMonitorMBean {
051: // Static --------------------------------------------------------
052:
053: public final static String MONITORED_MBEAN_ELEMENT = "monitoredmbean";
054: public final static String MBEAN_NAME_ATTRIBUTE = "name";
055: public final static String MBEAN_LOGGER_ATTRIBUTE = "logger";
056: public final static String MBEAN_ATTRIBUTE_ELEMENT = "attribute";
057:
058: public final static String DEFAULT_PATTERN_LAYOUT = "%d %-5p [%c] %m%n";
059:
060: // Private data --------------------------------------------------
061:
062: private String filename;
063: private boolean appendToFile;
064: private RolloverPeriod rolloverPeriod;
065: private MonitoredMBean[] monitoredObjects;
066: private long monitorPeriod;
067: private String patternLayout;
068:
069: private Appender appender;
070: private Timer timer;
071:
072: // Constructors -------------------------------------------------
073:
074: /**
075: * Default constructor.
076: */
077: public LoggingMonitor() {
078: appendToFile = true;
079: rolloverPeriod = new RolloverPeriod("DAY");
080: patternLayout = DEFAULT_PATTERN_LAYOUT;
081: }
082:
083: // Attributes ----------------------------------------------------
084:
085: /**
086: * @jmx.managed-attribute
087: */
088: public void setFilename(String filename) {
089: if (filename == null || filename.length() == 0) {
090: throw new IllegalArgumentException(
091: "Logging monitor's filename can not be null or empty");
092: }
093: this .filename = filename;
094: }
095:
096: /**
097: * @jmx.managed-attribute
098: */
099: public String getFilename() {
100: return filename;
101: }
102:
103: /**
104: * @jmx.managed-attribute
105: */
106: public void setAppendToFile(boolean appendToFile) {
107: this .appendToFile = appendToFile;
108: }
109:
110: /**
111: * @jmx.managed-attribute
112: */
113: public boolean getAppendToFile() {
114: return appendToFile;
115: }
116:
117: /**
118: * @jmx.managed-attribute
119: */
120: public void setRolloverPeriod(String rolloverPeriod) {
121: this .rolloverPeriod = new RolloverPeriod(rolloverPeriod);
122: }
123:
124: /**
125: * @jmx.managed-attribute
126: */
127: public String getRolloverPeriod() {
128: return rolloverPeriod.toString();
129: }
130:
131: /**
132: * @jmx.managed-attribute
133: */
134: public void setMonitorPeriod(long monitorPeriod) {
135: if (monitorPeriod < 1) {
136: throw new IllegalArgumentException(
137: "Logging monitor's monitor period must be a positive, non-zero value");
138: }
139: this .monitorPeriod = monitorPeriod;
140: }
141:
142: /**
143: * @jmx.managed-attribute
144: */
145: public long getMonitorPeriod() {
146: return monitorPeriod;
147: }
148:
149: /**
150: * @jmx.managed-attribute
151: */
152: public void setPatternLayout(String patternLayout) {
153: this .patternLayout = patternLayout;
154: }
155:
156: /**
157: * @jmx.managed-attribute
158: */
159: public String getPatternLayout() {
160: return patternLayout;
161: }
162:
163: /**
164: * @jmx.managed-attribute
165: */
166: public String getRolloverFormat() {
167: return rolloverPeriod.getRolloverFormat();
168: }
169:
170: /**
171: * @jmx.managed-attribute
172: */
173: public void setMonitoredObjects(Element monitoredObjects)
174: throws MalformedObjectNameException {
175: NodeList monitoredMBeans = monitoredObjects
176: .getElementsByTagName(MONITORED_MBEAN_ELEMENT);
177:
178: int mbeanCount = monitoredMBeans.getLength();
179: if (mbeanCount < 1) {
180: throw createMissingElementException(MONITORED_MBEAN_ELEMENT);
181: }
182:
183: this .monitoredObjects = new MonitoredMBean[mbeanCount];
184: for (int i = 0; i < mbeanCount; ++i) {
185: Node monitoredMBean = monitoredMBeans.item(i);
186: this .monitoredObjects[i] = toMonitoredMBean((Element) monitoredMBean);
187: }
188: }
189:
190: // ServiceMBeanSupport overrides ---------------------------------
191:
192: protected void startService() {
193: if (monitoredObjects == null) {
194: throw new IllegalStateException(
195: "'MonitoredObjects' attribute not configured");
196: }
197: DailyRollingFileAppender appender = new DailyRollingFileAppender();
198: appender.setFile(filename);
199: appender.setAppend(appendToFile);
200: appender.setDatePattern(rolloverPeriod.getRolloverFormat());
201: appender.setLayout(new PatternLayout(patternLayout));
202: appender.setThreshold(Level.INFO);
203: appender.activateOptions();
204: this .appender = appender;
205:
206: for (int i = 0; i < monitoredObjects.length; ++i) {
207: monitoredObjects[i].getLogger().addAppender(appender);
208: }
209:
210: // use the ServiceMBeanSupport logger for reporting errors
211: TimerTask task = new LoggingMonitorTimerTask(monitoredObjects,
212: log);
213:
214: timer = new Timer();
215: timer.schedule(task, 0, monitorPeriod);
216:
217: log.debug("Logging monitor started logging to " + filename);
218: }
219:
220: protected void stopService() {
221: timer.cancel();
222:
223: for (int i = 0; i < monitoredObjects.length; ++i) {
224: monitoredObjects[i].getLogger().removeAllAppenders();
225: }
226:
227: appender.close();
228:
229: log.debug("Logging monitor stopped logging to " + filename);
230: }
231:
232: // Private -------------------------------------------------------
233:
234: /**
235: * Converts the specified XML DOM element to a monitored MBean.
236: *
237: * @param element the XML DOM element to be converted.
238: * @return a monitored MBean represented by the specified XML DOM element.
239: * @throws MalformedObjectNameException if the specified XML DOM element
240: * does not contain a valid object
241: * name.
242: */
243: private MonitoredMBean toMonitoredMBean(Element element)
244: throws MalformedObjectNameException {
245: String objectName = element.getAttribute(MBEAN_NAME_ATTRIBUTE);
246:
247: if ("".equals(objectName)) {
248: throw createAttributeNotFoundException(MBEAN_NAME_ATTRIBUTE);
249: }
250:
251: String loggerName = element
252: .getAttribute(MBEAN_LOGGER_ATTRIBUTE);
253: if ("".equals(loggerName)) {
254: throw createAttributeNotFoundException(MBEAN_LOGGER_ATTRIBUTE);
255: }
256:
257: Logger logger = Logger.getLogger(loggerName.toLowerCase());
258: logger.setAdditivity(false);
259: logger.setLevel(Level.INFO);
260:
261: String[] attributes = getMonitoredAttributes(element);
262:
263: return new MonitoredMBean(objectName, attributes, logger);
264: }
265:
266: /**
267: * Retrieves the attributes of the MBean to monitor.
268: *
269: * @param monitoredMBean a MBean, represented as a XML DOM element, for
270: * which to retrieve the attributes to monitor.
271: * @return the attributes of the MBean to monitor.
272: */
273: private String[] getMonitoredAttributes(Element monitoredMBean) {
274: NodeList monitoredAttributes = monitoredMBean
275: .getElementsByTagName(MBEAN_ATTRIBUTE_ELEMENT);
276:
277: int monitoredAttributesCount = monitoredAttributes.getLength();
278: if (monitoredAttributesCount < 1) {
279: throw createMissingElementException(MBEAN_ATTRIBUTE_ELEMENT);
280: }
281:
282: String[] attributes = new String[monitoredAttributesCount];
283: for (int i = 0; i < monitoredAttributesCount; ++i) {
284: Node node = monitoredAttributes.item(i);
285: Node attribute = node.getFirstChild();
286:
287: if (attribute.getNodeType() != Node.TEXT_NODE) {
288: throw new DOMException(
289: DOMException.HIERARCHY_REQUEST_ERR,
290: "Unexpected node type inside <attribute> for monitored MBean.");
291: }
292: attributes[i] = (((Text) attribute).getData()).trim();
293: }
294: return attributes;
295: }
296:
297: /**
298: * Creates a <code>DOMException</code> relating that at least one occurrence
299: * of the specified element was not found as expected.
300: *
301: * @param element the expected element.
302: * @return a <code>DOMException</code> relating that at least one occurrence
303: * of the specified element was not found as expected.
304: */
305: private DOMException createMissingElementException(String element) {
306: return new DOMException(DOMException.NOT_FOUND_ERR,
307: "At least one <" + element + "> element is expected");
308: }
309:
310: /**
311: * Creates a <code>DOMException</code> relating that the specified attribute
312: * was not found as expected.
313: *
314: * @param attribute the expected attribute.
315: * @return a <code>DOMException</code> relating that the specified attribute
316: * was not found as expected.
317: */
318: private DOMException createAttributeNotFoundException(
319: String attribute) {
320: return new DOMException(DOMException.NOT_FOUND_ERR,
321: "Missing expected '" + attribute
322: + "' attribute of a <monitoredmbean> element");
323: }
324:
325: }
|