# Java Integration Guide

> Add TraceKit to Java applications for distributed tracing and dynamic logs. Monitor Spring Boot, Micronaut, and Java microservices with production-safe dynamic logs.

- Canonical: https://tracekit.dev/docs/languages/java

Learn how to integrate the official TraceKit Java SDK into your applications for distributed tracing, code monitoring, and security scanning.

**Let AI set up TraceKit for you** -- AI assistants can guide you through the entire setup process. [Try AI Setup](/docs/ai)

## Prerequisites

- Java 8 or higher (Java 11+ recommended)
- A TraceKit account ([create one free](https://app.tracekit.dev/register))
- A generated API key from the [API Keys](https://app.tracekit.dev/api-keys) page

## What Gets Traced Automatically?

With the Java Agent, these operations are traced automatically with ZERO code changes:

| Component | Span Type | Captured Attributes | Auto-Traced? |
|-----------|-----------|-------------------|-------------|
| HTTP Endpoints | SERVER | method, route, status_code, duration, client_ip | Yes |
| JDBC Queries | DB | db.system, db.statement, db.name, duration | Yes |
| HTTP Client Calls | CLIENT | method, url, status_code, duration, peer.service | Yes |
| Spring Boot | SERVER | spring.handler, method, route | Yes (auto-config) |
| Kafka/RabbitMQ | Custom | messaging.system, messaging.destination, messaging.operation | Yes |
| Custom Business Logic | Custom | user-defined attributes | Manual |

## Installation

### Spring Boot (Recommended)

Add the Spring Boot starter dependency:

```xml
<dependency>
    <groupId>dev.tracekit</groupId>
    <artifactId>tracekit-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>
```

Or with Gradle:

```groovy
implementation 'dev.tracekit:tracekit-spring-boot-starter:1.3.1'
```

### Vanilla Java

Add the core SDK dependency:

```xml
<dependency>
    <groupId>dev.tracekit</groupId>
    <artifactId>tracekit-core</artifactId>
    <version>1.3.1</version>
</dependency>
```

Or with Gradle:

```groovy
implementation 'dev.tracekit:tracekit-core:1.3.1'
```

## Basic Setup

### Spring Boot Configuration

Configure in `application.yml`:

```yaml
tracekit:
  enabled: true
  api-key: ${TRACEKIT_API_KEY}
  service-name: my-service
  environment: production
  endpoint: https://app.tracekit.dev/v1/traces
  enable-code-monitoring: true  # Enables snapshot capture with security scanning
```

**Auto-Configuration!** The SDK auto-configures and starts tracing automatically. Spring Boot controllers, database queries, and HTTP clients are traced with zero code changes.

### Vanilla Java Initialization

Initialize the SDK programmatically:

```java
import dev.tracekit.TracekitSDK;
import dev.tracekit.TracekitConfig;

public class Application {
    public static void main(String[] args) {
        // Initialize SDK
        TracekitConfig config = TracekitConfig.builder()
            .apiKey(System.getenv("TRACEKIT_API_KEY"))
            .serviceName("my-service")
            .environment("production")
            .endpoint("https://app.tracekit.dev/v1/traces")
            .enableCodeMonitoring(true)
            .build();

        TracekitSDK sdk = TracekitSDK.create(config);

        // Your application code here...

        // Shutdown on application exit
        Runtime.getRuntime().addShutdownHook(new Thread(sdk::shutdown));
    }
}
```

**Verify It Works** -- Start your application and make a few requests. Then open the [Traces page](https://app.tracekit.dev/traces) in your TraceKit dashboard. You should see:

- SERVER spans for each incoming HTTP request (auto-configured for Spring Boot)
- DB spans for JDBC database queries
- CLIENT spans for outgoing HTTP calls

Traces typically appear within 30 seconds. If you don't see them, check the [Troubleshooting](#troubleshooting) section.

## Framework Integration

The TraceKit SDK automatically instruments popular frameworks with zero code changes.

### Spring Boot Example

Your existing Spring Boot controllers are automatically traced:

```java
// Your existing Spring Boot application - NO CHANGES NEEDED!
@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/users")
    public List<User> getUsers() {
        // This endpoint is automatically traced!
        return userService.findAll();
    }

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        // This endpoint is also automatically traced!
        return userService.save(user);
    }
}

// Just add the dependency and configure - everything is traced automatically!
```

### Spring Boot Auto-Configuration API

These classes are automatically registered when you add the Spring Boot starter dependency:

| Class | Type | Registration | Description |
|-------|------|-------------|-------------|
| `TracekitAutoConfiguration` | @Configuration | Auto (spring.factories) | Spring Boot auto-configuration class. Automatically creates TracekitSDK bean when `tracekit.api-key` is set. |
| `TracekitProperties` | @ConfigurationProperties | Auto | Maps Spring Boot application.properties/yml to TracekitConfig. |
| `TracekitWebMvcConfigurer` | WebMvcConfigurer | Auto | Registers HTTP interceptor for automatic SERVER span creation on all Spring MVC endpoints. |

### Spring Boot Auto-Configuration Example

Zero-configuration setup with Spring Boot:

```java
// application.yml -- TracekitAutoConfiguration creates the bean automatically
tracekit:
  api-key: ${TRACEKIT_API_KEY}
  service-name: my-service
  enable-code-monitoring: true

// TracekitWebMvcConfigurer auto-registers the HTTP interceptor
// No @Bean or @Configuration needed -- just add the dependency:
//   implementation 'dev.tracekit:tracekit-spring-boot-starter:1.3.1'

// Your controllers are automatically traced!
@RestController
public class OrderController {
    @GetMapping("/api/orders")
    public List<Order> listOrders() {
        return orderService.findAll(); // Auto-traced!
    }
}
```

## Code Monitoring & Security Scanning

Enable code monitoring to capture snapshots with automatic security scanning. The SDK detects and redacts sensitive information (PII, credentials, API keys) automatically.

```java
// Code monitoring is enabled via configuration
tracekit:
  enable-code-monitoring: true

// Use captureSnapshot() to capture variable state with security scanning
import dev.tracekit.TracekitSDK;

public class OrderService {
    private final TracekitSDK tracekit;

    public void processOrder(Order order) {
        // Capture snapshot with automatic security scanning
        tracekit.captureSnapshot("order_processing",
            Map.of(
                "orderId", order.getId(),
                "customerId", order.getCustomerId(),
                "total", order.getTotal()
            )
        );

        // Process order...
    }
}
```

**Automatic Security Scanning** -- Sensitive data is automatically detected and redacted before sending to the backend. This includes email addresses, phone numbers, SSNs, credit cards, API keys, passwords, and private keys.

### Code Monitoring API

Internal components used by the SDK for snapshot capture:

| Symbol | Type | Description |
|--------|------|-------------|
| `SnapshotClient` | Class (Internal) | Used internally for breakpoint polling and snapshot transmission. Use `captureSnapshot()` instead. |
| `CaptureConfig` | Class | Configuration for snapshot capture including PII scrubbing, custom redaction patterns, and rate limits. |
| `CircuitBreakerConfig` | Class | Circuit breaker thresholds: failure count (default 3), window (default 60s), cooldown (default 5min). |
| `SensitiveDataDetector` | Class | Scans snapshot data for 13 built-in PII patterns and replaces matches with `[REDACTED:type]` markers. |

### Security Scanning API

| Symbol | Type | Description |
|--------|------|-------------|
| `enableSecurityScanning(boolean)` | Builder method | Enable code-level security scanning for hardcoded secrets in snapshot data. |
| `SensitiveDataDetector` | Class (Advanced) | Scans source code for hardcoded API keys, passwords, and tokens. |

### End-to-End Workflow

1. **Enable Code Monitoring** -- Code monitoring defaults to **disabled** in Java. Enable via builder API (`enableCodeMonitoring(true)`) or Spring Boot property (`tracekit.enable-code-monitoring: true`).

2. **Add Snapshot Capture Points** -- Place `captureSnapshot()` calls at points of interest.

```java
tracekit.captureSnapshot("order-checkout", Map.of(
    "orderId", order.getId(),
    "total", order.getTotal(),
    "items", order.getItems().size(),
    "customer", customer.getEmail()
));
```

3. **Deploy and Verify Traces** -- Deploy via `mvn spring-boot:run` or as a JAR. Check the [Traces dashboard](https://app.tracekit.dev/traces).

4. **Navigate to Code Monitoring** -- Go to [/snapshots](https://app.tracekit.dev/snapshots) and click the "Browse Code" tab.

5. **Set Breakpoints** -- Breakpoints are auto-registered on the first `captureSnapshot()` call. You can also set them via the UI.

6. **Trigger Snapshot Capture** -- Make a request that hits a code path containing `captureSnapshot()`.

7. **View Captured Snapshot** -- Inspect variables, stack trace, request context, and security flags at [/snapshots](https://app.tracekit.dev/snapshots).

**Important:** Spring Boot properties use **kebab-case** (`enable-code-monitoring`) while Java code uses **camelCase** (`enableCodeMonitoring`). This is standard Spring Boot relaxed binding.

### Production Safety

v6.0 adds multiple layers of protection so code monitoring is safe for production workloads:

- **PII Scrubbing** -- 13 built-in patterns via `SensitiveDataDetector`, enabled by default. Matched values are replaced with typed `[REDACTED:type]` markers before data leaves the process.
- **Crash Isolation** -- Every capture entry point is wrapped in `catch(Throwable)` so a failure in the SDK never propagates to your application code.
- **Circuit Breaker** -- After 3 failures within 60 seconds the SDK enters a 5-minute cooldown, then auto-recovers. Uses a `synchronized` thread-safe implementation.
- **Remote Kill Switch** -- Toggle code monitoring on or off from the TraceKit dashboard. The change propagates to all connected SDK instances via SSE (daemon thread) in real time.

**Real-Time Updates via SSE** -- The Java SDK opens a Server-Sent Events connection using a `BufferedReader` on a daemon thread. Breakpoint enables/disables, kill-switch toggles, and configuration changes are pushed instantly -- no polling delay.

[Full Code Monitoring Documentation](/docs/code-monitoring)

### v25: Enhanced Code Monitoring

#### Log-Point Mode

When a breakpoint is set to **log-point mode** in the dashboard, the SDK captures only the specified expressions -- no local variables, stack traces, or request context. This dramatically reduces overhead for high-frequency code paths.

No code changes needed. The SDK reads the breakpoint's `mode` field from the server and adjusts capture behavior automatically.

#### SDK-Side Condition Evaluation

Breakpoint conditions (e.g., `status > 200`) are now evaluated **locally in the SDK** without a server round-trip. The server classifies each condition as `sdk-evaluable` or `server-only` at creation time. Simple expressions (comparisons, boolean logic, property access) are evaluated in-process.

No code changes needed. The SDK checks the `condition_eval` field and evaluates locally when possible.

#### Per-Breakpoint Capture Limits

New breakpoints default to:
- **max_depth: 5** -- variables nested deeper than 5 levels are truncated
- **max_payload_bytes: 128KB** -- snapshots larger than 128KB are truncated

These limits are configurable per breakpoint in the dashboard. The SDK respects per-breakpoint overrides automatically.

#### Auto-Expire

New breakpoints **auto-expire after 48 hours of inactivity**. Each capture resets the timer. Pin a breakpoint in the dashboard to prevent auto-expiry.

Existing breakpoints are not affected -- auto-expire only applies to breakpoints created after the v25 update.

## Manual Instrumentation (Optional)

For custom business logic not automatically traced, you can manually create spans using the OpenTelemetry API:

```java
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Scope;

public class OrderService {
    private static final Tracer tracer =
        GlobalOpenTelemetry.getTracer("order-service");

    public void processOrder(String orderId) {
        // Start a parent span
        Span span = tracer.spanBuilder("process_order")
            .setAttribute("order.id", orderId)
            .startSpan();

        try (Scope scope = span.makeCurrent()) {
            // Child spans automatically link to parent
            validateOrder(orderId);
            chargePayment(orderId);

            span.setAttribute("order.status", "completed");
        } finally {
            span.end();
        }
    }

    private void validateOrder(String orderId) {
        Span span = tracer.spanBuilder("validate_order")
            .setAttribute("order.id", orderId)
            .startSpan();
        try (Scope scope = span.makeCurrent()) {
            // Validation logic here
        } finally {
            span.end();
        }
    }
}
```

### Core Tracing API

| Method | Parameters | Returns | Description |
|--------|-----------|---------|-------------|
| `getOpenTelemetry()` | none | OpenTelemetry | Returns the underlying OpenTelemetry instance for advanced usage. |
| `getServiceName()` | none | String | Returns the configured service name. |

## LLM Instrumentation

TraceKit can instrument OpenAI and Anthropic API calls made via OkHttp. LLM calls appear as spans with [OTel GenAI semantic convention](https://opentelemetry.io/docs/specs/semconv/gen-ai/) attributes.

**Manual setup required** -- Add the TraceKit LLM interceptor to your OkHttp client to instrument OpenAI and Anthropic API calls. The interceptor captures model, tokens, cost, and latency automatically.

### Setup

```java
import dev.tracekit.llm.TracekitLlmInterceptor;
import okhttp3.OkHttpClient;

// Create an OkHttpClient with TraceKit LLM interceptor
OkHttpClient llmClient = new OkHttpClient.Builder()
    .addInterceptor(new TracekitLlmInterceptor())
    .build();

// Use this client with your OpenAI/Anthropic SDK
// OpenAI example:
OpenAIClient openai = OpenAIClient.builder()
    .httpClient(llmClient)
    .apiKey(System.getenv("OPENAI_API_KEY"))
    .build();
```

### Configuration

```java
TracekitLlmInterceptor interceptor = new TracekitLlmInterceptor(LlmConfig.builder()
    .captureContent(false)  // Capture prompts/completions (off by default)
    .openai(true)           // OpenAI instrumentation
    .anthropic(true)        // Anthropic instrumentation
    .build());
```

### Captured Attributes

| Attribute | Description |
|-----------|-------------|
| `gen_ai.system` | Provider name (openai, anthropic) |
| `gen_ai.request.model` | Model name (gpt-4o, claude-sonnet-4-20250514, etc.) |
| `gen_ai.usage.input_tokens` | Prompt token count |
| `gen_ai.usage.output_tokens` | Completion token count |
| `gen_ai.response.finish_reasons` | Finish reason (stop, end_turn, tool_calls) |

### Environment Variable Override

Use `TRACEKIT_LLM_CAPTURE_CONTENT=true` to enable prompt/completion capture without code changes. Useful for enabling in staging but not production.

### Streaming Support

Streaming responses produce a single span that covers the entire stream. Token counts are accumulated from the final chunk. No special configuration needed.

### LLM Dashboard

View LLM cost, token usage, and latency metrics on the dedicated **LLM Observability** dashboard at `/ai/llm` in your TraceKit instance.

## Environment Variables

Best practice: Store sensitive configuration in environment variables:

```bash
# .env or environment variables
TRACEKIT_API_KEY=ctxio_your_generated_api_key_here
OTEL_SERVICE_NAME=my-java-app
OTEL_EXPORTER_OTLP_ENDPOINT=https://app.tracekit.dev/v1/traces
```

### Core Configuration (TracekitConfig)

| Option | Type | Default | Env Variable | Description |
|--------|------|---------|-------------|-------------|
| `apiKey` | String | *required* | `TRACEKIT_API_KEY` | Your TraceKit API key for authentication |
| `serviceName` | String | *required* | `TRACEKIT_SERVICE_NAME` | Name of your service in the trace dashboard |
| `endpoint` | String | "https://app.tracekit.dev/v1/traces" | `TRACEKIT_ENDPOINT` | TraceKit collector endpoint URL |
| `environment` | String | "production" | `TRACEKIT_ENVIRONMENT` | Deployment environment (e.g. production, staging) |
| `enableCodeMonitoring` | boolean | false | `TRACEKIT_CODE_MONITORING_ENABLED` | Enable live code debugging / snapshot capture |
| `enableSecurityScanning` | boolean | false | `TRACEKIT_SECURITY_SCANNING_ENABLED` | Enable security scanning for code analysis |
| `localUIPort` | int | 9999 | `TRACEKIT_LOCAL_UI_PORT` | Port for the local development UI |

### Spring Boot Properties

| Property | Type | Default | Description |
|----------|------|---------|-------------|
| `tracekit.enabled` | boolean | true | Enable or disable TraceKit auto-configuration |
| `tracekit.api-key` | String | *required* | Your TraceKit API key |
| `tracekit.service-name` | String | *required* | Name of your service |
| `tracekit.endpoint` | String | "https://app.tracekit.dev/v1/traces" | TraceKit collector endpoint URL |
| `tracekit.environment` | String | "production" | Deployment environment |
| `tracekit.enable-code-monitoring` | boolean | false | Enable live code debugging |
| `tracekit.enable-security-scanning` | boolean | false | Enable security scanning |
| `tracekit.local-ui-port` | int | 9999 | Port for local development UI |

Spring Boot auto-reads properties from `application.properties` or `application.yml`. For standalone Java, use environment variables with manual loading.

## Production Configuration

**Production Checklist:**
- Use HTTPS/TLS for the OTLP endpoint
- Store API keys in a secrets manager (AWS Secrets Manager, HashiCorp Vault)
- Set appropriate service names and versions
- Configure resource attributes (deployment.environment, host.name, etc.)
- Adjust sampling rates if needed for high-traffic services
- Monitor Java Agent overhead (typically less than 5%)

## Custom Metrics

Track custom metrics like request counts, queue sizes, and response times using the TraceKit metrics API.

### Counter

Track monotonically increasing values (requests, events):

```java
import dev.tracekit.TracekitSDK;
import dev.tracekit.metrics.Counter;
import java.util.Map;

// Get the SDK (injected in Spring Boot or created manually)
TracekitSDK sdk = ...;

// Create a counter with optional tags
Counter counter = sdk.counter("http.requests.total",
    Map.of("service", "api"));

// Increment by 1
counter.inc();

// Add a specific value
counter.add(5.0);
```

### Gauge

Track values that can go up or down (queue size, connections):

```java
import dev.tracekit.metrics.Gauge;

// Create a gauge
Gauge gauge = sdk.gauge("http.connections.active", Map.of());

// Set to specific value
gauge.set(42.0);

// Increment/decrement
gauge.inc();
gauge.dec();
```

### Histogram

Track value distributions (latencies, sizes):

```java
import dev.tracekit.metrics.Histogram;

// Create a histogram with tags
Histogram histogram = sdk.histogram("http.request.duration",
    Map.of("unit", "ms"));

// Record values
histogram.record(45.2);
histogram.record(123.5);
```

[View Custom Metrics Guide](/docs/metrics)

## Troubleshooting

### Traces not appearing?

**Cause:** The SDK is not initialized or the endpoint URL is incorrect.

**Fix:**
- Verify TracekitConfig is created with correct apiKey and endpoint
- For Spring Boot, check `application.properties` has `tracekit.api-key` (kebab-case, NOT `tracekit.apiKey`)
- Look for OpenTelemetry errors in application logs

### -javaagent not working?

**Cause:** JVM argument ordering matters -- the agent must be loaded before the application.

**Fix:**
- Place `-javaagent:/path/to/opentelemetry-javaagent.jar` BEFORE `-jar myapp.jar` in your command
- Verify with `java -javaagent:otel.jar -jar myapp.jar` (not `java -jar myapp.jar -javaagent:otel.jar`)

### Code monitoring not working?

**Cause:** Code monitoring defaults to disabled in the Java SDK.

**Fix:**
- Set `enableCodeMonitoring(true)` in TracekitConfig builder
- For Spring Boot: `tracekit.enable-code-monitoring=true` in application.properties
- Add `TracekitSDK.captureSnapshot("label")` calls in target code
- Verify permissions in dashboard

### Spring Boot property names not recognized?

**Cause:** Spring Boot uses kebab-case for properties, not camelCase.

**Fix:**
- Use `tracekit.api-key` not `tracekit.apiKey`
- Use `tracekit.service-name` not `tracekit.serviceName`
- Use `tracekit.enable-code-monitoring` not `tracekit.enableCodeMonitoring`
- Spring Boot's relaxed binding handles some variations but kebab-case is the canonical form

### Authentication errors (401/403)?

**Cause:** API key is wrong or has extra characters.

**Fix:**
- For Spring Boot, verify in application.properties (no quotes needed around values)
- For standalone Java, trim whitespace from env var
- Regenerate key in dashboard if expired
- Check that the full endpoint URL includes the path: `https://app.tracekit.dev/v1/traces`

## Complete Example

Here's a complete working Spring Boot example:

```java
// Complete Spring Boot example with TraceKit SDK

// pom.xml
<dependency>
    <groupId>dev.tracekit</groupId>
    <artifactId>tracekit-spring-boot-starter</artifactId>
    <version>1.3.1</version>
</dependency>

// application.yml
tracekit:
  enabled: true
  api-key: ${TRACEKIT_API_KEY}
  service-name: spring-api
  environment: production
  enable-code-monitoring: true

// Application.java
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

// UserController.java - automatically traced!
@RestController
@RequestMapping("/api")
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("/users")
    public List<User> getUsers() {
        // Automatically traced - no code changes needed!
        return userRepository.findAll();
    }

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        // Automatically traced - no code changes needed!
        return userRepository.save(user);
    }
}

// That's it! Run normally: mvn spring-boot:run
```

**You're all set!** Your Java application is now sending traces to TraceKit. Visit the [Dashboard](https://app.tracekit.dev) to see your traces.

## Migrating from OpenTelemetry

TraceKit wraps OpenTelemetry internally, so you get the same standards-based trace data with significantly less setup code.

### Before vs After

**Before: Raw OpenTelemetry** (~40 lines):

```java
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.ResourceAttributes;

OtlpGrpcSpanExporter exporter = OtlpGrpcSpanExporter.builder()
    .setEndpoint("https://api.tracekit.io")
    .build();
SdkTracerProvider tracerProvider = SdkTracerProvider.builder()
    .addSpanProcessor(BatchSpanProcessor.builder(exporter).build())
    .setResource(Resource.getDefault().toBuilder()
        .put(ResourceAttributes.SERVICE_NAME, "my-service")
        .build())
    .build();
OpenTelemetrySdk.builder()
    .setTracerProvider(tracerProvider)
    .buildAndRegisterGlobal();
```

**After: TraceKit SDK** (6 lines):

```yaml
# application.yml (Spring Boot)
tracekit:
  api-key: ${TRACEKIT_API_KEY}
  service-name: my-service
  enabled: true
```

### Migration Steps

1. **Add dependency:** Maven or Gradle: `dev.tracekit:tracekit-spring-boot-starter`
2. **For Spring Boot:** Add YAML config. For programmatic: replace OTel init with `TracekitSDK.create()`
3. **Remove agent flags:** If using the OTel Java agent, remove `-javaagent` JVM flags
4. **Remove OTel dependencies:** Remove `opentelemetry-api`, `opentelemetry-sdk`, and `opentelemetry-exporter-otlp`
5. **Verify:** Start your app and check the [Traces page](https://app.tracekit.dev/traces) for incoming data

**Key Migration Benefits:**
- **40 lines to 6 lines** -- Spring Boot auto-configuration replaces manual SDK setup
- **No OTel dependency management** -- TraceKit handles version pinning internally
- **Built-in code monitoring** -- not available with raw OpenTelemetry
- **Built-in security scanning** -- automatic variable redaction on snapshots
- **Spring Boot starter** -- auto-configuration with YAML properties

## Performance Overhead

TraceKit is built on OpenTelemetry's efficient batch processing pipeline. The SDK adds minimal overhead to your Java application.

| Metric | Impact | Details |
|--------|--------|---------|
| Request Tracing | < 1ms per request | Spans are batched and exported asynchronously |
| Code Monitoring (Idle) | Zero overhead | No performance impact when no active breakpoints |
| Code Monitoring (Capture) | < 5ms per snapshot | Includes variable serialization and security scanning |
| Memory Footprint | ~20-40 MB | SDK runtime and span buffer |
| SDK Initialization | < 1s one-time | Includes OpenTelemetry provider setup and Spring Boot auto-configuration |

**Java Agent:** Monitor Java Agent overhead is typically less than 5% CPU. The agent uses bytecode instrumentation to minimize runtime impact.

Performance characteristics are typical for production workloads and may vary with application complexity, request volume, and number of instrumented libraries. Use [sampling configuration](#environment-variables) to reduce overhead in high-traffic services.

## Best Practices

**DO:**
- Use Spring Boot auto-configuration when possible -- just add the dependency and configure via `application.yml`
- Use `application.yml` / `application.properties` for config in Spring Boot apps
- Use environment variables for API keys -- store in `TRACEKIT_API_KEY` rather than hardcoding
- Call shutdown/flush on application exit -- Spring Boot handles this via `@PreDestroy`
- Enable code monitoring in staging first before production
- Use sampling in high-traffic services -- set `TRACEKIT_SAMPLING_RATE` below 1.0
- Set meaningful service names for easy identification in the trace viewer

**DON'T:**
- Mix Java agent and SDK initialization -- use one approach, not both
- Use `TracekitSDK.create()` manually in Spring Boot apps -- auto-configuration handles it
- Create spans for every function -- trace boundaries like HTTP handlers, DB calls, and external services
- Add high-cardinality attributes -- avoid user IDs, request IDs, or session tokens as span attributes
- Disable TLS in production -- the `TRACEKIT_INSECURE` flag is for local development only

## Next Steps

- Explore your traces on the [Traces page](https://app.tracekit.dev/traces) to identify performance bottlenecks
- Optionally add custom spans for specific business logic you want to measure
- Configure sampling for high-traffic services to reduce overhead
- Set up [alert rules](/docs/alerts) to get notified when issues occur
