在 .Net Core 中写入事件日志

24
我需要一种在使用dnx的应用程序中写入Windows事件查看器的方法。但是,在System.Diagnostics命名空间中不可用EventLog类,所以我被卡住了。是否有其他方法可以写入EventViewer?

5
你看到了 https://www.nuget.org/packages/Microsoft.Extensions.Logging.EventLog/ 吗? - StingyJack
4
如果您的应用程序必须针对Core进行目标设置,那么我认为不行——事件日志不是跨平台概念。如果您的应用程序可以针对net461或其他完整的.NET平台进行目标设置,则可以使用@StingyJack提到的软件包。 - stephen.vakil
@stephen.vakil,我该如何在我的project.json中同时针对net461和core进行设置? - Ameno acids
通过 project.jsonframeworks 部分,可以查看 https://dev59.com/S10a5IYBdhLWcg3wD1Ae 获取一些选项。 - stephen.vakil
据我所知,您始终可以有条件地加载程序集,但是检测何时进行可能是困难的部分。https://dev59.com/52oy5IYBdhLWcg3wfeER#8543850 - StingyJack
显示剩余2条评论
5个回答

22

NuGet 添加 Microsoft.Extensions.Logging.EventLog,版本为 2.1.1

CM> Install-Package Microsoft.Extensions.Logging.EventLog -Version 2.1.1

Program.cs文件中包含命名空间Microsoft.Extensions.Logging.EventLog

这解决了我的问题。


3
你好,欢迎来到StackOverflow。你能否编辑你的回答以澄清如何回答提问者的问题?谢谢。 - MandyShaw
1
你可以安装版本为6.0.0的System.Diagnostics.EventLog包。 - Uday

13

在 .Net Core 中写入事件日志需要先安装一个 Nuget 包。

Install-Package Microsoft.Extensions.Logging.EventLog -Version 3.1.2

请注意,安装的正确版本取决于您正在运行的 .Net Core 版本。上面的软件包已在 .Net Core 下进行了测试并且可以正常运行。

然后我们需要添加 EventLog。我们可以在 Program 类中这样做:

    using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;

namespace SomeAcme.SomeApi
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureLogging((hostingContext, logging) =>
                {
                    logging.ClearProviders();
                    logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
                    logging.AddEventLog(new EventLogSettings()
                    {
                        **SourceName = "SomeApi",
                        LogName = "SomeApi",**
                        Filter = (x, y) => y >= LogLevel.Warning
                    });
                    logging.AddConsole();
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

而我们的 appsettings.json 文件包含设置:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.\\SQLEXPRESS;Database=SomeApi;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  **"Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },**
  "AllowedHosts": "*"
}
我们可以注入ILogger实例。
using SomeAcme.SomeApi.SomeModels;
using SomeAcme.SomeApi.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;

namespace SomeAcme.SomeApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class SomeController : ControllerBase
    {
        private readonly ISomeService _healthUnitService;
        private readonly ILogger<SomeController> _logger;

        public SomeController(ISomeService someService, ILogger<SomeController> logger)
        {
            _someService= someService;
            _logger = logger;
        }
        // GET: api/Some
        [HttpGet]
        public IEnumerable<SomeModel> GetAll()
        {
            return _someService.GetAll();
        }
    }
}
在 .Net Core 中,更高级的用法是在 Startup 类的 Configure 方法中添加全局异常处理程序:

更高级的用法,可以在 .Net Core 的 Startup 类的 Configure 方法中添加全局异常处理器:

  //Set up a global error handler for handling Unhandled exceptions in the API by logging it and giving a HTTP 500 Error with diagnostic information in Development and Staging
        app.UseExceptionHandler(errorApp =>
        {
            errorApp.Run(async context =>
            {
                context.Response.StatusCode = 500; // or another Status accordingly to Exception Type
                context.Response.ContentType = "application/json";

                var status = context.Features.Get<IStatusCodeReExecuteFeature>();

                var error = context.Features.Get<IExceptionHandlerFeature>();
                if (error != null)
                {
                    var ex = error.Error;
                    string exTitle = "Http 500 Internal Server Error in SomeAcme.SomeApi occured. The unhandled error is: ";
                    string exceptionString = !env.IsProduction() ? (new ExceptionModel
                    {
                        Message = exTitle + ex.Message,
                        InnerException = ex?.InnerException?.Message,
                        StackTrace = ex?.StackTrace,
                        OccuredAt = DateTime.Now,
                        QueryStringOfException = status?.OriginalQueryString,
                        RouteOfException = status?.OriginalPath
                    }).ToString() : new ExceptionModel()
                    {
                        Message = exTitle + ex.Message,
                        OccuredAt = DateTime.Now
                    }.ToString();
                    try
                    {
                        _logger.LogError(exceptionString);
                    }
                    catch (Exception err)
                    {
                        Console.WriteLine(err);
                    }
                    await context.Response.WriteAsync(exceptionString, Encoding.UTF8);
                }
            });
        });

