简体   繁体   中英

Serilog not writing to File (.net core 2.2)

I am writing a web service that is using Serilog. I was having problems getting files to write out (but console logging worked). I noticed that the setup changed when .net core 2.0 came out based on this and this pages' explanation.

However, now, I can't see any logging (perhaps in the past the default M$ loggers were actually what I was seeing).

Here's how program.cs is set up:

public class Program
    {
        public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true, reloadOnChange: true)
            .AddEnvironmentVariables()
            .AddUserSecrets<Startup>()
            .Build();

        public static int Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
                .ReadFrom.Configuration(Configuration.GetSection("Serilog"))
                .CreateLogger();

            try
            {
                Log.Information("Starting webhost...");
                BuildWebHost(args).Run();
                return 0;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly");
                return 1;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                   .UseStartup<Startup>()
                   .UseConfiguration(Configuration)
                   .UseSerilog()
                   .Build();
    }

My appsettings.json has this section in the root:

"Serilog": {
    "Using" : ["Serilog.Sinks.Console", "Serilog.Sinks.File"], 
    "MinimumLevel": {
      "Default": "Debug",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      },
      "Enrich" :  ["FromLogContext"],
      "WriteTo":  [
        {"Name": "Console" },
        {"Name": "Debug" },
        {"Name": "File", "Args":  {"path": "%LogDir%\\sampleapp\\log-{Date}.txt", "rollingInterval":  "Day", "shared": true }  }
      ] 
    },
    "Properties": {
      "Application": "sampleapp"
    }
  },

Note that %LogDir% is an environment variable on my machine and resolves fine in other applications. The path is already created and the Logs folder has full RW permissions for the credentials this app uses.

I call logging like so...

    private readonly ILogger<PartnerController> _logger;
    private readonly IPartnerDao _partnerDao;

    public PartnerController(ILogger<PartnerController> logger, IPartnerDao partnerDao)
    {
        _logger = logger;
        _partnerDao = partnerDao;
    }

    [HttpGet]
    [Route("{titleCode}")]
    public async Task<IActionResult> Get(string titleCode)
    {
        _logger.LogInformation("Test logging");
    }

Yet, somehow nothing shows in the ASP.NET Core Web Server window and not file is created on my machine when running the service.

Am I missing something obvious?

Turns out I had copied some of the JSON from documentation incorrectly. It's hard to tell but in the original question I actually had Enrich , WriteTo , and Properties sections embedded within the MinimumLevel section.

Obviously this prevented Serilog from correctly knowing which Sinks to write to.

Here's my corrected settings JSON:

"Serilog": {
    "Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File"],
    "MinimumLevel": {
      "Default": "Debug",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "Enrich": ["FromLogContext"],
    "WriteTo": [
      { "Name": "Console" },
      { "Name": "Debug" },
      {
        "Name": "File",
        "Args": {
          "path": "%LogDir%\\sampleapp\\log-.txt",
          "rollingInterval": "Day",
          "shared": true
        }
      }
    ],
    "Properties": {
      "Application":  "sampleapp" 
    } 
  },

Note that I also removed the {Date} from the filename. Apparently it'll tack that on if you set the rolling interval to day....

appsettings.Development.json overwrite settings in appsettings.json

I repeat again, configuration in appsettings.Development.json will take precedence over appsettings.json . I know that sounds obvious but I bet someone could overlook this in the future just like I did.

I spent nearly an hour scratching my head why no log was being written to the file, only to notice later I had only Console sink in my appsettings.Development.json (effectively removing my File sink in appsettings.json duh!).

Here is a sample of a proper configuration (modify according to your needs):

ASP.NET Core 3.1

Program.cs

using Microsoft.AspNetCore.Hosting;
using Serilog;
using System;
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog.Core;

namespace My.App
{
    public class Program
    {
        private static bool IsDevelopment =>
            Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development";

        public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", false, true)
            .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", true)
            .AddEnvironmentVariables()
            .Build();

        public static Logger Logger { get; } = new LoggerConfiguration()
            .ReadFrom.Configuration(Configuration)
            .Enrich.FromLogContext()
            .CreateLogger();

        public static int Main(string[] args)
        {
            Log.Logger = Logger;

            try
            {
                Log.Information("Starting...");
                var host = CreateHostBuilder(args).Build();                      
                host.Run();                        
                return 0;
            }
            catch (Exception ex)
            {
                Log.Fatal(ex, "Host terminated unexpectedly");
                return 1;
            }
            finally
            {
                Log.CloseAndFlush();
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args)
        {
            var host = Host.CreateDefaultBuilder(args)
                .UseSerilog()
                .UseServiceProviderFactory(
                    new AutofacMultitenantServiceProviderFactory(Startup.ConfigureMultitenantContainer))
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder
                        .UseContentRoot(Directory.GetCurrentDirectory())
                        .UseIISIntegration()
                        .UseStartup<Startup>();
                });

            return host;
        }
    }
}

ASP.NET Core 2.2

Program.cs

public class Program
{
    public static IConfiguration Configuration { get; } = new ConfigurationBuilder()
        .SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
        .AddEnvironmentVariables()
        .Build();

