You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检测Windows服务是否崩溃并实现日志记录与邮件告警?

解决Windows服务崩溃时的日志记录与邮件告警问题

先帮你解决之前OnShutdown未生效的小问题:大概率是因为没有设置CanShutdown = true(ServiceBase的默认值为false),系统不会给服务发送关机通知。你只需要在服务的构造函数里加上CanShutdown = true,OnShutdown方法就能正常触发了。

接下来重点说服务崩溃时的异常捕获与告警,核心思路是捕获全局未处理异常,在异常发生时写入事件日志并发送邮件,具体实现步骤如下:


1. 注册全局未捕获异常事件

在服务的OnStart方法中,给当前AppDomain注册UnhandledException事件,这样任何未被捕获的异常都会触发这个事件,我们就能在事件处理方法里做后续操作:

protected override void OnStart(string[] args)
{
    // 注册全局未处理异常捕获
    AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    
    // 其他初始化逻辑...
}

private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
    Exception ex = e.ExceptionObject as Exception;
    if (ex != null)
    {
        // 写入事件日志(标记为错误类型)
        WriteToEventLog($"服务崩溃,异常信息:{ex.Message}\n堆栈跟踪:{ex.StackTrace}", EventLogEntryType.Error);
        
        // 发送邮件告警
        SendAlertEmail(ex);
    }
}

2. 完善事件日志写入方法

你之前的WriteToEventLog可以优化一下,支持指定日志类型,同时注意权限问题(创建日志源需要管理员权限,建议在服务安装时提前创建):

private void WriteToEventLog(string logMessage, EventLogEntryType entryType = EventLogEntryType.Warning)
{
    const string sourceName = "MyServiceCrashLog";
    const string logName = "Application"; // 也可以使用自定义日志名,比如"MyServiceLogs"

    try
    {
        if (!EventLog.SourceExists(sourceName))
        {
            // 注意:创建日志源需要管理员权限,建议在服务安装程序中执行此操作
            EventLog.CreateEventSource(sourceName, logName);
        }

        using (var eventLog = new EventLog(logName))
        {
            eventLog.Source = sourceName;
            eventLog.WriteEntry(logMessage, entryType, 1003); // 用1003作为崩溃事件的ID,和之前的关机事件区分
        }
    }
    catch (Exception ex)
    {
        // 防止日志写入失败导致二次异常,可将异常信息写入临时文件(可选)
        File.AppendAllText(@"C:\Temp\ServiceLogError.txt", $"日志写入失败:{ex.Message}\n");
    }
}

3. 实现邮件告警功能

使用System.Net.Mail命名空间下的类实现邮件发送,注意配置正确的SMTP服务器信息,同时考虑服务运行账户的网络权限:

private void SendAlertEmail(Exception ex)
{
    try
    {
        using (var smtpClient = new SmtpClient("your-smtp-server.com", 587)) // 例如smtp.office365.com、smtp.gmail.com
        {
            smtpClient.Credentials = new NetworkCredential("your-email@domain.com", "your-password-or-app-password");
            smtpClient.EnableSsl = true; // 多数SMTP服务器需要SSL加密

            var mailMessage = new MailMessage
            {
                From = new MailAddress("your-email@domain.com"),
                Subject = "紧急:MyService服务崩溃告警",
                Body = $"服务于{DateTime.Now:yyyy-MM-dd HH:mm:ss}崩溃,异常详情:\n\n异常消息:{ex.Message}\n\n堆栈跟踪:{ex.StackTrace}",
                IsBodyHtml = false
            };
            mailMessage.To.Add("admin-alert@domain.com"); // 接收告警的目标邮箱

            // 建议用异步发送避免阻塞,替换为smtpClient.SendMailAsync(mailMessage)即可
            smtpClient.Send(mailMessage);
        }
    }
    catch (Exception emailEx)
    {
        // 邮件发送失败也要记录日志
        WriteToEventLog($"邮件告警发送失败:{emailEx.Message}", EventLogEntryType.Error);
    }
}

4. 关键注意事项

  • 权限问题:
    • 创建事件日志源需要管理员权限,建议在服务安装程序(比如使用InstallUtil工具)中提前创建,避免服务运行时因权限不足创建失败。
    • 服务运行的账户需要具备:写入事件日志的权限、访问SMTP服务器的网络权限。使用LocalSystem账户通常权限足够,若用自定义账户需手动配置这些权限。
  • 异常处理鲁棒性:日志写入和邮件发送的代码自身要加try-catch,避免因这些操作失败导致服务二次崩溃。
  • 异步优化:邮件发送是IO操作,建议使用异步方法smtpClient.SendMailAsync,避免阻塞服务的异常处理流程(注意在异步方法中正确捕获异常)。

内容的提问来源于stack exchange,提问作者Sharpeye500

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:49:17