Story
How to use UniCoreFW AuditLogger ?
UniCoreFW AuditLogger
AuditLogger provides structured, thread-safe audit logging for security-sensitive application events. It writes audit records as UTF-8 JSON Lines and supports both secure file logging and integration with Python's standard logging framework.
Installation
Install UniCoreFW from PyPI:
pip install unicorefw
Import AuditLogger from the security module:
from unicorefw.security import AuditLogger
Quick Start
The recommended pattern uses AuditLogger as a context manager:
from unicorefw.security import AuditLogger
with AuditLogger(log_file="security.jsonl") as audit:
audit.log(
"USER_LOGIN",
{
"user_id": "42",
"result": "success",
},
)
The context manager closes the internally managed log handler when execution leaves the with block.
Audit Record Format
Each call to log() produces a JSON object containing:
timestamp— UTC timestamp in ISO 8601 formatevent_type— application-defined event identifierdetails— event-specific datamessage— human-readable representation of the event
Example:
{
"timestamp": "2026-09-21T16:30:00.123456+00:00",
"event_type": "USER_LOGIN",
"details": {
"user_id": "42",
"result": "success"
},
"message": "USER_LOGIN: {'user_id': '42', 'result': 'success'}"
}
The file uses JSON Lines format, so each audit event occupies one line. The implementation serializes non-JSON-native values with their string representation rather than rejecting the event.
Logging Events
The primary API is:
audit.log(event_type, details)
Successful Login
audit.log(
"LOGIN_SUCCESS",
{
"user_id": "user-1001",
"ip_address": "192.0.2.10",
},
)
Failed Login
audit.log(
"LOGIN_FAILED",
{
"username": "kenny",
"reason": "invalid_credentials",
},
)
Access Denied
audit.log(
"ACCESS_DENIED",
{
"user_id": "user-1001",
"resource": "/admin/users",
"action": "delete",
},
)
Configuration Change
audit.log(
"CONFIG_UPDATED",
{
"user_id": "admin-7",
"component": "rate_limiter",
"setting": "max_calls",
"old_value": 100,
"new_value": 200,
},
)
Use stable event names so log collectors and SIEM systems can filter events without parsing free-form messages.
Manual Lifecycle Management
You can manage the logger without a context manager:
from unicorefw.security import AuditLogger
audit = AuditLogger("security.jsonl")
try:
audit.log(
"SERVICE_STARTED",
{
"service": "payments-api",
},
)
finally:
audit.close()
close() removes and closes the file handler owned by the AuditLogger instance. Calling it again is safe because the method returns when no owned handler remains.
Using an Existing Python Logger
AuditLogger can send events through an existing logging.Logger instead of writing directly to its own file:
import logging
from unicorefw.security import AuditLogger
logger = logging.getLogger("application.audit")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
logger.addHandler(handler)
audit = AuditLogger(logger=logger)
audit.log(
"USER_CREATED",
{
"user_id": "user-2001",
"created_by": "admin-7",
},
)
When logger= is supplied, the supplied logger owns routing and handler management. log_file is not used.
UniCoreFW also places the structured event in the standard logging record:
record.audit_event
Internally, the call is equivalent to logging the serialized JSON message with:
extra={"audit_event": event}
This makes integration with custom formatters and centralized logging pipelines possible.
Secure File Handling
When AuditLogger manages its own file, UniCoreFW applies several protections.
Owner-Only Permissions
On platforms supporting fchmod, the audit file receives:
0600
This grants read and write access to the file owner while denying group and other users.
Append-Only File Access
The handler opens the destination with append semantics:
O_APPEND | O_CREAT | O_WRONLY
Existing audit records therefore remain in place when new records are written.
Symbolic-Link Protection
When the operating system provides O_NOFOLLOW, UniCoreFW enables it to prevent the audit destination from resolving through a symbolic link.
Regular Files Only
After opening the destination, UniCoreFW verifies that it is a regular file:
if not stat.S_ISREG(file_status.st_mode):
raise SecurityError(...)
This blocks destinations such as devices or other unsupported filesystem objects.
Input Validation
event_type must contain non-empty text:
audit.log("", {})
raises InputValidationError.
An event type containing a null byte is also rejected:
audit.log("LOGIN\x00SUCCESS", {})
raises InputValidationError.
Handling Audit Write Failures
Filesystem failures from the secure file handler raise SecurityError.
from unicorefw.security import AuditLogger, SecurityError
try:
with AuditLogger("/secure/path/audit.jsonl") as audit:
audit.log(
"ADMIN_ACTION",
{
"user_id": "admin-7",
"action": "delete_user",
},
)
except SecurityError as exc:
# Apply the application's security or availability policy.
print(f"Audit logging failed: {exc}")
For security-sensitive systems, decide whether failure to persist an audit event should fail the associated operation, trigger an alert, or enter a degraded state.
The parent directory must already exist. AuditLogger opens the requested file directly and does not create missing directory trees. Filesystem failures are converted into SecurityError.
Security Guidelines
Do Not Log Secrets
Do not place credentials or secrets inside details.
Avoid:
audit.log(
"LOGIN",
{
"username": "alice",
"password": "super-secret-password",
"access_token": "abc123",
},
)
Prefer:
audit.log(
"LOGIN",
{
"user_id": "user-1001",
"result": "success",
"authentication_method": "password",
},
)
AuditLogger does not automatically redact sensitive values from details. Applications remain responsible for deciding which information may enter the audit trail.
Prefer Identifiers Over Payloads
Prefer:
{
"user_id": "user-1001",
"document_id": "doc-5832",
"action": "delete"
}
over storing entire user records, documents, request bodies, or authentication credentials.
Use Consistent Event Names
A predictable naming scheme improves searching and alerting:
LOGIN_SUCCESS
LOGIN_FAILED
LOGOUT
ACCESS_GRANTED
ACCESS_DENIED
USER_CREATED
USER_UPDATED
USER_DELETED
CONFIG_UPDATED
RATE_LIMIT_EXCEEDED
Production Example
from unicorefw.security import AuditLogger
def delete_user(actor_id: str, target_user_id: str) -> None:
with AuditLogger("/var/log/myapp/audit.jsonl") as audit:
try:
# Application operation
remove_user(target_user_id)
audit.log(
"USER_DELETED",
{
"actor_id": actor_id,
"target_user_id": target_user_id,
"result": "success",
},
)
except Exception as exc:
audit.log(
"USER_DELETE_FAILED",
{
"actor_id": actor_id,
"target_user_id": target_user_id,
"error_type": type(exc).__name__,
},
)
raise
Notice that the failure event records the exception type rather than dumping the entire exception, request, or sensitive application state.
API Reference
Constructor
AuditLogger(
log_file: str = "unicore_audit.log",
logger: logging.Logger | None = None,
)
| Parameter | Description |
|---|---|
log_file |
Destination for the internally managed secure audit file |
logger |
Existing Python logging.Logger; when supplied, it controls event routing |
log()
log(event_type: str, details: Any) -> None
Writes one structured audit event.
| Parameter | Description |
|---|---|
event_type |
Non-empty event identifier |
details |
Event metadata; JSON-compatible values are recommended |
close()
close() -> None
Closes the internally owned file handler.
Context Manager
with AuditLogger(...) as audit:
...
The context manager calls close() on exit.
Recommended Pattern
For most applications:
from unicorefw.security import AuditLogger
with AuditLogger("audit.jsonl") as audit:
audit.log(
"RESOURCE_UPDATED",
{
"actor_id": "user-42",
"resource_id": "resource-123",
"result": "success",
},
)
Use the built-in file mode when you need UniCoreFW's secure local append behavior. Use logger= when your application already routes Python logging to systems such as syslog, Fluent Bit, Datadog, ELK, or another centralized logging pipeline.
Keep audit records structured, use stable event names, and exclude passwords, tokens, API keys, session data, and other secrets from details.