or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

counter.mdenumeration.mdexemplars.mdgauge.mdhistogram.mdindex.mdinfo.mdregistry.mdsummary.md

exemplars.mddocs/

0

# Exemplar Support

1

2

Exemplars link individual metric observations to distributed trace data, enabling correlation between metrics and traces. They provide sample trace information alongside metric values, making it easier to investigate performance issues and understand system behavior.

3

4

## Capabilities

5

6

### Exemplar Creation

7

8

Create exemplars with trace information to link metrics to distributed traces.

9

10

```java { .api }

11

/**

12

* Create an exemplar without timestamp

13

* @param value The observed metric value

14

* @param labels Name/value pairs for trace information (even number required)

15

*/

16

public Exemplar(double value, String... labels);

17

18

/**

19

* Create an exemplar with timestamp

20

* @param value The observed metric value

21

* @param timestampMs Timestamp in milliseconds (System.currentTimeMillis())

22

* @param labels Name/value pairs for trace information (even number required)

23

*/

24

public Exemplar(double value, Long timestampMs, String... labels);

25

26

/**

27

* Create an exemplar with Map labels

28

* @param value The observed metric value

29

* @param labels Map of label names to values

30

*/

31

public Exemplar(double value, Map<String, String> labels);

32

```

33

34

**Usage Examples:**

35

36

```java

37

import io.prometheus.client.exemplars.Exemplar;

38

39

// Basic exemplar with trace ID

40

Exemplar exemplar1 = new Exemplar(0.123, "trace_id", "abc123");

41

42

// Exemplar with timestamp and multiple labels

43

Exemplar exemplar2 = new Exemplar(

44

0.456,

45

System.currentTimeMillis(),

46

"trace_id", "def456",

47

"span_id", "span789",

48

"user_id", "user123"

49

);

50

51

// Exemplar using Map

52

Map<String, String> traceLabels = new HashMap<>();

53

traceLabels.put("trace_id", "xyz789");

54

traceLabels.put("operation", "database_query");

55

Exemplar exemplar3 = new Exemplar(0.089, traceLabels);

56

```

57

58

### Exemplar Data Access

59

60

Retrieve exemplar information for trace correlation and debugging.

61

62

```java { .api }

63

/**

64

* Get the observed metric value

65

* @return The metric value this exemplar represents

66

*/

67

public double getValue();

68

69

/**

70

* Get the timestamp in milliseconds

71

* @return Timestamp or null if not set

72

*/

73

public Long getTimestampMs();

74

75

/**

76

* Get the trace labels as array

77

* @return Array of alternating label names and values

78

*/

79

public String[] getLabels();

80

81

/**

82

* Convert Map labels to array format

83

* @param labels Map of label names to values

84

* @return Array of alternating names and values

85

*/

86

public static String[] mapToArray(Map<String, String> labels);

87

```

88

89

**Usage Examples:**

90

91

```java

92

// Access exemplar data

93

Exemplar exemplar = new Exemplar(0.234, "trace_id", "trace123", "span_id", "span456");

94

95

double value = exemplar.getValue(); // 0.234

96

Long timestamp = exemplar.getTimestampMs(); // null (not set)

97

String[] labels = exemplar.getLabels(); // ["trace_id", "trace123", "span_id", "span456"]

98

99

// Utility method for label conversion

100

Map<String, String> labelMap = Map.of("trace_id", "abc", "span_id", "def");

101

String[] labelArray = Exemplar.mapToArray(labelMap);

102

// Result: ["trace_id", "abc", "span_id", "def"] (order may vary)

103

```

104

105

### Global Exemplar Configuration

106

107

Configure exemplar sampling behavior globally across all metrics.

108

109

```java { .api }

110

/**

111

* Check if exemplars are globally enabled

112

* @return true if exemplars should be collected

113

*/

114

public static boolean isExemplarsEnabled();

115

116

/**

117

* Enable or disable exemplars globally

118

* @param enabled Whether to enable exemplar collection

119

*/

120

public static void setExemplarsEnabled(boolean enabled);

121

122

/**

123

* Get the global counter exemplar sampler

124

* @return Current counter sampler or null if not set

125

*/

126

public static CounterExemplarSampler getCounterExemplarSampler();

127

128

/**

129

* Set global counter exemplar sampler

130

* @param sampler Sampler implementation for counter metrics

131

*/

132

public static void setCounterExemplarSampler(CounterExemplarSampler sampler);

133

134

/**

135

* Get the global histogram exemplar sampler

136

* @return Current histogram sampler or null if not set

137

*/

138

public static HistogramExemplarSampler getHistogramExemplarSampler();

139

140

/**

141

* Set global histogram exemplar sampler

142

* @param sampler Sampler implementation for histogram metrics

143

*/

144

public static void setHistogramExemplarSampler(HistogramExemplarSampler sampler);

145

```

