How to Configure and Leverage CoSky's Audit Logging Feature

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

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:

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:

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:

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. 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:

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:

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 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 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. The default value is WRITE, which only captures POST, PUT, DELETE, and PATCH requests. Changing this configuration in 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. 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 with offset and limit query parameters for pagination. For type-safe access, import the generated 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →