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

# Audit

> Audit logging middleware for tracking and recording all HTTP requests.

## Overview

The `audit` middleware captures detailed information about every HTTP request for compliance, debugging, and security monitoring. It provides flexible handlers for storing audit logs.

Use it when you need:

* Compliance audit trails
* Security event logging
* Request debugging and analysis

## Installation

```go theme={null}
import "github.com/go-mizu/mizu/middlewares/audit"
```

## Quick Start

```go theme={null}
app := mizu.New()

// Log all requests
app.Use(audit.New(func(entry *audit.Entry) {
    log.Printf("%s %s -> %d", entry.Method, entry.Path, entry.Status)
}))
```

## Configuration

### Options

| Option               | Type                                | Default  | Description               |
| -------------------- | ----------------------------------- | -------- | ------------------------- |
| `Handler`            | `func(*Entry)`                      | Required | Callback for each entry   |
| `Skip`               | `func(*mizu.Ctx) bool`              | `nil`    | Skip logging for requests |
| `IncludeRequestBody` | `bool`                              | `false`  | Include request body      |
| `MaxBodySize`        | `int`                               | `1024`   | Max body size to capture  |
| `Metadata`           | `func(*mizu.Ctx) map[string]string` | `nil`    | Custom metadata           |

### Entry Fields

| Field         | Type                | Description               |
| ------------- | ------------------- | ------------------------- |
| `Timestamp`   | `time.Time`         | Request time              |
| `Method`      | `string`            | HTTP method               |
| `Path`        | `string`            | Request path              |
| `Query`       | `string`            | Query string              |
| `Status`      | `int`               | Response status           |
| `Latency`     | `time.Duration`     | Request duration          |
| `IP`          | `string`            | Client IP                 |
| `UserAgent`   | `string`            | User-Agent header         |
| `RequestID`   | `string`            | X-Request-ID header       |
| `RequestBody` | `string`            | Request body (if enabled) |
| `Metadata`    | `map[string]string` | Custom metadata           |

## Examples

### Basic Logging

```go theme={null}
app.Use(audit.New(func(entry *audit.Entry) {
    fmt.Printf("[%s] %s %s %d %v\n",
        entry.Timestamp.Format(time.RFC3339),
        entry.Method,
        entry.Path,
        entry.Status,
        entry.Latency,
    )
}))
```

### With Request Body

```go theme={null}
app.Use(audit.WithOptions(audit.Options{
    Handler: func(entry *audit.Entry) {
        log.Printf("Body: %s", entry.RequestBody)
    },
    IncludeRequestBody: true,
    MaxBodySize:        4096,
}))
```

### Skip Health Checks

```go theme={null}
app.Use(audit.WithOptions(audit.Options{
    Handler: logEntry,
    Skip: func(c *mizu.Ctx) bool {
        return c.Request().URL.Path == "/health"
    },
}))
```

### With Custom Metadata

```go theme={null}
app.Use(audit.WithOptions(audit.Options{
    Handler: logEntry,
    Metadata: func(c *mizu.Ctx) map[string]string {
        return map[string]string{
            "version": "1.0",
            "env":     os.Getenv("ENV"),
            "user_id": getUserID(c),
        }
    },
}))
```

### Channel Handler (Async)

```go theme={null}
ch := make(chan *audit.Entry, 100)

// Consumer goroutine
go func() {
    for entry := range ch {
        // Process entries asynchronously
        saveToDatabase(entry)
    }
}()

app.Use(audit.New(audit.ChannelHandler(ch)))
```

### Buffered Handler (Batch)

```go theme={null}
handler := audit.NewBufferedHandler(
    100,           // Batch size
    time.Minute,   // Flush interval
    func(entries []*audit.Entry) {
        // Process batch
        bulkInsert(entries)
    },
)
defer handler.Close()

app.Use(audit.New(handler.Handler()))
```

## API Reference

### Functions

