Skip to content

axonops/audit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

855 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
audit

audit

Structured, Schema-Enforced Audit Logging for Go Services

CI Go Reference Go Report Card OpenSSF Scorecard License Status

๐Ÿš€ Quick Start | โœจ Features | ๐Ÿ“š Examples | ๐Ÿ“– API Reference


โš ๏ธ Status

This library is pre-release (v0.x). The API may change between minor versions until v1.0.0. Pin your dependency version.


๐Ÿ” Overview

audit logs who did what, when, and to which resource โ€” for compliance, forensics, and accountability. Unlike log/slog or zap, audit events are schema-enforced: a code generator turns your YAML taxonomy into typed Go builders, so missing required fields and typo'd event names are compile errors. Output destinations (file, syslog, webhook, Loki) are configured separately at runtime; the bundled audittest package gives in-memory event capture for unit tests with the same validation path as production.


๐Ÿ’ก Why audit?

  • ๐Ÿ“‹ Schema enforcement โ€” every event validated against your taxonomy; missing required fields rejected at compile time
  • ๐Ÿ›ก๏ธ SIEM-native output โ€” CEF understood by Splunk, ArcSight, QRadar; JSON for log aggregators
  • ๐Ÿ“ก Multi-output fan-out โ€” files, syslog, webhooks, Loki, Splunk, stdout simultaneously, each with its own formatter and filters
  • ๐Ÿ”’ Sensitive field stripping โ€” classify fields as PII/financial; strip per-output for GDPR/PCI compliance
  • โšก Non-blocking โ€” sub-microsecond AuditEvent(); async delivery with completeness monitoring
  • ๐Ÿงช Production-grade testing โ€” audittest recorder shares the production validation path โ€” no mock drift

๐Ÿš€ Quick Start

YAML-first: define events in a taxonomy, configure outputs, generate type-safe Go code.

1๏ธโƒฃ Define your taxonomy (taxonomy.yaml)

version: 1
categories:
  write:
    severity: 3
    events: [user_create]
events:
  user_create:
    description: "A new user account was created"
    fields:
      outcome:  { required: true }
      actor_id: { required: true }

2๏ธโƒฃ Configure outputs (outputs.yaml)

version: 1
app_name: my-service
host: "${HOSTNAME:-localhost}"
outputs:
  console:
    type: stdout

3๏ธโƒฃ Generate type-safe code

go run github.com/axonops/audit/cmd/audit-gen \
  -input taxonomy.yaml -output audit_generated.go -package main

audit-gen writes audit_generated.go next to your main.go. Each event in the taxonomy becomes a typed constructor โ€” NewUserCreateEvent(actorID, outcome) here, with required fields as parameters โ€” plus .SetXxx() setters for optional fields. Setters for reserved standard fields like target_id, source_ip, and reason are also generated, no taxonomy declaration needed. See docs/code-generation.md for the full generated surface.

The same command appears as a //go:generate directive in main.go below, so after taxonomy edits you can re-run it with go generate ./....

4๏ธโƒฃ Wire it up and emit events (main.go)

package main

import (
    "context"
    _ "embed"
    "log"

    "github.com/axonops/audit/outputconfig"
    _ "github.com/axonops/audit/outputs" // registers stdout/file/syslog/webhook/loki factories
)

//go:generate go run github.com/axonops/audit/cmd/audit-gen -input taxonomy.yaml -output audit_generated.go -package main
//go:embed taxonomy.yaml
var taxonomyYAML []byte

func main() {
    // outputconfig.New: ctx, embedded taxonomy bytes, runtime outputs.yaml path.
    auditor, err := outputconfig.New(context.Background(), taxonomyYAML, "outputs.yaml")
    if err != nil {
        log.Fatal(err)
    }
    defer func() { _ = auditor.Close() }()

    if err := auditor.AuditEvent(
        // target_id is a reserved standard field โ€” generator emits
        // a setter even though it isn't declared in taxonomy.yaml.
        NewUserCreateEvent("alice", "success").SetTargetID("user-42"),
    ); err != nil {
        log.Printf("audit: %v", err)
    }
}

go run . prints one JSON event to stdout โ€” see Output below for the wire format and examples/02-code-generation/ for the full project.

5๏ธโƒฃ Test your audited code (main_test.go)

package main

import (
    "testing"

    "github.com/axonops/audit/audittest"
)

func TestCreateUser_EmitsAudit(t *testing.T) {
    // NewQuick returns (*Auditor, *Recorder, *MetricsRecorder); the
    // metrics recorder is unused here.
    auditor, events, _ := audittest.NewQuick(t, "user_create")
    _ = auditor.AuditEvent(NewUserCreateEvent("alice", "success"))
    events.RequireEvent(t, "user_create")
}

audittest.NewQuick shares the production validation path โ€” full reference at docs/testing.md and examples/17-testing/.

Output

