How to Debug Request Routing Issues and Visualize Routing Decisions in INFINI Gateway
Enable global debug mode and inspect trace logs to see exactly how INFINI Gateway routes requests through its pipeline of flow filters, or add diagnostic headers to track A/B routing decisions in real time.
INFINI Gateway processes incoming requests through a dynamic pipeline of flow filters defined in YAML configuration, making it essential to understand how to debug request routing issues and visualize routing decisions in INFINI Gateway when troubleshooting complex traffic patterns. The open-source project infinilabs/gateway implements routing logic as data-driven filter chains in proxy/filters/routing/, allowing developers to trace every decision point from router configuration through flow execution using built-in diagnostics and configurable logging.
Understanding the Routing Pipeline Architecture
Before debugging, you must understand how INFINI Gateway structures its routing decisions. The gateway evaluates requests through three layers defined in common/entry.go:
- Router Configuration (
RouterConfig): Defines default flows and pattern-matching rules - Flow Configuration (
FlowConfig): Lists the ordered filter chain for each flow - Filter Execution: Individual filters in
proxy/filters/routing/that mutate the request or terminate processing
Each filter can change the request URI, forward to a different flow, or stop processing entirely. Because this logic is expressed as configuration data rather than hardcoded paths, you need specialized strategies to observe the decision chain.
Strategy 1: Enable Global Debug Mode for Trace Logging
The most direct way to debug request routing issues is activating the global debug environment. When global.Env().IsDebug is true, filters throughout the routing pipeline emit detailed trace logs showing exactly which flow is chosen, cache hit status, and ratio decisions.
Set the environment variable before starting the gateway:
DEBUG=true ./gateway
Or configure gateway.yml to enable trace-level logging:
log:
level: trace # enable seelog TRACE level
format: "%Date %Time [%Level] %Msg%n"
output: stdout
When active, you will see log entries from proxy/filters/routing/flow.go showing the resolved flow ID, and from proxy/filters/routing/ratio.go displaying probabilistic routing decisions.
Strategy 2: Inspect Request-Level Diagnostic Headers
INFINI Gateway enriches requests with diagnostic markers that you can inspect using any HTTP client or log exporter. These headers allow you to visualize routing decisions without accessing server logs.
A/B Testing Headers from Ratio Filter
The RatioRoutingFlowFilter in proxy/filters/routing/ratio.go adds the X-Ratio-Hit header to indicate which branch a request took:
curl -i http://gateway.local/api/v1/items
# Response includes:
# X-Ratio-Hit: true → routed to secondary flow
# X-Ratio-Hit: false → remained on primary flow
Flow Process Annotations
The FlowFilter in proxy/filters/routing/flow.go uses ctx.AddFlowProcess() to annotate the request context with processing metadata. When combined with debug logging, these annotations appear in trace output showing the exact sequence of filters executed.
Strategy 3: Leverage the Flow Cache for Testing Templated Routes
The FlowFilter implements a request-scoped cache for resolved flow IDs to optimize performance of templated routing decisions. When debugging routing issues with dynamic flow selection (using variables like ${request.host}), you may need to clear or tune this cache to see immediate effects of configuration changes.
The cache is initialized in proxy/filters/routing/flow.go lines 52-55:
runner.cache = util.NewCacheWithExpireOnAdd(10*time.Minute, 1000)
For debugging purposes, temporarily reduce the TTL to force frequent re-evaluation:
// Temporary debug change in flow.go
runner.cache = util.NewCacheWithExpireOnAdd(5*time.Second, 100) // 5-second TTL
After recompiling and restarting, the gateway recomputes flow IDs every 5 seconds instead of caching for 10 minutes, allowing you to test routing template changes immediately.
Strategy 4: Add Custom Debug Filters for Deep Inspection
When standard logging insufficiently explains routing behavior, you can inject a lightweight custom filter that prints the full request context before other routing filters execute. This pattern follows the implementation in proxy/filters/routing/redirect.go or switch.go.
Create proxy/filters/transform/dump_request.go:
package transform
import (
"fmt"
log "github.com/cihub/seelog"
"infini.sh/framework/core/config"
"infini.sh/framework/core/pipeline"
"infini.sh/framework/lib/fasthttp"
)
type DumpRequestFilter struct{}
func (f *DumpRequestFilter) Name() string { return "dump_request" }
func (f *DumpRequestFilter) Filter(ctx *fasthttp.RequestCtx) {
fmt.Printf("=== DEBUG REQUEST %s ===\n", ctx.PhantomURI().String())
fmt.Printf("%s %s %s\n", ctx.Method(), ctx.RequestURI(), ctx.Protocol())
ctx.Request.Header.VisitAll(func(k, v []byte) {
fmt.Printf("%s: %s\n", k, v)
})
log.Tracef("dumped request %s", ctx.PhantomURI())
}
func init() {
pipeline.RegisterFilterPluginWithConfigMetadata("dump_request", NewDumpRequestFilter, &DumpRequestFilter{})
}
func NewDumpRequestFilter(c *config.Config) (pipeline.Filter, error) {
return &DumpRequestFilter{}, nil
}
Register this filter in your flow configuration to see exact request state before routing decisions:
flow:
- filter:
id: debug_dump
name: dump_request
- filter:
id: router
name: flow
parameters:
flow: main_router
Strategy 5: Visualize Routing with External Log Analysis
For production environments, export the seelog trace output to log aggregation platforms like Grafana Loki or ELK Stack. Create dashboards that group logs by request-id, which the gateway generates uniquely for each request via ctx.PhantomURI().String() used throughout the filter pipeline.
Configure gateway.yml to output JSON logs for easier parsing:
log:
level: trace
format: '{"time":"%Date %Time","level":"%Level","msg":"%Msg","request_id":"%RequestId"}%n'
output: file:/var/log/gateway/routing.log
Then ingest /var/log/gateway/routing.log into your visualization tool to trace request paths across distributed flows.
Summary
- Enable global debug mode by setting
DEBUG=trueorlog.level: traceingateway.ymlto see detailed routing decisions inproxy/filters/routing/flow.goandratio.go. - Inspect diagnostic headers like
X-Ratio-Hitadded by theRatioRoutingFlowFilterto verify A/B routing without accessing server logs. - Clear the flow cache by reducing TTL in
flow.golines 52-55 when testing templated flow IDs that use request variables. - Add custom debug filters using the
pipeline.RegisterFilterPluginWithConfigMetadatapattern to dump full request context before routing filters execute. - Export logs to external tools like Loki or ELK, grouping by
ctx.PhantomURI().String()to visualize routing paths across distributed systems.
Frequently Asked Questions
How do I enable debug logging to trace routing decisions?
Set the environment variable DEBUG=true when starting the gateway, or modify gateway.yml to set log.level: trace. This activates global.Env().IsDebug, causing filters in proxy/filters/routing/flow.go and ratio.go to emit trace logs showing which flow ID was resolved and whether ratio-based routing triggered.
What header indicates which branch an A/B test request took?
The RatioRoutingFlowFilter in proxy/filters/routing/ratio.go sets the X-Ratio-Hit header. A value of true means the request was routed to the secondary flow (the "B" branch), while false indicates it remained on the primary flow.
How can I clear the flow cache to test routing configuration changes?
The FlowFilter caches resolved flow IDs in proxy/filters/routing/flow.go using util.NewCacheWithExpireOnAdd. To force immediate re-evaluation of templated flow IDs during testing, temporarily change the cache initialization on lines 52-55 to use a short TTL (e.g., 5*time.Second instead of 10*time.Minute), then recompile and restart.
Can I add custom tracing without modifying the core gateway code?
Yes. Implement the pipeline.Filter interface in a new file (e.g., proxy/filters/transform/dump_request.go), register it using pipeline.RegisterFilterPluginWithConfigMetadata, and reference it in your flow YAML configuration. This filter can print the full request context including the phantom URI from ctx.PhantomURI().String() before subsequent routing filters execute.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →