0
# Tracing and Debugging
1
2
Comprehensive tracing system for monitoring and debugging atomic operations in concurrent code, providing detailed operation logs and custom formatting support.
3
4
## Capabilities
5
6
### Trace Factory Function
7
8
Creates trace objects for monitoring atomic operations with configurable size and formatting.
9
10
```kotlin { .api }
11
/**
12
* Creates Trace object for tracing atomic operations.
13
* @param size - Size of the circular trace buffer (default: 32)
14
* @param format - Custom trace formatter (default: traceFormatDefault)
15
* @returns TraceBase instance for tracing operations
16
*/
17
fun Trace(size: Int = 32, format: TraceFormat = traceFormatDefault): TraceBase
18
```
19
20
**Usage Examples:**
21
22
```kotlin
23
import kotlinx.atomicfu.*
24
25
class TracedCounter {
26
private val trace = Trace(64) // Larger buffer for more history
27
private val count = atomic(0, trace.named("count"))
28
29
fun increment(): Int {
30
trace { "Incrementing counter" }
31
return count.incrementAndGet()
32
}
33
34
fun add(delta: Int): Int {
35
trace { "Adding $delta to counter" }
36
return count.addAndGet(delta)
37
}
38
39
fun getTraceHistory(): String = trace.toString()
40
}
41
42
// Usage
43
val counter = TracedCounter()
44
counter.increment()
45
counter.add(5)
46
println(counter.getTraceHistory())
47
```
48
49
### Named Tracing
50
51
Adds meaningful names to traces for better debugging context.
52
53
```kotlin { .api }
54
/**
55
* Adds a name to the trace for better identification.
56
* @param name - Name prefix for all trace messages
57
* @returns Named TraceBase instance
58
*/
59
fun TraceBase.named(name: String): TraceBase
60
```
61
62
**Usage Examples:**
63
64
```kotlin
65
import kotlinx.atomicfu.*
66
67
class ConnectionPool {
68
private val trace = Trace(128)
69
private val activeConnections = atomic(0, trace.named("active"))
70
private val totalRequests = atomic(0L, trace.named("requests"))
71
72
fun borrowConnection(): Boolean {
73
totalRequests.incrementAndGet()
74
75
val current = activeConnections.value
76
if (current < 10) { // Max 10 connections
77
return activeConnections.compareAndSet(current, current + 1)
78
}
79
80
trace { "Connection pool exhausted, current: $current" }
81
return false
82
}
83
84
fun returnConnection() {
85
activeConnections.decrementAndGet()
86
trace { "Connection returned to pool" }
87
}
88
89
fun printTraceHistory() {
90
println("Connection Pool Trace:")
91
println(trace.toString())
92
}
93
}
94
```
95
96
### TraceBase Class
97
98
Base class for all trace implementations providing multiple append methods.
99
100
```kotlin { .api }
101
open class TraceBase {
102
/** Appends single event to the trace */
103
open fun append(event: Any)
104
105
/** Appends two events to the trace */
106
open fun append(event1: Any, event2: Any)
107
108
/** Appends three events to the trace */
109
open fun append(event1: Any, event2: Any, event3: Any)
110
111
/** Appends four events to the trace */
112
open fun append(event1: Any, event2: Any, event3: Any, event4: Any)
113
114
/** Functional style event appending */
115
inline operator fun invoke(event: () -> Any)
116
117
/** NOP tracing singleton */
118
object None : TraceBase()
119
}
120
```
121
122
**Usage Examples:**
123
124
```kotlin
125
import kotlinx.atomicfu.*
126
127
class MessageProcessor {
128
private val trace = Trace(256)
129
private val messagesProcessed = atomic(0L)
130
private val errors = atomic(0)
131
132
fun processMessage(messageId: String, content: String) {
133
val startTime = System.currentTimeMillis()
134
135
try {
136
// Multi-append for garbage-free logging
137
trace.append("Processing message", messageId, content.length, Thread.currentThread())
138
139
// Simulate processing
140
Thread.sleep(10)
141
142
messagesProcessed.incrementAndGet()
143
val endTime = System.currentTimeMillis()
144
145
trace.append("Message processed", messageId, endTime - startTime)
146
147
} catch (e: Exception) {
148
errors.incrementAndGet()
149
trace.append("Message failed", messageId, e.message ?: "Unknown error")
150
}
151
}
152
153
fun getStats(): String {
154
return "Processed: ${messagesProcessed.value}, Errors: ${errors.value}"
155
}
156
157
fun getTrace(): String = trace.toString()
158
}
159
```
160
161
### TraceFormat Class
162
163
Custom trace formatting for specialized logging requirements.
164
165
```kotlin { .api }
166
/**
167
* Trace string formatter for customizing trace output.
168
*/
169
open class TraceFormat {
170
/**
171
* Formats trace entry with index and event information.
172
* @param index - Sequential index of the trace entry
173
* @param event - Event object to format
174
* @returns Formatted string representation
175
*/
176
open fun format(index: Int, event: Any): String
177
}
178
179
/**
180
* Creates trace string formatter with custom format function.
181
* @param format - Lambda function for custom formatting
182
* @returns TraceFormat instance with custom formatting
183
*/
184
inline fun TraceFormat(crossinline format: (index: Int, event: Any) -> String): TraceFormat
185
```
186
187
**Usage Examples:**
188
189
```kotlin
190
import kotlinx.atomicfu.*
191
192
class CustomTracedService {
193
// Custom formatter with timestamp and thread info
194
private val customFormat = TraceFormat { index, event ->
195
val timestamp = System.currentTimeMillis()
196
val threadName = Thread.currentThread().name
197
"[$timestamp][$threadName][$index] $event"
198
}
199
200
private val trace = Trace(100, customFormat)
201
private val state = atomic("IDLE", trace.named("state"))
202
203
fun start() {
204
trace { "Service starting" }
205
state.value = "STARTING"
206
207
// Simulate startup work
208
Thread.sleep(100)
209
210
state.value = "RUNNING"
211
trace { "Service started successfully" }
212
}
213
214
fun stop() {
215
trace { "Service stopping" }
216
state.value = "STOPPING"
217
218
// Simulate cleanup work
219
Thread.sleep(50)
220
221
state.value = "STOPPED"
222
trace { "Service stopped" }
223
}
224
225
fun getDetailedTrace(): String = trace.toString()
226
}
227
```
228
229
### Default Trace Format
230
231
Pre-configured trace formatters with optional thread information.
232
233
```kotlin { .api }
234
/**
235
* The default trace string formatter.
236
* On JVM, when 'kotlinx.atomicfu.trace.thread' system property is set,
237
* the default format also includes thread name for each operation.
238
*/
239
val traceFormatDefault: TraceFormat
240
```
241
242
**Usage Examples:**
243
244
```kotlin
245
import kotlinx.atomicfu.*
246
247
// Enable thread names in trace (set system property)
248
// -Dkotlinx.atomicfu.trace.thread=true
249
250
class ThreadAwareService {
251
private val trace = Trace() // Uses traceFormatDefault
252
private val workItems = atomic(0, trace.named("work"))
253
254
fun doWork() {
255
trace { "Starting work on ${Thread.currentThread().name}" }
256
workItems.incrementAndGet()
257
258
// Simulate work
259
Thread.sleep(50)
260
261
trace { "Work completed" }
262
}
263
264
fun printTrace() {
265
println("Service Trace (with thread info if enabled):")
266
println(trace.toString())
267
}
268
}
269
270
// Usage with multiple threads
271
val service = ThreadAwareService()
272
273
// Create multiple worker threads
274
repeat(3) { threadId ->
275
Thread {
276
repeat(2) {
277
service.doWork()
278
}
279
}.apply {
280
name = "Worker-$threadId"
281
start()
282
}
283
}
284
```
285
286
## Advanced Tracing Patterns
287
288
### Conditional Tracing
289
290
Tracing that can be enabled/disabled at runtime:
291
292
```kotlin
293
import kotlinx.atomicfu.*
294
295
class ConditionalTracer {
296
private val debugEnabled = atomic(false)
297
private val trace = Trace(512)
298
private val operations = atomic(0L)
299
300
fun enableDebug() = debugEnabled.compareAndSet(false, true)
301
fun disableDebug() = debugEnabled.compareAndSet(true, false)
302
303
private fun debugTrace(event: () -> Any) {
304
if (debugEnabled.value) {
305
trace(event)
306
}
307
}
308
309
fun performOperation(operationName: String) {
310
debugTrace { "Starting operation: $operationName" }
311
312
operations.incrementAndGet()
313
314
// Simulate work
315
Thread.sleep(10)
316
317
debugTrace { "Completed operation: $operationName" }
318
}
319
320
fun getTrace(): String = if (debugEnabled.value) trace.toString() else "Tracing disabled"
321
}
322
```
323
324
### Performance Monitoring
325
326
Using traces for performance analysis:
327
328
```kotlin
329
import kotlinx.atomicfu.*
330
331
class PerformanceTracer {
332
private val performanceFormat = TraceFormat { index, event ->
333
val memoryUsage = Runtime.getRuntime().let { rt ->
334
(rt.totalMemory() - rt.freeMemory()) / 1024 / 1024
335
}
336
"[$index][${System.nanoTime()}][${memoryUsage}MB] $event"
337
}
338
339
private val trace = Trace(1000, performanceFormat)
340
private val operationCount = atomic(0L, trace.named("ops"))
341
342
fun timedOperation(name: String, block: () -> Unit) {
343
val startTime = System.nanoTime()
344
trace { "START: $name" }
345
346
try {
347
block()
348
operationCount.incrementAndGet()
349
} finally {
350
val duration = (System.nanoTime() - startTime) / 1_000_000 // Convert to ms
351
trace { "END: $name (${duration}ms)" }
352
}
353
}
354
355
fun getPerformanceReport(): String = trace.toString()
356
}
357
```
358
359
## Implementation Notes
360
361
### Trace Buffer Management
362
363
- Traces use circular buffers with configurable size
364
- Older entries are overwritten when buffer is full
365
- Buffer size should be power of 2 for optimal performance
366
- Larger buffers provide more history but use more memory
367
368
### Thread Safety
369
370
- All trace operations are thread-safe
371
- Multiple threads can append to the same trace concurrently
372
- Trace formatting is applied during toString() call
373
- No synchronization overhead during trace appending
374
375
### Performance Considerations
376
377
- Trace operations have minimal overhead when tracing is disabled (TraceBase.None)
378
- Lambda-based tracing (`trace { ... }`) only evaluates when tracing is enabled
379
- Multi-append methods are garbage-free for performance-critical code
380
- Custom formatters are only called during trace output, not during appending