CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-prometheus--simpleclient

Core instrumentation library for the Prometheus Java client, providing fundamental metric types for application monitoring

Pending
Overview
Eval results
Files

registry.mddocs/

Registry Management

The CollectorRegistry manages metric registration, collection, and provides access to metric samples for export to monitoring systems. It serves as the central hub for all metric collectors in an application.

Capabilities

Registry Creation and Access

Access the default registry or create custom registries for specific use cases.

/**
 * Default registry singleton for most applications
 */
public static final CollectorRegistry defaultRegistry;

/**
 * Create a new CollectorRegistry instance
 */
public CollectorRegistry();

/**
 * Create a new CollectorRegistry with auto-describe option
 * @param autoDescribe Whether to automatically describe collectors
 */
public CollectorRegistry(boolean autoDescribe);

Usage Example:

import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.Counter;

// Use default registry (most common)
Counter requests = Counter.build()
    .name("requests_total")
    .help("Total requests")
    .register(); // Registers to defaultRegistry

// Create custom registry for testing
CollectorRegistry testRegistry = new CollectorRegistry();
Counter testCounter = Counter.build()
    .name("test_counter")
    .help("Test counter")
    .register(testRegistry);

// Registry with auto-describe for development
CollectorRegistry devRegistry = new CollectorRegistry(true);

Collector Registration

Register and unregister metric collectors with the registry.

/**
 * Register a collector with this registry
 * @param collector Collector to register
 * @throws IllegalArgumentException if collector names conflict
 */
public void register(Collector collector);

/**
 * Unregister a collector from this registry
 * @param collector Collector to unregister
 */
public void unregister(Collector collector);

/**
 * Unregister all collectors from this registry
 */
public void clear();

Usage Examples:

// Register collectors explicitly
CollectorRegistry customRegistry = new CollectorRegistry();

Counter requests = Counter.build()
    .name("requests_total")
    .help("Total requests")
    .create(); // Create without registering

Gauge memory = Gauge.build()
    .name("memory_usage_bytes")
    .help("Memory usage")
    .create();

// Register to custom registry
customRegistry.register(requests);
customRegistry.register(memory);

// Unregister when no longer needed
customRegistry.unregister(requests);

// Name conflict detection
Counter duplicate = Counter.build()
    .name("requests_total") // Same name as existing
    .help("Duplicate counter")
    .create();

try {
    customRegistry.register(duplicate); // Throws IllegalArgumentException
} catch (IllegalArgumentException e) {
    System.err.println("Name conflict: " + e.getMessage());
}

Metric Sample Collection

Collect metric samples from all registered collectors for export.

/**
 * Get all metric family samples from registered collectors
 * @return Enumeration of MetricFamilySamples
 */
public Enumeration<MetricFamilySamples> metricFamilySamples();

/**
 * Get filtered metric family samples
 * @param sampleNameFilter Predicate to filter sample names
 * @return Enumeration of filtered MetricFamilySamples
 */
public Enumeration<MetricFamilySamples> filteredMetricFamilySamples(Predicate<String> sampleNameFilter);

/**
 * Get metric family samples for specific metric names
 * @param includedNames Set of metric names to include
 * @return Enumeration of MetricFamilySamples for specified names
 */
public Enumeration<MetricFamilySamples> filteredMetricFamilySamples(Set<String> includedNames);

Usage Examples:

// Collect all metrics
Enumeration<MetricFamilySamples> allMetrics = 
    CollectorRegistry.defaultRegistry.metricFamilySamples();

while (allMetrics.hasMoreElements()) {
    MetricFamilySamples family = allMetrics.nextElement();
    System.out.println("Metric: " + family.name + " (" + family.type + ")");
    
    for (MetricFamilySamples.Sample sample : family.samples) {
        System.out.println("  " + sample.name + " = " + sample.value);
    }
}

// Filter metrics by name pattern
Predicate<String> httpMetricsFilter = name -> name.startsWith("http_");
Enumeration<MetricFamilySamples> httpMetrics = 
    CollectorRegistry.defaultRegistry.metricFamilySamples(httpMetricsFilter);

// Get specific metrics by name
Set<String> requestMetrics = Set.of("requests_total", "request_duration_seconds");
Enumeration<MetricFamilySamples> specificMetrics = 
    CollectorRegistry.defaultRegistry.filteredMetricFamilySamples(requestMetrics);

Single Sample Value Access

Retrieve individual metric values for specific use cases.

/**
 * Get sample value for metric without labels
 * @param name Metric name
 * @return Sample value or null if not found
 */
public Double getSampleValue(String name);

/**
 * Get sample value for labeled metric
 * @param name Metric name
 * @param labelNames Array of label names
 * @param labelValues Array of label values (must match labelNames)
 * @return Sample value or null if not found
 */
public Double getSampleValue(String name, String[] labelNames, String[] labelValues);

Usage Examples:

// Get simple metric value
Double totalRequests = CollectorRegistry.defaultRegistry
    .getSampleValue("requests_total");

if (totalRequests != null) {
    System.out.println("Total requests: " + totalRequests);
}

// Get labeled metric value
String[] labelNames = {"method", "status"};
String[] labelValues = {"GET", "200"};
Double getRequests = CollectorRegistry.defaultRegistry
    .getSampleValue("http_requests_total", labelNames, labelValues);

