001: /* LogService.java
002:
003: {{IS_NOTE
004:
005: Purpose: The MBean-able log service
006: Description:
007: History:
008: 2001/6/4, Tom M. Yeh: Created.
009:
010: }}IS_NOTE
011:
012: Copyright (C) 2001 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.util.logging;
020:
021: import java.util.Map;
022: import java.util.HashMap;
023: import java.util.Iterator;
024: import java.util.Properties;
025: import java.util.logging.*;
026: import java.io.FileInputStream;
027: import java.io.File;
028: import java.io.IOException;
029: import java.io.FileNotFoundException;
030:
031: import org.zkoss.lang.D;
032: import org.zkoss.lang.Classes;
033: import org.zkoss.lang.SystemException;
034: import org.zkoss.mesg.MCommon;
035: import org.zkoss.io.FileWatchdog;
036: import org.zkoss.io.Files;
037:
038: /**
039: * The log service which is used to monitor i3-log.conf.
040: * To initialize it, invoke {@link #init}. Note: {@link Log} could work
041: * without {@link LogService}.
042: *
043: * <p>Design consideration: the configure and configureAndWatch
044: * methods could be declared as static, but we don't.
045: * Reason: easilier to extend (also the performance gained by
046: * the static method is neglectable comparing its complexity).
047: *
048: * <p>Implementation Note:
049: * LogService cannot be a component (because ComponentManager depedns
050: * on iDom, which depends on log). Thus, we use Singleton instead.
051: *
052: * @author tomyeh
053: */
054: public class LogService {
055: private static final Log log = Log.lookup(LogService.class);
056:
057: /** One log service per the root logger.
058: * <p>Note: Tomcat has one root logger per Web application.
059: */
060: private static final Map _svcs = new HashMap(5);
061: /** The service name. */
062: private static final String SERVICE_NAME = "logging";
063:
064: /** The name of the root logger that this logging service is monitoring.
065: * Note: we cannot use Logger because Tomcat has one root per Web app
066: */
067: protected final String _root;
068:
069: private FileWatchdog _logwdog;
070: private File _logfn;
071:
072: /** Returns whether the logging service is started, i.e., whether
073: * {@link #init} is invoked.
074: */
075: public static final boolean isInited(String rootnm) {
076: synchronized (_svcs) {
077: return _svcs.containsKey(rootnm);
078: }
079: }
080:
081: /** Initializes the logging service.
082: * If the log service already started, an error message is logged.
083: *
084: * <p>Note: it also enables the hierarchy support of loggers by
085: * calling {@link Log#setHierarchy} with true.
086: *
087: * @param rootnm the name of the root logger. The logging service
088: * registered handlers at the specified logger.
089: * @param cls the implementation to start. If null, {@link LogService}
090: * is used.
091: */
092: public static final LogService init(String rootnm, Class cls) {
093: if (rootnm == null)
094: throw new IllegalArgumentException("null");
095:
096: Log.setHierarchy(true); //turn on the hierarchy
097:
098: final Logger root = Logger.getLogger(rootnm);
099: //Tomcat has one root per Web app, while Logger.global is shared
100: //Thus we have to use getLogger to verify whether it is installed
101:
102: synchronized (_svcs) {
103: LogService svc = (LogService) _svcs.get(root);
104: if (svc != null) {
105: log.warning("Already started: " + rootnm);
106: } else {
107: try {
108: svc = (LogService) Classes.newInstance(
109: cls != null ? cls : LogService.class,
110: new Class[] { String.class },
111: new Object[] { rootnm });
112: } catch (Exception ex) {
113: throw SystemException.Aide.wrap(ex);
114: }
115: _svcs.put(root, svc);
116: }
117: return svc;
118: }
119: }
120:
121: /** Stops the logging service
122: */
123: public static final void stop(String rootnm) {
124: if (rootnm == null)
125: throw new IllegalArgumentException("null");
126:
127: final Logger root = Logger.getLogger(rootnm);
128: //Tomcat has one root per Web app, while Logger.global is shared
129: //Thus we have to use getLogger to verify whether it is installed
130:
131: final LogService svc;
132: synchronized (_svcs) {
133: svc = (LogService) _svcs.get(root);
134: if (svc == null)
135: return;
136: }
137: svc.stop();
138: }
139:
140: /** Constructor.
141: * Don't call this method directly. Rather, use {@link #init} to
142: * start the service.
143: */
144: public LogService(String root) {
145: if (root == null)
146: throw new IllegalArgumentException("null");
147: _root = root;
148: //Note: we don't store Logger because Tomcat has one root
149: //per Web app, so we have to getLogger dynamically
150:
151: //monitor i3-log.conf
152: try {
153: _logfn = new File(Files.getConfigDirectory(), "i3-log.conf");
154: if (_logfn.exists())
155: log.info("Monitor " + _logfn);
156: else
157: log.info("File not found: " + _logfn);
158: _logwdog = configureAndWatch(_logfn, D.ON ? 10000 : 360000);//millisec
159: } catch (Exception ex) {
160: log.warning(ex);
161: }
162:
163: log.debug(MCommon.SERVICE_INIT_OK, SERVICE_NAME);
164: //1. don't put it in constructor because log is not ready until start
165: //2. use debug because LogService is also used by client
166: }
167:
168: /** Stops the service. You rarely need to do so. This service monitors
169: * whether any preference is changed, and automatically reflects
170: * the new changes.
171: */
172: synchronized public void stop() {
173: System.out.println("Stopping " + SERVICE_NAME + "...");
174:
175: final Logger root = Logger.getLogger(_root);
176: synchronized (_svcs) {
177: final Object o = _svcs.remove(root);
178: if (o != this ) {
179: _svcs.put(root, this ); //resotre
180: throw new IllegalStateException(
181: "LogService has beeing stopped");
182: }
183: }
184:
185: if (_logwdog != null)
186: _logwdog.cease();
187: }
188:
189: /**
190: * Configures based the properties.
191: *
192: * <p>The key is a logger name and the value is the level.
193: * A special level, INHERIT or NULL, to denote resetting the level
194: * to be the same as the logger's parent.
195: *
196: * @param props the properties
197: */
198: public final void configure(Properties props) {
199: for (final Iterator it = props.entrySet().iterator(); it
200: .hasNext();) {
201: final Map.Entry me = (Map.Entry) it.next();
202: final String key = (String) me.getKey();
203: String val = (String) me.getValue();
204: if (val != null)
205: val = val.trim();
206:
207: final Level level = Log.getLevel(val);
208: if (level != null || val.equalsIgnoreCase("NULL")
209: || val.equalsIgnoreCase("INHERIT")) {
210: Logger.getLogger(key).setLevel(level);
211: } else {
212: log.warning("Illegal log level, " + val + ", for "
213: + key);
214: }
215: }
216: }
217:
218: /**
219: * Configures based the properties stored in a file.
220: * <p>The key is a logger name and the value is the level.
221: *
222: * @param file the file
223: */
224: public final void configure(File file)
225: throws FileNotFoundException, IOException {
226: log.info(MCommon.FILE_OPENING, file);
227: final Properties props = new Properties();
228: props.load(new FileInputStream(file));
229: configure(props);
230: }
231:
232: /**
233: * Configures based the properties stored in a file.
234: * <p>The key is a logger name and the value is the level.
235: *
236: * @param filename the filename
237: */
238: public final void configure(String filename)
239: throws FileNotFoundException, IOException {
240: configure(new File(filename));
241: }
242:
243: public class WatchdogCallback implements FileWatchdog.Callback {
244: private WatchdogCallback() {
245: }
246:
247: public final void onModified(File file) {
248: try {
249: configure(file);
250: } catch (Exception ex) {
251: log.warning(MCommon.FILE_READ_FAILED, file, ex);
252: }
253: }
254: }
255:
256: /**
257: * Periodically checks whether a file is modified, and configures
258: * based the properties stored in the file, if any modifications.
259: *
260: * <p>The key is a logger name and the value is the level.
261: *
262: * @param file the file
263: * @param delay the delay in milliseconds to wait between each check.
264: */
265: public final FileWatchdog configureAndWatch(File file, long delay) {
266: FileWatchdog wg = new FileWatchdog(file, delay,
267: new WatchdogCallback());
268: wg.start();
269: return wg;
270: }
271: }
|