最后,一个帮助模型来将我们的异常信息打包。

using System;
using Newtonsoft.Json;

namespace SomeAcme.SomeApi.Models
{
    /// <summary>
    /// Exception model for generic useful information to be returned to client caller
    /// </summary>
    public class ExceptionModel
    {
        public string Message { get; set; }
        public string InnerException { get; set; }
        public DateTime OccuredAt { get; set; }
        public string StackTrace { get; set; }
        public string RouteOfException { get; set; }
        public string QueryStringOfException { get; set; }

        public override string ToString()
        {
            return JsonConvert.SerializeObject(this);
        }
    }
}

这里的关键是在Startup类中获取一个记录器。

您可以注入ILoggerFactory来实现,并只需执行以下操作:

  _logger = loggerFactory.CreateLogger<Startup>();

上面的全局错误处理程序中使用了_logger。

现在回到如何写入事件日志的问题,看一下上面的SomeController源代码。我们在这里注入ILogger。 只需使用该实例即可,它提供不同的方法来写入您配置的日志。由于我们在Program类中添加了事件日志,所以这会自动发生。

在测试上面的代码之前,请以管理员身份运行以下Powershell脚本以获取您的事件日志来源:

New-EventLog -LogName SomeApi -SourceName SomeApi

我喜欢这种方法的原因是,如果我们做得正确,异常会很好地弹出在SomeApi源内,而不是应用程序事件日志内(在我看来这样可以减少混乱)。


7
创建事件日志的PowerShell语法是 New-EventLog -LogName SomeApi -Source SomeApi - MadMarc
1
是的,如果 -LogName 和 -Source 具有相同的值,则日志也将作为单独节点在事件查看器中的日志列表左侧而不是与其他日志混合显示,通常是应用程序日志。 - Tore Aurstad

2
好消息!EventLog已经移植到corefx中,将在.NET Core 2.1中提供。
现在,您可以从MyGet获取预览包System.Diagnostics.EventLog。

1

0

EventLog.WriteEntry是Diagnostics中的一个静态方法:

System.Diagnostics.EventLog.WriteEntry(source, message, MessageType, EventId);

我已经多年来一直在调用它,而且它运行良好。与其他答案相比只需要一行代码,直接回答了提出的问题。

默认使用应用程序事件日志。使用自定义日志需要更多的工作,包括注册表键权限。


问题在于在使用之前必须存在源。根据文档,您需要创建源并退出。第二次运行应用程序时将可用。这使其成为在安装程序项目中运行的良好选择。https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.eventlog.source?view=dotnet-plat-ext-7.0 - HackSlash
所以,首先要阅读注册表。无论何时访问应用程序之外的任何内容,都应该这样做。如果使用自定义事件日志,则还需要修改注册表权限。然而,如果使用应用程序或系统事件日志,那么源已经存在,如果我正确解读了您的帖子。 - undefined
问题通常是管理权限的问题。如果您的应用程序具有普通用户权限,并且用户无法在没有管理员权限的情况下提升权限,那么您将需要一个具有管理员权限的安装程序。如果您希望您的安装程序以每个用户模式运行,那么您根本无法做到这一点。 - undefined
不相关。我不确定你是如何一直忽略关于“源”的话题的,你输入到“源”中的字符串是什么?
"在使用源写入第一条记录之前,您必须创建和配置事件源。... 如果操作系统尚未刷新其事件源列表,并且您尝试使用新的源写入事件,则写入操作将失败。... 您必须具有计算机上的管理员权限才能创建新的事件源。" 阅读文档:https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.eventlog.writeentry
- undefined
好的--我重新测试了一下。对于应用程序事件日志:使用“Application”作为事件源属性确实像我之前描述的那样有效,它已经内置在Windows中,不需要管理员权限或注册表编辑。尝试使用自定义源,是的,它需要管理员权限(在打包分发时添加密钥是一个选项)。所以,是的--使用“application”作为源,它已经内置在Windows中并且有效,不需要注册表编辑,也不需要管理员权限。 - undefined
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接