// Null check for missing metrics
if (getRequests != null) {
    System.out.println("GET 200 requests: " + getRequests);
} else {
    System.out.println("Metric not found or no samples");
}

// Utility method for safe value retrieval
public double getMetricValueOrDefault(String name, double defaultValue) {
    Double value = CollectorRegistry.defaultRegistry.getSampleValue(name);
    return value != null ? value : defaultValue;
}

Important Notes

Default Registry Usage

Most applications should use the default registry:

// Recommended approach for most applications
Counter requests = Counter.build()
    .name("requests_total")
    .help("Total requests")
    .register(); // Uses defaultRegistry automatically

// Equivalent explicit registration
Counter requests2 = Counter.build()
    .name("requests_total_2")
    .help("Total requests")
    .register(CollectorRegistry.defaultRegistry);

Custom Registry Use Cases

Create custom registries for:

  • Unit Testing: Isolated metrics for test cases
  • Subsystem Isolation: Separate metrics for different components
  • Partial Export: Export subset of metrics to different endpoints
  • Development/Debug: Enhanced introspection with auto-describe
// Testing with isolated registry
@Test
public void testMetrics() {
    CollectorRegistry testRegistry = new CollectorRegistry();
    Counter testCounter = Counter.build()
        .name("test_operations")
        .help("Test operations")
        .register(testRegistry);
    
    testCounter.inc();
    
    Double value = testRegistry.getSampleValue("test_operations_total");
    assertEquals(1.0, value, 0.001);
}

// Subsystem-specific registry
public class DatabaseMetrics {
    private static final CollectorRegistry dbRegistry = new CollectorRegistry();
    
    public static final Counter queries = Counter.build()
        .name("db_queries_total")
        .help("Database queries")
        .register(dbRegistry);
    
    public static Enumeration<MetricFamilySamples> getMetrics() {
        return dbRegistry.metricFamilySamples();
    }
}

Name Conflict Resolution

The registry prevents duplicate metric names:

CollectorRegistry registry = new CollectorRegistry();

// First registration succeeds
Counter counter1 = Counter.build()
    .name("operations")
    .help("First counter")
    .create();
registry.register(counter1);

// Second registration with same name fails
Counter counter2 = Counter.build()
    .name("operations") // Conflict!
    .help("Second counter")
    .create();

try {
    registry.register(counter2);
} catch (IllegalArgumentException e) {
    // Handle name conflict
    System.err.println("Metric name already exists: " + e.getMessage());
}

// Solution: Use different names or unregister first
registry.unregister(counter1);
registry.register(counter2); // Now succeeds

Thread Safety

All registry operations are thread-safe:

// Safe concurrent registration
CollectorRegistry registry = new CollectorRegistry();

// Multiple threads can safely register different collectors
Thread t1 = new Thread(() -> {
    Counter c1 = Counter.build().name("metric1").help("M1").create();
    registry.register(c1);
});

Thread t2 = new Thread(() -> {
    Gauge g1 = Gauge.build().name("metric2").help("M2").create();
    registry.register(g1);
});

t1.start();
t2.start();

Sample Name Filtering

Use filtering for performance and relevance:

// Performance: Only collect metrics you need
Predicate<String> relevantMetrics = name -> 
    name.startsWith("app_") || name.startsWith("business_");

Enumeration<MetricFamilySamples> filtered = 
    registry.metricFamilySamples(relevantMetrics);

// Custom filter implementation
public class MetricNameFilter implements Predicate<String> {
    private final Set<String> allowedPrefixes;
    
    public MetricNameFilter(String... prefixes) {
        this.allowedPrefixes = Set.of(prefixes);
    }
    
    @Override
    public boolean test(String name) {
        return allowedPrefixes.stream().anyMatch(name::startsWith);
    }
}

// Usage
MetricNameFilter filter = new MetricNameFilter("http_", "db_", "cache_");
Enumeration<MetricFamilySamples> metrics = registry.metricFamilySamples(filter);

Integration Patterns

// Metrics exporter pattern
public class PrometheusExporter {
    private final CollectorRegistry registry;
    
    public PrometheusExporter(CollectorRegistry registry) {
        this.registry = registry;
    }
    
    public String exportMetrics() {
        StringBuilder output = new StringBuilder();
        Enumeration<MetricFamilySamples> samples = registry.metricFamilySamples();
        
        while (samples.hasMoreElements()) {
            MetricFamilySamples family = samples.nextElement();
            // Format for Prometheus text format
            output.append(formatFamily(family));
        }
        
        return output.toString();
    }
}

// Health check integration
public class MetricsHealthCheck {
    public boolean isHealthy() {
        Double errorRate = CollectorRegistry.defaultRegistry
            .getSampleValue("error_rate");
        
        return errorRate == null || errorRate < 0.05; // 5% threshold
    }
    
    public Map<String, Object> getHealthDetails() {
        Map<String, Object> details = new HashMap<>();
        
        details.put("total_requests", 
            CollectorRegistry.defaultRegistry.getSampleValue("requests_total"));
        details.put("error_count", 
            CollectorRegistry.defaultRegistry.getSampleValue("errors_total"));
        
        return details;
    }
}

Install with Tessl CLI

npx tessl i tessl/maven-io-prometheus--simpleclient

docs

counter.md

enumeration.md

exemplars.md

gauge.md

histogram.md

index.md

info.md

registry.md

summary.md

tile.json