log

Log transactions with an AAA logger

log

Logs the transaction with a named AAA logger. Use the log action in handler hooks to record authentication, accounting, or other AAA events to files, memory buffers, syslog, or any other output configured on the target logger.

Syntax

log "<LOGGER_NAME>" {
    format "<format_string>";
}

or

log "<LOGGER_NAME>" {
    json {
        "<key>" <value>;
        ...
    }
}

The <LOGGER_NAME> must match the name of an AAA logger defined in the logging configuration. Each log action uses exactly one output format: either format or json.

Format modes

format

The format mode produces a plain-text log line built from a format string. Use %{...} interpolation to insert context values:

log "AUTHENTICATION" {
    format "AUTH user=%{aaa.identity} result=%{aaa.result} timestamp=%{datetime.timestamp}";
}

The resulting log message is a single string with all %{...} placeholders replaced by their runtime values. If a referenced value is not available, it is replaced with an empty string.

json

The json mode produces a JSON object where each line defines a key-value pair. The key is always a quoted string. The value can be a bare expression or a format string:

log "AUTHENTICATION" {
    json {
        "User-Name" aaa.identity;
        "Result" "%{aaa.result}";
        "Timestamp" datetime.timestamp;
    }
}

This produces a JSON object such as:

{"User-Name":"alice","Result":"accept","Timestamp":"2025-01-15T12:00:00Z"}
Bare expressions vs. format strings in JSON values

A bare expression (without quotes) resolves to a typed value. If the underlying value is a number, it appears as a JSON number; if it is absent, it appears as null.

A format string (with %{...} inside quotes) always resolves to a JSON string, even when the underlying value is numeric or absent (in which case the placeholder is replaced with an empty string).

json {
    "Elapsed-Time" context.latency;       # bare expression - may produce a number
    "Result" "%{aaa.result}";             # format string - always a string
}
Multi-value attributes

Use the [*] suffix on a bare expression to log all values of a multi-value attribute as a JSON array:

json {
    "cisco-avpair" radius.request.attr.cisco-avpair[*];
    "Filter-Id" radius.reply.attr.Filter-Id[*];
    "Groups" user.group;
}
Static values

Use a quoted string without %{...} interpolation to log a fixed literal value:

json {
    "Log-Type" "authentication";
}

Action Result

The log action always returns accept.

Prerequisites

The referenced AAA logger must be defined in the logging configuration. If the logger does not exist, Radiator reports an error at startup.

logging {
    aaa {
        logger "AUTHENTICATION" {
            file {
                filename "/var/log/radiator/authentication.log";
            }
        }
    }
}

Examples

Authentication logging with JSON format

aaa {
    policy "DEFAULT" {
        handler "AUTHENTICATION" {
            @final-execute {
                log "AUTHENTICATION" {
                    json {
                        "AAA-Identity" aaa.identity;
                        "AAA-Method" "%{aaa.method}";
                        "Result" "%{aaa.result}";
                        "Reason" aaa.reason;
                        "Handler" aaa.handler;
                        "Policy" aaa.policy;
                        "Timestamp" datetime.timestamp;
                        "Elapsed-Time" context.latency;
                        "Called-Station-Id" radius.request.attr.Called-Station-Id;
                        "Calling-Station-Id" radius.request.attr.Calling-Station-Id;
                        "NAS-IP-Address" radius.request.attr.NAS-IP-Address;
                        "User-Name" radius.request.attr.User-Name;
                    }
                }
            }
        }
    }
}

Accounting logging with JSON format

@final-execute {
    log "ACCOUNTING" {
        json {
            "Timestamp" datetime.timestamp;
            "Log-Type" "accounting";
            "Acct-Status-Type" radius.request.attr.Acct-Status-Type;
            "Acct-Session-Id" radius.request.attr.Acct-Session-Id;
            "Acct-Session-Time" radius.request.attr.Acct-Session-Time;
            "Acct-Input-Octets" radius.request.attr.Acct-Input-Octets;
            "Acct-Output-Octets" radius.request.attr.Acct-Output-Octets;
            "User-Name" radius.request.attr.User-Name;
            "NAS-IP-Address" radius.request.attr.NAS-IP-Address;
            "Result" "%{aaa.result}";
        }
    }
}

Management API audit logging

@final-execute {
    log "MANAGEMENT" {
        json {
            "User-Name" aaa.identity;
            "Reason" aaa.reason;
            "Result" "%{aaa.result}";
            "Method" http.method;
            "Path" http.path;
            "Code" http.status;
            "Ip" http.client.ip;
        }
    }
}

Reusing log actions with pipelines

When multiple handlers need to produce the same log output, extract the log action into a named @pipeline and call it with invoke. This keeps the log format defined in one place and avoids duplication across handlers.

Define the pipeline at the aaa level:

aaa {
    @pipeline "LOG_AUTHENTICATION" {
        log "AUTHENTICATION" {
            json {
                "Timestamp" datetime.timestamp;
                "AAA-Identity" aaa.identity;
                "AAA-Method" aaa.method;
                "Reason" aaa.reason;
                "Result" "%{aaa.result}";
                "Errors" "%{aaa.errors}";
            }
        }
    }
}

Then invoke it from any handler hook:

aaa {
    policy "DEFAULT" {
        handler "LDAP_AUTH" {
            @final-execute {
                invoke "LOG_AUTHENTICATION";
            }
        }

        handler "SQL_AUTH" {
            @final-execute {
                invoke "LOG_AUTHENTICATION";
            }
        }
    }
}

Both handlers now share the same log format. To change the logged fields, update the pipeline definition and every handler that invokes it picks up the change automatically.

  • logging - Top-level logging configuration
  • logging aaa - AAA logger configuration
  • logger - Individual logger configuration
  • @pipeline - Reusable named pipelines
  • invoke - Invoke a named pipeline
  • debug - Debug logging for troubleshooting policies
  • scripts - Logging from Lua scripts via print() and context:log()