count

Increment a counter for metrics and monitoring

count

Increments a named counter by 1. Counters are created dynamically on first use without pre-declaration and are accessible via the Management API for monitoring and metrics collection.

Syntax

count "counter_name";

Namespace Behavior

Counters are organized hierarchically using namespaces. The namespace is determined by the presence of :: in the counter name:

Without :: - Current Context Namespace

Counter inherits the full server execution context:

servers {
    radius "AUTH_SERVER" {
        port 1812;
        address 0.0.0.0;
    }
}

aaa {
    policy "DEFAULT" {
        handler "PAP_HANDLER" {
            conditions all {
                radius.request.code == radius.ACCESS_REQUEST;
            }

            @final-execute {
                count "auth_attempts";
                # Creates: server::radius-udp::AUTH_SERVER::policy::DEFAULT::handler::PAP_HANDLER::auth_attempts
            }
        }
    }
}

With :: - Custom Namespace

The :: separator creates an explicit namespace hierarchy independent of server context:

handler "MY_HANDLER" {
    conditions all {
        radius.request.code == radius.ACCESS_REQUEST;
    }

    @final-execute {
        count "custom::metrics::login_count";
        # Creates: custom::metrics::login_count

        count "myapp::requests";
        # Creates: myapp::requests
    }
}

See namespace documentation for detailed explanation of hierarchy structure and pattern matching.

Examples

Basic Request Counting

handler "AUTH_HANDLER" {
    conditions all {
        radius.request.code == radius.ACCESS_REQUEST;
    }

    @final-execute {
        # Count all authentication attempts in current handler namespace
        count "total_auth";

        match "%{aaa.result}" {
            "accept" => {
                count "successful";
            }
            "reject" => {
                count "failed";
            }
        }
    }
}

Custom Application Metrics

handler "AUTH_HANDLER" {
    conditions all {
        radius.request.code == radius.ACCESS_REQUEST;
    }

    @final-execute {
        # Application-specific counters in custom namespace
        count "custom::app::requests";

        if all {
            vars.premium_user == true;
        } then {
            count "custom::app::premium_users";
        }
    }
}

Backend Error Tracking

@execute {
    try {
        backend {
            name "SQL";
            query "CHECK_USER";
        }
    } catch {
        # Track database errors in custom namespace
        count "custom::backend_errors::sql";
        reject "Database error: %{aaa.caught_error}";
    }
}

Accessing Counter Values

Counters are read-only via the Management API:

# Get counter value (total count)
GET /api/v1/statistics/counter/{namespace}

# Examples:
GET /api/v1/statistics/counter/server/radius-udp/AUTH_SERVER/policy/DEFAULT/handler/PAP_HANDLER/auth_attempts
GET /api/v1/statistics/counter/custom/metrics/login_count

Configuring Counter History

Configure retention and collection intervals using the statistics block. Pattern matching applies to the full namespace path:

statistics {
    defaults {
        samples 600;      # 10 hours at 1m intervals
        interval 1m;
    }

    # Match handler counters (pattern matches anywhere in full namespace path)
    # Will match: server::radius-udp::AUTH_SERVER::policy::DEFAULT::handler::PAP_HANDLER::auth_attempts
    counter /.*::auth.*/ {
        samples 8640;     # 24 hours at 10s intervals
        interval 10s;
    }

    # Custom application metrics
    counter /custom::.*/ {
        samples 288;      # 24 hours at 5m intervals
        interval 5m;
    }
}

Pattern matching supports:

  • Regex patterns: /server::.*::error/ matches any namespace containing that pattern
  • Exact matches: "critical_auth" matches only the leaf counter name
  • Cascading: Patterns → defaults → built-in defaults (600 samples, 60s interval)

Lua Scripting

Counters can also be incremented from Lua scripts:

-- Simple counter (uses current context namespace)
context.count("auth_attempts")

-- Custom namespace
context.count("custom::metrics::login_count")

Counters vs Cache Increment

Use counters for:

  • Permanent metrics that accumulate over server lifetime (never reset)
  • Write-only operation in scripts (not readable from Lua/pipeline)
  • Monitoring request counts, error rates, success/failure ratios
  • Long-term trend analysis with time series history via Management API
  • Operational dashboards and alerting

Use cache.increment for:

  • Temporary tracking with automatic expiry and reset
  • Read/write operations in scripts (can check current value)
  • Rate limiting (increment and check within time window)
  • Throttling (limit requests per user/IP over sliding window)
  • Short-term burst detection with automatic cleanup

See Also