146

147

### Exemplar Sampling Interfaces

148

149

Implement custom sampling strategies for different metric types.

150

151

```java { .api }

152

/**

153

* Base interface for exemplar sampling

154

*/

155

public interface ExemplarSampler {

156

// Marker interface for exemplar sampling implementations

157

}

158

159

/**

160

* Exemplar sampler for counter metrics

161

*/

162

public interface CounterExemplarSampler extends ExemplarSampler {

163

/**

164

* Sample an exemplar for counter increment

165

* @param increment The increment amount

166

* @param previous Previous exemplar (may be null)

167

* @return New exemplar or null to skip sampling

168

*/

169

Exemplar sample(double increment, Exemplar previous);

170

}

171

172

/**

173

* Exemplar sampler for histogram metrics

174

*/

175

public interface HistogramExemplarSampler extends ExemplarSampler {

176

/**

177

* Sample an exemplar for histogram observation

178

* @param value The observed value

179

* @param bucketFrom Lower bound of the bucket

180

* @param bucketTo Upper bound of the bucket

181

* @param previous Previous exemplar for this bucket

182

* @return New exemplar or null to skip sampling

183

*/

184

Exemplar sample(double value, double bucketFrom, double bucketTo, Exemplar previous);

185

}

186

```

187

188

### Default Exemplar Sampler

189

190

Built-in sampling implementation with reasonable defaults.

191

192

```java { .api }

193

/**

194

* Default exemplar sampler implementation

195

*/

196

public class DefaultExemplarSampler implements CounterExemplarSampler, HistogramExemplarSampler {

197

// Implementation details handled internally

198

}

199

```

200

201

**Usage Examples:**

202

203

```java

204

import io.prometheus.client.exemplars.*;

205

206

// Enable exemplars globally

207

ExemplarConfig.setExemplarsEnabled(true);

208

209

// Set up default samplers

210

ExemplarConfig.setCounterExemplarSampler(new DefaultExemplarSampler());

211

ExemplarConfig.setHistogramExemplarSampler(new DefaultExemplarSampler());

212

213

// Custom counter sampler

214

CounterExemplarSampler customCounterSampler = new CounterExemplarSampler() {

215

@Override

216

public Exemplar sample(double increment, Exemplar previous) {

217

// Sample every 100th increment

218

if (Math.random() < 0.01) {

219

String traceId = getCurrentTraceId(); // Your trace context

220

return new Exemplar(increment, "trace_id", traceId);

221

}

222

return null; // Skip sampling

223

}

224

};

225

226

// Custom histogram sampler

227

HistogramExemplarSampler customHistogramSampler = new HistogramExemplarSampler() {

228

@Override

229

public Exemplar sample(double value, double bucketFrom, double bucketTo, Exemplar previous) {

230

// Sample slow requests (>1 second)

231

if (value > 1.0) {

232

String traceId = getCurrentTraceId();

233

String spanId = getCurrentSpanId();

234

return new Exemplar(value, "trace_id", traceId, "span_id", spanId);

235

}

236

return null;

237

}

238

};

239

240

ExemplarConfig.setCounterExemplarSampler(customCounterSampler);

241

ExemplarConfig.setHistogramExemplarSampler(customHistogramSampler);

242

```

243

244

### Trace Integration Interface

245

246

Interface for integrating with distributed tracing systems.

247

248

```java { .api }

249

/**

250

* Interface for trace context integration

251

*/

252

public interface Tracer {

253

/**

254

* Get current span context for exemplar labels

255

* @return Map of trace labels or null if no active span

256

*/

257

Map<String, String> getCurrentSpanContext();

258

}

259

```

260

261

## Important Notes

262

263

### Exemplar Label Requirements

264

265

Exemplar labels must follow specific rules:

266

- Total label length (names + values) cannot exceed 128 UTF-8 characters

267

- Label names must match regex pattern: `[a-zA-Z_][a-zA-Z_0-9]*`

268

- Must provide even number of strings (alternating names and values)

269

- Neither label names nor values can be null

270

271

