or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

advanced-gauges.mdcore-metrics.mdindex.mdreporting.mdreservoirs-sampling.mdutilities.md

advanced-gauges.mddocs/

0

# Advanced Gauge Types

1

2

Beyond the basic `Gauge<T>` interface, Metrics Core provides several specialized gauge implementations that address common patterns in application monitoring. These advanced gauge types offer caching, value derivation, ratio calculations, and settable values while maintaining the same lightweight, thread-safe characteristics as core metrics.

3

4

## CachedGauge

5

6

`CachedGauge` is an abstract base class that caches gauge values for a specified duration, reducing the computational cost of expensive gauge calculations. This is particularly useful when the underlying value is expensive to compute but doesn't change frequently.

7

8

```java { .api }

9

public abstract class CachedGauge<T> implements Gauge<T> {

10

// Constructors

11

public CachedGauge(long timeout, TimeUnit timeoutUnit);

12

public CachedGauge(Clock clock, long timeout, TimeUnit timeoutUnit);

13

14

// Implemented method

15

public T getValue();

16

17

// Abstract method to implement

18

protected abstract T loadValue();

19

}

20

```

21

22

### Usage Examples

23

24

**Database Connection Pool Size:**

25

```java

26

// Cache expensive database query result for 30 seconds

27

CachedGauge<Integer> connectionPoolSize = new CachedGauge<Integer>(30, TimeUnit.SECONDS) {

28

@Override

29

protected Integer loadValue() {

30

// Expensive operation - query database connection pool

31

return dataSource.getActiveConnections().size();

32

}

33

};

34

registry.gauge("db.pool.active.connections", connectionPoolSize);

35

```

36

37

**File System Space Usage:**

38

```java

39

// Cache disk space calculation for 5 minutes

40

CachedGauge<Long> diskSpaceUsed = new CachedGauge<Long>(5, TimeUnit.MINUTES) {

41

@Override

42

protected Long loadValue() {

43

// Expensive file system operation

44

Path dataDir = Paths.get("/var/data");

45

try {

46

return Files.walk(dataDir)

47

.filter(Files::isRegularFile)

48

.mapToLong(path -> {

49

try {

50

return Files.size(path);

51

} catch (IOException e) {

52

return 0L;

53

}

54

})

55

.sum();

56

} catch (IOException e) {

57

return -1L;

58

}

59

}

60

};

61

registry.gauge("filesystem.data.used.bytes", diskSpaceUsed);

62

```

63

64

**Custom Clock for Testing:**

65

```java

66

// Using custom clock for deterministic testing

67

Clock testClock = new Clock() {

68

private long time = 0;

69

public long getTick() { return time; }

70

public long getTime() { return time; }

71

public void advance(long millis) { time += millis * 1_000_000; }

72

};

73

74

CachedGauge<String> testGauge = new CachedGauge<String>(testClock, 1, TimeUnit.SECONDS) {

75

@Override

76

protected String loadValue() {

77

return "computed-" + System.currentTimeMillis();

78

}

79

};

80

```

81

82

## DerivativeGauge

83

84

`DerivativeGauge` creates a new gauge by transforming the value of an existing gauge. This is useful for converting units, calculating percentages, or performing other transformations on existing metrics.

85

86

```java { .api }

87

public abstract class DerivativeGauge<F, T> implements Gauge<T> {

88

// Constructor

89

public DerivativeGauge(Gauge<F> base);

90

91

// Implemented method

92

public T getValue();

93

94

// Abstract method to implement

95

protected abstract T transform(F value);

96

}

97

```

98

99

### Usage Examples

100

101

**Memory Usage Percentage:**

102

```java

103

// Base gauge for used memory in bytes

104

Gauge<Long> usedMemoryBytes = () -> {

105

Runtime runtime = Runtime.getRuntime();

106

return runtime.totalMemory() - runtime.freeMemory();

107

};

108

registry.gauge("memory.used.bytes", usedMemoryBytes);

109

110

// Derived gauge for memory usage percentage

111

DerivativeGauge<Long, Double> memoryUsagePercent = new DerivativeGauge<Long, Double>(usedMemoryBytes) {

112

@Override

113

protected Double transform(Long usedBytes) {

114

Runtime runtime = Runtime.getRuntime();

115

long totalBytes = runtime.totalMemory();

116

return totalBytes > 0 ? (usedBytes.doubleValue() / totalBytes) * 100.0 : 0.0;

117

}

118

};

119

registry.gauge("memory.used.percent", memoryUsagePercent);

120

```

121

122

**Unit Conversion:**

123

```java

124

// Base gauge in bytes

125

Gauge<Long> fileSizeBytes = () -> {

126

try {

127

return Files.size(Paths.get("/var/log/application.log"));

128

} catch (IOException e) {

129

return 0L;

130

}

131

};

132

133

// Derived gauge in megabytes

134

DerivativeGauge<Long, Double> fileSizeMB = new DerivativeGauge<Long, Double>(fileSizeBytes) {

135

@Override

136

protected Double transform(Long bytes) {

137

return bytes / (1024.0 * 1024.0);

138

}

139

};

140

registry.gauge("log.file.size.mb", fileSizeMB);

141

```