go run . prints one JSON event to stdout (default formatter):

{"timestamp":"2026-01-15T09:00:00.000Z","event_type":"user_create","severity":3,"app_name":"my-service","host":"inventory-01","timezone":"UTC","pid":12345,"actor_id":"alice","outcome":"success","target_id":"user-42","event_category":"write"}

๐Ÿ“ Field order is deterministic โ€” framework fields first, then required and optional fields (alphabetical), then extra fields, then event_category. See JSON format โ†’ Field Order.

๐Ÿ’ก app_name and host are framework fields set once in outputs.yaml; pid is auto-captured from os.Getpid(). For SIEM-native CEF output, add a formatter: { type: cef, vendor: โ€ฆ, product: โ€ฆ } block to your output โ€” Splunk, ArcSight, and QRadar parse it natively.


โœจ Key Features

Feature Description Docs
๐Ÿ“‹ Taxonomy Validation Define event schemas in YAML; every event validated at runtime Learn more
โš™๏ธ Code Generation audit-gen generates typed builders; typos become compile errors Learn more
โœ… Pre-deploy Validator audit-validate validates outputs.yaml in CI; distinct exit codes per failure class Learn more
๐Ÿ›ก๏ธ CEF Format Common Event Format for SIEM platforms (Splunk, ArcSight, QRadar) Learn more
๐Ÿ“„ JSON Format Line-delimited JSON with deterministic field order Learn more
๐Ÿ“ก 6 Output Types File (rotation), syslog (RFC 5424), webhook (NDJSON), Loki (stream labels), Splunk (HEC + ACK + CIM), stdout โ€” fan-out to all simultaneously Learn more
๐Ÿ”€ Event Routing Route events by category or severity to specific outputs Learn more
๐Ÿ”’ Sensitivity Labels Classify fields as PII/financial; strip per-output for compliance Learn more
โšก Async Delivery Sub-microsecond enqueue; background drain goroutine Learn more
๐ŸŒ HTTP Middleware Automatically captures HTTP request fields for audit logging Learn more
๐Ÿ“Š Metrics & Monitoring Track dropped events, delivery errors, and output health Learn more
๐Ÿ“ YAML Configuration Configure outputs in YAML with environment variable substitution Learn more
๐Ÿ” HMAC Integrity Per-output tamper detection with NIST-approved algorithms Learn more
๐Ÿงช Testing Support In-memory recorder with same validation as production Learn more

โ“ Audit logging vs application logging

If you're wondering whether audit logging differs from application logging, here's the comparison:

๐Ÿ”ง Application Logging ๐Ÿ“‹ Audit Logging
Purpose Debugging, troubleshooting, observability Compliance, forensics, accountability
Audience Developers, SREs Security teams, auditors, legal
Guarantees Best-effort โ€” missing a log line is fine Schema-enforced โ€” missing a field is a compliance gap
Retention Days to weeks Months to years (regulatory requirements)
Content Technical details (errors, stack traces) Who did what, when, to which resource, and why
Destinations Log aggregator (OpenSearch, Datadog, Loki) SIEM (Splunk, ArcSight, QRadar), compliance archives

If your application handles user data, financial transactions, authentication, or access control, regulations like SOX, HIPAA, GDPR, and PCI-DSS require audit trails. Application loggers (log/slog, zap, zerolog) do not enforce the structure, completeness, or delivery guarantees that compliance demands.


๐ŸŒ Building an HTTP service?

Skip ahead to the HTTP Service Quickstart โ€” a self-contained ~10-minute walkthrough from go get to an audited POST endpoint with stdout output, no clicking through other docs.


๐Ÿ“ฆ Installation

Requires Go 1.26+.

go get github.com/axonops/audit             # core: auditor, taxonomy, validation, formatters, stdout output
go get github.com/axonops/audit/file         # file output with rotation
go get github.com/axonops/audit/syslog       # RFC 5424 syslog (TCP/UDP/TLS/mTLS)
go get github.com/axonops/audit/webhook      # batched HTTP webhook with SSRF protection
go get github.com/axonops/audit/loki         # Grafana Loki with stream labels and gzip
go get github.com/axonops/audit/splunk       # Splunk HEC with ACK + CIM Change formatter
go get github.com/axonops/audit/outputconfig # YAML-based output configuration

๐Ÿ’ก The core module includes StdoutOutput (no additional dependency) and the audittest package for testing.

๐Ÿš€ On Unix the file output uses writev(2) via the iouring submodule to collapse batched events into a single syscall โ€” measured faster than io_uring at every batch size for our submit-and-wait pattern (see ADR 0002). On Windows it falls back transparently to buffered writes. No configuration needed. The io_uring primitive ships in the submodule and is available for post-v1.0 async workloads (WAL, O_DIRECT) that genuinely benefit.


๐Ÿ—๏ธ Module Structure

