ASP.Net Core 2.1 Serilog SQL 使用 Azure SQL 的服务器接收器在本地工作,但不是来自 Azure 应用服务

ASP.Net Core 2.1 Serilog SQL Server Sink using Azure SQL is working locally but not from Azure app service

我有一个 ASP.Net Core 2.1 网站,它使用 Azure SQL 数据库作为 Microsoft Identity 组件。

我向该数据库添加了一个日志 table,并使用 SQL 服务器接收器向我的网站添加了 Serilog。

当我在本地 运行 网站时,同时仍然连接到 Azure SQL 数据库,我可以在 Logs table 中看到我的日志条目就好了。但是,当我将网站部署到我的 Azure 应用服务时,我不再在数据库的 Logs table 中获得任何日志条目。

请注意,在已部署的版本中,我连接到 Azure SQL 数据库并将其用于我的 MS Identity 内容,我可以很好地创建新用户和编辑现有用户。所以我知道我的 App Service 应用程序设置中的连接字符串是正确的。

我查看了 Serilog MSSQL Github 以将他们的配置建议与我自己的进行比较,但没有找到任何突出的地方。

我在部署到另一个 Azure 应用服务的 ASP.Net 核心 API 上正确运行了此设置。该服务使用不同的数据库,但它位于相同的 SQL 服务器资源上。

我已经查看了在我开始这个问题时推荐的 SO 帖子列表,但运气不佳。

我运行第一次设置用户账号时在数据库上SQL;

EXEC sp_addrolemember N'db_datareader', N'myuser'
EXEC sp_addrolemember N'db_datawriter', N'myuser'
EXEC sp_addrolemember N'db_ddladmin', N'myuser'

而且,正如我所说,用户帐户可以在 AspNetUsers table 中更新和添加用户数据就好了。因此,这似乎不是用户帐户问题。

我已验证我的 Azure 应用服务 DEV 部署插槽(我正在测试的那个)、应用程序设置、连接字符串中的连接字符串与我在本地 DEV UserSecrets 中的连接字符串完全相同。另外,再一次,当部署到 Azure 时,我可以 read/write 到同一数据库中的 AspNet* table。

这是我设置 Serilog 的 Program.cs class;

    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)
            .AddUserSecrets<Startup>()
            .AddEnvironmentVariables()
            .Build();

        public static void Main(string[] args)
        {
            var connectionString = Configuration.GetConnectionString("MyConnectionString");
            const string tableName = "Logs";

            Log.Logger = new LoggerConfiguration()
                .MinimumLevel.Information()
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .Enrich.FromLogContext()
                .Enrich.WithMachineName()
                .Enrich.WithThreadId()
                .WriteTo.MSSqlServer(connectionString, tableName)
                .CreateLogger();

            // TODO Enable to debug any startup Serilog issues. Make sure to comment out for PROD
            //Serilog.Debugging.SelfLog.Enable(msg =>
            //{
            //    Debug.Print(msg);
            //    Debugger.Break();
            //});

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

        }

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

    }
}

我在 Azure 中部署的正在将日志写入 Azure SQL 的 API 与此网站之间的唯一区别是 API,它较旧,我有

public static IWebHost BuildWebHost(string[] args)

在 program.cs 而较新的网站有

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>

所以...任何想法将不胜感激。

[19 年 1 月 23 日更新]

我直接将连接字符串添加到

var connectionString 

在 Program.cs 而不是从

获取
Configuration.GetConnectionString("MyConnectionString") 

它开始记录到数据库。

所以问题似乎出在 Program.cs 能够从 Azure 应用服务部署槽、应用程序设置、连接字符串部分读取连接字符串。

此连接字符串已从 Startup.cs 中正确读取,并且自从我首次创建该网站以来一直有效。

那么,Azure 是否存在无法从 Program.cs 的部署槽应用程序设置/连接字符串中读取值的已知问题?

由于 Azure 似乎存在问题,它在调用 CreateWebHostBuilder 之前不会向 Web 应用程序提供应用程序设置,这是一个简单的解决方法(假设在源代码中硬编码连接字符串不是一个可行的选择)是将 Serilog 配置为在 Startup.cs 而不是 Program.cs.
中使用 SqlServer 如果记录应用程序启动期间发生的初始事件很重要,可以在 Program.cs 中临时配置 Serilog 以写入文件。

Program.cs:

    public class Program
{
    public static void Main(string[] args)
    {
        var AzureLogFilePath = @"D:\home\LogFiles\Application\log.txt";

        Log.Logger = new LoggerConfiguration()
         .MinimumLevel.Information()
         .Enrich.FromLogContext()
         .WriteTo.File(path: AzureLogFilePath, fileSizeLimitBytes: 1_000_000, rollOnFileSizeLimit: true, shared: true);
         .CreateLogger();

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

    }

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

Startup.cs:

    public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
          .SetBasePath(Directory.GetCurrentDirectory())
          .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
          .AddJsonFile($"appsettings.{env.EnvironmentName ?? "Production"}.json", optional: true)
          .AddEnvironmentVariables();

        if (env.IsDevelopment()) builder.AddUserSecrets<Startup>();      // according to the docs, AddUserSecrets should be used only in development

    Configuration = builder.Build();
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        var connectionString = Configuration.GetConnectionString("MyConnectionString");
        const string tableName = "Logs";

        Log.Logger = new LoggerConfiguration()
            .MinimumLevel.Information()
            .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
            .Enrich.FromLogContext()
            .Enrich.WithMachineName()
            .Enrich.WithThreadId()
            .WriteTo.MSSqlServer(connectionString, tableName)
            .CreateLogger();

        //...
    }
}