# How NLog Logging Is Configured and Verbose Logging Works in Shadowsocks-Windows

> Discover how Shadowsocks-Windows configures NLog via an embedded XML file. Learn about verbose logging and runtime toggling without restarting the application.

- Repository: [shadowsocks/shadowsocks-windows](https://github.com/shadowsocks/shadowsocks-windows)
- Tags: internals
- Published: 2026-03-05

---

**Shadowsocks-Windows configures NLog through an XML file embedded as an assembly resource, mapping Debug/Trace levels to verbose mode while allowing runtime toggling without application restart.**

Shadowsocks-Windows utilizes **NLog** as its unified logging framework to capture diagnostic information and runtime events. The application manages its logging configuration through a dynamic XML file that ships as an embedded resource and gets written to disk on first launch. Understanding how this NLog logging system is configured and how verbose logging works reveals the mechanisms behind the application's diagnostic capabilities and the seamless way users can adjust log verbosity on the fly.

## Configuration File Initialization and Loading

The logging lifecycle begins when the application starts. In [`shadowsocks-csharp/Program.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Program.cs), the entry point immediately invokes `Model.NLogConfig.TouchAndApplyNLogConfig()` to ensure the configuration file exists before any logging occurs.

### Embedded Resource and First-Run Setup

The default XML configuration lives as an embedded resource named `NLog_config` inside [`shadowsocks-csharp/Properties/Resources.Designer.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Properties/Resources.Designer.cs). During first run, the application checks for the presence of `NLog.config` in the working directory. If the file is missing, the startup code extracts the embedded resource and writes it to disk as `"NLog.config"`.

### The TouchAndApplyNLogConfig Method

Located in [`shadowsocks-csharp/Model/NLogConfig.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Model/NLogConfig.cs), the `TouchAndApplyNLogConfig()` method performs two critical operations:

1. **File Creation**: Writes the embedded XML to disk if `NLog.config` does not exist
2. **Configuration Loading**: Invokes `LoadConfiguration()` which calls `LogManager.LoadConfiguration(NLOG_CONFIG_FILE_NAME)` to apply settings to the global `LogManager`

This ensures NLog is fully initialized before the main application window appears.

## Understanding Verbose Logging Levels

Verbose logging in Shadowsocks-Windows is not a separate logging framework but rather a mapping of NLog's native levels to a user-facing boolean flag.

### Mapping NLog Levels to Verbose Mode

During configuration initialization in [`shadowsocks-csharp/Model/Configuration.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Model/Configuration.cs), the `Process()` method reads the XML via `NLogConfig.LoadXML()` and checks the `minlevel` attribute through `GetLogLevel()`. The logic follows this strict mapping:

```csharp
switch (config.nLogConfig.GetLogLevel())
{
    case NLogConfig.LogLevel.Debug:
    case NLogConfig.LogLevel.Trace:
        config.isVerboseLogging = true;   // verbose mode enabled
        break;
    default:
        config.isVerboseLogging = false;  // normal mode
}

```

- **Debug or Trace** → `isVerboseLogging = true` (Verbose mode)
- **Fatal, Error, Warn, or Info** → `isVerboseLogging = false` (Normal mode)

### The Configuration Flag

The `isVerboseLogging` boolean is persisted in [`gui-config.json`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/gui-config.json) and drives the UI checkbox state in [`shadowsocks-csharp/View/MenuViewController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/View/MenuViewController.cs). This creates a bridge between the technical NLog levels and user-friendly application settings.

## Runtime Toggle Without Restart

A key feature of the implementation is the ability to change logging verbosity without restarting the application. This is handled through the `ToggleVerboseLogging` pipeline.

### The ToggleVerboseLogging Flow

When a user clicks the "Verbose Logging" menu item (handled in [`MenuViewController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/MenuViewController.cs)), the following sequence executes:

1. **Flag Update**: `Configuration.isVerboseLogging` is set to the requested value
2. **Level Calculation**: `verboseLogLevel` is determined—`Debug` for release builds and `Trace` for DEBUG builds
3. **XML Modification**: The code updates the NLog configuration object and writes it back:

```csharp
config.nLogConfig.SetLogLevel(
    config.isVerboseLogging ? verboseLogLevel : NLogConfig.LogLevel.Info);
NLogConfig.SaveXML(config.nLogConfig);

```

4. **Configuration Reload**: `ShadowsocksController.ToggleVerboseLogging` triggers `NLogConfig.LoadConfiguration()` to reload the modified XML

### Reloading Configuration Dynamically

