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        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}