Skip to content

Latest commit

 

History

101 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Xray Exporter

A Prometheus exporter for Xray and V2Ray. It reads runtime and traffic statistics from Xray's gRPC Stats API, and optionally parses the access log to report who is connecting, from where, and to which destinations.

Rotation is detected by inode, so log parsing is unix only. Linux and macOS builds are published.

What you can put on a dashboard

From the access log:

  • Unique users and open connections over a rolling window
  • Top requested domains, with subdomains folded into their registrable domain
  • Top direct IP destinations, with localhost, private ranges, and the usual public DNS resolvers filtered out
  • Top ASNs with their network name, top countries, and top cities, resolved from GeoLite2 databases the exporter downloads and refreshes on its own
  • A world map of connections by country or city
  • How traffic splits across your outbounds, including whichever tag your config uses for blocked traffic

From the Stats API:

  • Bytes up and down per inbound and per outbound
  • Bytes up and down per user, off by default because it is one series per user
  • Xray process health: uptime, goroutine count, heap usage, and GC pauses
  • Whether the last scrape worked and how long it took

Our CompassVPN dashboard is published on grafana.com if you want a starting point. The metrics reference below has the queries if you would rather build your own.

Install

Binaries

Linux (amd64, arm64, arm) and macOS (amd64, arm64) builds are on the releases page.

Docker

Multi-arch images (amd64, arm64, arm/v7) are published to the GitHub Container Registry:

docker run --rm -it ghcr.io/compassvpn/xray-exporter:latest

Three kinds of tag are available: latest follows the newest release, a version tag like v0.6.6 pins to one release, and main is built from the tip of the main branch and may not be stable.

Setup

Xray config

The exporter talks to Xray's Stats API, so the api, stats, and policy blocks all need to be present, along with a routing rule that sends the API inbound to the API outbound:

