Contained WithinFind More DocumentationFeatured Support Resources | Download this book in PDF (949 KB)
Chapter 5 Adding Monitoring CapabilitiesMonitoring is the process of reviewing the statistics of a system to improve performance or solve problems. By monitoring the state of components and services that are deployed in the Enterprise Server, system administrators can identify performance bottlenecks, predict failures, perform root cause analysis, and ensure that everything is functioning as expected. Monitoring data can also be useful in performance tuning and capacity planning. An add-on component typically generates statistics that the Enterprise Server can gather at run time. Adding monitoring capabilities enables an add-on component to provide statistics to Enterprise Server in the same way as components that are supplied in Enterprise Server distributions. As a result, system administrators can use the same administrative interfaces to monitor statistics from any installed Enterprise Server component, regardless of the origin of the component. The following topics are addressed here: Defining Statistics That Are to Be MonitoredAt runtime, your add-on component might perform operations that affect the behavior and performance of your system. For example, your component might start a thread of control, receive a request from a service, or request a connection from a connection pool. Monitoring the statistics that are related to these operations helps a system administrator maintain the system. To provide statistics to Enterprise Server, your component must define events for the operations that generate these statistics. At runtime, your component must send these events when performing the operations for which the events are defined. For example, to enable the number of received requests to be monitored, a component must send a “request received” event each time that the component receives a request. A statistic can correspond to single event or to multiple events.
Defining statistics that are to be monitored involves the following tasks: Defining an Event ProviderAn event provider defines the types of events for the operations that generate statistics for an add-on component. To define an event provider, write a JavaTM language interface that defines the types of events for the component. In the interface, define one method for each type of event that is related to the component. Note – You are not required to implement the event provider interface. After you register the event provider, Enterprise Server generates the implementation class at runtime for you. For more information, see Registering an Event Provider. Identifying the Event TypeIf you overload a method in your implementation, annotate each form of the method with the @org.glassfish.flashlight.provider.annotations.ProbeName annotation to uniquely identify the event type. Set the value element of the @ProbeName annotation to the name of the event type. Note – If you do not annotate a method, the name of the event type is the method name. Therefore, you are not required to annotate methods that are not overloaded. Specifying Event ParametersTo enable methods in an event listener to select a subset of values, annotate each parameter in the method signature with the org.glassfish.flashlight.provider.annotations.ProbeParam annotation. Set the value element of the @ProbeParam annotation to the name of the parameter. Example of Defining an Event ProviderExample 5–1 Defining an Event ProviderThis example shows the definition of the TxManager interface. This interface defines events for the start and end of transactions that are performed by a transaction manager. The methods in this interface are as follows:
import org.glassfish.flashlight.provider.annotations.ProbeName;
import org.glassfish.flashlight.provider.annotations.ProbeParam;
public interface TxManager {
@ProbeName("begin")
public void onTxBegin(
@ProbeParam("{txId}") String txId
);
public void onCompletion(
@ProbeParam("{outcome}") boolean outcome
);
}
Registering an Event ProviderRegistering an event provider generates a class that implements the event provider interface. Enterprise Server provides the org.glassfish.flashlight.provider.ProbeProviderFactory factory class that generates the event provider class at runtime. To generate the class, Enterprise Server uses the ASM framework for manipulating and analyzing Java byte codes. By default, a nonoperational implementation of the methods is created. If monitoring is not enabled, which means that no listeners are registered, the methods do not consume any computing resources, such as memory or processor cycles. Note – The ProbeProviderFactory.getProbeProvider method is an unstable interface and is subject to change. To register an event provider, invoke the ProbeProviderFactory.getProbeProvider method in the class that represents your add-on component. In the invocation of the ProbeProviderFactory.getProbeProvider method, pass the following information as parameters to the method:
Example 5–2 Registering an Event ProviderThis example shows the code for registering the event provider interface TxManager for the add-on component that is represented by the class TransactionManagerImpl. The definition of the TxManager interface is shown in Example 5–1. The component name is tx and the provider name is TxManager. No application name is specified. ...
import org.glassfish.flashlight.provider.ProbeProviderFactory;
...
public class TransactionManagerImpl {
...
@Inject
protected ProbeProviderFactory probeProviderFactory;
...
TxManager txProvider = probeProviderFactory.getProbeProvider(
"tx", "TxManager", null, TxManager.class);
...
}
Sending an EventAt runtime, your add-on component might perform an operation that generates statistics. To provide statistics about the operation to Enterprise Server, your component must send an event of the correct type when performing the operation. To send an event, invoke the method of your event provider class that is defined for the type of the event. Ensure that the method is invoked when your component performs the operation for which the event was defined. One way to meet this requirement is to invoke the method for sending the event in the body of the method for performing the operation. Example 5–3 Sending an EventThis example shows the code for invoking the onTxBegin method to send an event of type begin. This event indicates that a component is about to begin a transaction. To ensure that the event is sent at the correct time, the onTxBegin method is invoked in the body of the begin method, which starts a transaction. The declaration of the onTxBegin method in the event provider interface is shown in Example 5–1. The creation of the txProvider object is shown in Example 5–2. ...
public class TransactionManagerImpl {
...
public void begin() {
String txId = createTransactionId();
....
txProvider.onTxBegin(txId); //emit
}
...
}
Updating the Monitorable Object TreeA monitorable object is a component, subcomponent, or service that can be monitored. Enterprise Server uses a tree structure to track monitorable objects. Because the tree is dynamic, the tree changes as components of the Enterprise Server instance are added, modified, or removed. Objects are also added to or removed from the tree in response to configuration changes. For example, if monitoring for a component is turned off, the component's monitorable object is removed from the tree. To enable system administrators to access statistics for all components in the same way, you must provide statistics for an add-on component by updating the monitorable object tree. Statistics for the add-on component are then available through the Enterprise Server administrative commands get(1), list(1), and set(1). These commands locate an object in the tree through the object's dotted name. For more information about the tree structure of monitorable objects, see How the Monitoring Tree Structure Works in Sun GlassFish Enterprise Server v3 Prelude Administration Guide. To make an add-on component a monitorable object, you must add the add-on component to the monitorable object tree. To update the statistics for an add-on component, you must add the statistics to the monitorable object tree, and create event listeners to gather statistics from events that represent these statistics. At runtime, these listeners must update monitorable objects with statistics that these events contain. The events are sent by event provider classes. For information about how to create event provider classes and send events, see Defining Statistics That Are to Be Monitored. Updating the monitorable object tree involves the following tasks: Creating Event ListenersAn event listener gathers statistics from events that an event provider sends. To enable an add-on component to gather statistics from events, create listeners to receive events from the event provider. The listener can receive events from the add-on component in which the listener is created and from other components. To create an event listener, write a Java class to represent the listener. The listener can be any Java object. Ensure that the class that you write meets these requirements:
A listener is called in the same thread as the event method. As a result, the listener can use thread locals. If the monitored system allows access to thread locals, the listener can access thread locals of the monitored system. Note – A listener that is not registered to listen for events is never called by the framework. Therefore, unregistered listeners do not consume any computing resources, such as memory or processor cycles. Subscribing to Events From Event Provider ClassesTo receive events from event provider classes, a listener must subscribe to the events. Subscribing to events also specifies the provider and the type of events that the listener will receive. To subscribe to events from event provider classes, write one method in your listener class to process each type of event. To specify the provider and the type of event, annotate the method with the org.glassfish.flashlight.client.ProbeListener annotation. In the @ProbeListener annotation, specify the provider and the type as follows: "component-name:provider-name:app-name:event-type" Note – The @ProbeListener annotation is an unstable interface and is subject to change.
In the method body, provide the code to update monitoring statistics in response to the event. Example 5–4 Subscribing to Events From Event Provider ClassesThis example shows the code for subscribing to events of type begin from the tx component. The provider of the component is TxManager. The body of the begin method contains code to increase the transaction count txcount by 1 each time that an event is received. The definition of the begin event type is shown in Example 5–1. The code for sending begin events is shown in Example 5–3. ...
import org.glassfish.flashlight.client.ProbeListener;
...
public class TxListener {
AtomicInteger txCount = ....;
@ProbeListner("tx:TxManager::begin")
public void begin(String txId) {
txCount++;
}
}
Registering an Event ListenerRegistering an event listener enables the listener to receive callbacks from the Enterprise Server event infrastructure. The listener can then collect data from events and update monitorable objects in the object tree. These monitorable objects form the basis for monitoring statistics. At runtime, the Enterprise Server event infrastructure registers listeners for an event provider when the event provider is started and unregisters them when the event provider is shut down. As a result, listeners have no dependencies on other components. To register a listener, invoke the org.glassfish.flashlight.client.ProbeClientMediator.registerListener method in the class that represents your add-on component. In the method invocation, pass the listener object as a parameter. The registerListener method returns a collection of ProbeClientMethodHandle objects. To enable a listener to turn on or turn off monitoring, pass this collection to your listener class. In your listener class, invoke methods of the ProbeClientMethodHandle objects to turn on and turn off monitoring.
For information about how to register an event provider, see Registering an Event Provider. Example 5–5 Registering an Event ListenerThis example shows the code for registering the event listener TxListener for the add-on component that is represented by the class TransactionManagerImpl. Code for the following methods of the TxListener class is beyond the scope of this example:
...
import org.glassfish.flashlight.client.ProbeClientMediator;
...
public class TransactionManagerImpl {
@Inject
private ProbeClientMediator pcm;
...
TxListener txL = new TxListener(txmNode);
Collection<ProbeClientMethodHandle> handles = pcm.registerListener(txL);
txL.setProbeListenerHandles(handles);
...
}
Adding Statistics for a Component to the Monitorable Object TreeAdding statistics for a component to the monitorable object tree makes the component and its statistics monitorable objects. Adding a statistics for a component to the monitorable object tree involves the following tasks:
Creating a TreeNode Object to Represent a ComponentTo a create a TreeNode object to represent a component in the monitorable object tree, invoke the static method createTreeNode of the org.glassfish.flashlight.datatree.factory.TreeNodeFactory class. Invoke this method in the class that represents your component. In the invocation of the createTreeNode method, pass the following information as parameters to the method:
Creating Objects to Represent a Component's StatisticsCreate one object to represent each statistic that you are adding to the monitorable object tree. Create the objects in your listener class or in the class that represents your add-on component. Each object must be an implementation of an interface that extends org.glassfish.flashlight.datatree.TreeNode. Note – The TreeNode is an unstable interface and is subject to change. To specify the name of the node in the monitorable object tree that the object represents, invoke the object's setName method. In the invocation of the setName method, pass a string that contains the name of the node as a parameter to the method. The object that represents a statistic must also provide methods for computing the statistic from event data. The org.glassfish.flashlight.statistics package provides the following utility classes to gather and compute statistics data:
Note – The classes in the org.glassfish.flashlight.statistics package are unstable interfaces and are subject to change. The org.glassfish.flashlight.statistics.factory package provides a factory class for each utility class as shown in the following table.
Getting the server Node ObjectThe server node object is the parent of the TreeNode object that represents the component in the monitorable object tree. To get the server node object, invoke the get method of the org.glassfish.flashlight.MonitoringRuntimeDataRegistry class. In the invocation of the get method, pass the string server as a parameter. Adding an Object to the TreeTo add an object to the tree, invoke the addChild method of the object's parent object. In the invocation of the addChild method, pass the TreeNode object that you are adding as a parameter. The parent object depends on whether the object represents an add-on component or a statistic:
Dotted Names for an Add-On Component's StatisticsThe Enterprise Server administrative commands get(1), list(1), and set(1) locate a statistic through the dotted name of the statistic. The dotted name of a statistic for an add-on component is determined from the names of the TreeNode objects that represent the statistic and the component in the monitorable object tree as follows: server.componenent-treenode-name.statistic-treenode-name
Example of Adding Statistics for a Component to the Monitorable Object TreeExample 5–6 Adding Statistics for a Component to the Monitorable Object TreeThis example shows the code for adding the totaltransactioncount statistic to the monitorable object tree. To add this statistic, objects are added to the monitorable object tree as follows:
...
import org.glassfish.flashlight.client.ProbeListener;
import org.glassfish.flashlight.datatree.TreeNode;
import org.glassfish.flashlight.datatree.factory.TreeNodeFactory;
import org.glassfish.flashlight.statistics.Counter;
...
public class TransactionManagerImpl {
@Inject
private MonitoringRuntimeDataRegistry mrdr;
private TreeNode serverNode;
private Counter totalTxCount = CounterFactory.createCount();
public void init (){
...
TreeNode txNode = TreeNodeFactory.createTreeNode("tx", null, "transactions");
TreeNode serverNode = getServerNode();
serverNode.addChild(txNode);
...
totalTxCount.setName("totaltransactioncount");
txNode.addChild(totalTxCount);
...
}
...
private TreeNode getServerNode() {
TreeNode serverNode = null;
if (mrdr.get("server") != null) {
serverNode = mrdr.get("server");
} else {
serverNode = TreeNodeFactory.createTreeNode("server", null, "server");
mrdr.add("server", serverNode);
}
return serverNode;
}
...
}
Example of Adding Monitoring CapabilitiesThis example shows a component that monitors the number of requests that a container receives. The following table provides a cross-reference to the listing of each class or interface in the example.
Example 5–7 Event Provider InterfaceThis example illustrates how to define an event provider as explained in Defining an Event Provider. The example shows the definition of the ModuleProbeProvider interface. The event provider sends events when the request count is increased by 1 or decreased by 1. This interface declares the following methods:
The name of each method is also the name of the event type that is associated with the method. A parameter that is named count is passed to each method. package com.example.count.monitoring;
import org.glassfish.flashlight.provider.annotations.ProbeParam;
/**
* Monitoring count eventse
* Provider interface for module specific probe events.
*
*/
public interface ModuleProbeProvider {
/**
* Emits probe event whenever the count is incremented
*/
public void moduleCountIncrementEvent(
@ProbeParam("count") Integer count
);
/**
* Emits probe event whenever the count is decremented
*/
public void moduleCountDecrementEvent(
@ProbeParam("count") Integer count
);
}
Example 5–8 Bootstrap ClassThis example illustrates how to perform the following tasks:
package com.example.count.monitoring;
import java.lang.management.ManagementFactory;
import org.jvnet.hk2.component.PostConstruct;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.jvnet.hk2.annotations.Inject;
import org.jvnet.hk2.annotations.Service;
import org.jvnet.hk2.annotations.Scoped;
import org.jvnet.hk2.component.Singleton;
import org.glassfish.flashlight.MonitoringRuntimeDataRegistry;
import org.glassfish.internal.api.Init;
import org.glassfish.flashlight.provider.ProbeProviderFactory;
import org.glassfish.flashlight.client.ProbeClientMediator;
import org.glassfish.flashlight.client.ProbeClientMethodHandle;
import java.util.Collection;
import org.glassfish.flashlight.datatree.TreeNode;
import org.glassfish.flashlight.datatree.factory.TreeNodeFactory;
/**
* Monitoring Count Example
* Bootstrap object for registering probe provider and listener
*
*/
@Service
@Scoped(Singleton.class)
public class ModuleBootStrap implements Init, PostConstruct {
@Inject
private MonitoringRuntimeDataRegistry mrdr;
@Inject
protected ProbeProviderFactory probeProviderFactory;
@Inject
private ProbeClientMediator pcm;
private TreeNode serverNode;
public void postConstruct() {
try {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("count.example.monitoring:name=countMBean");
Module mbean = new Module();
mbs.registerMBean(mbean, name);
ModuleProbeProvider mpp = probeProviderFactory.getProbeProvider(
"count", "example", "countapp", ModuleProbeProvider.class);
mbean.setProbeProvider(mpp);
TreeNode serverNode = getServerNode();
TreeNode countNode = TreeNodeFactory.createTreeNode("count", null, "example");
serverNode.addChild(countNode);
ModuleStatsTelemetry modTM = new ModuleStatsTelemetry(countNode);
Collection<ProbeClientMethodHandle> handles = pcm.registerListener(modTM);
modTM.setProbeListenerHandles(handles);
} catch (Exception e) {
System.out.println("Caught exception in postconstruct");
e.printStackTrace();
}
}
private TreeNode getServerNode() {
TreeNode serverNode = null;
if (mrdr.get("server") != null) {
serverNode = mrdr.get("server");
} else {
serverNode = TreeNodeFactory.createTreeNode("server", null, "server");
mrdr.add("server", serverNode);
}
return serverNode;
}
}
Example 5–9 Listener ClassThis example shows how to perform the following tasks:
The example also shows the code for processing the events and updating the monitoring statistics. To enable the listener to update statistics in the monitorable object tree, the parent of statistics object in the tree is passed in the constructor of this class. package com.example.count.monitoring;
import java.util.Collection;
import org.glassfish.flashlight.client.ProbeClientMethodHandle;
import org.glassfish.flashlight.statistics.*;
import org.glassfish.flashlight.statistics.factory.CounterFactory;
import org.glassfish.flashlight.datatree.TreeNode;
import org.glassfish.flashlight.datatree.factory.*;
import org.glassfish.flashlight.client.ProbeListener;
import org.glassfish.flashlight.provider.annotations.ProbeParam;
import org.glassfish.flashlight.provider.annotations.*;
/**
* Monitoring counter example
* Telemtry object which listens to probe events and updates
* the monitoring stats
*
*/
public class ModuleStatsTelemetry{
private Counter k = CounterFactory.createCount();
private Collection<ProbeClientMethodHandle> handles;
public ModuleStatsTelemetry(TreeNode parent) {
k.setName("countMBeanCount");
parent.addChild(k);
}
@ProbeListener("count:example:countapp:moduleCountIncrementEvent")
public void moduleCountIncrementEvent(
@ProbeParam("count") Integer count) {
k.increment();
}
@ProbeListener("count:example:countapp:moduleCountDecrementEvent")
public void moduleCountDecrementEvent(
@ProbeParam("count") Integer count) {
k.decrement();
}
public void setProbeListenerHandles(Collection<ProbeClientMethodHandle> handles) {
this.handles = handles;
// by default, the handles are enabled
// following template is provided to enable/disable
/*
for (ProbeClientMethodHandle handle : handles) {
handle.enable();
}
*/
}
}
Example 5–10 MBean InterfaceThis example defines the interface for a simple standard MBean that has methods to increase and decrease a counter by 1. package com.example.count.monitoring;
/**
* Monitoring counter example
* ModuleMBean interface
*
*/
public interface ModuleMBean {
public Integer getCount() ;
public void incrementCount() ;
public void decrementCount() ;
}
Example 5–11 MBean ImplementationThis example illustrates how to send an event as explained in Sending an Event. The example shows code for sending events as follows:
The methods incrementCount and decrementCount are invoked by an entity that is beyond the scope of this example, for example, JConsole. package com.example.count.monitoring;
/**
* Monitoring counter example
* ModuleMBean implementation
*
*/
public class Module implements ModuleMBean {
private int k = 0;
private ModuleProbeProvider mpp = null;
public Integer getCount() {
return k;
}
public void incrementCount() {
k++;
if (mpp != null) {
mpp.moduleCountIncrementEvent(k);
}
}
public void decrementCount() {
k--;
if (mpp != null) {
mpp.moduleCountDecrementEvent(k);
}
}
void setProbeProvider(ModuleProbeProvider mpp) {
this.mpp = mpp;
}
}
|
||||||||||||||||||||||||