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:

  1. Router Configuration (RouterConfig): Defines default flows and pattern-matching rules
  2. Flow Configuration (FlowConfig): Lists the ordered filter chain for each flow
  3. 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=true or log.level: trace in gateway.yml to see detailed routing decisions in proxy/filters/routing/flow.go and ratio.go.
  • Inspect diagnostic headers like X-Ratio-Hit added by the RatioRoutingFlowFilter to verify A/B routing without accessing server logs.
  • Clear the flow cache by reducing TTL in flow.go lines 52-55 when testing templated flow IDs that use request variables.
  • Add custom debug filters using the pipeline.RegisterFilterPluginWithConfigMetadata pattern 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →