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.activemq.plugin; 018 019import java.io.File; 020import java.net.URI; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.Set; 024 025import javax.jms.JMSException; 026import javax.management.ObjectName; 027 028import org.apache.activemq.advisory.AdvisorySupport; 029import org.apache.activemq.broker.Broker; 030import org.apache.activemq.broker.BrokerFilter; 031import org.apache.activemq.broker.BrokerService; 032import org.apache.activemq.broker.ConnectionContext; 033import org.apache.activemq.broker.ProducerBrokerExchange; 034import org.apache.activemq.broker.jmx.BrokerViewMBean; 035import org.apache.activemq.broker.jmx.SubscriptionViewMBean; 036import org.apache.activemq.broker.region.Destination; 037import org.apache.activemq.broker.region.DestinationStatistics; 038import org.apache.activemq.broker.region.Queue; 039import org.apache.activemq.broker.region.RegionBroker; 040import org.apache.activemq.broker.region.Topic; 041import org.apache.activemq.command.ActiveMQDestination; 042import org.apache.activemq.command.ActiveMQMapMessage; 043import org.apache.activemq.command.Message; 044import org.apache.activemq.command.MessageId; 045import org.apache.activemq.command.ProducerId; 046import org.apache.activemq.command.ProducerInfo; 047import org.apache.activemq.state.ProducerState; 048import org.apache.activemq.usage.SystemUsage; 049import org.apache.activemq.util.IdGenerator; 050import org.apache.activemq.util.LongSequenceGenerator; 051import org.slf4j.Logger; 052import org.slf4j.LoggerFactory; 053/** 054 * A StatisticsBroker You can retrieve a Map Message for a Destination - or 055 * Broker containing statistics as key-value pairs The message must contain a 056 * replyTo Destination - else its ignored 057 * 058 */ 059public class StatisticsBroker extends BrokerFilter { 060 private static Logger LOG = LoggerFactory.getLogger(StatisticsBroker.class); 061 static final String STATS_PREFIX = "ActiveMQ.Statistics"; 062 063 static final String STATS_DESTINATION_PREFIX = "ActiveMQ.Statistics.Destination"; 064 static final String STATS_BROKER_PREFIX = "ActiveMQ.Statistics.Broker"; 065 static final String STATS_BROKER_RESET_HEADER = "ActiveMQ.Statistics.Broker.Reset"; 066 static final String STATS_SUBSCRIPTION_PREFIX = "ActiveMQ.Statistics.Subscription"; 067 068 // Query-message properties controlling features of Destination-query replies: 069 static final String STATS_DENOTE_END_LIST = "ActiveMQ.Statistics.Destination.List.End.With.Null"; 070 static final String STATS_FIRST_MESSAGE_TIMESTAMP = "ActiveMQ.Statistics.Destination.Include.First.Message.Timestamp"; 071 072 private static final IdGenerator ID_GENERATOR = new IdGenerator(); 073 private final LongSequenceGenerator messageIdGenerator = new LongSequenceGenerator(); 074 protected final ProducerId advisoryProducerId = new ProducerId(); 075 protected BrokerViewMBean brokerView; 076 077 /** 078 * 079 * Constructor 080 * 081 * @param next 082 */ 083 public StatisticsBroker(Broker next) { 084 super(next); 085 this.advisoryProducerId.setConnectionId(ID_GENERATOR.generateId()); 086 } 087 088 /** 089 * Sets the persistence mode 090 * 091 * @see org.apache.activemq.broker.BrokerFilter#send(org.apache.activemq.broker.ProducerBrokerExchange, 092 * org.apache.activemq.command.Message) 093 */ 094 @Override 095 public void send(ProducerBrokerExchange producerExchange, Message messageSend) throws Exception { 096 ActiveMQDestination msgDest = messageSend.getDestination(); 097 ActiveMQDestination replyTo = messageSend.getReplyTo(); 098 if ((replyTo != null) && (msgDest.getPhysicalName().startsWith(STATS_PREFIX))) { 099 String physicalName = msgDest.getPhysicalName(); 100 boolean destStats = physicalName.startsWith(STATS_DESTINATION_PREFIX); 101 boolean brokerStats = physicalName.startsWith(STATS_BROKER_PREFIX); 102 boolean subStats = physicalName.startsWith(STATS_SUBSCRIPTION_PREFIX); 103 BrokerService brokerService = getBrokerService(); 104 RegionBroker regionBroker = (RegionBroker) brokerService.getRegionBroker(); 105 if (destStats) { 106 String destinationName = physicalName.substring(STATS_DESTINATION_PREFIX.length()); 107 if (destinationName.startsWith(".")) { 108 destinationName = destinationName.substring(1); 109 } 110 String destinationQuery = destinationName.replace(STATS_DENOTE_END_LIST,""); 111 boolean endListMessage = !destinationName.equals(destinationQuery) 112 || messageSend.getProperties().containsKey(STATS_DENOTE_END_LIST); 113 ActiveMQDestination queryDestination = ActiveMQDestination.createDestination(destinationQuery,msgDest.getDestinationType()); 114 Set<Destination> destinations = getDestinations(queryDestination); 115 116 boolean includeFirstMessageTimestamp = messageSend.getProperties().containsKey(STATS_FIRST_MESSAGE_TIMESTAMP); 117 List<Message> tempFirstMessage = includeFirstMessageTimestamp ? new ArrayList<>(1) : null; 118 119 for (Destination dest : destinations) { 120 DestinationStatistics stats = dest.getDestinationStatistics(); 121 if (stats != null) { 122 ActiveMQMapMessage statsMessage = new ActiveMQMapMessage(); 123 statsMessage.setString("brokerName", regionBroker.getBrokerName()); 124 statsMessage.setString("brokerId", regionBroker.getBrokerId().toString()); 125 statsMessage.setString("destinationName", dest.getActiveMQDestination().toString()); 126 statsMessage.setLong("size", stats.getMessages().getCount()); 127 statsMessage.setLong("enqueueCount", stats.getEnqueues().getCount()); 128 statsMessage.setLong("dequeueCount", stats.getDequeues().getCount()); 129 statsMessage.setLong("dispatchCount", stats.getDispatched().getCount()); 130 statsMessage.setLong("expiredCount", stats.getExpired().getCount()); 131 statsMessage.setLong("inflightCount", stats.getInflight().getCount()); 132 statsMessage.setLong("messagesCached", stats.getMessagesCached().getCount()); 133 // we are okay with the size without decimals so cast to long 134 statsMessage.setLong("averageMessageSize", (long) stats.getMessageSize().getAverageSize()); 135 statsMessage.setInt("memoryPercentUsage", dest.getMemoryUsage().getPercentUsage()); 136 statsMessage.setLong("memoryUsage", dest.getMemoryUsage().getUsage()); 137 statsMessage.setLong("memoryLimit", dest.getMemoryUsage().getLimit()); 138 statsMessage.setDouble("averageEnqueueTime", stats.getProcessTime().getAverageTime()); 139 statsMessage.setDouble("maxEnqueueTime", stats.getProcessTime().getMaxTime()); 140 statsMessage.setDouble("minEnqueueTime", stats.getProcessTime().getMinTime()); 141 statsMessage.setLong("consumerCount", stats.getConsumers().getCount()); 142 statsMessage.setLong("producerCount", stats.getProducers().getCount()); 143 if (includeFirstMessageTimestamp) { 144 if (dest instanceof Queue) { 145 ((Queue) dest).doBrowse(tempFirstMessage, 1); 146 } 147 else if (dest instanceof Topic) { 148 ((Topic) dest).doBrowse(tempFirstMessage, 1); 149 } 150 if (!tempFirstMessage.isEmpty()) { 151 Message message = tempFirstMessage.get(0); 152 // NOTICE: Client-side, you may get the broker "now" Timestamp by msg.getJMSTimestamp() 153 // This allows for calculating age. 154 statsMessage.setLong("firstMessageTimestamp", message.getBrokerInTime()); 155 tempFirstMessage.clear(); 156 } 157 } 158 statsMessage.setJMSCorrelationID(messageSend.getCorrelationId()); 159 sendStats(producerExchange.getConnectionContext(), statsMessage, replyTo); 160 } 161 } 162 if(endListMessage){ 163 ActiveMQMapMessage statsMessage = new ActiveMQMapMessage(); 164 statsMessage.setJMSCorrelationID(messageSend.getCorrelationId()); 165 sendStats(producerExchange.getConnectionContext(),statsMessage,replyTo); 166 } 167 168 } else if (subStats) { 169 sendSubStats(producerExchange.getConnectionContext(), getBrokerView().getQueueSubscribers(), replyTo); 170 sendSubStats(producerExchange.getConnectionContext(), getBrokerView().getTopicSubscribers(), replyTo); 171 } else if (brokerStats) { 172 173 if (messageSend.getProperties().containsKey(STATS_BROKER_RESET_HEADER)) { 174 getBrokerView().resetStatistics(); 175 } 176 177 ActiveMQMapMessage statsMessage = new ActiveMQMapMessage(); 178 SystemUsage systemUsage = brokerService.getSystemUsage(); 179 DestinationStatistics stats = regionBroker.getDestinationStatistics(); 180 statsMessage.setString("brokerName", regionBroker.getBrokerName()); 181 statsMessage.setString("brokerId", regionBroker.getBrokerId().toString()); 182 statsMessage.setLong("size", stats.getMessages().getCount()); 183 statsMessage.setLong("enqueueCount", stats.getEnqueues().getCount()); 184 statsMessage.setLong("dequeueCount", stats.getDequeues().getCount()); 185 statsMessage.setLong("dispatchCount", stats.getDispatched().getCount()); 186 statsMessage.setLong("expiredCount", stats.getExpired().getCount()); 187 statsMessage.setLong("inflightCount", stats.getInflight().getCount()); 188 // we are okay with the size without decimals so cast to long 189 statsMessage.setLong("averageMessageSize",(long) stats.getMessageSize().getAverageSize()); 190 statsMessage.setLong("messagesCached", stats.getMessagesCached().getCount()); 191 statsMessage.setInt("memoryPercentUsage", systemUsage.getMemoryUsage().getPercentUsage()); 192 statsMessage.setLong("memoryUsage", systemUsage.getMemoryUsage().getUsage()); 193 statsMessage.setLong("memoryLimit", systemUsage.getMemoryUsage().getLimit()); 194 statsMessage.setInt("storePercentUsage", systemUsage.getStoreUsage().getPercentUsage()); 195 statsMessage.setLong("storeUsage", systemUsage.getStoreUsage().getUsage()); 196 statsMessage.setLong("storeLimit", systemUsage.getStoreUsage().getLimit()); 197 statsMessage.setInt("tempPercentUsage", systemUsage.getTempUsage().getPercentUsage()); 198 statsMessage.setLong("tempUsage", systemUsage.getTempUsage().getUsage()); 199 statsMessage.setLong("tempLimit", systemUsage.getTempUsage().getLimit()); 200 statsMessage.setDouble("averageEnqueueTime", stats.getProcessTime().getAverageTime()); 201 statsMessage.setDouble("maxEnqueueTime", stats.getProcessTime().getMaxTime()); 202 statsMessage.setDouble("minEnqueueTime", stats.getProcessTime().getMinTime()); 203 statsMessage.setLong("consumerCount", stats.getConsumers().getCount()); 204 statsMessage.setLong("producerCount", stats.getProducers().getCount()); 205 String answer = brokerService.getTransportConnectorURIsAsMap().get("tcp"); 206 answer = answer != null ? answer : ""; 207 statsMessage.setString("openwire", answer); 208 answer = brokerService.getTransportConnectorURIsAsMap().get("stomp"); 209 answer = answer != null ? answer : ""; 210 statsMessage.setString("stomp", answer); 211 answer = brokerService.getTransportConnectorURIsAsMap().get("ssl"); 212 answer = answer != null ? answer : ""; 213 statsMessage.setString("ssl", answer); 214 answer = brokerService.getTransportConnectorURIsAsMap().get("stomp+ssl"); 215 answer = answer != null ? answer : ""; 216 statsMessage.setString("stomp+ssl", answer); 217 URI uri = brokerService.getVmConnectorURI(); 218 answer = uri != null ? uri.toString() : ""; 219 statsMessage.setString("vm", answer); 220 File file = brokerService.getDataDirectoryFile(); 221 answer = file != null ? file.getCanonicalPath() : ""; 222 statsMessage.setString("dataDirectory", answer); 223 statsMessage.setJMSCorrelationID(messageSend.getCorrelationId()); 224 sendStats(producerExchange.getConnectionContext(), statsMessage, replyTo); 225 } else { 226 super.send(producerExchange, messageSend); 227 } 228 } else { 229 super.send(producerExchange, messageSend); 230 } 231 } 232 233 BrokerViewMBean getBrokerView() throws Exception { 234 if (this.brokerView == null) { 235 ObjectName brokerName = getBrokerService().getBrokerObjectName(); 236 this.brokerView = (BrokerViewMBean) getBrokerService().getManagementContext().newProxyInstance(brokerName, 237 BrokerViewMBean.class, true); 238 } 239 return this.brokerView; 240 } 241 242 @Override 243 public void start() throws Exception { 244 super.start(); 245 LOG.info("Starting StatisticsBroker"); 246 } 247 248 @Override 249 public void stop() throws Exception { 250 super.stop(); 251 } 252 253 protected void sendSubStats(ConnectionContext context, ObjectName[] subscribers, ActiveMQDestination replyTo) throws Exception { 254 for (int i = 0; i < subscribers.length; i++) { 255 ObjectName name = subscribers[i]; 256 SubscriptionViewMBean subscriber = (SubscriptionViewMBean)getBrokerService().getManagementContext().newProxyInstance(name, SubscriptionViewMBean.class, true); 257 ActiveMQMapMessage statsMessage = prepareSubscriptionMessage(subscriber); 258 sendStats(context, statsMessage, replyTo); 259 } 260 } 261 262 protected ActiveMQMapMessage prepareSubscriptionMessage(SubscriptionViewMBean subscriber) throws JMSException { 263 Broker regionBroker = getBrokerService().getRegionBroker(); 264 ActiveMQMapMessage statsMessage = new ActiveMQMapMessage(); 265 statsMessage.setString("brokerName", regionBroker.getBrokerName()); 266 statsMessage.setString("brokerId", regionBroker.getBrokerId().toString()); 267 statsMessage.setString("destinationName", subscriber.getDestinationName()); 268 statsMessage.setString("clientId", subscriber.getClientId()); 269 statsMessage.setString("connectionId", subscriber.getConnectionId()); 270 statsMessage.setLong("sessionId", subscriber.getSessionId()); 271 statsMessage.setString("selector", subscriber.getSelector()); 272 statsMessage.setLong("enqueueCounter", subscriber.getEnqueueCounter()); 273 statsMessage.setLong("dequeueCounter", subscriber.getDequeueCounter()); 274 statsMessage.setLong("dispatchedCounter", subscriber.getDispatchedCounter()); 275 statsMessage.setLong("dispatchedQueueSize", subscriber.getDispatchedQueueSize()); 276 statsMessage.setInt("prefetchSize", subscriber.getPrefetchSize()); 277 statsMessage.setInt("maximumPendingMessageLimit", subscriber.getMaximumPendingMessageLimit()); 278 statsMessage.setBoolean("exclusive", subscriber.isExclusive()); 279 statsMessage.setBoolean("retroactive", subscriber.isRetroactive()); 280 statsMessage.setBoolean("slowConsumer", subscriber.isSlowConsumer()); 281 return statsMessage; 282 } 283 284 protected void sendStats(ConnectionContext context, ActiveMQMapMessage msg, ActiveMQDestination replyTo) 285 throws Exception { 286 msg.setPersistent(false); 287 msg.setTimestamp(System.currentTimeMillis()); 288 msg.setPriority((byte) javax.jms.Message.DEFAULT_PRIORITY); 289 msg.setType(AdvisorySupport.ADIVSORY_MESSAGE_TYPE); 290 msg.setMessageId(new MessageId(this.advisoryProducerId, this.messageIdGenerator.getNextSequenceId())); 291 msg.setDestination(replyTo); 292 msg.setResponseRequired(false); 293 msg.setProducerId(this.advisoryProducerId); 294 boolean originalFlowControl = context.isProducerFlowControl(); 295 final ProducerBrokerExchange producerExchange = new ProducerBrokerExchange(); 296 producerExchange.setConnectionContext(context); 297 producerExchange.setMutable(true); 298 producerExchange.setProducerState(new ProducerState(new ProducerInfo())); 299 try { 300 context.setProducerFlowControl(false); 301 this.next.send(producerExchange, msg); 302 } finally { 303 context.setProducerFlowControl(originalFlowControl); 304 } 305 } 306 307}