    public static void Main(string[] args)
    {
        Log.Logger = new LoggerConfiguration()
            .ReadFrom.Configuration(Configuration)
            .Enrich.FromLogContext()
            .CreateLogger();

        try
        {
            Log.Information("Starting...");
            CreateWebHostBuilder(args).Run();
        }
        catch (Exception ex)
        {
            Log.Fatal(ex, "Host terminated unexpectedly");
        }
        finally
        {
            Log.CloseAndFlush();
        }
    }

    public static IWebHost CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .UseSerilog()
            .Build();
}

appsettings.json

{  
  "Serilog": {
    "MinimumLevel": {
      "Default": "Debug",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ]
  }
}

appsettings.Development.json

{
  "Serilog": {
    "WriteTo": [
      {
        "Name": "Async",
        "Args": {
          "configure": [
            {
              "Name": "Console"
            },
            {
              "Name": "Debug"
            },
            {
              "Name": "DiagnosticTrace"
            },
            {
              "Name": "File",
              "Args": {
                "path": "/home/log/api-log-.txt",
                "rollingInterval": "Day",
                "retainedFileCountLimit": 7,
                "buffered": true
              }
            }
          ]
        }
      }
    ]
  }
}

appsettings.Production.json

{
  "Serilog": {
    "MinimumLevel": {
      "Default": "Information"
    },
    "WriteTo": [
      {
        "Name": "Async",
        "Args": {
          "configure": [
            {
              "Name": "ApplicationInsights",
              "Args": {
                "restrictedToMinimumLevel": "Information",
                "telemetryConverter": "Serilog.Sinks.ApplicationInsights.Sinks.ApplicationInsights.TelemetryConverters.TraceTelemetryConverter, Serilog.Sinks.ApplicationInsights"
              }
            },
            {
              "Name": "Email",
              "Args": {
                "EmailConnectionInfo": {
                  "EmailSubject": "PRODUCTION error logs",
                  "FromEmail": "xxxxxxx",
                  "ToEmail": "xxxxxxx",
                  "MailServer": "xxxx",
                  "NetworkCredentials": {
                    "username": "xxxxxx",
                    "password": "xxxxxx",
                    "domain": "xxxxx"
                  },
                  "Port": 25
                },
                "restrictedToMinimumLevel": "Error"
              }
            },
            {
              "Name": "File",
              "Args": {
                "path": "/home/log/api-log-.txt",
                "rollingInterval": "Day",
                "retainedFileCountLimit": 15,
                "buffered": true
              }
            }
          ]
        }
      }
    ]
  }
}

Here's my corrected settings JSON:

In Startup class:

Log.Logger = new LoggerConfiguration()
                         .ReadFrom.Configuration(config)
                         .Enrich.With<EventTypeEnricher>()
                         .CreateLogger();

In appsettings.json

{
  "Serilog": {
    "Using": [
      "Serilog.Sinks.Console",
      "Serilog.Sinks.Seq",
      "Serilog.Sinks.Async",
      "Serilog.Sinks.File",
      "Serilog.Sinks.Debug"
    ],
    "MinimumLevel": {
      "Default": "Information",
      "Override": {
        "Microsoft": "Warning",
        "System": "Warning"
      }
    },
    "Enrich": [
      "FromLogContext",
      "WithMachineName"
    ],
    "Properties": {
      "ApplicationName": "AppSim Crawler"
    },
    "WriteTo": [
      {
        "Name": "Seq",
        "Args": {
          "serverUrl": "http://localhost:5341",
          "apiKey": "none"
        }
      },
      {
        "Name": "Debug",
        "Args": {
          "outputTemplate": "{Timestamp:dd-MM-yyyy HH:mm:ss.fff} [{EventType:x8} {Level:u3}] <s:[{SourceContext}]> <method:[{FileName} > {MemberName}]>{NewLine}at {FilePath}:{LineNumber}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}"
        }
      },
      {
        "Name": "Async",
        "Args": {
          "configure": [
            {
              "Name": "Console",
              "Args": {
                "theme": "Serilog.Sinks.SystemConsole.Themes.AnsiConsoleTheme::Code, Serilog.Sinks.Console",
                "outputTemplate": "{Timestamp:dd-MM-yyyy HH:mm:ss.fff} [{EventType:x8} {Level:u3}] <c:[%COMPUTERNAME%]> <s:[{SourceContext}]> <method:[{FileName} > {MemberName}]>{NewLine}at {FilePath}:{LineNumber}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}"
              }
            }
          ]
        }
      },
      {
        "Name": "Async",
        "Args": {
          "configure": [
            {
              "Name": "File",
              "Args": {
                "path": "C:/appsim/logs/appSimCrawler.log",
                "outputTemplate": "{Timestamp:dd-MM-yyyy HH:mm:ss.fff} [{EventType:x8} {Level:u3}] <s:[{SourceContext}]>  <method:[{FileName} > {MemberName}]>{NewLine}at {FilePath}:{LineNumber}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}",
                "rollingInterval": "Day",
                "retainedFileCountLimit": 30,
                "shared": true,
                "rollOnFileSizeLimit": true
              }
            }
          ]
        }
      }
    ]
  }
}

For me, serilog did not write to log file because I was missing one of the many nuget packages (Serilog.Sinks.Async). So besides making sure that your config in appsettings.json is correct - and that you are using the right file for dev vs prod, also:

I recommend looking at the documentation, or tutorial, again and make sure you add every nuget package that it says in the instructions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM