Skip to content

Function Tracing with Annotations

For Kotlin projects, Tracy provides the @Trace annotation, which allows you to automatically trace any function. The Kotlin compiler plugin instruments annotated functions to capture execution details such as start and end time, duration, inputs, and outputs.

Technical Implementation

Tracy uses a Kotlin Compiler Plugin to transform annotated functions at compile time. For details on the IR transformation process, see the Compiler Plugin section.

Usage

To use annotation-based tracing, you must:

  1. Apply the org.jetbrains.ai.tracy plugin in your build.gradle.kts.
  2. Annotate your functions with @Trace.

Preserving Parameter Names

By default, the Kotlin compiler may not preserve parameter names in the generated bytecode, resulting in generic names like arg0, arg1, etc., in your traces. To ensure that Tracy can capture the actual parameter names, add the -java-parameters compiler option to your build.gradle.kts:

tasks.withType<KotlinCompile> {
    compilerOptions {
        freeCompilerArgs.addAll("-java-parameters")
    }
}

Basic Example

@Trace(name = "GreetUser")
fun greetUser(name: String): String {
    println("Hello, $name!")
    return "Greeting sent to $name"
}

Nested Spans

When one traced function calls another, Tracy automatically creates a hierarchical trace structure. The outer call is recorded as a parent span, and the inner call as its child span.

fun main() {
    outerFunction()
}

See the full example: NestedSpansExample.kt

Advanced Usage

Inheritance and Propagation

The @Trace annotation is automatically propagated through interfaces and class hierarchies. If you annotate a method in an interface, all implementations of that method will be traced automatically.

val service: Service = ServiceImpl()
service.execute("test") // This will be traced!

See the full example: TracingPropagationExample.kt

Customizing Span Metadata

You can customize how spans are named and how inputs/outputs are serialized by providing an implementation of the SpanMetadataCustomizer interface.

Must be a Kotlin object

SpanMetadataCustomizer implementations must be declared as Kotlin objects. Passing a class will throw a runtime error.

object MyCustomizer : SpanMetadataCustomizer {
    override fun formatInputAttributes(
        method: PlatformMethod,
        args: Array<Any?>
    ): String = "${method.name}(${args.joinToString { it.toString() }})"

    // Implement other methods as needed
}

@Trace(metadataCustomizer = MyCustomizer::class)
fun myCustomFunction(input: String) {
    // ...
}

See the full example: MetadataCustomizerExample.kt

Limitations

Annotation-based tracing has some limitations (local functions, Java interoperability). See the Limitations page for details.