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.broker.util;
018
019import java.util.Set;
020
021import javax.annotation.PostConstruct;
022
023import org.apache.activemq.broker.BrokerPluginSupport;
024import org.apache.activemq.broker.Connection;
025import org.apache.activemq.broker.ConnectionContext;
026import org.apache.activemq.broker.ConsumerBrokerExchange;
027import org.apache.activemq.broker.ProducerBrokerExchange;
028import org.apache.activemq.broker.region.Destination;
029import org.apache.activemq.broker.region.MessageReference;
030import org.apache.activemq.broker.region.Subscription;
031import org.apache.activemq.command.*;
032import org.apache.activemq.usage.Usage;
033import org.slf4j.Logger;
034import org.slf4j.LoggerFactory;
035
036/**
037 * A simple Broker intercepter which allows you to enable/disable logging.
038 *
039 * @org.apache.xbean.XBean
040 */
041public class LoggingBrokerPlugin extends BrokerPluginSupport {
042
043    private static final Logger LOG = LoggerFactory.getLogger(LoggingBrokerPlugin.class);
044
045    private boolean logAll = false;
046    private boolean logConnectionEvents = true;
047    private boolean logSessionEvents = true;
048    private boolean logTransactionEvents = false;
049    private boolean logConsumerEvents = false;
050    private boolean logProducerEvents = false;
051    private boolean logInternalEvents = false;
052    private boolean perDestinationLogger = false;
053
054    /**
055     * JSR-250 callback wrapper; converts checked exceptions to runtime exceptions
056     *
057     * delegates to afterPropertiesSet, done to prevent backwards incompatible signature change
058     */
059    @PostConstruct
060    private void postConstruct() {
061        try {
062            afterPropertiesSet();
063        } catch (Exception ex) {
064            throw new RuntimeException(ex);
065        }
066    }
067
068    /**
069     * @throws Exception
070     * @org.apache.xbean.InitMethod
071     */
072    public void afterPropertiesSet() throws Exception {
073        LOG.info("Created LoggingBrokerPlugin: {}", this.toString());
074    }
075
076    public boolean isLogAll() {
077        return logAll;
078    }
079
080    /**
081     * Logger all Events that go through the Plugin
082     */
083    public void setLogAll(boolean logAll) {
084        this.logAll = logAll;
085    }
086
087
088    public boolean isLogConnectionEvents() {
089        return logConnectionEvents;
090    }
091
092    /**
093     * Logger Events that are related to connections
094     */
095    public void setLogConnectionEvents(boolean logConnectionEvents) {
096        this.logConnectionEvents = logConnectionEvents;
097    }
098
099    public boolean isLogSessionEvents() {
100        return logSessionEvents;
101    }
102
103    /**
104     * Logger Events that are related to sessions
105     */
106    public void setLogSessionEvents(boolean logSessionEvents) {
107        this.logSessionEvents = logSessionEvents;
108    }
109
110    public boolean isLogTransactionEvents() {
111        return logTransactionEvents;
112    }
113
114    /**
115     * Logger Events that are related to transaction processing
116     */
117    public void setLogTransactionEvents(boolean logTransactionEvents) {
118        this.logTransactionEvents = logTransactionEvents;
119    }
120
121    public boolean isLogConsumerEvents() {
122        return logConsumerEvents;
123    }
124
125    /**
126     * Logger Events that are related to Consumers
127     */
128    public void setLogConsumerEvents(boolean logConsumerEvents) {
129        this.logConsumerEvents = logConsumerEvents;
130    }
131
132    public boolean isLogProducerEvents() {
133        return logProducerEvents;
134    }
135
136    /**
137     * Logger Events that are related to Producers
138     */
139    public void setLogProducerEvents(boolean logProducerEvents) {
140        this.logProducerEvents = logProducerEvents;
141    }
142
143    public boolean isLogInternalEvents() {
144        return logInternalEvents;
145    }
146
147    /**
148     * Logger Events that are normally internal to the broker
149     */
150    public void setLogInternalEvents(boolean logInternalEvents) {
151        this.logInternalEvents = logInternalEvents;
152    }
153
154    @Override
155    public void acknowledge(ConsumerBrokerExchange consumerExchange, MessageAck ack) throws Exception {
156        if (isLogAll() || isLogConsumerEvents()) {
157            LOG.info("Acknowledging message for client ID: {}{}", consumerExchange.getConnectionContext().getClientId(), (ack.getMessageCount() == 1 ? ", " + ack.getLastMessageId() : ""));
158            if (ack.getMessageCount() > 1) {
159                LOG.trace("Message count: {}, First Message Id: {}, Last Message Id: {}",
160                        ack.getMessageCount(), ack.getFirstMessageId(), ack.getLastMessageId());
161            }
162        }
163        super.acknowledge(consumerExchange, ack);
164    }
165
166    @Override
167    public Response messagePull(ConnectionContext context, MessagePull pull) throws Exception {
168        if (isLogAll() || isLogConsumerEvents()) {
169            LOG.info("Message Pull from: {} on {}", context.getClientId(), pull.getDestination().getPhysicalName());
170        }
171        return super.messagePull(context, pull);
172    }
173
174    @Override
175    public void addConnection(ConnectionContext context, ConnectionInfo info) throws Exception {
176        if (isLogAll() || isLogConnectionEvents()) {
177            LOG.info("Adding Connection: {}", info);
178        }
179        super.addConnection(context, info);
180    }
181
182    @Override
183    public Subscription addConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
184        if (isLogAll() || isLogConsumerEvents()) {
185            LOG.info("Adding Consumer: {}", info);
186        }
187        return super.addConsumer(context, info);
188    }
189
190    @Override
191    public void addProducer(ConnectionContext context, ProducerInfo info) throws Exception {
192        if (isLogAll() || isLogProducerEvents()) {
193            LOG.info("Adding Producer: {}", info);
194        }
195        super.addProducer(context, info);
196    }
197
198    @Override
199    public void commitTransaction(ConnectionContext context, TransactionId xid, boolean onePhase) throws Exception {
200        if (isLogAll() || isLogTransactionEvents()) {
201            LOG.info("Committing transaction: {}", xid.getTransactionKey());
202        }
203        super.commitTransaction(context, xid, onePhase);
204    }
205
206    @Override
207    public void removeSubscription(ConnectionContext context, RemoveSubscriptionInfo info) throws Exception {
208        if (isLogAll() || isLogConsumerEvents()) {
209            LOG.info("Removing subscription: {}", info);
210        }
211        super.removeSubscription(context, info);
212    }
213
214    @Override
215    public TransactionId[] getPreparedTransactions(ConnectionContext context) throws Exception {
216
217        TransactionId[] result = super.getPreparedTransactions(context);
218        if ((isLogAll() || isLogTransactionEvents()) && result != null) {
219            StringBuffer tids = new StringBuffer();
220            for (TransactionId tid : result) {
221                if (tids.length() > 0) {
222                    tids.append(", ");
223                }
224                tids.append(tid.getTransactionKey());
225            }
226            LOG.info("Prepared transactions: {}", tids);
227        }
228        return result;
229    }
230
231    @Override
232    public int prepareTransaction(ConnectionContext context, TransactionId xid) throws Exception {
233        if (isLogAll() || isLogTransactionEvents()) {
234            LOG.info("Preparing transaction: {}", xid.getTransactionKey());
235        }
236        return super.prepareTransaction(context, xid);
237    }
238
239    @Override
240    public void removeConnection(ConnectionContext context, ConnectionInfo info, Throwable error) throws Exception {
241        if (isLogAll() || isLogConnectionEvents()) {
242            LOG.info("Removing Connection: {}", info);
243        }
244        super.removeConnection(context, info, error);
245    }
246
247    @Override
248    public void removeConsumer(ConnectionContext context, ConsumerInfo info) throws Exception {
249        if (isLogAll() || isLogConsumerEvents()) {
250            LOG.info("Removing Consumer: {}", info);
251        }
252        super.removeConsumer(context, info);
253    }
254
255    @Override
256    public void removeProducer(ConnectionContext context, ProducerInfo info) throws Exception {
257        if (isLogAll() || isLogProducerEvents()) {
258            LOG.info("Removing Producer: {}", info);
259        }
260        super.removeProducer(context, info);
261    }
262
263    @Override
264    public void rollbackTransaction(ConnectionContext context, TransactionId xid) throws Exception {
265        if (isLogAll() || isLogTransactionEvents()) {
266            LOG.info("Rolling back Transaction: {}", xid.getTransactionKey());
267        }
268        super.rollbackTransaction(context, xid);
269    }
270
271    @Override
272    public void send(ProducerBrokerExchange producerExchange, Message messageSend) throws Exception {
273        if (isLogAll() || isLogProducerEvents()) {
274            logSend(messageSend.copy());
275        }
276        super.send(producerExchange, messageSend);
277    }
278
279    private void logSend(Message copy) {
280        copy.getSize();
281        Logger perDestinationsLogger = LOG;
282        if (isPerDestinationLogger()) {
283            ActiveMQDestination destination = copy.getDestination();
284            perDestinationsLogger = LoggerFactory.getLogger(LOG.getName() +
285                    "." + destination.getDestinationTypeAsString() + "." + destination.getPhysicalName());
286        }
287        perDestinationsLogger.info("Sending message: {}", copy);
288    }
289
290    @Override
291    public void beginTransaction(ConnectionContext context, TransactionId xid) throws Exception {
292        if (isLogAll() || isLogTransactionEvents()) {
293            LOG.info("Beginning transaction: {}", xid.getTransactionKey());
294        }
295        super.beginTransaction(context, xid);
296    }
297
298    @Override
299    public void forgetTransaction(ConnectionContext context, TransactionId transactionId) throws Exception {
300        if (isLogAll() || isLogTransactionEvents()) {
301            LOG.info("Forgetting transaction: {}", transactionId.getTransactionKey());
302        }
303        super.forgetTransaction(context, transactionId);
304    }
305
306    @Override
307    public Connection[] getClients() throws Exception {
308        Connection[] result = super.getClients();
309
310        if (isLogAll() || isLogInternalEvents()) {
311            if (result == null) {
312                LOG.info("Get Clients returned empty list.");
313            } else {
314                StringBuffer cids = new StringBuffer();
315                for (Connection c : result) {
316                    cids.append(cids.length() > 0 ? ", " : "");
317                    cids.append(c.getConnectionId());
318                }
319                LOG.info("Connected clients: {}", cids);
320            }
321        }
322        return super.getClients();
323    }
324
325    @Override
326    public org.apache.activemq.broker.region.Destination addDestination(ConnectionContext context,
327            ActiveMQDestination destination, boolean create) throws Exception {
328        if (isLogAll() || isLogInternalEvents()) {
329            LOG.info("Adding destination: {}:{}", destination.getDestinationTypeAsString(), destination.getPhysicalName());
330        }
331        return super.addDestination(context, destination, create);
332    }
333
334    @Override
335    public void removeDestination(ConnectionContext context, ActiveMQDestination destination, long timeout)
336            throws Exception {
337        if (isLogAll() || isLogInternalEvents()) {
338            LOG.info("Removing destination: {}:{}", destination.getDestinationTypeAsString(), destination.getPhysicalName());
339        }
340        super.removeDestination(context, destination, timeout);
341    }
342
343    @Override
344    public ActiveMQDestination[] getDestinations() throws Exception {
345        ActiveMQDestination[] result = super.getDestinations();
346        if (isLogAll() || isLogInternalEvents()) {
347            if (result == null) {
348                LOG.info("Get Destinations returned empty list.");
349            } else {
350                StringBuffer destinations = new StringBuffer();
351                for (ActiveMQDestination dest : result) {
352                    destinations.append(destinations.length() > 0 ? ", " : "");
353                    destinations.append(dest.getPhysicalName());
354                }
355                LOG.info("Get Destinations: {}", destinations);
356            }
357        }
358        return result;
359    }
360
361    @Override
362    public void start() throws Exception {
363        if (isLogAll() || isLogInternalEvents()) {
364            LOG.info("Starting {}", getBrokerName());
365        }
366        super.start();
367    }
368
369    @Override
370    public void stop() throws Exception {
371        if (isLogAll() || isLogInternalEvents()) {
372            LOG.info("Stopping {}", getBrokerName());
373        }
374        super.stop();
375    }
376
377    @Override
378    public void addSession(ConnectionContext context, SessionInfo info) throws Exception {
379        if (isLogAll() || isLogSessionEvents()) {
380            LOG.info("Adding Session: {}", info);
381        }
382        super.addSession(context, info);
383    }
384
385    @Override
386    public void removeSession(ConnectionContext context, SessionInfo info) throws Exception {
387        if (isLogAll() || isLogSessionEvents()) {
388            LOG.info("Removing Session: {}", info);
389        }
390        super.removeSession(context, info);
391    }
392
393    @Override
394    public void addBroker(Connection connection, BrokerInfo info) {
395        if (isLogAll() || isLogInternalEvents()) {
396            LOG.info("Adding Broker {}", info.getBrokerName());
397        }
398        super.addBroker(connection, info);
399    }
400
401    @Override
402    public void removeBroker(Connection connection, BrokerInfo info) {
403        if (isLogAll() || isLogInternalEvents()) {
404            LOG.info("Removing Broker {}", info.getBrokerName());
405        }
406        super.removeBroker(connection, info);
407    }
408
409    @Override
410    public BrokerInfo[] getPeerBrokerInfos() {
411        BrokerInfo[] result = super.getPeerBrokerInfos();
412        if (isLogAll() || isLogInternalEvents()) {
413            if (result == null) {
414                LOG.info("Get Peer Broker Infos returned empty list.");
415            } else {
416                StringBuffer peers = new StringBuffer();
417                for (BrokerInfo bi : result) {
418                    peers.append(peers.length() > 0 ? ", " : "");
419                    peers.append(bi.getBrokerName());
420                }
421                LOG.info("Get Peer Broker Infos: {}", peers);
422            }
423        }
424        return result;
425    }
426
427    @Override
428    public void preProcessDispatch(MessageDispatch messageDispatch) {
429        if (isLogAll() || isLogInternalEvents() || isLogConsumerEvents()) {
430            LOG.info("preProcessDispatch: {}", messageDispatch);
431        }
432        super.preProcessDispatch(messageDispatch);
433    }
434
435    @Override
436    public void postProcessDispatch(MessageDispatch messageDispatch) {
437        if (isLogAll() || isLogInternalEvents() || isLogConsumerEvents()) {
438            LOG.info("postProcessDispatch: {}", messageDispatch);
439        }
440        super.postProcessDispatch(messageDispatch);
441    }
442
443    @Override
444    public void processDispatchNotification(MessageDispatchNotification messageDispatchNotification) throws Exception {
445        if (isLogAll() || isLogInternalEvents() || isLogConsumerEvents()) {
446            LOG.info("ProcessDispatchNotification: {}", messageDispatchNotification);
447        }
448        super.processDispatchNotification(messageDispatchNotification);
449    }
450
451    @Override
452    public Set<ActiveMQDestination> getDurableDestinations() {
453        Set<ActiveMQDestination> result = super.getDurableDestinations();
454        if (isLogAll() || isLogInternalEvents()) {
455            if (result == null) {
456                LOG.info("Get Durable Destinations returned empty list.");
457            } else {
458                StringBuffer destinations = new StringBuffer();
459                for (ActiveMQDestination dest : result) {
460                    destinations.append(destinations.length() > 0 ? ", " : "");
461                    destinations.append(dest.getPhysicalName());
462                }
463                LOG.info("Get Durable Destinations: {}", destinations);
464            }
465        }
466        return result;
467    }
468
469    @Override
470    public void addDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception {
471        if (isLogAll() || isLogInternalEvents()) {
472            LOG.info("Adding destination info: {}", info);
473        }
474        super.addDestinationInfo(context, info);
475    }
476
477    @Override
478    public void removeDestinationInfo(ConnectionContext context, DestinationInfo info) throws Exception {
479        if (isLogAll() || isLogInternalEvents()) {
480            LOG.info("Removing destination info: {}", info);
481        }
482        super.removeDestinationInfo(context, info);
483    }
484
485    @Override
486    public void messageExpired(ConnectionContext context, MessageReference message, Subscription subscription) {
487        if (isLogAll() || isLogInternalEvents()) {
488            String msg = "Unable to display message.";
489
490            msg = message.getMessage().toString();
491
492            LOG.info("Message has expired: {}", msg);
493        }
494        super.messageExpired(context, message, subscription);
495    }
496
497    @Override
498    public boolean sendToDeadLetterQueue(ConnectionContext context, MessageReference messageReference,
499                                         Subscription subscription, Throwable poisonCause) {
500        if (isLogAll() || isLogInternalEvents()) {
501            String msg = "Unable to display message.";
502
503            msg = messageReference.getMessage().toString();
504
505            LOG.info("Sending to DLQ: {}", msg);
506        }
507        return super.sendToDeadLetterQueue(context, messageReference, subscription, poisonCause);
508    }
509
510    @Override
511    public void fastProducer(ConnectionContext context, ProducerInfo producerInfo,ActiveMQDestination destination) {
512        if (isLogAll() || isLogProducerEvents() || isLogInternalEvents()) {
513            LOG.info("Fast Producer: {}", producerInfo);
514        }
515        super.fastProducer(context, producerInfo, destination);
516    }
517
518    @Override
519    public void isFull(ConnectionContext context, Destination destination, Usage<?> usage) {
520        if (isLogAll() || isLogProducerEvents() || isLogInternalEvents()) {
521            LOG.info("Destination is full: {}", destination.getName());
522        }
523        super.isFull(context, destination, usage);
524    }
525
526    @Override
527    public void messageConsumed(ConnectionContext context, MessageReference messageReference) {
528        if (isLogAll() || isLogConsumerEvents() || isLogInternalEvents()) {
529            String msg = "Unable to display message.";
530
531            msg = messageReference.getMessage().toString();
532
533            LOG.info("Message consumed: {}", msg);
534        }
535        super.messageConsumed(context, messageReference);
536    }
537
538    @Override
539    public void messageDelivered(ConnectionContext context, MessageReference messageReference) {
540        if (isLogAll() || isLogConsumerEvents() || isLogInternalEvents()) {
541            String msg = "Unable to display message.";
542
543            msg = messageReference.getMessage().toString();
544
545            LOG.info("Message delivered: {}", msg);
546        }
547        super.messageDelivered(context, messageReference);
548    }
549
550    @Override
551    public void messageDiscarded(ConnectionContext context, Subscription sub, MessageReference messageReference) {
552        if (isLogAll() || isLogInternalEvents()) {
553            String msg = "Unable to display message.";
554
555            msg = messageReference.getMessage().toString();
556
557            LOG.info("Message discarded: {}", msg);
558        }
559        super.messageDiscarded(context, sub, messageReference);
560    }
561
562    @Override
563    public void slowConsumer(ConnectionContext context, Destination destination, Subscription subs) {
564        if (isLogAll() || isLogConsumerEvents() || isLogInternalEvents()) {
565            LOG.info("Detected slow consumer on {}", destination.getName());
566            StringBuffer buf = new StringBuffer("Connection(");
567            buf.append(subs.getConsumerInfo().getConsumerId().getConnectionId());
568            buf.append(") Session(");
569            buf.append(subs.getConsumerInfo().getConsumerId().getSessionId());
570            buf.append(")");
571            LOG.info(buf.toString());
572        }
573        super.slowConsumer(context, destination, subs);
574    }
575
576    @Override
577    public void nowMasterBroker() {
578        if (isLogAll() || isLogInternalEvents()) {
579            LOG.info("Is now the master broker: {}", getBrokerName());
580        }
581        super.nowMasterBroker();
582    }
583
584    @Override
585    public String toString() {
586        StringBuffer buf = new StringBuffer();
587        buf.append("LoggingBrokerPlugin(");
588        buf.append("logAll=");
589        buf.append(isLogAll());
590        buf.append(", logConnectionEvents=");
591        buf.append(isLogConnectionEvents());
592        buf.append(", logSessionEvents=");
593        buf.append(isLogSessionEvents());
594        buf.append(", logConsumerEvents=");
595        buf.append(isLogConsumerEvents());
596        buf.append(", logProducerEvents=");
597        buf.append(isLogProducerEvents());
598        buf.append(", logTransactionEvents=");
599        buf.append(isLogTransactionEvents());
600        buf.append(", logInternalEvents=");
601        buf.append(isLogInternalEvents());
602        buf.append(")");
603        return buf.toString();
604    }
605
606    public void setPerDestinationLogger(boolean perDestinationLogger) {
607        this.perDestinationLogger = perDestinationLogger;
608    }
609
610    public boolean isPerDestinationLogger() {
611        return perDestinationLogger;
612    }
613}