{
  "routing": {
    "rules": [
      {
        "inboundTag": [
          "api"
        ],
        "outboundTag": "api"
      }
    ]
  },
  "policy": {
    "levels": {
      "0": {
        "statsUserUplink": true,
        "statsUserDownlink": true
      }
    },
    "system": {
      "statsInboundUplink": true,
      "statsInboundDownlink": true,
      "statsOutboundUplink": true,
      "statsOutboundDownlink": true
    }
  },
  "stats": {},
  "api": {
    "tag": "api",
    "services": [
      "StatsService"
    ]
  },
  "inbounds": [
    {
      "tag": "api",
      "listen": "127.0.0.1",
      "port": 54321,
      "protocol": "dokodemo-door",
      "settings": {
        "address": "127.0.0.1"
      }
    },
    {
      "tag": "inbound-1",
      "port": 12345,
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "email": "email",
            "id": "uuid",
            "level": 0
          }
        ]
      }
    }
  ],
  "outbounds": [
    {
      "tag": "direct",
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

There are two inbounds here. The first listens on port 54321 on localhost and answers the API calls the exporter makes. The second is an ordinary VMess inbound for the user email. If the exporter runs on a different machine than Xray, listen on 0.0.0.0 instead of 127.0.0.1 and firewall the port, since anyone who can reach it can read your stats.

See the xray-core API docs and stats docs for the details.

Access log

Everything under user activity below comes from the access log, which is off unless you turn it on:

{
  "log": {
    "access": "/var/log/xray/access.log",
    "error": "/var/log/xray/error.log",
    "loglevel": "warning"
  }
}

Running it

Starting the exporter

# gRPC metrics only
xray-exporter --xray-endpoint "127.0.0.1:54321"

# plus user activity from the access log
xray-exporter --xray-endpoint "127.0.0.1:54321" --log-path "/var/log/xray/access.log"

# with a 10 minute window instead of the default 5
xray-exporter --xray-endpoint "127.0.0.1:54321" --log-time-window 10

Or in Docker, with the log mounted read-only and a writable directory for the GeoIP databases:

docker run --rm -d \
  -v /var/log/xray:/var/log/xray:ro \
  -v xray-exporter-geoip:/geoip \
  ghcr.io/compassvpn/xray-exporter:latest \
  --xray-endpoint "xray:54321" \
  --log-path "/var/log/xray/access.log" \
  --geoip-dir /geoip

The GeoLite2 databases are written to --geoip-dir, which defaults to the working directory. Inside the container that is /, so a container started with --read-only and no writable volume cannot download them. That is not fatal, but the ASN, country, and city metrics stay empty.

On startup you should see:

Xray Exporter XXX-a1b2c3d (built 2025-01-01T21:00:00Z)
time="2025-01-15T10:30:45Z" level=info msg="Log parser started successfully"
time="2025-01-15T10:30:45Z" level=info msg="Server starting on :9550"

Open http://ip:9550 and follow the Scrape Xray Metrics link to see the output. If xray_up 1 is missing from the response, the scrape failed and the exporter logs will say why.

Prometheus

global:
  scrape_interval: 15s
  scrape_timeout: 5s

scrape_configs:
  - job_name: xray
    metrics_path: /scrape
    static_configs:
      - targets: [IP:9550]

Grafana

Import dashboard 23181 from grafana.com, or build your own from the metrics reference.

Flags

Flag Default Description
-l, --listen [ADDR]:PORT :9550 Address to listen on
-m, --metrics-path PATH /scrape Path that serves the Xray metrics
-e, --xray-endpoint HOST:PORT 127.0.0.1:8080 Xray API endpoint
-t, --scrape-timeout N 5 Timeout in seconds for each individual scrape
-u, --user-traffic-metrics off Export per-user traffic byte counters
-p, --log-path PATH /var/log/xray/access.log Access log to parse. Empty disables the log metrics
-w, --log-time-window N 5 Window in minutes for the log metrics
-g, --geoip-dir PATH . Directory for the GeoLite2 databases
--log-level LEVEL info error, warn, info, or debug. Also read from LOG_LEVEL
--log-format FORMAT text text or json. Also read from LOG_FORMAT
--version Print the version and exit

xray-exporter -h prints the same list.

Metrics reference

Xray runtime

Xray stat Exported metric Description
uptime xray_uptime_seconds Xray uptime in seconds
num_goroutine xray_goroutines Number of goroutines
alloc xray_memstats_alloc_bytes Bytes allocated and in use
total_alloc xray_memstats_alloc_bytes_total Total bytes allocated
sys xray_memstats_sys_bytes Bytes obtained from the OS
mallocs xray_memstats_mallocs_total Total number of mallocs
frees xray_memstats_frees_total Total number of frees
num_gc xray_memstats_num_gc Number of GC cycles
pause_total_ns xray_memstats_pause_total_ns Total GC pause time

Traffic

Xray stat Exported metric
inbound>>>tag-name>>>traffic>>>uplink xray_traffic_uplink_bytes_total{dimension="inbound",target="tag-name"}
inbound>>>tag-name>>>traffic>>>downlink xray_traffic_downlink_bytes_total{dimension="inbound",target="tag-name"}
outbound>>>tag-name>>>traffic>>>uplink xray_traffic_uplink_bytes_total{dimension="outbound",target="tag-name"}
outbound>>>tag-name>>>traffic>>>downlink xray_traffic_downlink_bytes_total{dimension="outbound",target="tag-name"}
user>>>user-email>>>traffic>>>uplink xray_traffic_uplink_bytes_total{dimension="user",target="user-email"}
user>>>user-email>>>traffic>>>downlink xray_traffic_downlink_bytes_total{dimension="user",target="user-email"}

The dimension="user" rows are opt-in. Pass --user-traffic-metrics to turn them on. Every user becomes its own series with no top-N cap, so only do this where the set of users is small and known.

User activity

Only exported when --log-path points at a readable access log.

Metric Type Description Labels
xray_requested_domain_ip_total counter Requests per domain or IP since startup target
xray_asns_total counter Requests per ASN since startup asn, org
xray_countries_total counter Requests per country since startup country
xray_cities_total counter Requests per city since startup city, country
xray_unique_users gauge Unique users active in the time window
xray_total_connections gauge Connections in the time window
xray_outbound_requests gauge Requests per outbound in the time window outbound

Development

Before committing, run the checks CI runs:

gofmt -w . && go vet ./... && go build ./...

Special thanks

About

An exporter that collects Xray (and V2Ray) metrics over its Stats API and exports them to Prometheus. It also provides enhanced user activity metrics by parsing access logs.

Topics

Resources

Stars

18 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages