View Javadoc

1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2011, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *   or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  package ch.qos.logback.classic.jmx;
15  
16  import java.io.File;
17  import java.io.FileNotFoundException;
18  import java.net.MalformedURLException;
19  import java.net.URL;
20  import java.util.ArrayList;
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import javax.management.InstanceNotFoundException;
25  import javax.management.MBeanRegistrationException;
26  import javax.management.MBeanServer;
27  import javax.management.ObjectName;
28  
29  import ch.qos.logback.classic.Level;
30  import ch.qos.logback.classic.Logger;
31  import ch.qos.logback.classic.LoggerContext;
32  import ch.qos.logback.classic.joran.JoranConfigurator;
33  import ch.qos.logback.classic.spi.LoggerContextListener;
34  import ch.qos.logback.classic.util.ContextInitializer;
35  import ch.qos.logback.core.joran.spi.JoranException;
36  import ch.qos.logback.core.spi.ContextAwareBase;
37  import ch.qos.logback.core.status.Status;
38  import ch.qos.logback.core.status.StatusListener;
39  import ch.qos.logback.core.status.StatusListenerAsList;
40  import ch.qos.logback.core.status.StatusManager;
41  import ch.qos.logback.core.util.StatusPrinter;
42  
43  /**
44   * A class that provides access to logback components via JMX.
45   * 
46   * <p>Since this class implements {@link JMXConfiguratorMBean} it has to be
47   * named as JMXConfigurator}.
48   * 
49   * @author Ceki G&uuml;lc&uuml;
50   * @author S&eacute;bastien Pennec
51   * 
52   * Contributor: Sebastian Davids See http://bugzilla.qos.ch/show_bug.cgi?id=35
53   */
54  public class JMXConfigurator extends ContextAwareBase implements
55      JMXConfiguratorMBean, LoggerContextListener {
56  
57    private static String EMPTY = "";
58  
59    LoggerContext loggerContext;
60    MBeanServer mbs;
61    ObjectName objectName;
62    String objectNameAsString;
63  
64    // whether to output status events on the console when reloading the
65    // configuration
66    boolean debug = true;
67  
68    boolean started;
69  
70    public JMXConfigurator(LoggerContext loggerContext, MBeanServer mbs,
71        ObjectName objectName) {
72      started = true;
73      this.context = loggerContext;
74      this.loggerContext = loggerContext;
75      this.mbs = mbs;
76      this.objectName = objectName;
77      this.objectNameAsString = objectName.toString();
78      if (previouslyRegisteredListenerWithSameObjectName()) {
79        addError("Previously registered JMXConfigurator named ["
80            + objectNameAsString + "] in the logger context named ["
81            + loggerContext.getName() + "]");
82      } else {
83        // register as a listener only if there are no homonyms
84        loggerContext.addListener(this);
85      }
86    }
87  
88    private boolean previouslyRegisteredListenerWithSameObjectName() {
89      List<LoggerContextListener> lcll = loggerContext.getCopyOfListenerList();
90      for (LoggerContextListener lcl : lcll) {
91        if (lcl instanceof JMXConfigurator) {
92          JMXConfigurator jmxConfigurator = (JMXConfigurator) lcl;
93          if (objectName.equals(jmxConfigurator.objectName)) {
94            return true;
95          }
96        }
97      }
98      return false;
99    }
100 
101   public void reloadDefaultConfiguration() throws JoranException {
102     ContextInitializer ci = new ContextInitializer(loggerContext);
103     URL url = ci.findURLOfDefaultConfigurationFile(true);
104     reloadByURL(url);
105   }
106 
107   public void reloadByFileName(String fileName) throws JoranException,
108       FileNotFoundException {
109     File f = new File(fileName);
110     if (f.exists() && f.isFile()) {
111       URL url;
112       try {
113         url = f.toURI().toURL();
114         reloadByURL(url);
115       } catch (MalformedURLException e) {
116         throw new RuntimeException(
117             "Unexpected MalformedURLException occured. See nexted cause.", e);
118       }
119 
120     } else {
121       String errMsg = "Could not find [" + fileName + "]";
122       addInfo(errMsg);
123       throw new FileNotFoundException(errMsg);
124     }
125   }
126 
127   void addStatusListener(StatusListener statusListener) {
128     StatusManager sm = loggerContext.getStatusManager();
129     sm.add(statusListener);
130   }
131 
132   void removeStatusListener(StatusListener statusListener) {
133     StatusManager sm = loggerContext.getStatusManager();
134     sm.remove(statusListener);
135   }
136 
137   public void reloadByURL(URL url) throws JoranException {
138     StatusListenerAsList statusListenerAsList = new StatusListenerAsList();
139 
140     addStatusListener(statusListenerAsList);
141     addInfo("Resetting context: " + loggerContext.getName());
142     loggerContext.reset();
143     // after a reset the statusListenerAsList gets removed as a listener
144     addStatusListener(statusListenerAsList);
145 
146     try {
147       JoranConfigurator configurator = new JoranConfigurator();
148       configurator.setContext(loggerContext);
149       configurator.doConfigure(url);
150       addInfo("Context: " + loggerContext.getName() + " reloaded.");
151     } finally {
152       removeStatusListener(statusListenerAsList);
153       if (debug) {
154         StatusPrinter.print(statusListenerAsList.getStatusList());
155       }
156     }
157   }
158 
159   public void setLoggerLevel(String loggerName, String levelStr) {
160     if (loggerName == null) {
161       return;
162     }
163     if (levelStr == null) {
164       return;
165     }
166     loggerName = loggerName.trim();
167     levelStr = levelStr.trim();
168 
169     addInfo("Trying to set level " + levelStr + " to logger " + loggerName);
170     LoggerContext lc = (LoggerContext) context;
171 
172     Logger logger = lc.getLogger(loggerName);
173     if ("null".equalsIgnoreCase(levelStr)) {
174       logger.setLevel(null);
175     } else {
176       Level level = Level.toLevel(levelStr, null);
177       if (level != null) {
178         logger.setLevel(level);
179       }
180     }
181   }
182 
183   public String getLoggerLevel(String loggerName) {
184     if (loggerName == null) {
185       return EMPTY;
186     }
187 
188     loggerName = loggerName.trim();
189 
190     LoggerContext lc = (LoggerContext) context;
191     Logger logger = lc.exists(loggerName);
192     if (logger != null && logger.getLevel() != null) {
193       return logger.getLevel().toString();
194     } else {
195       return EMPTY;
196     }
197   }
198 
199   public String getLoggerEffectiveLevel(String loggerName) {
200     if (loggerName == null) {
201       return EMPTY;
202     }
203 
204     loggerName = loggerName.trim();
205 
206     LoggerContext lc = (LoggerContext) context;
207     Logger logger = lc.exists(loggerName);
208     if (logger != null) {
209       return logger.getEffectiveLevel().toString();
210     } else {
211       return EMPTY;
212     }
213   }
214 
215   public List<String> getLoggerList() {
216     LoggerContext lc = (LoggerContext) context;
217     List<String> strList = new ArrayList<String>();
218     Iterator<Logger> it = lc.getLoggerList().iterator();
219     while (it.hasNext()) {
220       Logger log = it.next();
221       strList.add(log.getName());
222     }
223     return strList;
224   }
225 
226   public List<String> getStatuses() {
227     List<String> list = new ArrayList<String>();
228     Iterator<Status> it = context.getStatusManager().getCopyOfStatusList()
229         .iterator();
230     while (it.hasNext()) {
231       list.add(it.next().toString());
232     }
233     return list;
234   }
235 
236   /**
237    * When the associated LoggerContext is stopped, this configurator must be
238    * unregistered
239    */
240   public void onStop(LoggerContext context) {
241     if (!started) {
242       addInfo("onStop() method called on a stopped JMXActivator ["
243           + objectNameAsString + "]");
244       return;
245     }
246     if (mbs.isRegistered(objectName)) {
247       try {
248         addInfo("Unregistering mbean [" + objectNameAsString + "]");
249         mbs.unregisterMBean(objectName);
250       } catch (InstanceNotFoundException e) {
251         // this is theoretically impossible
252         addError("Unable to find a verifiably registered mbean ["
253             + objectNameAsString + "]", e);
254       } catch (MBeanRegistrationException e) {
255         addError("Failed to unregister [" + objectNameAsString + "]", e);
256       }
257     } else {
258       addInfo("mbean [" + objectNameAsString
259           + "] was not in the mbean registry. This is OK.");
260     }
261     stop();
262   }
263 
264   public void onLevelChange(Logger logger, Level level) {
265     // nothing to do
266   }
267 
268   public void onReset(LoggerContext context) {
269     addInfo("onReset() method called JMXActivator [" + objectNameAsString + "]");
270   }
271 
272   /**
273    * JMXConfigurator should not be removed subsequent to a LoggerContext reset.
274    * 
275    * @return
276    */
277   public boolean isResetResistant() {
278     return true;
279   }
280 
281   private void clearFields() {
282     mbs = null;
283     objectName = null;
284     loggerContext = null;
285   }
286 
287   private void stop() {
288     started = false;
289     clearFields();
290   }
291 
292   public void onStart(LoggerContext context) {
293     // nop
294   }
295 
296   @Override
297   public String toString() {
298     return this.getClass().getName() + "(" + context.getName() + ")";
299   }
300 }