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}