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    
015    package org.apache.hivemind.management.log4j;
016    
017    import java.util.Enumeration;
018    import java.util.Iterator;
019    import java.util.List;
020    
021    import javax.management.InstanceAlreadyExistsException;
022    import javax.management.JMException;
023    import javax.management.MBeanAttributeInfo;
024    import javax.management.MBeanOperationInfo;
025    import javax.management.MBeanParameterInfo;
026    import javax.management.ObjectName;
027    
028    import org.apache.hivemind.ApplicationRuntimeException;
029    import org.apache.hivemind.management.ObjectNameBuilder;
030    import org.apache.hivemind.management.mbeans.AbstractDynamicMBean;
031    import org.apache.hivemind.util.StringUtils;
032    import org.apache.log4j.LogManager;
033    import org.apache.log4j.Logger;
034    import org.apache.log4j.helpers.OptionConverter;
035    import org.apache.log4j.spi.LoggerRepository;
036    import org.apache.oro.text.regex.MalformedPatternException;
037    import org.apache.oro.text.regex.Pattern;
038    import org.apache.oro.text.regex.Perl5Compiler;
039    import 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     */
051    public 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    }