View Javadoc

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