```java

272

// Valid exemplar labels

273

Exemplar valid = new Exemplar(1.0, "trace_id", "abc123");

274

275

// Invalid - odd number of strings

276

try {

277

Exemplar invalid = new Exemplar(1.0, "trace_id", "abc123", "span_id"); // Missing value

278

} catch (IllegalArgumentException e) {

279

// Handle invalid label format

280

}

281

282

// Invalid - label name format

283

try {

284

Exemplar invalid = new Exemplar(1.0, "trace-id", "abc123"); // Hyphen not allowed

285

} catch (IllegalArgumentException e) {

286

// Handle invalid label name

287

}

288

```

289

290

### Performance Considerations

291

292

- Exemplars add overhead to metric operations

293

- Sampling reduces performance impact while preserving useful traces

294

- Global configuration affects all metrics - use carefully in high-throughput systems

295

- Consider sampling rates based on your tracing system capacity

296

297

### Integration with Metrics

298

299

Exemplars work with Counter and Histogram metrics:

300

301

```java

302

// Counter with exemplars

303

Counter requests = Counter.build()

304

.name("requests_total")

305

.help("Total requests")

306

.withExemplars() // Enable exemplar support

307

.register();

308

309

// Manual exemplar

310

requests.incWithExemplar(1.0, "trace_id", "abc123");

311

312

// Histogram with exemplars

313

Histogram latency = Histogram.build()

314

.name("request_duration_seconds")

315

.help("Request duration")

316

.withExemplarSampler(customHistogramSampler)

317

.register();

318

319

// Automatic exemplar via sampler

320

latency.observe(0.123); // May create exemplar based on sampler logic

321

```

322

323

### Common Integration Patterns

324

325

```java

326

// OpenTelemetry integration

327

public class OpenTelemetryTracer implements Tracer {

328

@Override

329

public Map<String, String> getCurrentSpanContext() {

330

Span currentSpan = Span.current();

331

if (currentSpan.getSpanContext().isValid()) {

332

Map<String, String> context = new HashMap<>();

333

context.put("trace_id", currentSpan.getSpanContext().getTraceId());

334

context.put("span_id", currentSpan.getSpanContext().getSpanId());

335

return context;

336

}

337

return null;

338

}

339

}

340

341

// Jaeger integration example

342

public class JaegerIntegration {

343

public static void configureExemplars() {

344

CounterExemplarSampler counterSampler = (increment, previous) -> {

345

io.jaegertracing.internal.JaegerSpan span = getActiveSpan();

346

if (span != null) {

347

return new Exemplar(increment,

348

"trace_id", span.context().getTraceId(),

349

"span_id", span.context().getSpanId());

350

}

351

return null;

352

};

353

354

ExemplarConfig.setCounterExemplarSampler(counterSampler);

355

}

356

}

357

358

// Application startup configuration

359

public class MetricsConfiguration {

360

@PostConstruct

361

public void configureExemplars() {

362

// Enable exemplars if tracing is available

363

boolean tracingEnabled = isTracingConfigured();

364

ExemplarConfig.setExemplarsEnabled(tracingEnabled);

365

366

if (tracingEnabled) {

367

// Configure samplers based on your tracing system

368

ExemplarConfig.setCounterExemplarSampler(new DefaultExemplarSampler());

369

ExemplarConfig.setHistogramExemplarSampler(new DefaultExemplarSampler());

370

}

371

}

372

}

373

374

// Manual exemplar with current trace context

375

public void processRequestWithExemplar() {

376

String traceId = getCurrentTraceId();

377

String spanId = getCurrentSpanId();

378

379

if (traceId != null) {

380

requestCounter.incWithExemplar(1.0, "trace_id", traceId, "span_id", spanId);

381

382

double duration = requestLatency.time(() -> {

383

return handleRequest();

384

});

385

386

// Histogram automatically uses configured sampler for exemplars

387

}

388

}

389

```

390

391

### Debugging and Monitoring

392

393

```java

394

// Check exemplar configuration

395

public void debugExemplarConfig() {

396

System.out.println("Exemplars enabled: " + ExemplarConfig.isExemplarsEnabled());

397

System.out.println("Counter sampler: " + ExemplarConfig.getCounterExemplarSampler());

398

System.out.println("Histogram sampler: " + ExemplarConfig.getHistogramExemplarSampler());

399

}

400

401

// Validate exemplar creation

402

public Exemplar createSafeExemplar(double value, Map<String, String> traceContext) {

403

try {

404

return new Exemplar(value, traceContext);

405

} catch (IllegalArgumentException e) {

406

// Log warning and return null instead of failing

407

logger.warn("Failed to create exemplar: " + e.getMessage());

408

return null;

409

}

410

}

411

```