```go theme={null}
// New creates audit middleware with handler
func New(handler func(*Entry)) mizu.Middleware

// WithOptions creates middleware with configuration
func WithOptions(opts Options) mizu.Middleware

// ChannelHandler creates async handler
func ChannelHandler(ch chan *Entry) func(*Entry)

// NewBufferedHandler creates batching handler
func NewBufferedHandler(maxSize int, interval time.Duration, flush func([]*Entry)) *BufferedHandler
```

### BufferedHandler Methods

```go theme={null}
func (h *BufferedHandler) Handler() func(*Entry)
func (h *BufferedHandler) Flush()
func (h *BufferedHandler) Close()
```

## Technical Details

### Implementation Architecture

The audit middleware is built around a flexible handler pattern that processes audit entries containing comprehensive request/response metadata.

#### Core Components

**Entry Structure**: The `Entry` type captures all relevant information about an HTTP request/response cycle:

* Request metadata (timestamp, method, path, query, remote address, user agent)
* Request identification (request ID from configurable header)
* Optional request body capture with size limits
* Response status and latency measurement
* Error information if handler returns an error
* Custom metadata extension point

**Response Writer Wrapping**: The middleware uses a custom `auditResponseWriter` that wraps the standard `http.ResponseWriter` to capture the response status code. This wrapper intercepts `WriteHeader` and `Write` calls to record the status, defaulting to 200 OK if not explicitly set.

**Request Body Capture**: When enabled, the middleware reads the request body up to `MaxBodySize` using `io.LimitReader`, then restores it using `io.MultiReader` so downstream handlers can still access it. This ensures body capture doesn't interfere with normal request processing.

**Timing Measurement**: Latency is calculated by recording the start time before calling the next handler and computing the elapsed duration after it returns. This provides accurate end-to-end request processing time.

#### Handler Patterns

**Synchronous Handler**: The basic handler function receives each entry immediately and processes it in the request path. Suitable for logging to stdout or fast operations.

**Channel Handler**: Sends entries to a buffered channel for asynchronous processing. Uses a non-blocking select with default case to drop entries if the channel is full, preventing request blocking.

**Buffered Handler**: Collects entries in memory and flushes them in batches either when reaching `maxSize` or on a timer interval. Includes proper mutex protection for concurrent access and a background goroutine for periodic flushing.

### Configuration Defaults

* `RequestIDHeader`: "X-Request-ID"
* `MaxBodySize`: 1024 bytes
* `Handler`: Defaults to encoding JSON to `io.Discard` if not specified
* `IncludeRequestBody`: false (opt-in for performance and privacy)

## Best Practices

* Use async handlers for high-traffic applications
* Set reasonable `MaxBodySize` to prevent memory issues
* Skip logging for health checks and metrics endpoints
* Include request IDs for correlation with other logs
* Buffer writes for database storage

## Testing

The audit middleware includes comprehensive test coverage for all functionality:

| Test Case                     | Description                            | Expected Behavior                                                                                  |
| ----------------------------- | -------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `TestNew`                     | Basic middleware creation with handler | Captures all entry fields (method, path, query, user agent, request ID, status, latency) correctly |
| `TestWithOptions_RequestBody` | Request body capture with size limit   | Reads and stores request body up to MaxBodySize, body remains readable by handler                  |
| `TestWithOptions_Skip`        | Conditional logging with skip function | Skips audit logging for matched requests (/health), logs others normally                           |
| `TestWithOptions_Metadata`    | Custom metadata injection              | Adds custom key-value pairs to entry metadata via callback function                                |
| `TestWithOptions_Error`       | Error handling and status capture      | Records error status codes (500) correctly when handler returns error                              |
| `TestChannelHandler`          | Async channel-based handler            | Sends entries to channel without blocking request processing                                       |
| `TestBufferedHandler`         | Batch processing with size trigger     | Buffers entries and flushes when batch size (3) is reached                                         |
| `TestBufferedHandler_Flush`   | Manual flush operation                 | Immediately flushes buffered entries when Flush() is called                                        |
| `TestBufferedHandler_Handler` | Handler function retrieval             | Returns valid handler function for middleware integration                                          |

## Related Middlewares

* [logger](/middlewares/logger) - Simple request logging
* [requestid](/middlewares/requestid) - Request ID generation
* [timing](/middlewares/timing) - Performance timing