Because the code calls `LogManager.LoadConfiguration()` again with the updated file, all existing logger instances automatically respect the new minimum level. Loggers are typically declared as static fields throughout the codebase:

```csharp
private static readonly Logger logger = LogManager.GetCurrentClassLogger();

```

Subsequent calls to `logger.Info()`, `logger.Debug()`, or `logger.Trace()` filter according to the new configuration immediately.

## Log Output and Visualization

The default embedded XML defines a file target that directs all output to `ss_win_temp\shadowsocks.log`. The [`shadowsocks-csharp/View/LogForm.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/View/LogForm.cs) class reads this file in real-time to display logs in the user interface, providing immediate visibility into connection diagnostics and proxy events.

## Implementation Examples

### Reading the Current NLog Level

To programmatically check the current logging level:

```csharp
var nLogConfig = NLogConfig.LoadXML();          // loads NLog.config
var level = nLogConfig.GetLogLevel();           // enum LogLevel
Console.WriteLine($"Current NLog level: {level}");

```

*Source: [`shadowsocks-csharp/Model/NLogConfig.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/Model/NLogConfig.cs)*

### Enabling Verbose Logging from Code

To force verbose mode programmatically:

```csharp
var cfg = Configuration.Load();
cfg.isVerboseLogging = true;
cfg.nLogConfig.SetLogLevel(
    cfg.isVerboseLogging ? NLogConfig.LogLevel.Debug : NLogConfig.LogLevel.Info);
NLogConfig.SaveXML(cfg.nLogConfig);
NLogConfig.LoadConfiguration();

```

*Relevant sections: [`Configuration.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Configuration.cs) Save method, [`ShadowsocksController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/ShadowsocksController.cs) ToggleVerboseLogging*

### Writing Log Statements

Any class can log using the standard NLog pattern:

```csharp
private static readonly Logger logger = LogManager.GetCurrentClassLogger();

logger.Info("Application started");
logger.Debug("Detailed debug info – only visible when verbose logging is on");
logger.Trace("Trace-level tracing – shown when the config level is Trace");

```

*Examples appear throughout [`Program.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Program.cs) and [`ShadowsocksController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/ShadowsocksController.cs)*

## Summary

- **Shadowsocks-Windows** uses NLog with an XML configuration file (`NLog.config`) that ships as an embedded resource in [`Resources.Designer.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Resources.Designer.cs)
- The `TouchAndApplyNLogConfig()` method in [`NLogConfig.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/NLogConfig.cs) handles first-run file creation and initial loading via `LogManager.LoadConfiguration()`
- **Verbose mode** maps to NLog levels `Debug` (Release builds) or `Trace` (DEBUG builds), stored in the `isVerboseLogging` flag within [`Configuration.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Configuration.cs)
- **Runtime toggling** updates both [`gui-config.json`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/gui-config.json) and the `NLog.config` XML, then reloads the configuration without requiring an application restart
- Logs write to `ss_win_temp\shadowsocks.log` and display in the `LogForm` UI component

## Frequently Asked Questions

### How does Shadowsocks-Windows create the NLog configuration file on first run?

The application calls `Model.NLogConfig.TouchAndApplyNLogConfig()` during startup in [`Program.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Program.cs). This method checks for the existence of `NLog.config` on disk, and if missing, extracts the embedded resource `NLog_config` from [`Resources.Designer.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/Resources.Designer.cs) and writes it to the working directory before loading it into the NLog `LogManager`.

### What is the difference between Debug and Trace levels in verbose logging?

In release builds, verbose logging uses the `Debug` level, while DEBUG builds use `Trace` for maximum granularity. Both levels set `isVerboseLogging` to `true` in the configuration, but `Trace` provides more detailed diagnostic information than `Debug` during development.

### Can I change the logging level without restarting the application?

Yes. When you toggle verbose logging via the UI menu, [`MenuViewController.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/MenuViewController.cs) invokes `ShadowsocksController.ToggleVerboseLogging(bool)`, which updates the configuration object, writes the new level to the XML file via `NLogConfig.SaveXML()`, and immediately reloads the configuration using `NLogConfig.LoadConfiguration()`. NLog applies these changes dynamically to all existing logger instances.

### Where are the log files stored and how can I view them?

By default, logs are written to `ss_win_temp\shadowsocks.log` as defined in the XML target configuration. You can view these logs directly in the file system or through the built-in log viewer accessible from the application menu, which is implemented in [`shadowsocks-csharp/View/LogForm.cs`](https://github.com/shadowsocks/shadowsocks-windows/blob/main/shadowsocks-csharp/View/LogForm.cs) and reads the log file in real-time.