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 }