XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

LumaTunnel

【WinUI】LumaTunnel 是一个面向个人多设备的 Windows 代理系统。客户端在本机提供 HTTP/HTTPS CONNECT 与 SOCKS5 TCP 代理,并通过一个受信任 TLS 证书保护的 WSS 会话,将多个 TCP 流复用到自建 Windows Server 节点。

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Collections.Concurrent;

namespace LumaTunnel.Server;

internal sealed class RollingFileLoggerProvider(string directory) : ILoggerProvider
{
    private readonly ConcurrentDictionary<string, RollingFileLogger> _loggers = new(StringComparer.Ordinal);
    private readonly SharedWriter _writer = new(directory);

    public ILogger CreateLogger(string categoryName) => _loggers.GetOrAdd(categoryName, category => new RollingFileLogger(category, _writer));
    public void Dispose()
    {
        _writer.Dispose();
        _loggers.Clear();
        GC.SuppressFinalize(this);
    }

    private sealed class RollingFileLogger(string category, SharedWriter writer) : ILogger
    {
        public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
        public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Information;
        public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
        {
            if (!IsEnabled(logLevel)) return;
            var message = formatter(state, exception).Replace("\r", " ", StringComparison.Ordinal).Replace("\n", " ", StringComparison.Ordinal);
            writer.Write($"{DateTimeOffset.Now:O} [{logLevel}] {category}: {message}{(exception is null ? string.Empty : " | " + exception.GetType().Name)}");
        }
    }

    private sealed class SharedWriter : IDisposable
    {
        private readonly object _gate = new();
        private readonly string _directory;
        private DateOnly _date;
        private StreamWriter? _writer;
        private int _segment;

        public SharedWriter(string directory)
        {
            _directory = directory;
            Directory.CreateDirectory(directory);
        }

        public void Write(string line)
        {
            lock (_gate)
            {
                EnsureWriter();
                _writer!.WriteLine(line);
                _writer.Flush();
            }
        }

        public void Dispose()
        {
            lock (_gate) { _writer?.Dispose(); _writer = null; }
        }

        private void EnsureWriter()
        {
            var today = DateOnly.FromDateTime(DateTime.Today);
            if (_writer is null || today != _date || _writer.BaseStream.Length >= 10 * 1024 * 1024)
            {
                _writer?.Dispose();
                if (today != _date) _segment = 0; else _segment++;
                _date = today;
                var suffix = _segment == 0 ? string.Empty : $"-{_segment}";
                var path = Path.Combine(_directory, $"lumatunnel-{today:yyyyMMdd}{suffix}.log");
                _writer = new StreamWriter(new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read), System.Text.Encoding.UTF8);
                foreach (var old in Directory.GetFiles(_directory, "lumatunnel-*.log").OrderByDescending(static item => item, StringComparer.OrdinalIgnoreCase).Skip(10))
                    File.Delete(old);
            }
        }
    }
}