org.apache.cassandra.diag.DiagnosticEventService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cassandra-all Show documentation
Show all versions of cassandra-all Show documentation
The Apache Cassandra Project develops a highly scalable second-generation distributed database, bringing together Dynamo's fully distributed design and Bigtable's ColumnFamily-based data model.
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cassandra.diag;
import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Iterables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.utils.MBeanWrapper;
/**
* Service for publishing and consuming {@link DiagnosticEvent}s.
*/
public final class DiagnosticEventService implements DiagnosticEventServiceMBean
{
private static final Logger logger = LoggerFactory.getLogger(DiagnosticEventService.class);
// Subscribers interested in consuming all kind of events
private ImmutableSet> subscribersAll = ImmutableSet.of();
// Subscribers for particular event class, e.g. BootstrapEvent
private ImmutableSetMultimap, Consumer> subscribersByClass = ImmutableSetMultimap.of();
// Subscribers for event class and type, e.g. BootstrapEvent#TOKENS_ALLOCATED
private ImmutableMap, Consumer>> subscribersByClassAndType = ImmutableMap.of();
private static final DiagnosticEventService instance = new DiagnosticEventService();
private DiagnosticEventService()
{
MBeanWrapper.instance.registerMBean(this,"org.apache.cassandra.diag:type=DiagnosticEventService");
// register broadcasters for JMX events
DiagnosticEventPersistence.start();
}
/**
* Makes provided event available to all subscribers.
*/
public void publish(DiagnosticEvent event)
{
if (!DatabaseDescriptor.diagnosticEventsEnabled())
return;
logger.trace("Publishing: {}={}", event.getClass().getName(), event.toMap());
// event class + type
ImmutableMultimap, Consumer> consumersByType = subscribersByClassAndType.get(event.getClass());
if (consumersByType != null)
{
ImmutableCollection> consumers = consumersByType.get(event.getType());
if (consumers != null)
{
for (Consumer consumer : consumers)
consumer.accept(event);
}
}
// event class
Set> consumersByEvents = subscribersByClass.get(event.getClass());
if (consumersByEvents != null)
{
for (Consumer consumer : consumersByEvents)
consumer.accept(event);
}
// all events
for (Consumer consumer : subscribersAll)
consumer.accept(event);
}
/**
* Registers event handler for specified class of events.
* @param event DiagnosticEvent class implementation
* @param consumer Consumer for received events
*/
public synchronized void subscribe(Class event, Consumer consumer)
{
logger.debug("Adding subscriber: {}", consumer);
subscribersByClass = ImmutableSetMultimap., Consumer>builder()
.putAll(subscribersByClass)
.put(event, new TypedConsumerWrapper<>(consumer))
.build();
logger.debug("Total subscribers: {}", subscribersByClass.values().size());
}
/**
* Registers event handler for specified class of events.
* @param event DiagnosticEvent class implementation
* @param consumer Consumer for received events
*/
public synchronized > void subscribe(Class event,
T eventType,
Consumer consumer)
{
ImmutableSetMultimap.Builder, Consumer> byTypeBuilder = ImmutableSetMultimap.builder();
if (subscribersByClassAndType.containsKey(event))
byTypeBuilder.putAll(subscribersByClassAndType.get(event));
byTypeBuilder.put(eventType, new TypedConsumerWrapper<>(consumer));
ImmutableMap.Builder, Consumer>> byClassBuilder = ImmutableMap.builder();
for (Class clazz : subscribersByClassAndType.keySet())
{
if (!clazz.equals(event))
byClassBuilder.put(clazz, subscribersByClassAndType.get(clazz));
}
subscribersByClassAndType = byClassBuilder
.put(event, byTypeBuilder.build())
.build();
}
/**
* Registers event handler for all DiagnosticEvents published from this point.
* @param consumer Consumer for received events
*/
public synchronized void subscribeAll(Consumer consumer)
{
subscribersAll = ImmutableSet.>builder()
.addAll(subscribersAll)
.add(consumer)
.build();
}
/**
* De-registers event handler from receiving any further events.
* @param consumer Consumer registered for receiving events
*/
public synchronized void unsubscribe(Consumer consumer)
{
unsubscribe(null, consumer);
}
/**
* De-registers event handler from receiving any further events.
* @param event DiagnosticEvent class to unsubscribe from
* @param consumer Consumer registered for receiving events
*/
public synchronized void unsubscribe(@Nullable Class event, Consumer consumer)
{
// all events
subscribersAll = ImmutableSet.copyOf(Iterables.filter(subscribersAll, (c) -> c != consumer));
// event class
ImmutableSetMultimap.Builder, Consumer> byClassBuilder = ImmutableSetMultimap.builder();
Collection, Consumer>> entries = subscribersByClass.entries();
for (Map.Entry, Consumer> entry : entries)
{
Consumer subscriber = entry.getValue();
if (subscriber instanceof TypedConsumerWrapper)
subscriber = ((TypedConsumerWrapper)subscriber).wrapped;
// other consumers or other events
if (subscriber != consumer || (event != null && !entry.getKey().equals(event)))
{
byClassBuilder = byClassBuilder.put(entry);
}
}
subscribersByClass = byClassBuilder.build();
// event class + type
ImmutableMap.Builder, Consumer>> byClassAndTypeBuilder = ImmutableMap.builder();
for (Map.Entry, Consumer>> byClassEntry : subscribersByClassAndType.entrySet())
{
ImmutableSetMultimap.Builder, Consumer> byTypeBuilder = ImmutableSetMultimap.builder();
ImmutableSetMultimap, Consumer> byTypeConsumers = byClassEntry.getValue();
Iterables.filter(byTypeConsumers.entries(), (e) ->
{
if (e == null || e.getValue() == null) return false;
Consumer subscriber = e.getValue();
if (subscriber instanceof TypedConsumerWrapper)
subscriber = ((TypedConsumerWrapper) subscriber).wrapped;
return subscriber != consumer || (event != null && !byClassEntry.getKey().equals(event));
}).forEach(byTypeBuilder::put);
ImmutableSetMultimap, Consumer> byType = byTypeBuilder.build();
if (!byType.isEmpty())
byClassAndTypeBuilder.put(byClassEntry.getKey(), byType);
}
subscribersByClassAndType = byClassAndTypeBuilder.build();
}
/**
* Indicates if any {@link Consumer} has been registered for the specified class of events.
* @param event DiagnosticEvent class implementation
*/
public boolean hasSubscribers(Class event)
{
return !subscribersAll.isEmpty() || subscribersByClass.containsKey(event) || subscribersByClassAndType.containsKey(event);
}
/**
* Indicates if any {@link Consumer} has been registered for the specified class of events.
* @param event DiagnosticEvent class implementation
* @param eventType Subscribed event type matched against {@link DiagnosticEvent#getType()}
*/
public > boolean hasSubscribers(Class event, T eventType)
{
if (!subscribersAll.isEmpty())
return true;
ImmutableSet> subscribers = subscribersByClass.get(event);
if (subscribers != null && !subscribers.isEmpty())
return true;
ImmutableSetMultimap, Consumer> byType = subscribersByClassAndType.get(event);
if (byType == null || byType.isEmpty()) return false;
Set> consumers = byType.get(eventType);
return consumers != null && !consumers.isEmpty();
}
/**
* Indicates if events are enabled for specified event class based on {@link DatabaseDescriptor#diagnosticEventsEnabled()}
* and {@link #hasSubscribers(Class)}.
* @param event DiagnosticEvent class implementation
*/
public boolean isEnabled(Class event)
{
return DatabaseDescriptor.diagnosticEventsEnabled() && hasSubscribers(event);
}
/**
* Indicates if events are enabled for specified event class based on {@link DatabaseDescriptor#diagnosticEventsEnabled()}
* and {@link #hasSubscribers(Class, Enum)}.
* @param event DiagnosticEvent class implementation
* @param eventType Subscribed event type matched against {@link DiagnosticEvent#getType()}
*/
public > boolean isEnabled(Class event, T eventType)
{
return DatabaseDescriptor.diagnosticEventsEnabled() && hasSubscribers(event, eventType);
}
public static DiagnosticEventService instance()
{
return instance;
}
/**
* Removes all active subscribers. Should only be called from testing.
*/
public synchronized void cleanup()
{
subscribersByClass = ImmutableSetMultimap.of();
subscribersAll = ImmutableSet.of();
subscribersByClassAndType = ImmutableMap.of();
}
public boolean isDiagnosticsEnabled()
{
return DatabaseDescriptor.diagnosticEventsEnabled();
}
public void disableDiagnostics()
{
DatabaseDescriptor.setDiagnosticEventsEnabled(false);
}
public SortedMap> readEvents(String eventClazz, Long lastKey, int limit)
{
return DiagnosticEventPersistence.instance().getEvents(eventClazz, lastKey, limit, false);
}
public void enableEventPersistence(String eventClazz)
{
DiagnosticEventPersistence.instance().enableEventPersistence(eventClazz);
}
public void disableEventPersistence(String eventClazz)
{
DiagnosticEventPersistence.instance().disableEventPersistence(eventClazz);
}
/**
* Wrapper class for supporting typed event handling for consumers.
*/
private static class TypedConsumerWrapper implements Consumer
{
private final Consumer wrapped;
private TypedConsumerWrapper(Consumer wrapped)
{
this.wrapped = wrapped;
}
public void accept(DiagnosticEvent e)
{
wrapped.accept((E)e);
}
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TypedConsumerWrapper> that = (TypedConsumerWrapper>) o;
return Objects.equals(wrapped, that.wrapped);
}
public int hashCode()
{
return Objects.hash(wrapped);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy