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.List;
026import java.util.Locale;
027import java.util.Map;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031public class Log4JConfigView implements Log4JConfigViewMBean {
032
033    private static final Logger LOG = LoggerFactory.getLogger(Log4JConfigView.class);
034
035    @Override
036    public String getRootLogLevel() throws Exception {
037        ClassLoader cl = getClassLoader();
038
039        if (!isLog4JAvailable(cl)) {
040            return null;
041        }
042
043        Class<?> logManagerClass = getLogManagerClass(cl);
044        Class<?> loggerClass = getLoggerClass(cl);
045        if (logManagerClass == null || loggerClass == null) {
046            return null;
047        }
048
049        Method getRootLogger = logManagerClass.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<?> configuratorClass = getConfiguratorClass(cl);
065        Class<?> loggerClass = getLoggerClass(cl);
066        Class<?> levelClass = getLevelClass(cl);
067        if (configuratorClass == null || levelClass == null || loggerClass == null) {
068            return;
069        }
070
071        String targetLevel = level.toUpperCase(Locale.US);
072        Method setRootLevel = configuratorClass.getMethod("setRootLevel", levelClass);
073        Method toLevel = levelClass.getMethod("toLevel", String.class);
074        Object newLevel = toLevel.invoke(null, targetLevel);
075
076        // Check that the level conversion worked and that we got a level
077        // that matches what was asked for.  A bad level name will result
078        // in the lowest level value and we don't want to change unless we
079        // matched what the user asked for.
080        if (newLevel != null && newLevel.toString().equals(targetLevel)) {
081            LOG.debug("Set level {} for root logger.", level);
082            setRootLevel.invoke(configuratorClass, newLevel);
083        }
084    }
085
086    @Override
087    public List<String> getLoggers() throws Exception {
088
089        ClassLoader cl = getClassLoader();
090
091        if (!isLog4JAvailable(cl)) {
092            return Collections.emptyList();
093        }
094
095        Class<?> logManagerClass = getLogManagerClass(cl);
096        Class<?> loggerClass = getLoggerClass(cl);
097        Class<?> loggerConfigClass = getLoggerConfigClass(cl);
098        if (logManagerClass == null || loggerClass == null || loggerConfigClass == null) {
099            return Collections.emptyList();
100        }
101
102        Method getContext = logManagerClass.getMethod("getContext", boolean.class);
103        Object logContext = getContext.invoke(logManagerClass, false);
104        Method getConfiguration = logContext.getClass().getMethod("getConfiguration");
105        Object configuration = getConfiguration.invoke(logContext);
106        Method getLoggers = configuration.getClass().getMethod("getLoggers");
107
108        Method getName = loggerConfigClass.getMethod("getName");
109
110        List<String> list = new ArrayList<String>();
111        Map<String, ?> loggers = (Map<String, ?>)getLoggers.invoke(configuration);
112
113        for (Object logger : loggers.values()) {
114            if (logger != null) {
115                list.add((String) getName.invoke(logger, (Object[])null));
116            }
117        }
118
119        LOG.debug("Found {} loggers", list.size());
120
121        return list;
122    }
123
124    @Override
125    public String getLogLevel(String loggerName) throws Exception {
126
127        ClassLoader cl = getClassLoader();
128
129        if (!isLog4JAvailable(cl)) {
130            return null;
131        }
132
133        Class<?> logManagerClass = getLogManagerClass(cl);
134        Class<?> loggerClass = getLoggerClass(cl);
135        if (logManagerClass == null || loggerClass == null) {
136            return null;
137        }
138
139        Method getLogger = logManagerClass.getMethod("getLogger", String.class);
140        String logLevel = null;
141
142        if (loggerName != null && !loggerName.isEmpty()) {
143            Object logger = getLogger.invoke(null, loggerName);
144            if (logger != null) {
145                LOG.debug("Found level {} for logger: {}", logLevel, loggerName);
146                Method getLevel = loggerClass.getMethod("getLevel", new Class[]{});
147                Object level = getLevel.invoke(logger, (Object[])null);
148                if (level != null) {
149                    logLevel = level.toString();
150                } else {
151                    Method getRootLogger = loggerClass.getMethod("getRootLogger", new Class[]{});
152                    Object rootLogger = getRootLogger.invoke(null, (Object[])null);
153                    logLevel = getLevel.invoke(rootLogger, (Object[])null).toString();
154                }
155            }
156        } else {
157            throw new IllegalArgumentException("Logger names cannot be null or empty strings");
158        }
159
160        return logLevel;
161    }
162
163    @Override
164    public void setLogLevel(String loggerName, String level) throws Exception {
165
166        if (loggerName == null || loggerName.isEmpty()) {
167            throw new IllegalArgumentException("Logger names cannot be null or empty strings");
168        }
169
170        if (level == null || level.isEmpty()) {
171            throw new IllegalArgumentException("Level name cannot be null or empty strings");
172        }
173
174        ClassLoader cl = getClassLoader();
175
176        if (!isLog4JAvailable(cl)) {
177            return;
178        }
179
180        Class<?> configuratorClass = getConfiguratorClass(cl);
181        Class<?> loggerClass = getLoggerClass(cl);
182        Class<?> levelClass = getLevelClass(cl);
183        if (configuratorClass == null || loggerClass == null || levelClass == null) {
184            return;
185        }
186
187        String targetLevel = level.toUpperCase(Locale.US);
188        Method setLevel = configuratorClass.getMethod("setLevel", String.class, levelClass);
189        Method toLevel = levelClass.getMethod("toLevel", String.class);
190
191        Object newLevel = toLevel.invoke(null, targetLevel);
192
193        // Check that the level conversion worked and that we got a level
194        // that matches what was asked for.  A bad level name will result
195        // in the lowest level value and we don't want to change unless we
196        // matched what the user asked for.
197        if (newLevel != null && newLevel.toString().equals(targetLevel)) {
198            LOG.debug("Set level {} for logger: {}", level, loggerName);
199            setLevel.invoke(configuratorClass, loggerName, newLevel);
200        }
201    }
202
203    @Override
204    public void reloadLog4jProperties() throws Throwable {
205        doReloadLog4jProperties();
206    }
207
208    //---------- Static Helper Methods ---------------------------------------//
209
210    public static void doReloadLog4jProperties() throws Throwable {
211        try {
212            ClassLoader cl = Log4JConfigView.class.getClassLoader();
213            Class<?> logManagerClass = getLogManagerClass(cl);
214            if (logManagerClass == null) {
215                LOG.debug("Could not locate log4j classes on classpath.");
216                return;
217            }
218
219            Method resetConfiguration = logManagerClass.getMethod("resetConfiguration", new Class[]{});
220            resetConfiguration.invoke(null, new Object[]{});
221
222            String configurationOptionStr = System.getProperty("log4j.configuration");
223            URL log4jprops = null;
224            if (configurationOptionStr != null) {
225                try {
226                    log4jprops = new URL(configurationOptionStr);
227                } catch (MalformedURLException ex) {
228                    log4jprops = cl.getResource("log4j.properties");
229                }
230            } else {
231               log4jprops = cl.getResource("log4j.properties");
232            }
233
234            if (log4jprops != null) {
235                Class<?> propertyConfiguratorClass = cl.loadClass("org.apache.log4j.PropertyConfigurator");
236                Method configure = propertyConfiguratorClass.getMethod("configure", new Class[]{URL.class});
237                configure.invoke(null, new Object[]{log4jprops});
238            }
239        } catch (InvocationTargetException e) {
240            throw e.getTargetException();
241        }
242    }
243
244    public static boolean isLog4JAvailable() {
245        return isLog4JAvailable(getClassLoader());
246    }
247
248    private static ClassLoader getClassLoader() {
249        return Log4JConfigView.class.getClassLoader();
250    }
251
252    private static boolean isLog4JAvailable(ClassLoader cl) {
253        if (getLogManagerClass(cl) != null) {
254            return true;
255        }
256
257        LOG.debug("Could not locate log4j classes on classpath.");
258
259        return false;
260    }
261
262    private static Class<?> getLogManagerClass(ClassLoader cl) {
263        return getClass(cl, "org.apache.logging.log4j.LogManager");
264    }
265
266    private static Class<?> getLoggerClass(ClassLoader cl) {
267        return getClass(cl, "org.apache.logging.log4j.Logger");
268    }
269
270    private static Class<?> getLoggerConfigClass(ClassLoader cl) {
271        return getClass(cl, "org.apache.logging.log4j.core.config.LoggerConfig");
272    }
273
274    private static Class<?> getLevelClass(ClassLoader cl) {
275        return getClass(cl, "org.apache.logging.log4j.Level");
276    }
277
278    private static Class<?> getConfiguratorClass(ClassLoader cl) {
279        return getClass(cl, "org.apache.logging.log4j.core.config.Configurator");
280    }
281
282    private static Class<?> getClass(ClassLoader cl, String clazz) {
283        Class<?> configuratorClass = null;
284        try {
285            configuratorClass = cl.loadClass(clazz);
286        } catch (ClassNotFoundException e) {
287        }
288        return configuratorClass;
289    }
290}