142

143

**Status Code Mapping:**

144

```java

145

// Base gauge returning numeric status

146

Gauge<Integer> serviceStatus = () -> healthChecker.getStatusCode();

147

148

// Derived gauge converting to human-readable status

149

DerivativeGauge<Integer, String> serviceStatusText = new DerivativeGauge<Integer, String>(serviceStatus) {

150

@Override

151

protected String transform(Integer statusCode) {

152

switch (statusCode) {

153

case 200: return "HEALTHY";

154

case 503: return "DEGRADED";

155

case 500: return "UNHEALTHY";

156

default: return "UNKNOWN";

157

}

158

}

159

};

160

registry.gauge("service.status.text", serviceStatusText);

161

```

162

163

## RatioGauge

164

165

`RatioGauge` calculates ratios between two values with proper handling of edge cases like division by zero. It returns `NaN` when the denominator is zero or both numerator and denominator are zero.

166

167

```java { .api }

168

public abstract class RatioGauge implements Gauge<Double> {

169

// Implemented method

170

public Double getValue();

171

172

// Abstract method to implement

173

protected abstract Ratio getRatio();

174

175

// Nested class for ratio representation

176

public static class Ratio {

177

public static Ratio of(double numerator, double denominator);

178

public double getNumerator();

179

public double getDenominator();

180

}

181

}

182

```

183

184

### Usage Examples

185

186

**Cache Hit Rate:**

187

```java

188

Counter cacheHits = registry.counter("cache.hits");

189

Counter cacheMisses = registry.counter("cache.misses");

190

191

RatioGauge cacheHitRate = new RatioGauge() {

192

@Override

193

protected Ratio getRatio() {

194

return Ratio.of(cacheHits.getCount(), cacheHits.getCount() + cacheMisses.getCount());

195

}

196

};

197

registry.gauge("cache.hit.rate", cacheHitRate);

198

```

199

200

**Error Rate:**

201

```java

202

Counter successfulRequests = registry.counter("requests.successful");

203

Counter failedRequests = registry.counter("requests.failed");

204

205

RatioGauge errorRate = new RatioGauge() {

206

@Override

207

protected Ratio getRatio() {

208

long successful = successfulRequests.getCount();

209

long failed = failedRequests.getCount();

210

return Ratio.of(failed, successful + failed);

211

}

212

};

213

registry.gauge("requests.error.rate", errorRate);

214

```

215

216

**Memory Usage Ratio:**

217

```java

218

RatioGauge heapUsageRatio = new RatioGauge() {

219

@Override

220

protected Ratio getRatio() {

221

Runtime runtime = Runtime.getRuntime();

222

long used = runtime.totalMemory() - runtime.freeMemory();

223

long total = runtime.maxMemory();

224

return Ratio.of(used, total);

225

}

226

};

227

registry.gauge("memory.heap.usage.ratio", heapUsageRatio);

228

```

229

230

**Resource Utilization:**

231

```java

232

// Thread pool utilization

233

ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(10);

234

235

RatioGauge threadPoolUtilization = new RatioGauge() {

236

@Override

237

protected Ratio getRatio() {

238

return Ratio.of(executor.getActiveCount(), executor.getMaximumPoolSize());

239

}

240

};

241

registry.gauge("threadpool.utilization", threadPoolUtilization);

242

```

243

244

## SettableGauge

245

246

`SettableGauge` extends the basic `Gauge` interface to allow explicit setting of values. This is useful for metrics that are updated by external processes or for values that need to be set programmatically rather than calculated on demand.

247

248

```java { .api }

249

public interface SettableGauge<T> extends Gauge<T> {

250

void setValue(T value);

251

}

252

```

253

254

### DefaultSettableGauge

255

256

The default implementation of `SettableGauge` provides thread-safe value storage and retrieval.

257

258

```java { .api }

259

public class DefaultSettableGauge<T> implements SettableGauge<T> {

260

// Constructors

261

public DefaultSettableGauge();

262

public DefaultSettableGauge(T initialValue);

263

264

// SettableGauge implementation

265

public void setValue(T value);

266

public T getValue();

267

}

268

```

269

270

### Usage Examples

271

272

**Configuration Values:**

273

```java

274

// Track current configuration values

275

DefaultSettableGauge<String> activeProfile = new DefaultSettableGauge<>("development");

276

registry.gauge("config.active.profile", activeProfile);

277

278

DefaultSettableGauge<Integer> maxConnections = new DefaultSettableGauge<>(100);

279

registry.gauge("config.max.connections", maxConnections);

280

281

// Update when configuration changes

282

configurationManager.addListener(new ConfigurationListener() {

283

@Override

284

public void onConfigurationChanged(Configuration newConfig) {

285

activeProfile.setValue(newConfig.getProfile());

286

maxConnections.setValue(newConfig.getMaxConnections());

287

}

288

});

289

```

290

291

**External System Status:**

292

