DotNET Console Generic Host - Logging With Serilog

2022. 9. 8. 00:00DOTNET/Generic Host

반응형

이번에는 Console 앱에서 Serilog 를 사용해 보겠다.

일반적인 console 앱에서 사용하는 방식이 아닌 ASPNET Core 에서 사용했던 방식을 활용하겠다.

Generic Host 를 이용하면 appsettings.json 을 통해서 값을 읽어와서 Serilog 를 설정할 수 있다. 

 

아래 package 를 설치하자

 

dotnet add package Serilog.Extensions.Hosting
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Sinks.Console

dotnet add package Serilog.Settings.Configuration

 

Program.cs 를 다음과 같이 변경하자

using GenericHost.InjectableServices;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;

Log.Logger = CreateBootstrapLogger();
try
{
    IHost host = Host.CreateDefaultBuilder(args)
        .ConfigureServices((hostContext, services) =>
        {
            services.AddHostedService<Worker>();
            //services.AddTransient<IHelloworld, Helloworld>();
            services.Scan(scan => scan
                                .FromAssemblyOf<ITransientService>()
                                .AddClasses(classes => classes.AssignableTo<ITransientService>())
                                .AsSelfWithInterfaces()
                                .WithTransientLifetime()
                                .AddClasses(classes => classes.AssignableTo<IScopedService>())
                                .AsSelfWithInterfaces()
                                .WithScopedLifetime()
                                .AddClasses(classes => classes.AssignableTo<ISingletonService>())
                                .AsSelfWithInterfaces()
                                .WithSingletonLifetime()
                                );
        })
        .UseSerilog((context, configuration) => configuration.ReadFrom.Configuration(context.Configuration))
        .Build();
    
    host.Run();
}
catch(Exception ex)
{
    // ef migration 시에 나타나는 오류를 log 로 보내는 코드
    // https://github.com/dotnet/runtime/issues/60600
    string type = ex.GetType().Name;
    if (type.Equals("StopTheHostException", StringComparison.OrdinalIgnoreCase))
        throw;
    Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
    Log.CloseAndFlush();
}

static Serilog.ILogger CreateBootstrapLogger()
{
    var configurationBuilder = new ConfigurationBuilder()
      .AddJsonFile("appsettings.json", false, true)
      .AddJsonFile($"appsettings.Development.json", optional: true)
      .Build();

    return new LoggerConfiguration()
      .ReadFrom.Configuration(configurationBuilder)
      .CreateBootstrapLogger();
}

 

ApiLogger.cs 

using GenericHost.InjectableServices;
using Microsoft.Extensions.Logging;
using System.Runtime.CompilerServices;

namespace GenericHost;
public class ApiLogger : ITransientService
{
    private ILogger _logger;
    public ApiLogger(ILogger<ApiLogger> logger) => _logger = logger;
    public void Log(LogLevel logLevel, string message,
    [CallerMemberName] string membername = "",
    [CallerFilePath] string filePath = "",
    [CallerLineNumber] int lineNumber = 0,
    params object[] args) =>
    _logger.Log(logLevel, message, membername, filePath, lineNumber, args);
    public void LogDebug(string message,
    [CallerMemberName] string membername = "",
    [CallerFilePath] string filePath = "",
    [CallerLineNumber] int lineNumber = 0,
    params object[] args) =>
    _logger.LogDebug(
    CreateLogMessage(message, membername, filePath, lineNumber), args);
    public void LogError(string message,
    [CallerMemberName] string membername = "",
    [CallerFilePath] string filePath = "",
    [CallerLineNumber] int lineNumber = 0,
    params object[] args) =>
    _logger.LogError(
    CreateLogMessage(message, membername, filePath, lineNumber), args);
    public void LogError(Exception exception, string message,
    [CallerMemberName] string membername = "",
    [CallerFilePath] string filePath = "",
    [CallerLineNumber] int lineNumber = 0,
    params object[] args) =>
    _logger.LogError(exception,
    CreateLogMessage(message, membername, filePath, lineNumber), args);
    public void LogInformation(string message,
    [CallerMemberName] string membername = "",
    [CallerFilePath] string filePath = "",
    [CallerLineNumber] int lineNumber = 0,
    params object[] args) =>
    _logger.LogInformation(
    CreateLogMessage(message, membername, filePath, lineNumber), args);
    public void LogTrace(string message,
    [CallerMemberName] string membername = "",
    [CallerFilePath] string filePath = "",
    [CallerLineNumber] int lineNumber = 0,
    params object[] args) =>
    _logger.LogTrace(
    CreateLogMessage(message, membername, filePath, lineNumber), args);
    public void LogWarning(string message,
    [CallerMemberName] string membername = "",
    [CallerFilePath] string filePath = "",
    [CallerLineNumber] int lineNumber = 0,
    params object[] args) =>
    _logger.LogWarning(
    CreateLogMessage(message, membername, filePath, lineNumber), args);
    private string CreateLogMessage(string message,
    string memberName, string filePath, int lineNumber) =>
    $"[{memberName}] {message}. \nfilePath = {filePath} : line = {lineNumber}";
}

 

Worker.cs 에서 사용

using GenericHost;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

public sealed class Worker : IHostedService
{
    private readonly ApiLogger _logger;
    private readonly IHelloworld _helloworld;
    private readonly IConfiguration _configuration;

    public Worker(
        ApiLogger logger,
        IHostApplicationLifetime appLifetime,
        IHelloworld helloworld,
        IConfiguration configuration)
    {
        _logger = logger;
        _helloworld = helloworld;
        _configuration = configuration;
        appLifetime.ApplicationStarted.Register(OnStarted);
        appLifetime.ApplicationStopping.Register(OnStopping);
        appLifetime.ApplicationStopped.Register(OnStopped);
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("1. StartAsync has been called.");

        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("4. StopAsync has been called.");

        return Task.CompletedTask;
    }

    private void OnStarted()
    {
        _logger.LogInformation("2. OnStarted has been called.");
        _logger.LogInformation($"2.1. {_helloworld.Execute()} ");

        var name = _configuration.GetSection("User").GetValue<string>("Name");
        var age = _configuration.GetSection("User").GetValue<int>("Age");
        _logger.LogInformation($"2.2. Name = {name} , Age = {age} ");
    }

    private void OnStopping()
    {
        _logger.LogInformation("3. OnStopping has been called.");
    }

    private void OnStopped()
    {
        _logger.LogInformation("5. OnStopped has been called.");
        var name = _configuration.GetSection("User").GetValue<string>("Name");
        var age = _configuration.GetSection("User").GetValue<int>("Age");
        _logger.LogInformation($"5.1. Name = {name} , Age = {age} ");
    }
}

appsettings.json

{
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "Microsoft.Hosting.Lifetime": "Information",
        "System": "Warning"
      }
    },
    "WriteTo": [
      { "Name": "Console" },
      {
        "Name": "File",
        "Args": {
          "path": "./logs/GenericHost.log",
          "rollingInterval": "Day"
        }
      }
    ]
  },
  "User": {
    "Name": "Yogingang",
    "Age": 18
  }
}

 

실행 후 bin/Debug/net6.0 폴더로 이동하면 logs 폴더가 있다. 들어가 보자

GenericHost.log 라는 이름으로 파일이 존재한다. 

해당 파일을 영어보면 log 가 보일 것이다. 

 

 

관련영상

https://youtu.be/6_-LW7bHXMk

 

반응형