001// Copyright 2005 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.hivemind.management.log4j;
016
017import java.util.Enumeration;
018import java.util.Iterator;
019import java.util.List;
020
021import javax.management.InstanceAlreadyExistsException;
022import javax.management.JMException;
023import javax.management.MBeanAttributeInfo;
024import javax.management.MBeanOperationInfo;
025import javax.management.MBeanParameterInfo;
026import javax.management.ObjectName;
027
028import org.apache.hivemind.ApplicationRuntimeException;
029import org.apache.hivemind.management.ObjectNameBuilder;
030import org.apache.hivemind.management.mbeans.AbstractDynamicMBean;
031import org.apache.hivemind.util.StringUtils;
032import org.apache.log4j.LogManager;
033import org.apache.log4j.Logger;
034import org.apache.log4j.helpers.OptionConverter;
035import org.apache.log4j.spi.LoggerRepository;
036import org.apache.oro.text.regex.MalformedPatternException;
037import org.apache.oro.text.regex.Pattern;
038import org.apache.oro.text.regex.Perl5Compiler;
039import org.apache.oro.text.regex.Perl5Matcher;
040
041/**
042 * MBean that manages MBeans for Log4j Loggers. New MBeans can be added by specifying the Logger
043 * name or a logger pattern. Each MBean allows managing level and appenders of a single logger. Uses
044 * the LoggerDynamicMBean from the log4j library. Similar to
045 * {@link org.apache.log4j.jmx.HierarchyDynamicMBean} but implements the hivemind ObjectName scheme
046 * by using ObjectNameBuilder service.
047 * 
048 * @author Achim Huegen
049 * @since 1.1
050 */
051public class LogManagementMBean extends AbstractDynamicMBean implements LogManagement
052{
053    private static final String OBJECT_NAME_TYPE = "logger";
054
055    private static final char WILDCARD = '*';
056
057    private static Logger logger = Logger.getLogger(LogManagementMBean.class);
058
059    private ObjectNameBuilder _objectNameBuilder;
060
061    private LoggerRepository _loggerRepository;
062
063    private List _loggerContributions;
064
065    public LogManagementMBean(ObjectNameBuilder objectNameBuilder, List loggerContributions)
066    {
067        _objectNameBuilder = objectNameBuilder;
068        _loggerRepository = LogManager.getLoggerRepository();
069        _loggerContributions = loggerContributions;
070    }
071
072    protected MBeanAttributeInfo[] createMBeanAttributeInfo()
073    {
074        return new MBeanAttributeInfo[]
075        { new MBeanAttributeInfo("Threshold", String.class.getName(),
076                "The \"threshold\" state of the logger hierarchy.", true, true, false) };
077    }
078
079    protected MBeanOperationInfo[] createMBeanOperationInfo()
080    {
081        MBeanParameterInfo parameterInfo[] = new MBeanParameterInfo[1];
082        parameterInfo[0] = new MBeanParameterInfo("loggerPattern", "java.lang.String",
083                "Name of the Logger. Use * as wildcard");
084        return new MBeanOperationInfo[]
085        { new MBeanOperationInfo("addLoggerMBean", "Adds a MBean for a single Logger or "
086                + "a group of Loggers", parameterInfo, "void", 1) };
087    }
088    
089    public void postRegister(Boolean registrationDone)
090    {
091        addConfiguredLoggerMBeans();
092    }
093
094    public String getThreshold()
095    {
096        return _loggerRepository.getThreshold().toString();
097    }
098
099    public void setThreshold(String threshold)
100    {
101        OptionConverter.toLevel(threshold, _loggerRepository.getThreshold());
102
103        _loggerRepository.setThreshold(threshold);
104    }
105
106    /**
107     * @see org.apache.hivemind.management.log4j.LogManagement#addLoggerMBean(java.lang.String)
108     */
109    public void addLoggerMBean(String loggerPattern)
110    {
111        boolean hasWildcard = loggerPattern.indexOf(WILDCARD) >= 0;
112        if (hasWildcard)
113        {
114            addLoggerMBeansForPattern(loggerPattern);
115        }
116        else
117        {
118            Logger log = LogManager.getLogger(loggerPattern);
119            addLoggerMBean(log);
120        }
121    }
122
123    /**
124     * Adds a MBean for a logger.
125     * 
126     * @param log
127     *            the logger
128     * @return ObjectName of created MBean
129     */
130    protected ObjectName addLoggerMBean(Logger log)
131    {
132        String name = log.getName();
133        ObjectName objectname = null;
134        try
135        {
136            LoggerMBean loggerMBean = new LoggerMBean(log);
137            objectname = getObjectNameBuilder().createObjectName(name, OBJECT_NAME_TYPE);
138            getMBeanServer().registerMBean(loggerMBean, objectname);
139        }
140        catch (InstanceAlreadyExistsException exception)
141        {
142            // just warn
143            logger.warn("MBean for Logger " + log.getName() + " already exists");
144        }
145        catch (JMException exception)
146        {
147            throw new ApplicationRuntimeException(exception);
148        }
149        return objectname;
150    }
151
152    /**
153     * Adds MBeans for all Loggers that are defined in the service configuration
154     */
155    protected void addConfiguredLoggerMBeans()
156    {
157        for (Iterator iterContributions = _loggerContributions.iterator(); iterContributions
158                .hasNext();)
159        {
160            LoggerContribution contribution = (LoggerContribution) iterContributions.next();
161            String loggerPattern = contribution.getLoggerPattern();
162
163            addLoggerMBeansForPattern(loggerPattern);
164        }
165    }
166
167    /**
168     * Adds MBeans for all existing Loggers, that match the loggerPattern
169     * 
170     * @param loggerPattern
171     */
172    protected void addLoggerMBeansForPattern(String loggerPattern)
173    {
174        // Add MBeans for all loggers that match the pattern
175        Enumeration loggers = LogManager.getCurrentLoggers();
176        while (loggers.hasMoreElements())
177        {
178            Logger log = (Logger) loggers.nextElement();
179            if (isMatch(log.getName(), loggerPattern))
180                addLoggerMBean(log);
181        }
182    }
183
184    /**
185     * @return Returns the _objectNameBuilder.
186     */
187    public ObjectNameBuilder getObjectNameBuilder()
188    {
189        return _objectNameBuilder;
190    }
191
192    /**
193     * Returns true if loggerName matches a loggerPattern The pattern kann contain '*' as wildcard
194     * character. This gets translated to '.*' and is used for a regex match using jakarta oro
195     */
196    protected boolean isMatch(String loggerName, String loggerPattern)
197    {
198        // Adapt loggerPattern for oro
199        String realLoggerPattern = StringUtils
200                .replace(loggerPattern, "" + WILDCARD, "." + WILDCARD);
201
202        Perl5Compiler compiler = new Perl5Compiler();
203        Perl5Matcher matcher = new Perl5Matcher();
204        Pattern compiled;
205        try
206        {
207            compiled = compiler.compile(realLoggerPattern);
208        }
209        catch (MalformedPatternException e)
210        {
211            throw new ApplicationRuntimeException("Malformed Logger Pattern:" + realLoggerPattern);
212        }
213        return matcher.matches(loggerName, compiled);
214
215    }
216
217}