```java

293

// Track status reported by external monitoring system

294

DefaultSettableGauge<String> externalServiceStatus = new DefaultSettableGauge<>("UNKNOWN");

295

registry.gauge("external.service.status", externalServiceStatus);

296

297

// Update from monitoring callback

298

monitoringSystem.registerCallback(status -> {

299

externalServiceStatus.setValue(status.toString());

300

});

301

```

302

303

**Batch Processing Metrics:**

304

```java

305

// Track batch processing statistics

306

DefaultSettableGauge<Long> lastBatchSize = new DefaultSettableGauge<>(0L);

307

DefaultSettableGauge<Long> lastBatchDuration = new DefaultSettableGauge<>(0L);

308

registry.gauge("batch.last.size", lastBatchSize);

309

registry.gauge("batch.last.duration.ms", lastBatchDuration);

310

311

// Update after each batch completes

312

public void processBatch(List<Item> items) {

313

long startTime = System.currentTimeMillis();

314

315

// Process batch...

316

processItems(items);

317

318

long duration = System.currentTimeMillis() - startTime;

319

lastBatchSize.setValue((long) items.size());

320

lastBatchDuration.setValue(duration);

321

}

322

```

323

324

**Feature Flags:**

325

```java

326

// Track feature flag states

327

Map<String, DefaultSettableGauge<Boolean>> featureFlags = new HashMap<>();

328

329

public void registerFeatureFlag(String flagName, boolean initialState) {

330

DefaultSettableGauge<Boolean> flagGauge = new DefaultSettableGauge<>(initialState);

331

featureFlags.put(flagName, flagGauge);

332

registry.gauge("feature.flag." + flagName, flagGauge);

333

}

334

335

public void updateFeatureFlag(String flagName, boolean enabled) {

336

DefaultSettableGauge<Boolean> flagGauge = featureFlags.get(flagName);

337

if (flagGauge != null) {

338

flagGauge.setValue(enabled);

339

}

340

}

341

```

342

343

## Advanced Patterns

344

345

### Combining Gauge Types

346

347

You can combine different gauge types to create sophisticated monitoring solutions:

348

349

```java

350

// Cached base gauge for expensive computation

351

CachedGauge<Double> rawProcessorLoad = new CachedGauge<Double>(5, TimeUnit.SECONDS) {

352

@Override

353

protected Double loadValue() {

354

return managementFactory.getOperatingSystemMXBean().getProcessCpuLoad();

355

}

356

};

357

358

// Derived gauge for percentage conversion

359

DerivativeGauge<Double, Integer> processorLoadPercent = new DerivativeGauge<Double, Integer>(rawProcessorLoad) {

360

@Override

361

protected Integer transform(Double load) {

362

return (int) Math.round(load * 100);

363

}

364

};

365

366

// Settable gauge for alert threshold

367

DefaultSettableGauge<Integer> alertThreshold = new DefaultSettableGauge<>(80);

368

369

// Derived gauge for alert status

370

DerivativeGauge<Integer, String> alertStatus = new DerivativeGauge<Integer, String>(processorLoadPercent) {

371

@Override

372

protected String transform(Integer currentLoad) {

373

return currentLoad > alertThreshold.getValue() ? "ALERT" : "OK";

374

}

375

};

376

377

registry.gauge("cpu.load.raw", rawProcessorLoad);

378

registry.gauge("cpu.load.percent", processorLoadPercent);

379

registry.gauge("cpu.alert.threshold", alertThreshold);

380

registry.gauge("cpu.alert.status", alertStatus);

381

```

382

383

### Gauge Chaining

384

385

Create chains of derived gauges for complex transformations:

386

387

```java

388

// Chain: raw bytes -> MB -> formatted string

389

Gauge<Long> diskUsageBytes = () -> getDiskUsageInBytes();

390

391

DerivativeGauge<Long, Double> diskUsageMB = new DerivativeGauge<Long, Double>(diskUsageBytes) {

392

@Override

393

protected Double transform(Long bytes) {

394

return bytes / (1024.0 * 1024.0);

395

}

396

};

397

398

DerivativeGauge<Double, String> diskUsageFormatted = new DerivativeGauge<Double, String>(diskUsageMB) {

399

@Override

400

protected String transform(Double mb) {

401

return String.format("%.1f MB", mb);

402

}

403

};

404

```

405

406

## Best Practices

407

408

### Performance Considerations

409

- Use `CachedGauge` for expensive computations that don't need real-time accuracy

410

- Cache duration should balance accuracy requirements with computational cost

411

- Consider the frequency of gauge reads when setting cache timeouts

412

413

### Error Handling

414

- Always handle exceptions in gauge implementations to prevent metric collection failures

415

- Return meaningful default values (like -1 or 0) when computations fail

416

- Use `RatioGauge` for division operations to handle edge cases automatically

417

418

### Thread Safety

419

- All provided gauge implementations are thread-safe

420

- When implementing custom gauges, ensure thread safety if accessed concurrently

421

- `DefaultSettableGauge` uses atomic operations for thread-safe value updates

422

423

### Testing

424

- Use custom `Clock` implementations in `CachedGauge` for deterministic testing

425

- Test edge cases in `RatioGauge` implementations (zero denominators, negative values)

426

- Verify that derived gauges handle null or exceptional base gauge values appropriately