Ameno acids
Ameno acids

Reputation: 433

Write to EventLog in .Net Core

I need a way to write to Windows' event viewer in my app that's using dnx. But, the EventLog class isn't available in the System.Diagnostics namespace so I'm stuck. Is there any other way to write to the EventViewer?

Upvotes: 26

Views: 52125

Answers (5)

JRrelyea
JRrelyea

Reputation: 301

EventLog.WriteEntry is a static method in Diagnostics:

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

I've been calling it for years & it works fine. 1 line of code vs all the other answers & it directly answers the question asked

Defaults to the application event log. Using custom logs takes more work, including reg key permissions.

Upvotes: 2

Tore Aurstad
Tore Aurstad

Reputation: 3816

Writing to the Event Log in .Net Core requires first a Nuget package installation

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

Note that the correct version to install depends on the version of .Net Core you are running.The package above was tested OK with .Net Core.

Then we need to add EventLog. In the Program class we can do this like so:

    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>();
                });
    }
}

And our appsettings.json file includes setup:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=.\\SQLEXPRESS;Database=SomeApi;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  **"Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },**
  "AllowedHosts": "*"
}

We can inject the ILogger instance

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();
        }
    }
}

More advanced use, add a global exception handler inside Configure method of Startup class in .Net Core:

  //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);
                }
            });
        });

And finally a helper model to pack our exception information into.

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);
        }
    }
}

The tricky bit here is to get hold of a logger inside the Startup class. You can inject ILoggerFactory for this and just do :

  _logger = loggerFactory.CreateLogger<Startup>();

Where _logger is used in the global error handler above.

Now back again to the question of how to write to the event log, look at the source code for SomeController above. We inject ILogger here. Just use that instance and it offers different methods for writing to your configured logs. Since we added in the Program class event log, this happens automatically.

Before you test out the code above, run the following Powershell script as administrator to get your event log source:

New-EventLog -LogName SomeApi -SourceName SomeApi

What I like with this approach is that if we do everything correct, the exceptions pops up inside the SomeApi source nicely and not inside the application event log (clutter IMHO).

Upvotes: 13

praveen
praveen

Reputation: 246

Add from NuGet Microsoft.Extensions.Logging.EventLog Version 2.1.1

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

Include the namespace Microsoft.Extensions.Logging.EventLog in the Program.cs file

This resolved my problem.

Upvotes: 24

Eduardo Yupanqui
Eduardo Yupanqui

Reputation: 801

it is available in 2.0 but you have to install the compatibility pack. https://github.com/dotnet/corefx/issues/25440

Here for more information: https://blogs.msdn.microsoft.com/dotnet/2017/11/16/announcing-the-windows-compatibility-pack-for-net-core/

Upvotes: 1

nyjl
nyjl

Reputation: 29

Good news! EventLog is already ported to corefx and will be available in .NET Core 2.1.

Right now you can download a preview package System.Diagnostics.EventLog from their MyGet feed.

Upvotes: 1

Related Questions