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.logging.log4j.core.jmx;
018
019import java.io.IOException;
020import java.util.List;
021import java.util.concurrent.Executor;
022import java.util.concurrent.atomic.AtomicLong;
023
024import javax.management.MBeanNotificationInfo;
025import javax.management.Notification;
026import javax.management.NotificationBroadcasterSupport;
027import javax.management.ObjectName;
028
029import org.apache.logging.log4j.Level;
030import org.apache.logging.log4j.status.StatusData;
031import org.apache.logging.log4j.status.StatusListener;
032import org.apache.logging.log4j.status.StatusLogger;
033
034/**
035 * Implementation of the {@code StatusLoggerAdminMBean} interface.
036 */
037public 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}