Kong Debugging
Introduction
Debugging is an essential skill for any developer, and when working with Kong Gateway, having robust debugging techniques becomes even more crucial. Kong Gateway is a popular API gateway that manages API traffic between clients and services. When issues arise in your Kong configuration, plugins, or custom code, knowing how to effectively debug can save hours of development time.
This guide will walk you through the fundamentals of debugging in Kong, providing you with the tools and methods to identify, isolate, and resolve common issues. Whether you're developing custom plugins, configuring routes, or troubleshooting API connectivity problems, these debugging techniques will help you become more proficient in Kong development.
Kong Logging Basics
Understanding Kong's Log System
Kong provides extensive logging capabilities that serve as your first line of defense when debugging issues. By default, Kong logs errors to the following locations:
- Unix/Linux:
/usr/local/kong/logs/
- Docker containers: Output to stdout/stderr
- Windows: In the Kong installation directory
Let's examine how to configure and use Kong's logging system effectively.
Configuring Log Levels
You can configure Kong's log level in your kong.conf
file:
# Available levels are:
# debug, info, notice, warn, error, crit
log_level = debug
For debugging purposes, setting the log level to debug
provides the most detailed information:
# Update kong.conf
log_level = debug
# Then restart Kong
kong restart
Reading Kong Logs
Let's look at a typical debug log entry:
2023/05/22 15:30:45 [debug] 12345#0: *123 [kong] handler.lua:123 [my-custom-plugin] processing request, client: 192.168.1.1, server: kong
Breaking this down:
2023/05/22 15:30:45
: Timestamp[debug]
: Log level12345#0
: Process ID*123
: Connection ID[kong]
: Component identifierhandler.lua:123
: Source file and line number[my-custom-plugin]
: Plugin name (if applicable)processing request
: Log messageclient: 192.168.1.1, server: kong
: Connection details
Debugging Kong Plugins
Using ngx.log()
for Debugging
When developing custom plugins, the ngx.log()
function is your best friend. It allows you to output debug information at various stages of the request lifecycle.
Here's how to use it:
-- In your plugin code
local function some_function()
-- Local variables
local my_var = "test value"
-- Log the value
ngx.log(ngx.DEBUG, "my_var value: ", my_var)
-- You can log tables too with inspect
local inspect = require "inspect"
local complex_table = { foo = "bar", nested = { value = 123 } }
ngx.log(ngx.DEBUG, "complex table: ", inspect(complex_table))
end
Different log levels available:
ngx.DEBUG
(most verbose)ngx.INFO
ngx.NOTICE
ngx.WARN
ngx.ERR
ngx.CRIT
(least verbose)
Tracing Request Flow
To debug issues related to request processing, it's helpful to trace the flow through each phase of Kong's request lifecycle:
-- In your plugin's access.lua
function MyPlugin:access(conf)
ngx.log(ngx.DEBUG, "Starting access phase for request: ", ngx.var.request_uri)
-- Your access phase code
local start_time = ngx.now()
local result = some_function()
local elapsed = ngx.now() - start_time
ngx.log(ngx.DEBUG, "Access phase completed in ", elapsed, "s with result: ", result)
end
Let's visualize the Kong request lifecycle for better understanding:
Kong Debugging Tools
Using Kong Admin API
The Kong Admin API is a powerful tool for debugging configuration issues. You can inspect the current configuration of routes, services, plugins, and more.
Basic usage examples:
# List all services
curl -X GET http://localhost:8001/services
# Get details of a specific route
curl -X GET http://localhost:8001/routes/my-route
# List all plugins configured for a service
curl -X GET http://localhost:8001/services/my-service/plugins
Let's look at a sample output and how to interpret it:
{
"id": "5fd73c6a-8dfc-4838-a143-1226d3c33456",
"name": "rate-limiting",
"created_at": 1622548800,
"route": null,
"service": {
"id": "6f08a57a-e20c-4b77-8600-e5fc7d6f9234"
},
"consumer": null,
"config": {
"minute": 5,
"hour": 100,
"policy": "local"
},
"protocols": ["http", "https"],
"enabled": true,
"tags": ["api-v1"]
}
This output shows a rate-limiting plugin configured at the service level, limiting requests to 5 per minute and 100 per hour.
Kong Debug Plugin
For more advanced debugging, Kong provides a debug plugin that can be enabled for specific routes or services:
# Create a debug plugin for a specific route
curl -X POST http://localhost:8001/routes/my-route/plugins \
--data "name=request-transformer" \
--data "config.add.headers=X-Debug-Info:true" \
--data "config.add.headers=X-Kong-Route-Id:$(curl -s http://localhost:8001/routes/my-route | jq -r '.id')"
This adds debugging headers to responses that can help you understand how Kong processed the request.
Common Debugging Scenarios
Debugging Authentication Issues
Authentication problems are common when working with Kong. Here's a systematic approach to debugging:
- Check that the authentication plugin is properly configured:
# Get authentication plugin configuration
curl -X GET http://localhost:8001/routes/my-route/plugins | grep -A 20 "key-auth"
- Verify that credentials are correctly stored:
# List consumers
curl -X GET http://localhost:8001/consumers
# List credentials for a specific consumer
curl -X GET http://localhost:8001/consumers/my-consumer/key-auth
- Add debug logs in your custom authentication logic:
function MyAuthPlugin:access(conf)
ngx.log(ngx.DEBUG, "Auth plugin checking credential: ", credential_name)
local credential = get_credential(credential_name)
if not credential then
ngx.log(ngx.ERR, "Failed to find credential: ", credential_name)
return kong.response.exit(401, { message = "Invalid authentication credentials" })
end
ngx.log(ngx.DEBUG, "Authentication successful for consumer: ", credential.consumer.id)
end
Debugging Performance Issues
When your Kong gateway experiences performance problems, follow these steps:
- Enable detailed access logs:
# In kong.conf
log_level = debug
- Use the Kong Prometheus plugin to collect metrics:
# Enable the Prometheus plugin globally
curl -X POST http://localhost:8001/plugins/ \
--data "name=prometheus"
- Analyze latency in specific phases of request processing:
function MyPlugin:access(conf)
local start = ngx.now()
-- Your code here
local duration = ngx.now() - start
ngx.log(ngx.DEBUG, "Access phase took ", duration, " seconds")
-- Store timing info for later phases
ngx.ctx.my_plugin = ngx.ctx.my_plugin or {}
ngx.ctx.my_plugin.access_time = duration
end
function MyPlugin:header_filter(conf)
local start = ngx.now()
-- Your code here
local duration = ngx.now() - start
ngx.log(ngx.DEBUG, "Header filter phase took ", duration, " seconds")
end
Debugging Transformation Issues
When working with request/response transformations, debugging can be tricky. Here's how to debug effectively:
- Log the request/response body at different stages:
function MyPlugin:access(conf)
-- Read request body
kong.service.request.enable_buffering()
local body, err = kong.service.request.get_raw_body()
ngx.log(ngx.DEBUG, "Original request body: ", body)
-- Apply transformations
body = transform_body(body)
ngx.log(ngx.DEBUG, "Transformed request body: ", body)
-- Set the modified body
kong.service.request.set_raw_body(body)
end
function MyPlugin:body_filter(conf)
local chunk, eof = ngx.arg[1], ngx.arg[2]
-- Store chunks until we have the complete response
kong.ctx.plugin.response_chunks = kong.ctx.plugin.response_chunks or {}
if chunk then
table.insert(kong.ctx.plugin.response_chunks, chunk)
end
-- On the last chunk, process the full response
if eof then
local response_body = table.concat(kong.ctx.plugin.response_chunks)
ngx.log(ngx.DEBUG, "Original response body: ", response_body)
-- Apply transformations
response_body = transform_response(response_body)
ngx.log(ngx.DEBUG, "Transformed response body: ", response_body)
-- Send the modified response
ngx.arg[1] = response_body
else
ngx.arg[1] = nil
end
end
Debugging Real-World Examples
Example 1: Debugging a Rate Limiting Issue
Let's say users are reporting that they're being rate-limited unexpectedly. Here's how to debug:
# First, check the rate limiting configuration
curl -X GET http://localhost:8001/services/my-service/plugins | grep -A 20 "rate-limiting"
Example output:
{
"id": "a52568cb-5493-4ae9-9fd3-12345abcdef",
"name": "rate-limiting",
"config": {
"minute": 5,
"policy": "redis",
"redis_host": "redis.example.com"
}
}
Now, add custom logging to track rate limiting decisions:
-- In a custom plugin
function MyDebugPlugin:access(conf)
-- Get consumer identification
local consumer_id = (kong.client.get_consumer() or {}).id or "anonymous"
-- Get remaining requests from Redis
local redis = require "resty.redis"
local red = redis:new()
red:connect(redis_host, redis_port)
local key = "ratelimit:" .. consumer_id .. ":minute"
local remaining, err = red:get(key)
ngx.log(ngx.DEBUG, "Rate limit remaining for consumer ", consumer_id, ": ", remaining)
-- Add header for debugging
kong.response.set_header("X-RateLimit-Remaining-Minute", remaining)
end
Example 2: Debugging a Custom Authentication Plugin
If your custom authentication plugin isn't working as expected:
-- In your auth plugin
function MyAuthPlugin:access(conf)
-- Log authorization header
local auth_header = kong.request.get_header("Authorization")
ngx.log(ngx.DEBUG, "Auth header: ", auth_header)
-- Parse token
local token = auth_header:sub(8) -- Remove "Bearer " prefix
ngx.log(ngx.DEBUG, "Token: ", token)
-- Verify token (simplified example)
local jwt = require "resty.jwt"
local jwt_obj = jwt:verify(conf.jwt_secret, token)
if not jwt_obj.verified then
ngx.log(ngx.ERR, "JWT verification failed: ", jwt_obj.reason)
return kong.response.exit(401, { message = "Invalid token" })
end
ngx.log(ngx.DEBUG, "JWT verified successfully for subject: ", jwt_obj.payload.sub)
-- Set consumer for rate limiting plugins, etc.
kong.client.authenticate(jwt_obj.payload.sub, jwt_obj.payload.consumer)
end
Debugging in Different Environments
Local Development Environment
For local development, Docker Compose is a great way to set up a Kong environment:
# docker-compose.yml
version: '3'
services:
kong:
image: kong:latest
environment:
KONG_DATABASE: 'off'
KONG_DECLARATIVE_CONFIG: /etc/kong/kong.yml
KONG_LOG_LEVEL: debug
volumes:
- ./kong.yml:/etc/kong/kong.yml
ports:
- "8000:8000"
- "8001:8001"
You can use this setup to quickly test your plugins and configurations.
Production Debugging
In production, be careful with debug logs as they can impact performance. For safer debugging:
- Use targeted debugging for specific consumers or routes:
# Create a route-specific plugin that only applies to requests with a debug header
curl -X POST http://kong-admin:8001/routes/my-route/plugins \
--data "name=request-termination" \
--data "config.status_code=200" \
--data "config.echo=true" \
--data "config.content_type=application/json" \
--data 'config.trigger_condition={"http.headers.X-Kong-Debug":["true"]}' \
--data 'config.body={"route": "my-route", "headers": "$(headers)", "query": "$(query)"}'
- Use Kong's traffic control features to redirect a small percentage of traffic to a debug instance:
# Create a canary release
curl -X POST http://kong-admin:8001/services/my-service/plugins \
--data "name=canary" \
--data "config.percentage=5" \
--data "config.upstream_host=debug-backend.example.com"
Advanced Debugging Techniques
Custom Error Handlers
Create custom error handlers to provide more detailed information when things go wrong:
-- In your plugin
function MyPlugin:init_worker()
kong.log.debug("Initializing custom error handler")
-- Register error handler
kong.response.register_error_handler("my_plugin", function(err)
return {
message = "An error occurred: " .. err.message,
code = err.code,
debug_info = debug.traceback(),
timestamp = ngx.time(),
request_id = kong.ctx.req_id,
}
end)
end
function MyPlugin:access(conf)
local ok, err = pcall(function()
-- Your code that might error
local result = some_risky_function()
if not result then
error({ message = "Failed to get result", code = "RESULT_ERROR" })
end
end)
if not ok then
return kong.response.exit(500, err, { ["Content-Type"] = "application/json" })
end
end
Timing Analysis
To identify performance bottlenecks, implement detailed timing analysis:
-- In your plugin
function MyPlugin:access(conf)
local timings = {}
local function time_function(name, func, ...)
local start = ngx.now()
local result = { func(...) }
local duration = (ngx.now() - start) * 1000 -- convert to ms
timings[name] = duration
ngx.log(ngx.DEBUG, name, " took ", duration, "ms")
return unpack(result)
end
-- Use the timing function
time_function("database_query", query_database, params)
time_function("api_call", make_api_call, endpoint)
-- Store timings for log phase
ngx.ctx.my_plugin_timings = timings
end
function MyPlugin:log(conf)
local timings = ngx.ctx.my_plugin_timings or {}
-- Log all timings
for name, duration in pairs(timings) do
ngx.log(ngx.INFO, "Timing for ", name, ": ", duration, "ms")
end
-- Identify slow operations
for name, duration in pairs(timings) do
if duration > 100 then -- 100ms threshold
ngx.log(ngx.WARN, "Slow operation detected: ", name, " took ", duration, "ms")
end
end
end
Debugging Cheat Sheet
Here's a quick reference for common debugging tasks:
Task | Command/Code |
---|---|
View Kong logs | tail -f /usr/local/kong/logs/error.log |
Check Kong status | kong health |
List all plugins | curl -X GET http://localhost:8001/plugins |
Test a route | curl -i -X GET http://localhost:8000/my-route |
Check Kong configuration | kong config parse kong.conf |
Validate declarative config | kong config parse kong.yml |
Add debug header | curl -H "X-Kong-Debug: 1" http://localhost:8000/my-route |
Log request details | ngx.log(ngx.DEBUG, "Request: ", ngx.var.request_uri) |
Summary
Effective debugging is crucial for Kong development. In this guide, we've covered:
- Kong Logging Basics - Understanding and configuring logs
- Debugging Kong Plugins - Using
ngx.log()
and tracing request flow - Kong Debugging Tools - Leveraging Admin API and debug plugins
- Common Debugging Scenarios - Authentication, performance, and transformations
- Real-World Examples - Rate limiting and custom authentication
- Environment-Specific Debugging - Local and production considerations
- Advanced Techniques - Custom error handlers and timing analysis
By applying these techniques, you'll be able to identify and resolve issues in your Kong setup more efficiently.
Additional Resources
Exercises
-
Basic Logging: Add debug logs to each phase of a simple Kong plugin and trace a request through the system.
-
Performance Analysis: Create a custom plugin that measures the time spent in each phase and reports on slow operations.
-
Error Handling: Implement a custom error handler that provides detailed debug information for a specific plugin error.
-
Authentication Debugging: Debug an authentication issue by adding logging at key decision points in an authentication plugin.
-
API Monitoring: Create a plugin that logs detailed information about API calls including headers, timing, and response codes.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)