Skip to content

Conversation

@shadowsocks69420
Copy link
Contributor

@shadowsocks69420 shadowsocks69420 commented Jul 29, 2025

This is the second attempt of #1992, as requested in #1990 (comment)

Compare to the first attempt, this iteration enables more flexibility for logging configuration, supporting logging to stdout and multiple files simultaneously, which was a limitation in the previous version.

This new PR is intended as an alternative to the first iteration, feel free to close the other once decision is made.


I removed SS*Config structs as the amount of code duplication involved to deserialize the new LogWriterConfig enum is too much. And I don't really get the benefit of having them in the first place. I can see it makes sense for server configurations for backward compatibility reasons, but service configurations don't carry the same baggage and, in most cases, can be easily worked around by utilizing existing serde_derive features.

I've provided extra tests to ensure the change to the deserialization logic won't break existing configurations.

Copy link
Contributor Author

@shadowsocks69420 shadowsocks69420 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pointing out some changes that are worth discussing

Comment on lines -209 to 167
pub struct LogConfig {
/// Default logger log level, [0, 3]
/// Default log level for all writers, [0, 3]
pub level: u32,
/// Default logger format configuration
/// Default format configuration for all writers
pub format: LogFormatConfig,
Copy link
Contributor Author

@shadowsocks69420 shadowsocks69420 Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My original plan was to remove these fields from the struct while keeping the configuration format backward-compatible. E.g.: create a stdout writer using these values if no writers are configured, but ignore them otherwise (because it should be configured on a per-writer basis).

However, having to support the command line option prevented me from doing so because I figured that the command line option has to be handled in a somewhat sensible way and not ignored. But it is hard to reason about when there can be multiple writers. E.g.: which writer should the command line option apply to?

I'd still like to remove them if possible, let me know if you have opinion on this,

pub format: LogFormatConfig,
/// Logging configuration file path
/// Log writers configuration
pub writers: Vec<LogWriterConfig>,
Copy link
Contributor Author

@shadowsocks69420 shadowsocks69420 Jul 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the term writers as requested in your comment #1990 (comment)

However, please note that in tracing the term writer refers to the MakeWriter trait which only handles dispatching a formatted buffer to an io::Write. What each LogWriterConfig here corresponds to is a "subscriber" or "layer" in tracing-speak, or an "appender" in log4rs-speak.

@shadowsocks69420
Copy link
Contributor Author

@zonyitoo This is ready for review. Waiting for your feedback on this.

@zonyitoo
Copy link
Collaborator

zonyitoo commented Aug 7, 2025

I know. But I was quite busy these days.

@zonyitoo zonyitoo merged commit 4a105e9 into shadowsocks:master Aug 28, 2025
17 checks passed
@robot-dot-win
Copy link

My current log.yaml is:

appenders:
    logfile:
        kind: rolling_file
        #path: c:\\ss\\log\\ssl.log  # Windows
        path: /var/log/ss/ssl.log
        encoder:
            kind: pattern
            pattern: "{d(%Y.%m.%d %H:%M:%S)} {l} {M} - {m}{n}"
        policy:
            kind: compound
            trigger:
                kind: size
                limit: 10 mb
            roller:
                kind: fixed_window
                #pattern: "c:\\ss\\log\\ssl.log.{}"   # Windows
                pattern: "/var/log/ss/ssl.log.{}"
                base: 1
                count: 20

root:
    level: info
    appenders:
        - logfile

loggers:
    shadowsocks_service::local::utils:
        level: debug
    shadowsocks_service::local::socks::server:
        level: debug

I'm a user, not a go programmer. As for the new logger, how to config different service to use different log level? Thanks.

@zonyitoo
Copy link
Collaborator

  1. This is a Rust project, not Go.
  2. Logging configuration applies globally. What do you mean "services"?

@robot-dot-win
Copy link

  1. This is a Rust project, not Go.
  2. Logging configuration applies globally. What do you mean "services"?
  1. Sorry for my mistaking shadowsockes for coredns :)
  2. I meant that I would set the root log level to info, but set shadowsocks_service::local::utils to debug, just like what I'm using via log.yaml.

