View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.jmx;
18  
19  import java.io.IOException;
20  import java.util.List;
21  import java.util.concurrent.Executor;
22  import java.util.concurrent.atomic.AtomicLong;
23  
24  import javax.management.MBeanNotificationInfo;
25  import javax.management.Notification;
26  import javax.management.NotificationBroadcasterSupport;
27  import javax.management.ObjectName;
28  
29  import org.apache.logging.log4j.Level;
30  import org.apache.logging.log4j.status.StatusData;
31  import org.apache.logging.log4j.status.StatusListener;
32  import org.apache.logging.log4j.status.StatusLogger;
33  
34  /**
35   * Implementation of the {@code StatusLoggerAdminMBean} interface.
36   */
37  public class StatusLoggerAdmin extends NotificationBroadcasterSupport implements StatusListener, StatusLoggerAdminMBean {
38  
39      private final AtomicLong sequenceNo = new AtomicLong();
40      private final ObjectName objectName;
41      private final String contextName;
42      private Level level = Level.WARN;
43  
44      /**
45       * Constructs a new {@code StatusLoggerAdmin} with the {@code Executor} to
46       * be used for sending {@code Notification}s asynchronously to listeners.
47       *
48       * @param contextName name of the LoggerContext under which to register this
49       *            StatusLoggerAdmin. Note that the StatusLogger may be
50       *            registered multiple times, once for each LoggerContext. In web
51       *            containers, each web application has its own LoggerContext and
52       *            by associating the StatusLogger with the LoggerContext, all
53       *            associated MBeans can be unloaded when the web application is
54       *            undeployed.
55       * @param executor used to send notifications asynchronously
56       */
57      public StatusLoggerAdmin(final String contextName, final Executor executor) {
58          super(executor, createNotificationInfo());
59          this.contextName = contextName;
60          try {
61              final String mbeanName = String.format(PATTERN, Server.escape(contextName));
62              objectName = new ObjectName(mbeanName);
63          } catch (final Exception e) {
64              throw new IllegalStateException(e);
65          }
66          removeListeners(contextName);
67          StatusLogger.getLogger().registerListener(this);
68      }
69  
70      /**
71       * Add listener to StatusLogger for this context, or replace it if it already exists.
72       *
73       * @param ctxName
74       */
75      private void removeListeners(final String ctxName) {
76          final StatusLogger logger = StatusLogger.getLogger();
77          final Iterable<StatusListener> listeners = logger.getListeners();
78          // Remove any StatusLoggerAdmin listeners already registered for this context
79          for (final StatusListener statusListener : listeners) {
80              if (statusListener instanceof StatusLoggerAdmin) {
81                  final StatusLoggerAdmin adminListener = (StatusLoggerAdmin) statusListener;
82                  if (ctxName != null && ctxName.equals(adminListener.contextName)) {
83                      logger.removeListener(adminListener);
84                  }
85              }
86          }
87      }
88  
89      private static MBeanNotificationInfo createNotificationInfo() {
90          final String[] notifTypes = new String[] {
91                  NOTIF_TYPE_DATA,
92                  NOTIF_TYPE_MESSAGE };
93          final String name = Notification.class.getName();
94          final String description = "StatusLogger has logged an event";
95          return new MBeanNotificationInfo(notifTypes, name, description);
96      }
97  
98      @Override
99      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 }