    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            removeListeners(contextName);
    067            StatusLogger.getLogger().registerListener(this);
    068        }
    069
    070        /**
    071         * Add listener to StatusLogger for this context, or replace it if it already exists.
    072         *
    073         * @param ctxName
    074         */
    075        private void removeListeners(final String ctxName) {
    076            final StatusLogger logger = StatusLogger.getLogger();
    077            final Iterable<StatusListener> listeners = logger.getListeners();
    078            // Remove any StatusLoggerAdmin listeners already registered for this context
    079            for (final StatusListener statusListener : listeners) {
    080                if (statusListener instanceof StatusLoggerAdmin) {
    081                    final StatusLoggerAdmin adminListener = (StatusLoggerAdmin) statusListener;
    082                    if (ctxName != null && ctxName.equals(adminListener.contextName)) {
    083                        logger.removeListener(adminListener);
    084                    }
    085                }
    086            }
    087        }
    088
    089        private static MBeanNotificationInfo createNotificationInfo() {
    090            final String[] notifTypes = new String[] {
    091                    NOTIF_TYPE_DATA,
    092                    NOTIF_TYPE_MESSAGE };
    093            final String name = Notification.class.getName();
    094            final String description = "StatusLogger has logged an event";
    095            return new MBeanNotificationInfo(notifTypes, name, description);
    096        }
    097
    098        @Override
    099        public String[] getStatusDataHistory() {
    100            final List<StatusData> data = getStatusData();
    101            final String[] result = new String[data.size()];
    102            for (int i = 0; i < result.length; i++) {
    103                result[i] = data.get(i).getFormattedStatus();
    104            }
    105            return result;
    106        }
    107
    108        @Override
    109        public List<StatusData> getStatusData() {
    110            return StatusLogger.getLogger().getStatusData();
    111        }
    112
    113        @Override
    114        public String getLevel() {
    115            return this.level.name();
    116        }
    117
    118        @Override
    119        public Level getStatusLevel() {
    120            return this.level;
    121        }
    122
    123        @Override
    124        public void setLevel(final String level) {
    125            this.level = Level.toLevel(level, Level.ERROR);
    126        }
    127
    128        @Override
    129        public String getContextName() {
    130            return contextName;
    131        }
    132
    133        /*
    134         * (non-Javadoc)
    135         *
    136         * @see
    137         * org.apache.logging.log4j.status.StatusListener#log(org.apache.logging
    138         * .log4j.status.StatusData)
    139         */
    140        @Override
    141        public void log(final StatusData data) {
    142            final Notification notifMsg = new Notification(NOTIF_TYPE_MESSAGE, getObjectName(), nextSeqNo(), nowMillis(),
    143                    data.getFormattedStatus());
    144            sendNotification(notifMsg);
    145
    146            final Notification notifData = new Notification(NOTIF_TYPE_DATA, getObjectName(), nextSeqNo(), nowMillis());
    147            notifData.setUserData(data);
    148            sendNotification(notifData);
    149        }
    150
    151        /**
    152         * Returns the {@code ObjectName} of this mbean.
    153         *
    154         * @return the {@code ObjectName}
    155         * @see StatusLoggerAdminMBean#PATTERN
    156         */
    157        @Override
    158        public ObjectName getObjectName() {
    159            return objectName;
    160        }
    161
    162        private long nextSeqNo() {
    163            return sequenceNo.getAndIncrement();
    164        }
    165
    166        private long nowMillis() {
    167            return System.currentTimeMillis();
    168        }
    169
    170        @Override
    171        public void close() throws IOException {
    172            // nothing to close here
    173        }
    174    }