# How the Xray-core Logging System Works: Configuring Structured Logging

> Learn how Xray-core's logging system works with its Message Handler pipeline. Configure structured logging easily using JSON, TOML, or YAML for efficient debugging.

- Repository: [Project X Community, Not Porn-jet X Hub/Xray-core](https://github.com/XTLS/Xray-core)
- Tags: internals
- Published: 2026-04-21

---

**Xray-core employs a lightweight Message → Handler pipeline where all log entries implement the `Message` interface, with structured data carried in specific concrete types like `AccessMessage` and `DNSLog`, configurable via JSON/TOML/YAML sections that map to protobuf-based runtime settings.**

The **XTLS/Xray-core** repository implements a sophisticated yet extensible logging framework designed to handle high-throughput proxy traffic with structured context. Understanding how this **Xray-core logging system** operates enables you to capture detailed access records, debug DNS resolution, and eliminate noise through severity-based filtering. This guide examines the internal architecture and provides concrete configuration examples for implementing structured logging in production environments.

## Core Components of the Xray-core Logging System

The logging framework centers on three abstractions defined in [`common/log/log.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/log.go): the **Message** interface, the **Handler** interface, and concrete logger implementations that manage buffering and output.

### Message Interface and Structured Types

Every log entry in Xray-core implements the `Message` interface, which requires a `String()` method for text rendering. The system provides three primary concrete types that carry structured fields beyond simple text:

- **`GeneralMessage`** – Standard diagnostic logs with severity levels, defined in [`common/log/log.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/log.go) (lines 19-27)
- **`AccessMessage`** – Connection metadata including source IP, destination, status (Accepted/Rejected), email, and detour, defined in [`common/log/access.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/access.go) (lines 23-31)  
- **`DNSLog`** – DNS query details including domain, answer IPs, and resolution latency, defined in [`common/log/dns.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/dns.go) (lines 9-16)

These types encapsulate rich context that remains available programmatically even when rendered as human-readable strings via their `String()` implementations.

### Handler and Logger Architecture

The **Handler** interface in [`common/log/logger.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/logger.go) (lines 14-22) defines a single method `Handle(msg Message)` that consumes log entries. The architecture stacks multiple layers to provide buffering, filtering, and routing:

1. **General Logger** – Created by `log.NewLogger(WriterCreator)` in [`common/log/logger.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/logger.go) (lines 35-44), buffers messages and writes through concrete stdout or file Writers
2. **Severity Logger** – Wraps a general logger and filters `GeneralMessage` instances based on configured severity thresholds, implemented in [`common/log/logger.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/logger.go) (lines 45-69)
3. **Log Instance** – The concrete Xray logger in [`app/log/log.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/log.go) (lines 16-42) implements `log.Handler` and routes incoming messages to access or error sub-loggers based on runtime configuration

## Message Flow Through the Logging Pipeline

When application code emits a log entry, the message traverses a specific pipeline from emission to persistence:

1. **Emission** – Any component calls `log.Record(msg)` or uses helper functions from `common/errors`
2. **Global Handler** – `log.Record` forwards to the globally registered `logHandler`, which is the `app/log.Instance` created at startup (see the `New` function in [`app/log/log.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/log.go))
3. **Type-Based Routing** – `Instance.Handle` examines the concrete Message type:
   - `*log.AccessMessage` routes to the **access logger** (file or console as configured)
   - `*log.DNSLog` routes to the access logger only when `EnableDnsLog` is true in the configuration
   - `*log.GeneralMessage` filters by `ErrorLogLevel` and routes to the **error logger**
4. **Output** – Sub-loggers execute write operations through handlers created by [`app/log/log_creator.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/log_creator.go) via the `handlerCreatorMap`

This design separates the emission of structured data from its eventual formatting, enabling you to swap output formats without modifying the instrumentation code.

## Configuring Structured Logging in Xray-core

Configuration flows from JSON/TOML/YAML files through protocol buffer definitions to runtime logger initialization. The **Log Instance** reads the `log` section and constructs an `app/log.Config` protobuf message to build the runtime logger accordingly.

### JSON Configuration Fields

The logging configuration accepts the following fields mapped in [`infra/conf/log.go`](https://github.com/XTLS/Xray-core/blob/main/infra/conf/log.go) and defined in [`app/log/config.pb.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/config.pb.go) (lines 25-85):

| Field | Description | Protobuf Mapping |
|-------|-------------|------------------|
| `access` | Path to access log file; use `"none"` to disable | `AccessLogPath` / `AccessLogType` |
| `error` | Path to error log file; use `"none"` to disable | `ErrorLogPath` / `ErrorLogType` |
| `loglevel` | Minimum severity for general logs: `debug`, `info`, `warning`, `error` | `ErrorLogLevel` |
| `dnsLog` | Boolean to enable DNS query logging to the access log | `EnableDnsLog` |
| `maskAddress` | IP masking mode: `full`, `half`, `quarter`, or custom CIDR | `MaskAddress` |

Minimal configuration example:

```json
{
  "log": {
    "access": "access.log",
    "error": "error.log",
    "loglevel": "warning",
    "dnsLog": true,
    "maskAddress": "full"
  }
}

```

### Command Line Overrides

For runtime adjustments without modifying configuration files, use CLI flags defined in [`main/commands/all/api/logger_restart.go`](https://github.com/XTLS/Xray-core/blob/main/main/commands/all/api/logger_restart.go):

```bash

# Start with custom config

xray -c my-config.json

# Override specific fields

xray -c '' -log.level warning -log.dnsLog true

```

These flags re-initialize the logger while Xray runs, enabling dynamic log level adjustments without service restarts.

### Programmatic Usage

Within custom modules or plugins, import the logging package and construct structured messages:

```go
import (
    clog "github.com/xtls/xray-core/common/log"
    "net"
    "time"
)

// Emit structured access log
accessMsg := &clog.AccessMessage{
    From:   clientIP,
    To:     targetIP,
    Status: clog.AccessAccepted,
    Reason: "policy ok",
    Email:  "user@example.com",
}
clog.Record(accessMsg)

// Emit structured DNS log
dnsMsg := &clog.DNSLog{
    Domain:   "example.com",
    Answer:   []net.IP{net.ParseIP("93.184.216.34")},
    Duration: time.Millisecond * 42,
}
clog.Record(dnsMsg)

```

Because the system operates on the **Message** interface, you can implement custom `Handler` types (such as JSON exporters or remote aggregators) by registering new `HandlerCreator` functions in [`app/log/log_creator.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/log_creator.go) without modifying existing emission code.

## Summary

- **Xray-core** implements a **Message → Handler** pipeline where all log entries implement the `Message` interface with a `String()` method
- Structured data lives in concrete types: **`AccessMessage`** (connection metadata), **`DNSLog`** (resolution details), and **`GeneralMessage`** (diagnostics)
- The **Log Instance** in [`app/log/log.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/log.go) routes messages to access or error sub-loggers based on type and configuration
- Configure via JSON/TOML/YAML using fields: `access`, `error`, `loglevel`, `dnsLog`, and `maskAddress`, which map to protobuf definitions in [`app/log/config.pb.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/config.pb.go)
- Programmatic emission uses `clog.Record(msg)` with structured message types defined in [`common/log/access.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/access.go) and [`common/log/dns.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/dns.go)
- Custom output formats require implementing the **Handler** interface and registering via `handlerCreatorMap` in [`app/log/log_creator.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/log_creator.go)

## Frequently Asked Questions

### What log levels are supported in Xray-core?

Xray-core supports four severity levels for general logging: **`debug`**, **`info`**, **`warning`**, and **`error`**. These map to protobuf constants in [`app/log/config.pb.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/config.pb.go) (`Severity_Debug` through `Severity_Error`). The `loglevel` configuration field sets the minimum threshold; messages below this level are filtered by the `severityLogger` wrapper in [`common/log/logger.go`](https://github.com/XTLS/Xray-core/blob/main/common/log/logger.go) and never reach the output Writer.

### How do I disable access logs while keeping error logs?

Set the `access` field to `"none"` in your configuration while specifying a path for `error`:

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

```

This maps to `AccessLogType = None` in the protobuf configuration, causing the [`app/log/log.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/log.go) router to discard `AccessMessage` and `DNSLog` instances rather than forwarding them to a Writer, while `GeneralMessage` instances continue to flow to the error logger.

### Can I output logs in JSON format?

Xray-core does not include a built-in JSON formatter in the current source tree. However, because the logging system operates on the **Message** interface, you can implement structured JSON output by creating a custom `Handler` that implements `Handle(msg Message)` and serializes the structured fields of `AccessMessage` (source, destination, status) or `DNSLog` (domain, answers) rather than calling `msg.String()`. Register your handler via the `handlerCreatorMap` in [`app/log/log_creator.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/log_creator.go).

### How does IP masking work in Xray-core logs?

The **`maskAddress`** field supports four modes defined in the configuration: `full` (masks entire IP), `half` (masks last two octets of IPv4 or last 8 bytes of IPv6), `quarter` (masks last octet of IPv4 or last 4 bytes of IPv6), or custom CIDR notation like `"192.0.2.0/24"`. This setting is processed according to [`app/log/config.pb.go`](https://github.com/XTLS/Xray-core/blob/main/app/log/config.pb.go) and applied when stringifying `AccessMessage` instances, allowing privacy-compliant logging in production environments while retaining enough information for debugging.