# How to Debug Request Routing Issues and Visualize Routing Decisions in INFINI Gateway

> Debug INFINI Gateway request routing with trace logs and diagnostic headers. Visualize routing decisions in real time to solve complex issues effectively.

- Repository: [INFINI Labs/gateway](https://github.com/infinilabs/gateway)
- Tags: how-to-guide
- Published: 2026-03-04

---

**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`](https://github.com/infinilabs/gateway/blob/main/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:

```bash
DEBUG=true ./gateway

```

Or configure [`gateway.yml`](https://github.com/infinilabs/gateway/blob/main/gateway.yml) to enable trace-level logging:

```yaml
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`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/flow.go) showing the resolved flow ID, and from [`proxy/filters/routing/ratio.go`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/ratio.go) adds the `X-Ratio-Hit` header to indicate which branch a request took:

```bash
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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/flow.go) lines 52-55:

```go
runner.cache = util.NewCacheWithExpireOnAdd(10*time.Minute, 1000)

```

For debugging purposes, temporarily reduce the TTL to force frequent re-evaluation:

```go
// 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`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/redirect.go) or [`switch.go`](https://github.com/infinilabs/gateway/blob/main/switch.go).

Create [`proxy/filters/transform/dump_request.go`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/transform/dump_request.go):

```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:

```yaml
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`](https://github.com/infinilabs/gateway/blob/main/gateway.yml) to output JSON logs for easier parsing:

```yaml
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`](https://github.com/infinilabs/gateway/blob/main/gateway.yml) to see detailed routing decisions in [`proxy/filters/routing/flow.go`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/flow.go) and [`ratio.go`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/gateway.yml) to set `log.level: trace`. This activates `global.Env().IsDebug`, causing filters in [`proxy/filters/routing/flow.go`](https://github.com/infinilabs/gateway/blob/main/proxy/filters/routing/flow.go) and [`ratio.go`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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`](https://github.com/infinilabs/gateway/blob/main/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.