View Javadoc
1   /**
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    * Copyright (C) 1999-2016, 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 JMXConfiguratorMBean, LoggerContextListener {
55  
56      private static String EMPTY = "";
57  
58      LoggerContext loggerContext;
59      MBeanServer mbs;
60      ObjectName objectName;
61      String objectNameAsString;
62  
63      // whether to output status events on the console when reloading the
64      // configuration
65      boolean debug = true;
66  
67      boolean started;
68  
69      public JMXConfigurator(LoggerContext loggerContext, MBeanServer mbs, ObjectName objectName) {
70          started = true;
71          this.context = loggerContext;
72          this.loggerContext = loggerContext;
73          this.mbs = mbs;
74          this.objectName = objectName;
75          this.objectNameAsString = objectName.toString();
76          if (previouslyRegisteredListenerWithSameObjectName()) {
77              addError("Previously registered JMXConfigurator named [" + objectNameAsString + "] in the logger context named [" + 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, FileNotFoundException {
104         File f = new File(fileName);
105         if (f.exists() && f.isFile()) {
106             URL url;
107             try {
108                 url = f.toURI().toURL();
109                 reloadByURL(url);
110             } catch (MalformedURLException e) {
111                 throw new RuntimeException("Unexpected MalformedURLException occured. See nexted cause.", e);
112             }
113 
114         } else {
115             String errMsg = "Could not find [" + fileName + "]";
116             addInfo(errMsg);
117             throw new FileNotFoundException(errMsg);
118         }
119     }
120 
121     void addStatusListener(StatusListener statusListener) {
122         StatusManager sm = loggerContext.getStatusManager();
123         sm.add(statusListener);
124     }
125 
126     void removeStatusListener(StatusListener statusListener) {
127         StatusManager sm = loggerContext.getStatusManager();
128         sm.remove(statusListener);
129     }
130 
131     public void reloadByURL(URL url) throws JoranException {
132         StatusListenerAsList statusListenerAsList = new StatusListenerAsList();
133 
134         addStatusListener(statusListenerAsList);
135         addInfo("Resetting context: " + loggerContext.getName());
136         loggerContext.reset();
137 
138         // after a reset the statusListenerAsList gets removed as a listener
139         addStatusListener(statusListenerAsList);
140 
141         try {
142             if (url != null) {
143                 JoranConfigurator configurator = new JoranConfigurator();
144                 configurator.setContext(loggerContext);
145                 configurator.doConfigure(url);
146                 addInfo("Context: " + loggerContext.getName() + " reloaded.");
147             }
148         } finally {
149             removeStatusListener(statusListenerAsList);
150             if (debug) {
151                 StatusPrinter.print(statusListenerAsList.getStatusList());
152             }
153         }
154     }
155 
156     public void setLoggerLevel(String loggerName, String levelStr) {
157         if (loggerName == null) {
158             return;
159         }
160         if (levelStr == null) {
161             return;
162         }
163         loggerName = loggerName.trim();
164         levelStr = levelStr.trim();
165 
166         addInfo("Trying to set level " + levelStr + " to logger " + loggerName);
167         LoggerContext lc = (LoggerContext) context;
168 
169         Logger logger = lc.getLogger(loggerName);
170         if ("null".equalsIgnoreCase(levelStr)) {
171             logger.setLevel(null);
172         } else {
173             Level level = Level.toLevel(levelStr, null);
174             if (level != null) {
175                 logger.setLevel(level);
176             }
177         }
178     }
179 
180     public String getLoggerLevel(String loggerName) {
181         if (loggerName == null) {
182             return EMPTY;
183         }
184 
185         loggerName = loggerName.trim();
186 
187         LoggerContext lc = (LoggerContext) context;
188         Logger logger = lc.exists(loggerName);
189         if (logger != null && logger.getLevel() != null) {
190             return logger.getLevel().toString();
191         } else {
192             return EMPTY;
193         }
194     }
195 
196     public String getLoggerEffectiveLevel(String loggerName) {
197         if (loggerName == null) {
198             return EMPTY;
199         }
200 
201         loggerName = loggerName.trim();
202 
203         LoggerContext lc = (LoggerContext) context;
204         Logger logger = lc.exists(loggerName);
205         if (logger != null) {
206             return logger.getEffectiveLevel().toString();
207         } else {
208             return EMPTY;
209         }
210     }
211 
212     public List<String> getLoggerList() {
213         LoggerContext lc = (LoggerContext) context;
214         List<String> strList = new ArrayList<String>();
215         Iterator<Logger> it = lc.getLoggerList().iterator();
216         while (it.hasNext()) {
217             Logger log = it.next();
218             strList.add(log.getName());
219         }
220         return strList;
221     }
222 
223     public List<String> getStatuses() {
224         List<String> list = new ArrayList<String>();
225         Iterator<Status> it = context.getStatusManager().getCopyOfStatusList().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 [" + objectNameAsString + "]");
239             return;
240         }
241         if (mbs.isRegistered(objectName)) {
242             try {
243                 addInfo("Unregistering mbean [" + objectNameAsString + "]");
244                 mbs.unregisterMBean(objectName);
245             } catch (InstanceNotFoundException e) {
246                 // this is theoretically impossible
247                 addError("Unable to find a verifiably registered mbean [" + objectNameAsString + "]", e);
248             } catch (MBeanRegistrationException e) {
249                 addError("Failed to unregister [" + objectNameAsString + "]", e);
250             }
251         } else {
252             addInfo("mbean [" + objectNameAsString + "] was not in the mbean registry. This is OK.");
253         }
254         stop();
255     }
256 
257     public void onLevelChange(Logger logger, Level level) {
258         // nothing to do
259     }
260 
261     public void onReset(LoggerContext context) {
262         addInfo("onReset() method called JMXActivator [" + objectNameAsString + "]");
263     }
264 
265     /**
266      * JMXConfigurator should not be removed subsequent to a LoggerContext reset.
267      * 
268      * @return
269      */
270     public boolean isResetResistant() {
271         return true;
272     }
273 
274     private void clearFields() {
275         mbs = null;
276         objectName = null;
277         loggerContext = null;
278     }
279 
280     private void stop() {
281         started = false;
282         clearFields();
283     }
284 
285     public void onStart(LoggerContext context) {
286         // nop
287     }
288 
289     @Override
290     public String toString() {
291         return this.getClass().getName() + "(" + context.getName() + ")";
292     }
293 }