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     */
017    package org.apache.logging.log4j.core.jmx;
018    
019    import java.io.IOException;
020    import java.util.List;
021    import java.util.concurrent.Executor;
022    import java.util.concurrent.atomic.AtomicLong;
023    
024    import javax.management.MBeanNotificationInfo;
025    import javax.management.Notification;
026    import javax.management.NotificationBroadcasterSupport;
027    import javax.management.ObjectName;
028    
029    import org.apache.logging.log4j.Level;
030    import org.apache.logging.log4j.status.StatusData;
031    import org.apache.logging.log4j.status.StatusListener;
032    import org.apache.logging.log4j.status.StatusLogger;
033    
034    /**
035     * Implementation of the {@code StatusLoggerAdminMBean} interface.
036     */
037    public class StatusLoggerAdmin extends NotificationBroadcasterSupport implements StatusListener, StatusLoggerAdminMBean {
038    
039        private final AtomicLong sequenceNo = new AtomicLong();
040        private final ObjectName objectName;
041        private final String contextName;
042        private Level level = Level.WARN;
043    
044        /**
045         * Constructs a new {@code StatusLoggerAdmin} with the {@code Executor} to
046         * be used for sending {@code Notification}s asynchronously to listeners.
047         * 
048         * @param contextName name of the LoggerContext under which to register this
049         *            StatusLoggerAdmin. Note that the StatusLogger may be
050         *            registered multiple times, once for each LoggerContext. In web
051         *            containers, each web application has its own LoggerContext and
052         *            by associating the StatusLogger with the LoggerContext, all
053         *            associated MBeans can be unloaded when the web application is
054         *            undeployed.
055         * @param executor used to send notifications asynchronously
056         */
057        public StatusLoggerAdmin(final String contextName, final Executor executor) {
058            super(executor, createNotificationInfo());
059            this.contextName = contextName;
060            try {
061                final String mbeanName = String.format(PATTERN, Server.escape(contextName));
062                objectName = new ObjectName(mbeanName);
063            } catch (final Exception e) {
064                throw new IllegalStateException(e);
065            }
066            StatusLogger.getLogger().registerListener(this);
067        }
068    
069        private static MBeanNotificationInfo createNotificationInfo() {
070            final String[] notifTypes = new String[] {//
071            NOTIF_TYPE_DATA, NOTIF_TYPE_MESSAGE };
072            final String name = Notification.class.getName();
073            final String description = "StatusLogger has logged an event";
074            return new MBeanNotificationInfo(notifTypes, name, description);
075        }
076    
077        @Override
078        public String[] getStatusDataHistory() {
079            final List<StatusData> data = getStatusData();
080            final String[] result = new String[data.size()];
081            for (int i = 0; i < result.length; i++) {
082                result[i] = data.get(i).getFormattedStatus();
083            }
084            return result;
085        }
086    
087        @Override
088        public List<StatusData> getStatusData() {
089            return StatusLogger.getLogger().getStatusData();
090        }
091    
092        @Override
093        public String getLevel() {
094            return this.level.name();
095        }
096    
097        @Override
098        public Level getStatusLevel() {
099            return this.level;
100        }
101    
102        @Override
103        public void setLevel(final String level) {
104            this.level = Level.toLevel(level, Level.ERROR);
105        }
106    
107        @Override
108        public String getContextName() {
109            return contextName;
110        }
111    
112        /*
113         * (non-Javadoc)
114         * 
115         * @see
116         * org.apache.logging.log4j.status.StatusListener#log(org.apache.logging
117         * .log4j.status.StatusData)
118         */
119        @Override
120        public void log(final StatusData data) {
121            final Notification notifMsg = new Notification(NOTIF_TYPE_MESSAGE, getObjectName(), nextSeqNo(), now(),
122                    data.getFormattedStatus());
123            sendNotification(notifMsg);
124    
125            final Notification notifData = new Notification(NOTIF_TYPE_DATA, getObjectName(), nextSeqNo(), now());
126            notifData.setUserData(data);
127            sendNotification(notifData);
128        }
129    
130        /**
131         * Returns the {@code ObjectName} of this mbean.
132         * 
133         * @return the {@code ObjectName}
134         * @see StatusLoggerAdminMBean#PATTERN
135         */
136        @Override
137        public ObjectName getObjectName() {
138            return objectName;
139        }
140    
141        private long nextSeqNo() {
142            return sequenceNo.getAndIncrement();
143        }
144    
145        private long now() {
146            return System.currentTimeMillis();
147        }
148    
149        @Override
150        public void close() throws IOException {
151            // nothing to close here
152        }
153    }