@shadowsocks69420
Copy link
Contributor Author

@robot-dot-win The new logging implementation will still honer the RUST_LOG environment variable. The syntax is documented here. Though keep in mind that the environment variable applies/overrides to all writers globally.

@robot-dot-win
Copy link

robot-dot-win commented Sep 6, 2025

@robot-dot-win The new logging implementation will still honer the RUST_LOG environment variable. The syntax is documented here. Though keep in mind that the environment variable applies/overrides to all writers globally.

Thanks. My linux systemd service config:

[Unit]
Description=Shadowsocks Client
Documentation=https://github.com/shadowsocks/shadowsocks-rust
After=network.target

[Service]
Type=simple
User=root
Environment="RUST_LOG=info,shadowsocks_service::local::utils=debug,shadowsocks_service::local::socks::server=debug"
ExecStart=/opt/ss/sslocal --config /etc/ss/ssl.json
Restart=on-failure

[Install]
WantedBy=multi-user.target

And the log config for sslocal:

    "log": {
        "level": 2,
        "format": {
            "without_time": false,
        },
        "writers": [
            {
                "file": {
                    "directory": "/var/log/ss",
                    "rotation": "daily",
                    "max_files": 32
                }
            }
        ]
    }
[root@ss]# systemctl status ssl
● ssl.service - Shadowsocks Client
     Loaded: loaded (/usr/lib/systemd/system/ssl.service; enabled; preset: disabled)
     Active: active (running) since Sat 2025-09-06 14:06:52 CST; 18min ago
       Docs: https://github.com/shadowsocks/shadowsocks-rust
   Main PID: 5497 (sslocal)
      Tasks: 12 (limit: 48880)
     Memory: 2.1M (peak: 3.4M)
        CPU: 13ms
     CGroup: /system.slice/ssl.service
             └─5497 /opt/ss/sslocal --config /etc/ss/ssl.json

Sep 06 14:06:52 ss systemd[1]: Started Shadowsocks Client.
Sep 06 14:06:52 ss sslocal[5497]: 2025-09-06T14:06:52.33398795+08:00  INFO main ThreadId(01) shadowsocks_rust::service::local: shadowsocks local 1.23.5 build 2025-07-04T15:22:50.619501999+00:00
Sep 06 14:06:52 ss sslocal[5497]: 2025-09-06T14:06:52.336667036+08:00  INFO tokio-runtime-worker ThreadId(10) shadowsocks_service::local::socks::server::server: shadowsocks socks TCP listening on 192.168.252.48:1080

The service runs successfully, but there's nothing created in the directory /var/log/ss/.

Anything wrong with my config?

@shadowsocks69420
Copy link
Contributor Author

@robot-dot-win I see that you are using the 1.23.5 version built on July 4. However, the new implementation in this PR was merged on Aug 29 and it hasn't been released yet. If you want to try out the new feature before a release, you need to build the binary from master yourself.

@robot-dot-win
Copy link

robot-dot-win commented Sep 8, 2025

@robot-dot-win I see that you are using the 1.23.5 version built on July 4. However, the new implementation in this PR was merged on Aug 29 and it hasn't been released yet. If you want to try out the new feature before a release, you need to build the binary from master yourself.

Thanks for your comment. I thought it had been released when I checked the Configuration section of README. Better to keep README consistent with the latest release.

@robot-dot-win
Copy link

BTW, I'm not a rust programmer, but does the tracing_subscriber have to depend on the environment variable RUST_LOG? You can set environment variables in a Linux service config, but Windows services do not support that, and you have to write a powershell script to wrap the application.

@shadowsocks69420
Copy link
Contributor Author

@robot-dot-win

but does the tracing_subscriber have to depend on the environment variable RUST_LOG?

That is a convention used by logging libraries in Rust ecosystem, not just tracing_subscriber. See env_logger for example.

but Windows services do not support that, and you have to write a powershell script to wrap the application.

It is possible to set environment variable for a Window service, though a bit more involved:
https://serverfault.com/questions/813506/setting-environment-variable-for-service/1103091#1103091

@robot-dot-win
Copy link

@shadowsocks69420 Thank you so much, all cleared up!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants