0
# Distributed Tracing
1
2
OpenTelemetry-based distributed tracing support for observability in distributed testing environments. This system provides comprehensive instrumentation for WebDriver operations, enabling performance monitoring, debugging, and analysis of complex test execution flows across multiple services and infrastructure components.
3
4
## Capabilities
5
6
### Tracer Interface
7
8
Core tracing interface providing context management, span creation, and trace propagation.
9
10
```java { .api }
11
/**
12
* Main tracing interface for creating and managing distributed traces
13
*/
14
public interface Tracer {
15
16
/**
17
* Get the current trace context
18
* @return Current trace context
19
*/
20
TraceContext getCurrentContext();
21
22
/**
23
* Get the propagator for trace context propagation
24
* @return Trace context propagator
25
*/
26
Propagator getPropagator();
27
28
/**
29
* Create a new attribute map for span attributes
30
* @return New attribute map instance
31
*/
32
AttributeMap createAttributeMap();
33
}
34
```
35
36
**Usage Examples:**
37
38
```java
39
import org.openqa.selenium.remote.tracing.opentelemetry.OpenTelemetryTracer;
40
import org.openqa.selenium.remote.tracing.Tracer;
41
42
// Get tracer instance
43
Tracer tracer = OpenTelemetryTracer.getInstance();
44
45
// Get current context
46
TraceContext currentContext = tracer.getCurrentContext();
47
48
// Create child context for operation
49
TraceContext childContext = currentContext.createChild("webdriver-operation");
50
```
51
52
### Span Interface
53
54
Represents a single operation within a distributed trace, providing methods for adding metadata, events, and status information.
55
56
```java { .api }
57
/**
58
* Represents a trace span - a single operation within a distributed trace
59
*/
60
public interface Span extends Closeable {
61
62
/**
63
* Set the span name
64
* @param name Operation name
65
* @return This span for chaining
66
*/
67
Span setName(String name);
68
69
/**
70
* Add string tag to span
71
* @param key Tag key
72
* @param value Tag value
73
* @return This span for chaining
74
*/
75
Span addTag(String key, String value);
76
77
/**
78
* Add boolean tag to span
79
* @param key Tag key
80
* @param value Tag value
81
* @return This span for chaining
82
*/
83
Span addTag(String key, boolean value);
84
85
/**
86
* Add numeric tag to span
87
* @param key Tag key
88
* @param value Tag value
89
* @return This span for chaining
90
*/
91
Span addTag(String key, long value);
92
93
/**
94
* Add double tag to span
95
* @param key Tag key
96
* @param value Tag value
97
* @return This span for chaining
98
*/
99
Span addTag(String key, double value);
100
101
/**
102
* Add event to span
103
* @param name Event name
104
* @return This span for chaining
105
*/
106
Span addEvent(String name);
107
108
/**
109
* Add event with attributes to span
110
* @param name Event name
111
* @param attributeMap Event attributes
112
* @return This span for chaining
113
*/
114
Span addEvent(String name, AttributeMap attributeMap);
115
116
/**
117
* Set span status
118
* @param status Span status
119
* @return This span for chaining
120
*/
121
Span setStatus(Status status);
122
123
/**
124
* Close and finish the span
125
*/
126
void close();
127
}
128
```
129
130
**Usage Examples:**
131
132
```java
133
import org.openqa.selenium.remote.tracing.Span;
134
import org.openqa.selenium.remote.tracing.Status;
135
136
// Create and configure span
137
TraceContext context = tracer.getCurrentContext();
138
TraceContext childContext = context.createChild("find-element");
139
140
try (Span span = childContext.getActiveSpan()) {
141
span.setName("findElement")
142
.addTag("locator.type", "id")
143
.addTag("locator.value", "submit-button")
144
.addTag("timeout.ms", 10000);
145
146
// Perform operation
147
WebElement element = driver.findElement(By.id("submit-button"));
148
149
// Add success event
150
span.addEvent("element-found")
151
.addTag("element.tag", element.getTagName())
152
.setStatus(Status.OK);
153
154
} catch (NoSuchElementException e) {
155
// Handle error in span
156
span.addEvent("element-not-found")
157
.addTag("error", true)
158
.addTag("error.message", e.getMessage())
159
.setStatus(Status.NOT_FOUND);
160
throw e;
161
}
162
```
163
164
### TraceContext
165
166
Manages trace context and span hierarchy, providing methods for creating child contexts and accessing active spans.
167
168
```java { .api }
169
/**
170
* Manages trace context and span hierarchy
171
*/
172
public interface TraceContext {
173
174
/**
175
* Create child context for nested operation
176
* @param operationName Name of the child operation
177
* @return Child trace context
178
*/
179
TraceContext createChild(String operationName);
180
181
/**
182
* Get the currently active span
183
* @return Active span instance
184
*/
185
Span getActiveSpan();
186
}
187
```
188
189
**Usage Examples:**
190
191
```java
192
// Create nested trace contexts
193
TraceContext rootContext = tracer.getCurrentContext();
194
TraceContext pageContext = rootContext.createChild("page-operations");
195
TraceContext elementContext = pageContext.createChild("element-interaction");
196
197
// Work with active spans
198
Span pageSpan = pageContext.getActiveSpan();
199
pageSpan.setName("navigate-to-page")
200
.addTag("url", "https://example.com");
201
202
Span elementSpan = elementContext.getActiveSpan();
203
elementSpan.setName("click-button")
204
.addTag("element.id", "submit");
205
```
206
207
### AttributeMap
208
209
Type-safe attribute management for spans, supporting different value types with proper serialization.
210
211
```java { .api }
212
/**
213
* Type-safe attribute management for spans
214
*/
215
public interface AttributeMap {
216
217
/**
218
* Add boolean attribute
219
* @param key Attribute key
220
* @param value Boolean value
221
* @return This attribute map for chaining
222
*/
223
AttributeMap put(AttributeKey<Boolean> key, Boolean value);
224
225
/**
226
* Add long attribute
227
* @param key Attribute key
228
* @param value Long value
229
* @return This attribute map for chaining
230
*/
231
AttributeMap put(AttributeKey<Long> key, Long value);
232
233
/**
234
* Add string attribute
235
* @param key Attribute key
236
* @param value String value
237
* @return This attribute map for chaining
238
*/
239
AttributeMap put(AttributeKey<String> key, String value);
240
}
241
```
242
243
### AttributeKey
244
245
Typed keys for span attributes ensuring type safety and proper serialization.
246
247
```java { .api }
248
/**
249
* Typed keys for span attributes
250
*/
251
public class AttributeKey<T> {
252
253
/**
254
* Create boolean attribute key
255
* @param key Key name
256
* @return Boolean attribute key
257
*/
258
public static AttributeKey<Boolean> booleanKey(String key);
259
260
/**
261
* Create long attribute key
262
* @param key Key name
263
* @return Long attribute key
264
*/
265
public static AttributeKey<Long> longKey(String key);
266
267
/**
268
* Create string attribute key
269
* @param key Key name
270
* @return String attribute key
271
*/
272
public static AttributeKey<String> stringKey(String key);
273
}
274
```
275
276
**Usage Examples:**
277
278
```java
279
import org.openqa.selenium.remote.tracing.AttributeKey;
280
import org.openqa.selenium.remote.tracing.AttributeMap;
281
282
// Define typed attribute keys
283
AttributeKey<String> urlKey = AttributeKey.stringKey("http.url");
284
AttributeKey<Long> durationKey = AttributeKey.longKey("operation.duration");
285
AttributeKey<Boolean> successKey = AttributeKey.booleanKey("operation.success");
286
287
// Create attribute map
288
AttributeMap attributes = tracer.createAttributeMap()
289
.put(urlKey, "https://example.com")
290
.put(durationKey, 1500L)
291
.put(successKey, true);
292
293
// Use with span events
294
span.addEvent("http-request-completed", attributes);
295
```
296
297
### TracedCommandExecutor
298
299
Command executor wrapper that automatically traces all WebDriver command executions.
300
301
```java { .api }
302
/**
303
* Command executor that adds distributed tracing to all WebDriver commands
304
*/
305
public class TracedCommandExecutor implements CommandExecutor {
306
307
/**
308
* Create traced command executor
309
* @param executor Underlying command executor
310
* @param tracer Tracer instance for creating spans
311
*/
312
public TracedCommandExecutor(CommandExecutor executor, Tracer tracer);
313
314
/**
315
* Execute command with tracing
316
* @param command WebDriver command to execute
317
* @return Command response
318
*/
319
public Response execute(Command command);
320
}
321
```
322
323
**Usage Examples:**
324
325
```java
326
import org.openqa.selenium.remote.TracedCommandExecutor;
327
import org.openqa.selenium.remote.HttpCommandExecutor;
328
329
// Create traced WebDriver
330
URL gridUrl = new URL("http://selenium-grid:4444/wd/hub");
331
CommandExecutor baseExecutor = new HttpCommandExecutor(gridUrl);
332
Tracer tracer = OpenTelemetryTracer.getInstance();
333
CommandExecutor tracedExecutor = new TracedCommandExecutor(baseExecutor, tracer);
334
335
// Use with RemoteWebDriver - all commands will be traced
336
RemoteWebDriver driver = new RemoteWebDriver(tracedExecutor, capabilities);
337
338
// Every WebDriver operation will create trace spans
339
driver.get("https://example.com"); // Creates "get" span
340
driver.findElement(By.id("btn")); // Creates "findElement" span
341
driver.quit(); // Creates "quit" span
342
```
343
344
### OpenTelemetry Implementation
345
346
Production-ready OpenTelemetry-based implementation of the tracing interfaces.
347
348
```java { .api }
349
/**
350
* OpenTelemetry-based tracer implementation
351
*/
352
public class OpenTelemetryTracer implements Tracer {
353
354
/**
355
* Get singleton tracer instance
356
* @return OpenTelemetry tracer instance
357
*/
358
public static Tracer getInstance();
359
360
// Tracer interface implementation
361
public TraceContext getCurrentContext();
362
public Propagator getPropagator();
363
public AttributeMap createAttributeMap();
364
}
365
```
366
367
### Null Implementation
368
369
No-op implementations for production environments where tracing is disabled.
370
371
```java { .api }
372
/**
373
* No-op tracer implementation for environments without tracing
374
*/
375
public class NullTracer implements Tracer {
376
377
public TraceContext getCurrentContext();
378
public Propagator getPropagator();
379
public AttributeMap createAttributeMap();
380
}
381
382
/**
383
* No-op span implementation
384
*/
385
public class NullSpan implements Span {
386
387
public Span setName(String name);
388
public Span addTag(String key, String value);
389
public Span addTag(String key, boolean value);
390
public Span addTag(String key, long value);
391
public Span addTag(String key, double value);
392
public Span addEvent(String name);
393
public Span addEvent(String name, AttributeMap attributeMap);
394
public Span setStatus(Status status);
395
public void close();
396
}
397
```
398
399
## Advanced Tracing Patterns
400
401
### Custom Span Creation
402
403
```java
404
// Manual span creation for custom operations
405
TraceContext context = tracer.getCurrentContext();
406
TraceContext customContext = context.createChild("custom-test-setup");
407
408
try (Span span = customContext.getActiveSpan()) {
409
span.setName("test-data-preparation")
410
.addTag("test.name", "loginTest")
411
.addTag("test.suite", "authentication");
412
413
// Perform setup operations
414
setupTestData();
415
416
span.addEvent("test-data-ready")
417
.setStatus(Status.OK);
418
}
419
```
420
421
### Distributed Context Propagation
422
423
```java
424
// Extract trace context from HTTP headers
425
Map<String, String> headers = getIncomingHttpHeaders();
426
Propagator propagator = tracer.getPropagator();
427
TraceContext extractedContext = propagator.extract(headers);
428
429
// Inject trace context into outgoing HTTP requests
430
Map<String, String> outgoingHeaders = new HashMap<>();
431
propagator.inject(tracer.getCurrentContext(), outgoingHeaders);
432
httpRequest.setHeaders(outgoingHeaders);
433
```
434
435
### Performance Monitoring
436
437
```java
438
// Monitor WebDriver operation performance
439
long startTime = System.currentTimeMillis();
440
441
try (Span span = context.createChild("page-load").getActiveSpan()) {
442
span.setName("navigate-and-wait")
443
.addTag("url", targetUrl)
444
.addTag("expected.title", expectedTitle);
445
446
driver.get(targetUrl);
447
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
448
wait.until(ExpectedConditions.titleContains(expectedTitle));
449
450
long duration = System.currentTimeMillis() - startTime;
451
span.addTag("duration.ms", duration)
452
.addTag("performance.slow", duration > 5000)
453
.setStatus(Status.OK);
454
455
if (duration > 5000) {
456
span.addEvent("slow-page-load-detected");
457
}
458
}
459
```
460
461
### Error Tracking and Debugging
462
463
```java
464
// Comprehensive error tracking in spans
465
try (Span span = context.createChild("element-interaction").getActiveSpan()) {
466
span.setName("click-element")
467
.addTag("element.locator", locator.toString())
468
.addTag("retry.attempt", retryCount);
469
470
try {
471
WebElement element = driver.findElement(locator);
472
element.click();
473
474
span.addTag("element.found", true)
475
.addTag("element.tag", element.getTagName())
476
.setStatus(Status.OK);
477
478
} catch (NoSuchElementException e) {
479
span.addTag("element.found", false)
480
.addTag("error.type", "NoSuchElementException")
481
.addTag("error.message", e.getMessage())
482
.addEvent("element-not-found")
483
.setStatus(Status.NOT_FOUND);
484
throw e;
485
486
} catch (ElementNotInteractableException e) {
487
span.addTag("element.found", true)
488
.addTag("element.interactable", false)
489
.addTag("error.type", "ElementNotInteractableException")
490
.addEvent("element-not-interactable")
491
.setStatus(Status.FAILED_PRECONDITION);
492
throw e;
493
}
494
}
495
```
496
497
### Integration with Test Frameworks
498
499
```java
500
// JUnit integration example
501
@BeforeEach
502
void setupTracing() {
503
TraceContext testContext = tracer.getCurrentContext()
504
.createChild(testInfo.getDisplayName());
505
506
Span testSpan = testContext.getActiveSpan();
507
testSpan.setName("test-execution")
508
.addTag("test.class", testInfo.getTestClass().get().getSimpleName())
509
.addTag("test.method", testInfo.getTestMethod().get().getName());
510
}
511
512
@AfterEach
513
void finishTracing(TestInfo testInfo) {
514
Span testSpan = tracer.getCurrentContext().getActiveSpan();
515
if (testExecutionResult.wasSuccessful()) {
516
testSpan.setStatus(Status.OK);
517
} else {
518
testSpan.addTag("test.failed", true)
519
.setStatus(Status.ABORTED);
520
}
521
testSpan.close();
522
}
523
```