Module Description
github.com/axonops/audit Core: Auditor, taxonomy validation, JSON + CEF formatters, HTTP middleware, stdout output, fan-out, routing, audittest
github.com/axonops/audit/file File output with size-based rotation and gzip compression
github.com/axonops/audit/syslog RFC 5424 syslog output (TCP/UDP/TLS/mTLS)
github.com/axonops/audit/webhook Batched HTTP webhook with retry and SSRF protection
github.com/axonops/audit/loki Grafana Loki output with stream labels, gzip, multi-tenancy
github.com/axonops/audit/splunk Splunk HEC output with ACK + CIM Change formatter + splunkcloud:// URL expansion
github.com/axonops/audit/outputconfig YAML-based output configuration with env var substitution
github.com/axonops/audit/outputs Recommended default โ€” single blank import registers all output factories
github.com/axonops/audit/secrets Secret provider interface for ref+ URI resolution in YAML config
github.com/axonops/audit/secrets/openbao OpenBao KV v2 secret provider
github.com/axonops/audit/secrets/vault HashiCorp Vault KV v2 secret provider
github.com/axonops/audit/cmd/audit-gen Code generator: YAML taxonomy to typed Go builders

Outputs are isolated in separate modules so the core library carries minimal third-party dependencies. The default path is the convenience package โ€” one blank import registers every built-in output:

import _ "github.com/axonops/audit/outputs"

Production services typically import only the outputs they use โ€” shaving a few MB of transitive dependencies (goccy/go-yaml, srslog, HTTP stacks) per unused output โ€” and the convenience package is ideal for demos, examples, and apps that use most output types:

import _ "github.com/axonops/audit/file"   // only if you use type: file
import _ "github.com/axonops/audit/syslog" // only if you use type: syslog

๐Ÿงช Testing

The audittest package provides an in-memory recorder for testing audit events:

func TestUserCreation(t *testing.T) {
    auditor, events, _ := audittest.NewQuick(t, "user_create")

    // Exercise the code under test...
    err := auditor.AuditEvent(audit.MustNewEventKV("user_create",
        "outcome", "success", "actor_id", "alice"))
    require.NoError(t, err)

    // Assert immediately โ€” NewQuick uses synchronous delivery.
    assert.Equal(t, 1, events.Count())
    assert.Equal(t, "alice", events.Events()[0].StringField("actor_id"))
}

See Example 17 โ€” Testing and Testing docs for more.


๐Ÿ“‹ Reserved Standard Fields

Fields like target_id, source_ip, and reason are reserved standard fields โ€” 31 framework-defined fields (actor_id, outcome, target_id, source_ip, reason, request_id, session_id, role, dest_port, start_time, โ€ฆ) that are always available on every event without declaration in YAML. Generated builders expose them as typed setters: .SetTargetID(string), .SetSourceIP(string), .SetReason(string), .SetDestPort(int), .SetStartTime(time.Time), etc. The library rejects taxonomies that re-declare a reserved name. See docs/reserved-standard-fields.md for the full table of 31 names, types, and CEF mappings.


๐Ÿ“š Documentation

Resource Description
๐Ÿ“– Progressive Examples 20 examples from "hello world" to a complete inventory demo, an /healthz endpoint, a slog-coexistence migration demo, and a drop-in Prometheus reference adapter โ€” every output type, TLS policy, routing, formatters, testing, and buffering
๐Ÿ“˜ API Reference pkg.go.dev documentation
๐Ÿ—๏ธ Architecture Pipeline design, module boundaries, thread safety
๐Ÿค Contributing Development setup, PR process, code standards
๐Ÿ›  Development Workflow Multi-module monorepo workflow โ€” make workspace, go.work, release-flow implications, build/workspace troubleshooting
๐Ÿš€ Deployment Guide systemd, Kubernetes, Docker Compose; capacity planning; file-output parent-directory behaviour; secret injection patterns
๐Ÿ“ Changelog Release history and breaking changes
โŒ Error Reference Every error explained with recovery guidance
๐Ÿ”ง Troubleshooting Common problems and how to fix them
๐Ÿ”’ Security Policy Vulnerability reporting
โšก Benchmarks Performance baseline, methodology, and side-by-side comparison against log/slog
๐ŸŽฎ Playground note Why audit examples don't run on play.golang.org, and where to run them instead

๐Ÿ™ Acknowledgements

audit builds on excellent open-source projects. See ACKNOWLEDGEMENTS.md for full attribution and license details.


๐Ÿ“„ License

Apache License 2.0 โ€” Copyright 2026 AxonOps Limited.


Made with โค๏ธ by AxonOps

About

Structured, compile time schema-enforced audit logging for Go services. Taxonomy-driven validation, type-safe code generation, multi-output fan-out (file, syslog, webhook and more), CEF + JSON formatters, HMAC integrity checks, per-output event routing, sensitivity labels, and async delivery.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors