# INFINI Console Audit Logging: How User Actions and System Events Are Tracked

> Discover how INFINI Console tracks user actions and system events with its robust audit logging system. Learn about secure, non-blocking request handling for persistent storage.

- Repository: [INFINI Labs/console](https://github.com/infinilabs/console)
- Tags: how-to-guide
- Published: 2026-03-04

---

**INFINI Console captures every significant user action and system event by constructing a validated `AuditLog` object via a fluent builder, then asynchronously enqueuing the JSON payload to the `logging-audit-log-queue` for persistent storage without blocking the request path.**

The `infinilabs/console` repository implements a comprehensive audit logging system that records who performed what action, when, and from where. This article examines the core components, validation logic, and asynchronous processing pipeline that ensure every audit entry is reliably persisted to Elasticsearch or other backends.

## Core Components of INFINI Console Audit Logging

### AuditLog Model and Builder

The foundation resides in [`model/audit_log.go`](https://github.com/infinilabs/console/blob/main/model/audit_log.go), which defines the `AuditLog` struct and the `AuditLogBuilder` fluent API. The struct holds timestamps, operator identity, log type, resource type, and a flexible `Labels` map for event-specific metadata.

The builder pattern enforces required fields while supplying sensible defaults:

```go
auditLog, _ := model.NewAuditLogBuilderWithDefault().
    WithOperator(claims.Username).
    WithLogTypeAccess().
    WithResourceTypeClusterManagement().
    WithEventName("monitoring " + eventName).
    WithEventSourceIP(common.GetClientIP(req)).
    WithResourceName(targetClusterID).
    WithOperationTypeAccess().
    WithEventRecord(req.URL.RawQuery).
    Build()

```

### LogAuditLog Service

The [`service/audit_log.go`](https://github.com/infinilabs/console/blob/main/service/audit_log.go) file contains the `LogAuditLog` function, which serves as the primary entry point for persisting audit entries. This function validates the log structure before enqueueing:

```go
func LogAuditLog(log *model.AuditLog) error {
    if err := log.Validate(); err != nil {
        return err
    }
    _, err := NewAuditLogAction(log).Execute()
    return err
}

```

The `NewAuditLogAction(log).Execute()` method serializes the struct to JSON using `util.MustToJSONBytes` and pushes it to the queue named `logging-audit-log-queue`.

### Client IP Extraction

Located in [`common/audit_log.go`](https://github.com/infinilabs/console/blob/main/common/audit_log.go), the `GetClientIP` helper extracts the client IP from HTTP headers, checking `X-Forwarded-For`, then `X-Real-IP`, and falling back to `RemoteAddr` to ensure accurate attribution of user actions.

## The Audit Logging Flow in INFINI Console

The system follows a four-stage pipeline that decouples logging from request handling:

1. **Context Collection**: Interceptors or API handlers extract user claims, resource IDs, and request metadata from the HTTP context.

2. **Log Construction**: The `AuditLogBuilder` assembles the entry with mandatory fields including operator, log type, resource type, event name, source IP, and operation type.

3. **Validation and Enqueueing**: The `LogAuditLog` service validates timestamps, mandatory fields, IP format, and operation type before serializing to JSON and pushing to `logging-audit-log-queue`.

4. **Asynchronous Persistence**: A queue consumer (external to this repository) processes the JSON payload and persists it to Elasticsearch, ensuring the audit trail is searchable for compliance and troubleshooting.

## Implementing Audit Logging in Practice

### Generic Handler Implementation

When recording a login event, construct the log and fire the service call:

```go
import (
    "infini.sh/console/common"
    "infini.sh/console/model"
    "infini.sh/console/service"
    "net/http"
)

func recordLogin(w http.ResponseWriter, r *http.Request, user *User) {
    auditLog, _ := model.NewAuditLogBuilderWithDefault().
        WithOperator(user.Username).
        WithLogTypeAccess().
        WithResourceTypeAccountCenter().
        WithEventName("user login").
        WithEventSourceIP(common.GetClientIP(r)).
        WithOperationTypeLogin().
        Build()

    _ = service.LogAuditLog(auditLog)
}

```

### Monitoring Interceptor Pattern

The [`plugin/audit_log/monitoring_interceptor.go`](https://github.com/infinilabs/console/blob/main/plugin/audit_log/monitoring_interceptor.go) demonstrates automatic audit logging for monitoring API calls:

```go
func (m *MonitoringInterceptor) PreHandle(ctx context.Context, _ http.ResponseWriter, req *http.Request) (context.Context, error) {
    claims, _ := security.ValidateLogin(req.Header.Get("Authorization"))
    if claims != nil && handler.GetHeader(req, "Referer", "") != "" {
        auditLog, _ := model.NewAuditLogBuilderWithDefault().
            WithOperator(claims.Username).
            WithLogTypeAccess().
            WithResourceTypeClusterManagement().
            WithEventName("monitoring " + eventName).
            WithEventSourceIP(common.GetClientIP(req)).
            WithResourceName(targetClusterID).
            WithOperationTypeAccess().
            WithEventRecord(req.URL.RawQuery).
            Build()
        _ = service.LogAuditLog(auditLog)
    }
    return ctx, nil
}

```

### Extracting Client IP Addresses

Use the common utility to ensure accurate IP attribution across proxy layers:

```go
ip := common.GetClientIP(req)

```

## Summary

- **INFINI Console audit logging** uses a builder pattern in [`model/audit_log.go`](https://github.com/infinilabs/console/blob/main/model/audit_log.go) to construct validated `AuditLog` objects with mandatory fields including operator, resource type, and source IP.
- The `LogAuditLog` service in [`service/audit_log.go`](https://github.com/infinilabs/console/blob/main/service/audit_log.go) validates entries and asynchronously enqueues them to `logging-audit-log-queue` as JSON payloads.
- **Asynchronous processing** decouples audit persistence from request handling, ensuring zero latency impact while guaranteeing reliable storage to Elasticsearch.
- Interceptors like [`monitoring_interceptor.go`](https://github.com/infinilabs/console/blob/main/monitoring_interceptor.go) automatically generate audit entries for API calls, while the `GetClientIP` helper ensures accurate attribution behind proxies.

## Frequently Asked Questions

### How does INFINI Console ensure audit logs are not lost if the system crashes?

The system uses a **persistent message queue** (`logging-audit-log-queue`) to decouple log generation from storage. When `service.LogAuditLog` is called, the JSON payload is pushed to the queue immediately. A separate consumer process (typically external to the Console application) drains this queue and persists entries to Elasticsearch. This design ensures that even if the Console service restarts, queued messages remain intact and will be processed once the system recovers.

### What fields are mandatory when creating an audit log entry?

According to the validation logic in [`model/audit_log.go`](https://github.com/infinilabs/console/blob/main/model/audit_log.go), every audit entry must include: **operator** (who performed the action), **log type** (e.g., access or system), **resource type** (the subsystem affected), **event name** (human-readable description), **source IP** (client address validated for format), and **operation type** (the specific action category). The `AuditLogBuilder` enforces these requirements during construction, and the `Validate()` method returns errors if any mandatory field is missing or malformed.

### Can I customize which user actions generate audit logs?

Yes. While INFINI Console automatically logs many actions through **interceptors** like [`monitoring_interceptor.go`](https://github.com/infinilabs/console/blob/main/monitoring_interceptor.go), you can implement custom audit logging by invoking the `service.LogAuditLog` function directly within your API handlers or middleware. The `AuditLogBuilder` provides methods like `WithLabels()` to attach custom metadata, and you can define your own `ResourceType` or `EventName` values to categorize domain-specific actions. This flexibility allows you to capture business-critical events beyond the standard system-generated logs.

### How does the system handle client IP detection behind reverse proxies?

The `common.GetClientIP` function in [`common/audit_log.go`](https://github.com/infinilabs/console/blob/main/common/audit_log.go) implements a **hierarchical fallback strategy** to accurately identify the original client address. It first checks the `X-Forwarded-For` header (common in proxy chains), then falls back to `X-Real-IP` (used by Nginx and similar proxies), and finally uses the connection's `RemoteAddr` if neither header is present. This ensures that audit logs correctly attribute actions to the actual end-user rather than internal proxy addresses, maintaining accurate compliance records even in complex network topologies.