Log Consumer Labels in Access Log
Consumer labels are key-value pairs attached to consumers that can represent metadata such as department, organization, or service tier. By default, access logs only include the consumer username. This guide shows you how to include consumer labels in the gateway's access log for more granular traffic analysis.
How It Works
The approach uses three components:
- Custom NGINX variable: Declare a variable (e.g.,
$consumer_company) in the gateway configuration and set its default value. serverless-pre-function: A global plugin that reads the consumer label and assigns it to the variable at runtime.- Access log format: The custom variable is included in the log output.
Prerequisites
- A running API7 Gateway deployment.
- Access to the gateway's
config.yamlconfiguration file.
Step 1: Declare the Custom Variable
In the gateway's config.yaml, declare a custom NGINX variable and set its default value. You can declare multiple variables for different labels:
nginx_config:
http_server_location_configuration_snippet: |
set $consumer_company "-";
Step 2: Update the Access Log Format
In the same configuration file, add the custom variable to the access log format:
nginx_config:
http:
access_log_format: >-
$remote_addr - $remote_user [$time_local] $http_host
"$request" $status $body_bytes_sent $request_time
"$http_referer" "$http_user_agent"
$upstream_addr $upstream_status $upstream_response_time
"$consumer_company"
Reload or restart the gateway for the configuration changes to take effect.
Step 3: Assign the Variable with a Serverless Function
Configure serverless-pre-function as a global plugin to read the consumer label and assign it to the custom variable:
- Admin API
- ADC
curl -k "https://localhost:7443/apisix/admin/global_rules/serverless-pre-function?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"plugins": {
"serverless-pre-function": {
"phase": "rewrite",
"functions": [
"return function (conf, ctx) ngx.var.consumer_company = ctx.consumer and ctx.consumer.labels and ctx.consumer.labels[\"company\"] or \"unknown\" end"
]
}
}
}'
global_rules:
serverless-pre-function:
phase: rewrite
functions:
- >-
return function (conf, ctx)
ngx.var.consumer_company = ctx.consumer
and ctx.consumer.labels
and ctx.consumer.labels["company"]
or "unknown"
end
adc sync -f adc.yaml
The function reads the company label from the consumer. If the consumer does not have the label or no consumer is identified (e.g., unauthenticated requests), the value defaults to "unknown".
Step 4: Configure a Consumer and a Protected Route
Create a consumer with the company label and a key authentication credential, then expose a route protected by key-auth:
- Admin API
- ADC
# Create the consumer with a label
curl -k "https://localhost:7443/apisix/admin/consumers/john?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"username": "john",
"labels": {
"company": "smart-technology"
}
}'
# Add a key-auth credential
curl -k "https://localhost:7443/apisix/admin/consumers/john/credentials/primary-key?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "primary-key",
"plugins": {
"key-auth": {
"key": "john-key"
}
}
}'
# Create a service with the upstream
curl -k "https://localhost:7443/apisix/admin/services/consumer-label-demo?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "consumer-label-demo",
"upstream": {
"type": "roundrobin",
"nodes": [
{ "host": "httpbin.org", "port": 80, "weight": 1 }
]
}
}'
# Create a route protected by key-auth
curl -k "https://localhost:7443/apisix/admin/routes/consumer-label-demo?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "consumer-label-demo",
"paths": ["/anything"],
"service_id": "consumer-label-demo",
"plugins": {
"key-auth": {}
}
}'
consumers:
- username: john
labels:
company: smart-technology
credentials:
- name: primary-key
type: key-auth
config:
key: john-key
services:
- name: consumer-label-demo
routes:
- name: consumer-label-demo
uris:
- /anything
plugins:
key-auth: {}
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
adc sync -f adc.yaml
Step 5: Verify
Send a request with a valid consumer key:
curl -i "http://127.0.0.1:9080/anything" -H "apikey: john-key"
You should receive an HTTP/1.1 200 OK response. The access log entry includes the consumer label:
192.168.1.1 - - [18/Mar/2025:09:17:28 +0000] 127.0.0.1:9080 "GET /anything HTTP/1.1" 200 508 1.260 "-" "curl/8.6.0" 13.210.43.76:80 200 1.153 "smart-technology"
Send a request without a key:
curl -i "http://127.0.0.1:9080/anything"
The access log shows the default value:
192.168.1.1 - - [18/Mar/2025:09:18:27 +0000] 127.0.0.1:9080 "GET /anything HTTP/1.1" 401 52 0.000 "-" "curl/8.6.0" - - - "unknown"
Alternative: Forward Labels as Headers
If you want consumer labels included in structured log entries from logging plugins (e.g., http-logger, kafka-logger), use the attach-consumer-label plugin to map consumer labels to upstream request headers. The headers are then available as NGINX variables (e.g., $http_x_consumer_company) that any logging plugin's log_format can reference.
Update the route from Step 4 to add both plugins alongside key-auth. The example below uses a logging endpoint at http://log-collector:8080/logs:
- Admin API
- ADC
curl -k "https://localhost:7443/apisix/admin/routes/consumer-label-demo?gateway_group_id={group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "consumer-label-demo",
"paths": ["/anything"],
"service_id": "consumer-label-demo",
"plugins": {
"key-auth": {},
"attach-consumer-label": {
"headers": {
"X-Consumer-Company": "$company"
}
},
"http-logger": {
"uri": "http://log-collector:8080/logs",
"log_format": {
"consumer_company": "$http_x_consumer_company"
}
}
}
}'
services:
- name: consumer-label-demo
routes:
- name: consumer-label-demo
uris:
- /anything
plugins:
key-auth: {}
attach-consumer-label:
headers:
X-Consumer-Company: "$company"
http-logger:
uri: http://log-collector:8080/logs
log_format:
consumer_company: "$http_x_consumer_company"
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
adc sync -f adc.yaml
The $ prefix in attach-consumer-label references a consumer label key. Requests from authenticated consumers will have the corresponding header injected before reaching the upstream and the logging plugin.