python-micrometer-cardinality-control
4
总安装量
4
周安装量
#52041
全站排名
安装命令
npx skills add https://github.com/dawiddutoit/custom-claude --skill python-micrometer-cardinality-control
Agent 安装分布
opencode
4
gemini-cli
4
claude-code
4
codex
4
mcpjam
3
kilo
3
Skill 文档
Micrometer Cardinality Control
Quick Start
For any metric with dynamic tags, apply this pattern:
// â Dangerous: unbounded cardinality
.tag("supplier.id", supplierId) // 10,000+ unique values
// â
Safe: normalized to bounded categories
.tag("supplier.category", normalizeSupplier(supplier)) // 5-10 values
private String normalizeSupplier(Supplier s) {
if (s.isTopTier()) return "tier1";
if (s.isDirectSupplier()) return "direct";
return "standard";
}
When to Use
- Add tags to metrics (ensure bounded cardinality)
- Normalize high-cardinality data (URIs, IDs)
- Prevent OutOfMemoryError from metric explosion
- Control monitoring costs
- Debug metric growth
When NOT to use:
- Truly unbounded data (use distributed tracing)
- Per-request details (use structured logging)
Cardinality Rules
Safe Tags (Low Cardinality)
// â
HTTP method (4-10 values)
.tag("method", "GET")
// â
Status class (5 values)
.tag("status.class", "2xx")
// â
Environment (3-5 values)
.tag("env", "production")
Dangerous Tags (High Cardinality)
// â User ID (millions) â Use tracing
.tag("user.id", userId)
// â Request ID (infinite) â Use tracing
.tag("request.id", requestId)
// â Full URI â Normalize!
.tag("uri", "/api/charges?supplier=123")
Rule of Thumb:
- Safe per metric: < 1,000 combinations
- Safe application-wide: < 10,000 active metrics
Normalization Patterns
URI Normalization
@Bean
public MeterFilter uriNormalization() {
return MeterFilter.replaceTagValues("uri", uri -> {
// Strip query parameters
int queryIndex = uri.indexOf('?');
if (queryIndex > 0) uri = uri.substring(0, queryIndex);
// Replace IDs: /charges/123 â /charges/{id}
return uri.replaceAll("/\\d+", "/{id}")
.replaceAll("/[a-f0-9-]{36}", "/{uuid}");
});
}
Business Category Normalization
private String normalizeSupplier(String supplierId) {
Supplier supplier = supplierRepository.findById(supplierId);
if (supplier.getAnnualVolume() > 1_000_000) return "enterprise";
if (supplier.getAnnualVolume() > 100_000) return "mid-market";
if (supplier.isDirect()) return "direct";
return "standard";
}
Cardinality Limits
@Bean
public MeterFilter cardinalityLimiter() {
// Limit unique URIs to 100
return MeterFilter.maximumAllowableTags(
"http.server.requests",
"uri",
100,
MeterFilter.deny() // Deny new meters after limit
);
}
Monitor Cardinality
@Component
public class CardinalityMonitor {
private final MeterRegistry registry;
@Scheduled(fixedRate = 60_000)
public void monitorMetricCount() {
int meterCount = registry.getMeters().size();
if (meterCount > 8000) {
log.error("CRITICAL: {} metrics (threshold 8000)", meterCount);
}
Gauge.builder("micrometer.meter.count", () -> meterCount)
.register(registry);
}
}
Alternatives to High-Cardinality Tags
Use Distributed Tracing
// Store user ID in span, NOT metrics
span.setAttribute("user.id", userId);
// Metrics use only bounded tags
Timer.builder("charge.processing")
.tag("status", "processing") // bounded
.register(registry);
Use Structured Logging
// Add to MDC for logging, NOT metrics
MDC.put("user.id", userId);
Requirements
- Spring Boot 2.1+
- spring-boot-starter-actuator
- Java 11+
- For tracing: micrometer-tracing-bridge-otel
Anti-Patterns
// â NEVER add unbounded tags
.tag("user.id", userId)
.tag("request.id", requestId)
.tag("timestamp", Instant.now())
// â
DO normalize to bounded categories
.tag("customer.tier", normalizeCustomer(customer))
.tag("request.type", normalizeRequest(request))
See Also
- python-micrometer-core – Meter types
- python-micrometer-business-metrics – Business KPIs