> ## Documentation Index
> Fetch the complete documentation index at: https://docs.auditrails.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Java SDK — AuditRails Audit Logging and Integration

> Integrate AuditRails into any Java application. Zero dependencies, builder pattern, Spring Boot autoconfiguration, and AutoCloseable for safe shutdown.

The AuditRails Java SDK is a zero-dependency library that uses only `HttpURLConnection` from the standard library. It targets **Java 11+** and is designed for production use: a background thread drains the event buffer, exponential-backoff retries handle transient failures, and `AutoCloseable` ensures safe shutdown in Spring Boot applications and try-with-resources blocks alike.

## Installation

Add the dependency to your build file. The artifact is published to Maven Central.

<CodeGroup>
  ```xml Maven (pom.xml) theme={null}
  <dependency>
      <groupId>io.auditrails</groupId>
      <artifactId>auditrails-java</artifactId>
      <version>1.0.0</version>
  </dependency>
  ```

  ```groovy Gradle (build.gradle) theme={null}
  implementation 'io.auditrails:auditrails-java:1.0.0'
  ```

  ```kotlin Gradle Kotlin DSL (build.gradle.kts) theme={null}
  implementation("io.auditrails:auditrails-java:1.0.0")
  ```
</CodeGroup>

## Initialization

Use the fluent builder to create an `AuditRails` instance. The API key is the only required argument.

```java theme={null}
import io.auditrails.AuditRails;

AuditRails audit = AuditRails.builder("at_live_...")
    .baseUrl("https://api.auditrails.io")
    .batchSize(100)
    .flushIntervalMs(1000)
    .maxRetries(3)
    .timeoutMs(10_000)
    .build();
```

`AuditRails` implements `AutoCloseable`, so you can use it in a try-with-resources block for short-lived contexts (such as scripts or tests). In long-running applications, prefer Spring Boot's `destroyMethod` or a JVM shutdown hook — see [Graceful Shutdown](#graceful-shutdown).

### Configuration options

| Builder method          | Default                     | Description                                       |
| ----------------------- | --------------------------- | ------------------------------------------------- |
| `baseUrl(String)`       | `https://api.auditrails.io` | Override the API endpoint.                        |
| `batchSize(int)`        | `100`                       | Maximum events per HTTP request.                  |
| `flushIntervalMs(long)` | `1000` (ms)                 | How often the buffer is flushed, in milliseconds. |
| `maxRetries(int)`       | `3`                         | Retry attempts on `5xx` responses.                |
| `timeoutMs(int)`        | `10000` (ms)                | Per-request timeout in milliseconds.              |

## Logging events

### Buffered logging (recommended)

`audit.log()` adds an `AuditEvent` to the in-memory buffer and returns immediately. The buffer is flushed in the background on the `flushIntervalMs` schedule. This method **never throws**.

```java theme={null}
import io.auditrails.AuditEvent;

audit.log(AuditEvent.builder("user.login")
    .actorId("user_123")
    .resource("session/sess_abc")
    .metadata(Map.of("ip", "203.0.113.1", "method", "oauth2"))
    .build());
```

### Direct (immediate) logging

`audit.logDirect()` sends the event immediately and returns a `LogResponse`. It **throws `AuditRailsException`** on failure.

```java theme={null}
import io.auditrails.LogResponse;

LogResponse response = audit.logDirect(
    AuditEvent.builder("document.deleted")
        .actorId("user_456")
        .resource("document/doc-789")
        .build()
);

System.out.println(response.logId());     // "01HXYZ..."
System.out.println(response.requestId()); // AuditRails request ID for support
```

### Direct batch logging

Send multiple events in a single HTTP request, bypassing the buffer. Returns a `BatchResponse` with all assigned log IDs.

```java theme={null}
import io.auditrails.BatchResponse;

BatchResponse batch = audit.logBatchDirect(List.of(
    AuditEvent.builder("document.created").actorId("user_123").resource("document/doc-001").build(),
    AuditEvent.builder("document.shared").actorId("user_123").resource("document/doc-001").build()
));
System.out.println(batch.logIds()); // ["01HXYZ...", "01HABC..."]
```

### Manual flush

```java theme={null}
audit.flush(); // Blocks until the buffer is drained
```

## Framework integration

### Spring Boot

Declare `AuditRails` as a Spring-managed singleton bean. Setting `destroyMethod = "close"` ensures Spring calls `audit.close()` during application shutdown, flushing all buffered events before the JVM exits.

```java theme={null}
import io.auditrails.AuditRails;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AuditConfig {

    @Bean(destroyMethod = "close")
    public AuditRails auditRails(@Value("${auditrails.api-key}") String apiKey) {
        return AuditRails.builder(apiKey).build();
    }
}
```

```yaml # application.yml theme={null}
auditrails:
  api-key: ${AUDITRAILS_API_KEY}
```

### Spring MVC interceptor

Use a `HandlerInterceptor` to automatically audit every API request. `afterCompletion` fires after the response has been committed, so you always capture the final HTTP status.

```java theme={null}
import io.auditrails.AuditEvent;
import io.auditrails.AuditRails;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class AuditInterceptor implements HandlerInterceptor {

    private final AuditRails audit;

    public AuditInterceptor(AuditRails audit) {
        this.audit = audit;
    }

    @Override
    public void afterCompletion(
        HttpServletRequest request,
        HttpServletResponse response,
        Object handler,
        Exception ex
    ) {
        audit.log(AuditEvent.builder("api.request")
            .actorId(request.getHeader("X-User-ID"))
            .resource(request.getMethod() + " " + request.getRequestURI())
            .metadata(Map.of("status", response.getStatus()))
            .build());
    }
}
```

Register the interceptor in your `WebMvcConfigurer`:

```java theme={null}
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private final AuditInterceptor auditInterceptor;

    public WebConfig(AuditInterceptor auditInterceptor) {
        this.auditInterceptor = auditInterceptor;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(auditInterceptor);
    }
}
```

## Error handling

`log()` never throws. For `logDirect()` and `logBatchDirect()`, catch `AuditRailsException` to inspect the failure.

```java theme={null}
import io.auditrails.AuditRailsException;

try {
    audit.logDirect(AuditEvent.builder("user.login").actorId("user_123").build());
} catch (AuditRailsException e) {
    System.out.println(e.getStatusCode()); // HTTP status, e.g. 401
    System.out.println(e.getCode());       // Machine-readable code, e.g. "INVALID_API_KEY"
    System.out.println(e.getRequestId());  // AuditRails request ID for support
    System.out.println(e.getDocUrl());     // Link to relevant documentation
    System.out.println(e.isRetryable());   // true/false
}
```

## Graceful shutdown

`AuditRails` implements `java.lang.AutoCloseable`. Calling `close()` flushes all buffered events and stops the background flush thread.

**Spring Boot** (recommended): Use `@Bean(destroyMethod = "close")` as shown above. Spring will call `close()` automatically when the application context is shut down.

**Try-with-resources**: For short-lived use cases (scripts, tests, CLI tools):

```java theme={null}
try (AuditRails audit = AuditRails.builder(apiKey).build()) {
    audit.log(AuditEvent.builder("script.run").actorId("system").build());
} // close() is called automatically here
```

**Manual shutdown hook**: If you are not using Spring, register a JVM shutdown hook:

```java theme={null}
AuditRails audit = AuditRails.builder(apiKey).build();
Runtime.getRuntime().addShutdownHook(new Thread(audit::close));
```

<Note>
  Calling `close()` more than once is safe — subsequent calls are no-ops. Do not call `log()` after `close()` has been called; events submitted after shutdown are silently discarded.
</Note>
