001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.activemq.broker.jmx;
018
019import java.lang.reflect.InvocationTargetException;
020import java.lang.reflect.Method;
021import java.net.MalformedURLException;
022import java.net.URL;
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.Enumeration;
026import java.util.List;
027import java.util.Locale;
028
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032public class Log4JConfigView implements Log4JConfigViewMBean {
033
034    private static final Logger LOG = LoggerFactory.getLogger(Log4JConfigView.class);
035
036    @Override
037    public String getRootLogLevel() throws Exception {
038        ClassLoader cl = getClassLoader();
039
040        if (!isLog4JAvailable(cl)) {
041            return null;
042        }
043
044        Class<?> loggerClass = getLoggerClass(cl);
045        if (loggerClass == null) {
046            return null;
047        }
048
049        Method getRootLogger = loggerClass.getMethod("getRootLogger", new Class[]{});
050        Method getLevel = loggerClass.getMethod("getLevel", new Class[]{});
051        Object rootLogger = getRootLogger.invoke(null, (Object[])null);
052
053        return getLevel.invoke(rootLogger, (Object[])null).toString();
054    }
055
056    @Override
057    public void setRootLogLevel(String level) throws Exception {
058        ClassLoader cl = getClassLoader();
059
060        if (!isLog4JAvailable(cl)) {
061            return;
062        }
063
064        Class<?> loggerClass = getLoggerClass(cl);
065        Class<?> levelClass = getLevelClass(cl);
066        if (levelClass == null || loggerClass == null) {
067            return;
068        }
069
070        String targetLevel = level.toUpperCase(Locale.US);
071        Method getRootLogger = loggerClass.getMethod("getRootLogger", new Class[]{});
072        Method setLevel = loggerClass.getMethod("setLevel", levelClass);
073        Object rootLogger = getRootLogger.invoke(null, (Object[])null);
074        Method toLevel = levelClass.getMethod("toLevel", String.class);
075        Object newLevel = toLevel.invoke(null, targetLevel);
076
077        // Check that the level conversion worked and that we got a level
078        // that matches what was asked for.  A bad level name will result
079        // in the lowest level value and we don't want to change unless we
080        // matched what the user asked for.
081        if (newLevel != null && newLevel.toString().equals(targetLevel)) {
082            LOG.debug("Set level {} for root logger.", level);
083            setLevel.invoke(rootLogger, newLevel);
084        }
085    }
086
087    @Override
088    public List<String> getLoggers() throws Exception {
089
090        ClassLoader cl = getClassLoader();
091
092        if (!isLog4JAvailable(cl)) {
093            return Collections.emptyList();
094        }
095
096        Class<?> logManagerClass = getLogManagerClass(cl);
097        Class<?> loggerClass = getLoggerClass(cl);
098        if (logManagerClass == null || loggerClass == null) {
099            return Collections.emptyList();
100        }
101
102        Method getCurrentLoggers = logManagerClass.getMethod("getCurrentLoggers", new Class[]{});
103        Method getName = loggerClass.getMethod("getName", new Class[]{});
104
105        List<String> list = new ArrayList<String>();
106        Enumeration<?> loggers = (Enumeration<?>)getCurrentLoggers.invoke(null, (Object[])null);
107
108        while (loggers.hasMoreElements()) {
109            Object logger = loggers.nextElement();
110            if (logger != null) {
111                list.add((String) getName.invoke(logger, (Object[])null));
112            }
113        }
114
115        LOG.debug("Found {} loggers", list.size());
116
117        return list;
118    }
119
120    @Override
121    public String getLogLevel(String loggerName) throws Exception {
122
123        ClassLoader cl = getClassLoader();
124
125        if (!isLog4JAvailable(cl)) {
126            return null;
127        }
128
129        Class<?> loggerClass = getLoggerClass(cl);
130        if (loggerClass == null) {
131            return null;
132        }
133
134        Method getLogger = loggerClass.getMethod("getLogger", String.class);
135        String logLevel = null;
136
137        if (loggerName != null && !loggerName.isEmpty()) {
138            Object logger = getLogger.invoke(null, loggerName);
139            if (logger != null) {
140                LOG.debug("Found level {} for logger: {}", logLevel, loggerName);
141                Method getLevel = loggerClass.getMethod("getLevel", new Class[]{});
142                Object level = getLevel.invoke(logger, (Object[])null);
143                if (level != null) {
144                    logLevel = level.toString();
145                } else {
146                    Method getRootLogger = loggerClass.getMethod("getRootLogger", new Class[]{});
147                    Object rootLogger = getRootLogger.invoke(null, (Object[])null);
148                    logLevel = getLevel.invoke(rootLogger, (Object[])null).toString();
149                }
150            }
151        } else {
152            throw new IllegalArgumentException("Logger names cannot be null or empty strings");
153        }
154
155        return logLevel;
156    }
157
158    @Override
159    public void setLogLevel(String loggerName, String level) throws Exception {
160
161        if (loggerName == null || loggerName.isEmpty()) {
162            throw new IllegalArgumentException("Logger names cannot be null or empty strings");
163        }
164
165        if (level == null || level.isEmpty()) {
166            throw new IllegalArgumentException("Level name cannot be null or empty strings");
167        }
168
169        ClassLoader cl = getClassLoader();
170
171        if (!isLog4JAvailable(cl)) {
172            return;
173        }
174
175        Class<?> loggerClass = getLoggerClass(cl);
176        Class<?> levelClass = getLevelClass(cl);
177        if (loggerClass == null || levelClass == null) {
178            return;
179        }
180
181        String targetLevel = level.toUpperCase(Locale.US);
182        Method getLogger = loggerClass.getMethod("getLogger", String.class);
183        Method setLevel = loggerClass.getMethod("setLevel", levelClass);
184        Method toLevel = levelClass.getMethod("toLevel", String.class);
185
186        Object logger = getLogger.invoke(null, loggerName);
187        if (logger != null) {
188            Object newLevel = toLevel.invoke(null, targetLevel);
189
190            // Check that the level conversion worked and that we got a level
191            // that matches what was asked for.  A bad level name will result
192            // in the lowest level value and we don't want to change unless we
193            // matched what the user asked for.
194            if (newLevel != null && newLevel.toString().equals(targetLevel)) {
195                LOG.debug("Set level {} for logger: {}", level, loggerName);
196                setLevel.invoke(logger, newLevel);
197            }
198        }
199    }
200
201    @Override
202    public void reloadLog4jProperties() throws Throwable {
203        doReloadLog4jProperties();
204    }
205
206    //---------- Static Helper Methods ---------------------------------------//
207
208    public static void doReloadLog4jProperties() throws Throwable {
209        try {
210            ClassLoader cl = Log4JConfigView.class.getClassLoader();
211            Class<?> logManagerClass = getLogManagerClass(cl);
212            if (logManagerClass == null) {
213                LOG.debug("Could not locate log4j classes on classpath.");
214                return;
215            }
216
217            Method resetConfiguration = logManagerClass.getMethod("resetConfiguration", new Class[]{});
218            resetConfiguration.invoke(null, new Object[]{});
219
220            String configurationOptionStr = System.getProperty("log4j.configuration");
221            URL log4jprops = null;
222            if (configurationOptionStr != null) {
223                try {
224                    log4jprops = new URL(configurationOptionStr);
225                } catch (MalformedURLException ex) {
226                    log4jprops = cl.getResource("log4j.properties");
227                }
228            } else {
229               log4jprops = cl.getResource("log4j.properties");
230            }
231
232            if (log4jprops != null) {
233                Class<?> propertyConfiguratorClass = cl.loadClass("org.apache.log4j.PropertyConfigurator");
234                Method configure = propertyConfiguratorClass.getMethod("configure", new Class[]{URL.class});
235                configure.invoke(null, new Object[]{log4jprops});
236            }
237        } catch (InvocationTargetException e) {
238            throw e.getTargetException();
239        }
240    }
241
242    public static boolean isLog4JAvailable() {
243        return isLog4JAvailable(getClassLoader());
244    }
245
246    private static ClassLoader getClassLoader() {
247        return Log4JConfigView.class.getClassLoader();
248    }
249
250    private static boolean isLog4JAvailable(ClassLoader cl) {
251        if (getLogManagerClass(cl) != null) {
252            return true;
253        }
254
255        LOG.debug("Could not locate log4j classes on classpath.");
256
257        return false;
258    }
259
260    private static Class<?> getLogManagerClass(ClassLoader cl) {
261        Class<?> logManagerClass = null;
262        try {
263            logManagerClass = cl.loadClass("org.apache.log4j.LogManager");
264        } catch (ClassNotFoundException e) {
265        }
266        return logManagerClass;
267    }
268
269    private static Class<?> getLoggerClass(ClassLoader cl) {
270        Class<?> loggerClass = null;
271        try {
272            loggerClass = cl.loadClass("org.apache.log4j.Logger");
273        } catch (ClassNotFoundException e) {
274        }
275        return loggerClass;
276    }
277
278    private static Class<?> getLevelClass(ClassLoader cl) {
279        Class<?> levelClass = null;
280        try {
281            levelClass = cl.loadClass("org.apache.log4j.Level");
282        } catch (ClassNotFoundException e) {
283        }
284        return levelClass;
285    }
286}