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