C# Windows服务log4net配置异常:配置文件启动失败,代码配置日志丢失
首先,咱们一步步拆解你遇到的两个核心问题:app.config配置log4net时服务无法启动,以及代码初始化Logger后日志时常不生成,结合你新增Socket模块的背景,大概率是配置遗漏、初始化时机不对,或者Socket阻塞操作干扰了日志流程。下面是具体的排查和修复方案:
一、修复app.config配置方式的服务启动失败问题
你的app.config里缺少了log4net配置节的声明,这是导致配置加载失败、服务启动报错的关键原因。另外,log4net的初始化时机也需要调整到服务启动后的正确阶段。
1. 完善app.config的配置节声明
在<configuration>节点下首先添加<configSections>,明确声明log4net的配置节类型:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <!-- 新增配置节声明,这是log4net读取配置的前提 --> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> </configSections> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /> </startup> <log4net> <!-- 保留你原有log4net配置不变 --> <appender name="TestAppender" type="log4net.Appender.RollingFileAppender" > <file value="C:\log\impersonationlog_JamochaService.log" /> <encoding value="utf-8" /> <appendToFile value="true" /> <rollingStyle value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%date %level [%thread] %type.%method - %message%n" /> </layout> </appender> <root> <level value="All" /> <appender-ref ref="TestAppender" /> </root> </log4net> </configuration>
2. 调整log4net初始化时机到OnStart方法
Windows服务的构造函数是在服务实例被创建时调用,此时服务还未正式启动,环境权限和上下文都不适合初始化日志框架。把初始化代码移到OnStart方法中:
public partial class Service1 : ServiceBase { private static readonly ILog _log = LogManager.GetLogger(typeof(Service1)); public Service1() { InitializeComponent(); // 不要在这里初始化log4net } protected override void OnStart(string[] args) { // 初始化log4net log4net.Config.XmlConfigurator.Configure(); _log.Info("服务已启动"); // 关键:把Socket通信放到后台线程,避免阻塞主线程 Task.Run(() => SocketCommunication()); } // 你的SocketCommunication方法保持不变 }
3. 验证日志目录权限
确保Windows服务运行的账户(默认是Local System)对C:\log目录有读写权限。如果权限不足,日志文件无法创建,也会导致服务启动失败或日志丢失。
二、修复代码初始化Logger后日志时常不生成的问题
如果坚持用代码初始化Logger,需要修正几个关键配置和执行逻辑:
1. 修正Logger.Setup()的配置错误
你的Logger.Setup()里把AppendToFile设为false,这会每次启动服务时覆盖日志文件,且并发场景下容易出问题;另外可以移除不必要的MemoryAppender,避免分散日志输出:
public class Logger { public static void Setup() { Hierarchy hierarchy = (Hierarchy)LogManager.GetRepository(); PatternLayout patternLayout = new PatternLayout(); patternLayout.ConversionPattern = "%date [%thread] %-5level %logger - %message%newline"; patternLayout.ActivateOptions(); RollingFileAppender roller = new RollingFileAppender(); roller.AppendToFile = true; // 修改为true,追加日志而非覆盖 roller.File = "C:\\Logs\\EventLog.txt"; roller.Layout = patternLayout; roller.MaxSizeRollBackups = 5; roller.MaximumFileSize = "1GB"; roller.RollingStyle = RollingFileAppender.RollingMode.Size; roller.StaticLogFileName = true; roller.ActivateOptions(); hierarchy.Root.AddAppender(roller); // 移除MemoryAppender(除非你需要内存日志功能) // MemoryAppender memory = new MemoryAppender(); // memory.ActivateOptions(); // hierarchy.Root.AddAppender(memory); hierarchy.Root.Level = Level.Info; hierarchy.Configured = true; } }
2. 调整Logger初始化和Socket通信的线程逻辑
- 同样把
Logger.Setup()移到OnStart方法中执行,而非构造函数。 - 必须把SocketCommunication放到后台线程:
socket.Accept()是阻塞调用,如果在主线程执行会卡住服务的整个生命周期,包括日志写入线程,导致日志无法及时生成甚至丢失:
protected override void OnStart(string[] args) { Logger.Setup(); var log = LogManager.GetLogger(typeof(Service1)); log.Info("服务已启动"); // 用Task启动Socket通信的后台任务 Task.Run(() => SocketCommunication()); }
3. 确保日志对象的正确获取
在SocketCommunication方法中,不要用静态的Log.Info,而是通过LogManager.GetLogger获取当前类的日志实例,保证日志上下文正确:
private void SocketCommunication() { var log = LogManager.GetLogger(typeof(Service1)); // 正确获取日志实例 IPEndPoint ip = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999); permission = new SocketPermission(NetworkAccess.Accept, TransportType.Tcp, "", SocketPermission.AllPorts); IPHostEntry ipHost = Dns.GetHostEntry(""); IPAddress ipAddr = ipHost.AddressList[0]; IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 4510); socket.Bind(ipEndPoint); socket.Listen(10); sListener = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp); log.Info("Waiting for a client..."); client = socket.Accept(); IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint; log.Info("Connected with " + clientep.Address + " at port " + clientep.Port); while (true) { byte[] data = new byte[1024]; int receivedDataLength = client.Receive(data); string stringData = Encoding.ASCII.GetString(data, 0, receivedDataLength); log.Info("Data recieved :- " + stringData); int adder = Convert.ToInt32(stringData); adder++; string input = adder.ToString(); data = Encoding.ASCII.GetBytes(input); client.Send(data, data.Length, SocketFlags.None); log.Info("Data sent :- " + input); } }
三、额外排查点
- Socket异常捕获:你的SocketCommunication方法没有异常处理,若出现连接中断、数据转换错误等异常,会导致线程崩溃,日志也无法记录。建议添加try-catch块:
private void SocketCommunication() { var log = LogManager.GetLogger(typeof(Service1)); try { // 原有Socket逻辑 } catch (Exception ex) { log.Error("Socket通信异常", ex); // 可在这里添加重连逻辑 } }
- 服务账户权限:如果服务不是用Local System运行,要确保账户对日志目录和Socket端口有足够权限。
按照上面的步骤调整后,应该能恢复正常的日志记录,同时保证服务稳定启动运行。
内容的提问来源于stack exchange,提问作者Raghavendra B Srinidhi

