# How to Configure and Leverage CoSky's Audit Logging Feature

> Learn how to configure and leverage CoSky's audit logging feature. Automatically record API requests to Redis for real-time dashboard monitoring. Get started today.

- Repository: [Ahoo Wang/cosky](https://github.com/ahoo-wang/cosky)
- Tags: how-to-guide
- Published: 2026-02-23

---

**CoSky's audit logging automatically records REST API requests to Redis and exposes them via a paginated HTTP endpoint for real-time dashboard monitoring.**

CoSky provides a built-in audit logging mechanism that tracks every write-type request processed by the REST API. As implemented in the `ahoo-wang/cosky` repository, this feature stores logs in Redis under a system-wide key and surfaces them through a dedicated endpoint consumed by the dashboard UI. The implementation requires minimal configuration while providing comprehensive visibility into system operations.

## Understanding CoSky's Audit Logging Architecture

CoSky's audit logging system consists of several coordinated components that capture, persist, and serve audit records.

### Core Components

- **`SecurityProperties`**: Holds the configuration flag `auditLog.action` (default `WRITE`) in [`cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/SecurityProperties.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/SecurityProperties.kt). When the request action matches the configured `Action`, the interceptor generates an audit record.

- **`AuditLogHandlerInterceptor`**: A Spring WebFlux `WebFilter` implemented in [`cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/audit/AuditLogHandlerInterceptor.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/audit/AuditLogHandlerInterceptor.kt). It runs after request processing to extract the operator, IP, path, HTTP method, status, and error message, then forwards the data to `AuditLogService`.

- **`AuditLogService`**: Persists audit entries to Redis using `ReactiveStringRedisTemplate` in [`cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/audit/AuditLogService.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/audit/AuditLogService.kt). It stores records in the list key `Namespaced.SYSTEM + ":audit:log"` (e.g., `cosky:system:audit:log`) and provides `addLog`, `queryLog`, and `total` operations.

- **`AuditLogController`**: Exposes the REST endpoint `GET /v1/audit-log` (prefix defined in `RequestPathPrefix.AUDIT_LOG_PREFIX`) in [`cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/audit/AuditLogController.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/audit/AuditLogController.kt), returning paginated `QueryLogResponse` objects.

- **Dashboard UI**: The React component [`AuditLogPage.tsx`](https://github.com/ahoo-wang/cosky/blob/main/AuditLogPage.tsx) in [`dashboard/src/pages/audit/AuditLogPage.tsx`](https://github.com/ahoo-wang/cosky/blob/main/dashboard/src/pages/audit/AuditLogPage.tsx) calls the generated client to fetch logs and displays them in a paginated table.

## Configuring Audit Logging in CoSky

Enable and customize audit logging through Spring Boot configuration properties.

### Application Configuration

Add the `cosky.security.audit-log.action` property to your [`application.yaml`](https://github.com/ahoo-wang/cosky/blob/main/application.yaml):

```yaml
cosky:
  security:
    audit-log:
      # Allowed values: WRITE (default) | READ | ALL

      action: WRITE

```

### Action Types

- **`WRITE`**: Records only write operations (POST, PUT, DELETE, PATCH). This is the default behavior.
- **`READ`**: Records only read operations (GET requests).
- **`ALL`**: Records every request regardless of HTTP method.

### Redis Storage Configuration

Audit logs persist in the same Redis instance used by other CoSky modules. No additional configuration is required beyond standard Redis connection settings (`cosky.redis.*`). The system stores entries under the key pattern `cosky:system:audit:log`.

## How CoSky Audit Logging Works

The audit logging pipeline executes automatically once enabled, requiring no manual intervention for standard REST operations.

### Step 1: Request Interception

After the controller chain finishes processing an incoming request, `AuditLogHandlerInterceptor.filter` executes. The interceptor checks the configured `auditLog.action` against the current request method to determine if logging is required.

### Step 2: Data Extraction

For qualifying requests, the interceptor extracts:
- **Operator**: From the JWT principal or authentication path (`/v1/authenticate/{username}`)
- **Client IP**: The remote address of the request
- **Request Path**: The accessed URI
- **HTTP Method**: The action type
- **Response Status**: The HTTP status code
- **Error Message**: Any error details if the request failed

### Step 3: Redis Persistence

The interceptor builds an `AuditLog` instance and calls `auditService.addLog(auditLog)`. The `AuditLogService.addLog` method serializes the record with Jackson and pushes it to the Redis list using `leftPush` to the key `cosky:system:audit:log`.

### Step 4: Log Retrieval

The dashboard UI or external clients call `GET /v1/audit-log?offset=0&limit=10`. The `AuditLogController` aggregates the total count via `auditService.total` and retrieves paginated results via `auditService.queryLog`, returning a `QueryLogResponse`.

## Querying and Consuming Audit Logs

Access audit data through multiple interfaces depending on your integration requirements.

### Via REST API

Query logs directly using the paginated endpoint:

```bash
curl "http://localhost:8080/v1/audit-log?offset=0&limit=20" \
  -H "Authorization: Bearer ${JWT_TOKEN}"

```

The endpoint returns a JSON `QueryLogResponse` containing the total count and log entries.

### Via Generated TypeScript Client

For TypeScript applications, use the generated client in [`dashboard/src/generated/AuditLogApiClient.ts`](https://github.com/ahoo-wang/cosky/blob/main/dashboard/src/generated/AuditLogApiClient.ts):

```typescript
import { auditLogApiClient } from '@/generated/AuditLogApiClient';

async function fetchAuditLogs(page: number, size: number) {
  const response = await auditLogApiClient.queryLog(page, size);
  console.log('Total logs:', response.total);
  return response.list;
}

// Fetch first 20 entries
fetchAuditLogs(0, 20);

```

### Dashboard Integration

The built-in dashboard renders logs using the React component [`AuditLogPage.tsx`](https://github.com/ahoo-wang/cosky/blob/main/AuditLogPage.tsx). The component utilizes `useQuery` to fetch data via `auditLogApiClient.queryLog` and displays results in a paginated table with navigation controls.

## Programmatic Audit Log Examples

Extend or customize audit logging using the service layer directly.

### Adding Custom Audit Entries in Kotlin

Inject `AuditLogService` to manually log operations:

```kotlin
import me.ahoo.cosky.rest.security.audit.AuditLog
import me.ahoo.cosky.rest.security.audit.AuditLogService
import reactor.core.publisher.Mono

fun logCustomOperation(auditService: AuditLogService): Mono<Long> {
    val log = AuditLog(
        operator = "system",
        ip = "127.0.0.1",
        requestPath = "/custom/operation",
        action = "POST",
        status = 200,
        msg = "Custom operation executed",
        opTime = System.currentTimeMillis()
    )
    return auditService.addLog(log)
}

```

### React Component Implementation

Implement custom audit log viewers using the generated types:

```tsx
import React, { useState } from 'react';
import { useQuery } from 'react-query';
import { auditLogApiClient } from '../../generated';
import { QueryLogResponse, AuditLog } from '../../generated/types';

export const CustomAuditViewer: React.FC = () => {
  const [page, setPage] = useState(0);
  const pageSize = 10;

  const { data } = useQuery<QueryLogResponse>(
    ['auditLog', page],
    () => auditLogApiClient.queryLog(page, pageSize)
  );

  return (
    <table>
      <thead>
        <tr>
          <th>Operator</th>
          <th>IP</th>
          <th>Path</th>
          <th>Status</th>
        </tr>
      </thead>
      <tbody>
        {data?.list.map((log: AuditLog) => (
          <tr key={log.opTime}>
            <td>{log.operator}</td>
            <td>{log.ip}</td>
            <td>{log.requestPath}</td>
            <td>{log.status}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
};

```

## Summary

- **Configuration**: Set `cosky.security.audit-log.action` to `WRITE`, `READ`, or `ALL` in [`application.yaml`](https://github.com/ahoo-wang/cosky/blob/main/application.yaml) to control which requests generate audit records.
- **Storage**: Audit logs persist in Redis under the key `cosky:system:audit:log` using `AuditLogService` with automatic serialization.
- **Capture**: `AuditLogHandlerInterceptor` automatically intercepts requests and extracts operator, IP, path, method, status, and error details.
- **Access**: Query logs via the `GET /v1/audit-log` endpoint, the generated TypeScript client, or the built-in dashboard UI.
- **Customization**: Use `AuditLogService.addLog()` to programmatically insert custom audit entries from Kotlin code.

## Frequently Asked Questions

### Where are CoSky audit logs stored?

CoSky stores audit logs in Redis as a list under the system-wide key `cosky:system:audit:log`. The `AuditLogService` class in [`cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/audit/AuditLogService.kt`](https://github.com/ahoo-wang/cosky/blob/main/cosky-rest-api/src/main/kotlin/me/ahoo/cosky/rest/security/audit/AuditLogService.kt) handles persistence using `ReactiveStringRedisTemplate`, pushing serialized JSON entries via `leftPush` operations.

### How do I enable audit logging for read operations in CoSky?

Set the property `cosky.security.audit-log.action` to `READ` or `ALL` in your [`application.yaml`](https://github.com/ahoo-wang/cosky/blob/main/application.yaml). The default value is `WRITE`, which only captures POST, PUT, DELETE, and PATCH requests. Changing this configuration in [`SecurityProperties.kt`](https://github.com/ahoo-wang/cosky/blob/main/SecurityProperties.kt) instructs `AuditLogHandlerInterceptor` to also log GET requests or all HTTP methods respectively.

### What is the default action type for CoSky audit logging?

The default action type is `WRITE`, configured in [`SecurityProperties.kt`](https://github.com/ahoo-wang/cosky/blob/main/SecurityProperties.kt). This means only mutating HTTP methods (POST, PUT, DELETE, PATCH) generate audit records by default. This setting minimizes log volume while capturing the most critical state-changing operations.

### How can I query CoSky audit logs programmatically?

Use the `GET /v1/audit-log` endpoint exposed by [`AuditLogController.kt`](https://github.com/ahoo-wang/cosky/blob/main/AuditLogController.kt) with `offset` and `limit` query parameters for pagination. For type-safe access, import the generated [`AuditLogApiClient.ts`](https://github.com/ahoo-wang/cosky/blob/main/AuditLogApiClient.ts) from `dashboard/src/generated/`, which provides the `queryLog(page, size)` method returning a `QueryLogResponse` containing the total count and list of `AuditLog` entries.