User-Defined Metrics
In addition to the metrics shipped with the product, users can define additional metrics for application-specific data.
The metric name provided in the annotation is automatically prefixed with pu and reported with the processing unit
This is the unit of packaging and deployment in the GigaSpaces Data Grid, and is essentially the main GigaSpaces service. The Processing Unit (PU) itself is typically deployed onto the Service Grid. When a Processing Unit is deployed, a Processing Unit instance is the actual runtime entity._
This is the unit of packaging and deployment in the GigaSpaces Data Grid, and is essentially the main GigaSpaces service. The Processing Unit (PU) itself is typically deployed onto the Service Grid. When a Processing Unit is deployed, a Processing Unit instance is the actual runtime entity. tags, as explained here.
For example, suppose we have a FooService bean:
<beans xmlns="http://www.springframework.org/schema/beans">
<bean id="FooService" class="com.gigaspaces.demo.FooService" />
</beans>
And suppose it processes some application-specific requests:
public class FooService {
private final AtomicInteger processedRequests = new AtomicInteger();
public void processRequest(Object request) {
// TODO: Process request
processedRequests.incrementAndGet();
}
}
and we want to create a metric for the number of processed requests. We can use one of the following techniques:
Using Annotation
The simplest option is to use the @ServiceMetric annotation - simply create a method which returns the current value of the metric, and annotate it with @ServiceMetric along with the name you want the metric to be reported with. For example:
public class FooService {
private final AtomicInteger processedRequests = new AtomicInteger();
public void processRequest(Object request) {
// TODO: Process request
processedRequests.incrementAndGet();
}
@ServiceMetric(name="processed-requests")
public int getProcessedRequests() {
return processedRequests.get();
}
}
Using Code
If annotations are not granular enough (for example, you wish to dynamically register and unregister metrics at runtime), you can explicitly register metrics in your code. For that end:
- The bean should implement the
ProcessingUnitContainerContextAwareinterface. - Use the injected
ProcessingUnitContainerContextto create aBeanMetricManager. You'll need to provide a name which will prefix all the metrics registered using thisBeanMetricManager. - Use the new
BeanMetricManagerto register aGaugemetric - Implement thegetValue()method to return the value you wish to report.
For example:
public class FooService implements ProcessingUnitContainerContextAware {
private BeanMetricManager metricsManager;
private final AtomicInteger processedRequests = new AtomicInteger();
public void processRequest(Object request) {
// TODO: Process request
processedRequests.incrementAndGet();
}
@Override
public void setProcessingUnitContainerContext(ProcessingUnitContainerContext processingUnitContainerContext) {
this.metricsManager = processingUnitContainerContext.createBeanMetricManager("FooService");
this.metricsManager.register("processed-requests", new Gauge<Integer>() {
@Override
public Integer getValue() throws Exception {
return processedRequests.get();
}
});
}
}
Another option is to use LongCounter metric instead to simplify metric registration. LongCounter is a built-in metric type used to simplify reporting counters.
For example:
public class FooService implements ProcessingUnitContainerContextAware {
private BeanMetricManager metricsManager;
private final LongCounter processedRequests = new LongCounter();
public void processRequest(Object request) {
// TODO: Process request
processedRequests.inc();
}
@Override
public void setProcessingUnitContainerContext(ProcessingUnitContainerContext processingUnitContainerContext) {
this.metricsManager = processingUnitContainerContext.createBeanMetricManager("custom-name");
this.metricsManager.register("processed-requests", processedRequests);
}
}
Prefix And Tags
The specified metric name is automatically prefixed with pu_ and reported with the processing unit tags, as explained here.
Using Micrometer API
Starting from version 17.2, user-defined metrics in a GigaSpaces Processing Unit can use the native Micrometer API.
See the following code example:
public class CustomMetricsBean implements ProcessingUnitContainerContextAware {
// --- Simulated application state ---
private final AtomicInteger queueSize = new AtomicInteger(0);
private final AtomicLong activeUsers = new AtomicLong(0);
private Counter requestsCounter;
private Timer requestTimer;
private final ScheduledExecutorService simulator = Executors.newSingleThreadScheduledExecutor();
@Override
public void setProcessingUnitContainerContext(ProcessingUnitContainerContext context) {
BeanMetricManager beanMetricManager = context.createBeanMetricManager("custom-metrics");
if (!(beanMetricManager instanceof MicrometerRegistrator metricsManager)) {
// Metrics are not configured - skip registration
return;
}
// --- Gauge: current queue depth ---
Gauge.builder(metricsManager.getFullMetricName("queue-size"), queueSize, AtomicInteger::get)
.tags(metricsManager.evaluateTags())
.description("Current depth of the simulated work queue")
.register(metricsManager.getMeterRegistry());
// --- Gauge: number of active users ---
Gauge.builder(metricsManager.getFullMetricName("active-users"), activeUsers, AtomicLong::get)
.tags(metricsManager.evaluateTags())
.description("Number of simulated active users")
.register(metricsManager.getMeterRegistry());
// --- Counter: total requests processed ---
requestsCounter = Counter.builder(metricsManager.getFullMetricName("requests-total"))
.tags(metricsManager.evaluateTags())
.description("Total number of requests processed")
.register(metricsManager.getMeterRegistry());
// --- Timer: request processing duration ---
requestTimer = Timer.builder(metricsManager.getFullMetricName("request-duration"))
.tags(metricsManager.evaluateTags())
.description("Time spent processing a single request")
.register(metricsManager.getMeterRegistry());
// Simulate changing metric values every 5 seconds
simulator.scheduleAtFixedRate(this::simulateWork, 0, 5, TimeUnit.SECONDS);
}
private void simulateWork() {
// Fluctuate queue size between 0 and 99
queueSize.set((int) (Math.random() * 100));
// Fluctuate active users between 0 and 49
activeUsers.set((long) (Math.random() * 50));
// Record a counter increment and a timer sample
requestsCounter.increment();
requestTimer.record(() -> {
try {
Thread.sleep((long) (Math.random() * 50));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
}
Add the Micrometer dependency to your pom:
<!-- Micrometer core - provided by the XAP
GigaSpaces eXtreme Application Platform.
Provides a powerful solution for data processing, launching, and running digital services container at runtime -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
<version>${micrometer.version}</version>
<scope>provided</scope>
</dependency>
In the pu.xml define the bean as:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- Stateless PU
Built-in services such as mirror WAN-GW, data GW and custom services. They are displayed in some UI tools (Ops-UI/Web-UI) but NOT in SpaceDeck. Metrics are managed.: no embedded space, metrics only -->
<description>pu-type=stateless</description>
<!-- Registers user-defined Micrometer metrics on PU startup -->
<bean class="com.gigaspaces.example.metrics.CustomMetricsBean" />
</beans>
Please note that the @ServiceMetric annotation can still be used.
In-Memory Data Grid - achieve unparalleled speed, persistence, and accuracy.