001/** 002 * Logback: the reliable, generic, fast and flexible logging framework. 003 * Copyright (C) 1999-2016, QOS.ch. All rights reserved. 004 * 005 * This program and the accompanying materials are dual-licensed under 006 * either the terms of the Eclipse Public License v1.0 as published by 007 * the Eclipse Foundation 008 * 009 * or (per the licensee's choosing) 010 * 011 * under the terms of the GNU Lesser General Public License version 2.1 012 * as published by the Free Software Foundation. 013 */ 014package ch.qos.logback.classic.jmx; 015 016import java.io.File; 017import java.io.FileNotFoundException; 018import java.net.MalformedURLException; 019import java.net.URL; 020import java.util.ArrayList; 021import java.util.Iterator; 022import java.util.List; 023 024import javax.management.InstanceNotFoundException; 025import javax.management.MBeanRegistrationException; 026import javax.management.MBeanServer; 027import javax.management.ObjectName; 028 029import ch.qos.logback.classic.Level; 030import ch.qos.logback.classic.Logger; 031import ch.qos.logback.classic.LoggerContext; 032import ch.qos.logback.classic.joran.JoranConfigurator; 033import ch.qos.logback.classic.spi.LoggerContextListener; 034import ch.qos.logback.classic.util.ContextInitializer; 035import ch.qos.logback.core.joran.spi.JoranException; 036import ch.qos.logback.core.spi.ContextAwareBase; 037import ch.qos.logback.core.status.Status; 038import ch.qos.logback.core.status.StatusListener; 039import ch.qos.logback.core.status.StatusListenerAsList; 040import ch.qos.logback.core.status.StatusManager; 041import ch.qos.logback.core.util.StatusPrinter; 042 043/** 044 * A class that provides access to logback components via JMX. 045 * 046 * <p> 047 * Since this class implements {@link JMXConfiguratorMBean} it has to be named 048 * as JMXConfigurator}. 049 * 050 * @author Ceki Gülcü 051 * @author Sébastien Pennec 052 * 053 * Contributor: Sebastian Davids See 054 * http://bugzilla.qos.ch/show_bug.cgi?id=35 055 */ 056public class JMXConfigurator extends ContextAwareBase implements JMXConfiguratorMBean, LoggerContextListener { 057 058 private static String EMPTY = ""; 059 060 LoggerContext loggerContext; 061 MBeanServer mbs; 062 ObjectName objectName; 063 String objectNameAsString; 064 065 // whether to output status events on the console when reloading the 066 // configuration 067 boolean debug = true; 068 069 boolean started; 070 071 public JMXConfigurator(LoggerContext loggerContext, MBeanServer mbs, ObjectName objectName) { 072 started = true; 073 this.context = loggerContext; 074 this.loggerContext = loggerContext; 075 this.mbs = mbs; 076 this.objectName = objectName; 077 this.objectNameAsString = objectName.toString(); 078 if (previouslyRegisteredListenerWithSameObjectName()) { 079 addError("Previously registered JMXConfigurator named [" + objectNameAsString 080 + "] in the logger context named [" + loggerContext.getName() + "]"); 081 } else { 082 // register as a listener only if there are no homonyms 083 loggerContext.addListener(this); 084 } 085 } 086 087 private boolean previouslyRegisteredListenerWithSameObjectName() { 088 List<LoggerContextListener> lcll = loggerContext.getCopyOfListenerList(); 089 for (LoggerContextListener lcl : lcll) { 090 if (lcl instanceof JMXConfigurator) { 091 JMXConfigurator jmxConfigurator = (JMXConfigurator) lcl; 092 if (objectName.equals(jmxConfigurator.objectName)) { 093 return true; 094 } 095 } 096 } 097 return false; 098 } 099 100 public void reloadDefaultConfiguration() throws JoranException { 101 ContextInitializer ci = new ContextInitializer(loggerContext); 102 URL url = ci.findURLOfDefaultConfigurationFile(true); 103 reloadByURL(url); 104 } 105 106 public void reloadByFileName(String fileName) throws JoranException, FileNotFoundException { 107 File f = new File(fileName); 108 if (f.exists() && f.isFile()) { 109 URL url; 110 try { 111 url = f.toURI().toURL(); 112 reloadByURL(url); 113 } catch (MalformedURLException e) { 114 throw new RuntimeException("Unexpected MalformedURLException occured. See nexted cause.", e); 115 } 116 117 } else { 118 String errMsg = "Could not find [" + fileName + "]"; 119 addInfo(errMsg); 120 throw new FileNotFoundException(errMsg); 121 } 122 } 123 124 void addStatusListener(StatusListener statusListener) { 125 StatusManager sm = loggerContext.getStatusManager(); 126 sm.add(statusListener); 127 } 128 129 void removeStatusListener(StatusListener statusListener) { 130 StatusManager sm = loggerContext.getStatusManager(); 131 sm.remove(statusListener); 132 } 133 134 public void reloadByURL(URL url) throws JoranException { 135 StatusListenerAsList statusListenerAsList = new StatusListenerAsList(); 136 137 addStatusListener(statusListenerAsList); 138 addInfo("Resetting context: " + loggerContext.getName()); 139 loggerContext.reset(); 140 141 // after a reset the statusListenerAsList gets removed as a listener 142 addStatusListener(statusListenerAsList); 143 144 try { 145 if (url != null) { 146 JoranConfigurator configurator = new JoranConfigurator(); 147 configurator.setContext(loggerContext); 148 configurator.doConfigure(url); 149 addInfo("Context: " + loggerContext.getName() + " reloaded."); 150 } 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().iterator(); 229 while (it.hasNext()) { 230 list.add(it.next().toString()); 231 } 232 return list; 233 } 234 235 /** 236 * When the associated LoggerContext is stopped, this configurator must be 237 * unregistered 238 */ 239 public void onStop(LoggerContext context) { 240 if (!started) { 241 addInfo("onStop() method called on a stopped JMXActivator [" + objectNameAsString + "]"); 242 return; 243 } 244 if (mbs.isRegistered(objectName)) { 245 try { 246 addInfo("Unregistering mbean [" + objectNameAsString + "]"); 247 mbs.unregisterMBean(objectName); 248 } catch (InstanceNotFoundException e) { 249 // this is theoretically impossible 250 addError("Unable to find a verifiably registered mbean [" + objectNameAsString + "]", e); 251 } catch (MBeanRegistrationException e) { 252 addError("Failed to unregister [" + objectNameAsString + "]", e); 253 } 254 } else { 255 addInfo("mbean [" + objectNameAsString + "] was not in the mbean registry. This is OK."); 256 } 257 stop(); 258 } 259 260 public void onLevelChange(Logger logger, Level level) { 261 // nothing to do 262 } 263 264 public void onReset(LoggerContext context) { 265 addInfo("onReset() method called JMXActivator [" + objectNameAsString + "]"); 266 } 267 268 /** 269 * JMXConfigurator should not be removed subsequent to a LoggerContext reset. 270 * 271 * @return 272 */ 273 public boolean isResetResistant() { 274 return true; 275 } 276 277 private void clearFields() { 278 mbs = null; 279 objectName = null; 280 loggerContext = null; 281 } 282 283 private void stop() { 284 started = false; 285 clearFields(); 286 } 287 288 public void onStart(LoggerContext context) { 289 // nop 290 } 291 292 @Override 293 public String toString() { 294 return this.getClass().getName() + "(" + context.getName() + ")"; 295 } 296}