使用Azure SQL的ASP.Net Core 2.1 Serilog SQL Server Sink在本地运行,但不能通过Azure应用服务运行

时间:2019-01-22 23:24:19

标签: azure asp.net-core azure-sql-database asp.net-core-2.1 serilog

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

我将Logs表添加到该数据库,并使用SQL Server Sink将Serilog添加到我的网站。

当我在本地运行网站但仍连接到Azure SQL数据库时,可以在Logs表中看到我的日志条目。但是,将网站部署到Azure App Service时,不再在数据库的Logs表中获得任何日志条目。

请记住,在已部署的版本中,我可以正常连接并使用Azure SQL数据库存储我的MS Identity,并且可以很好地创建新用户和编辑现有用户。因此,我知道我的App Service应用程序设置中的连接字符串是正确的。

我查看了Serilog MSSQL Github,将其配置建议与我自己的配置建议进行了比较,但找不到任何突出的内容。

我让此设置在我部署到另一个Azure App Service的ASP.Net Core API上正常工作。该服务使用不同的数据库,但是它在同一SQL Server资源上。

当我开始这个问题时,我很幸运地查看了推荐的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表中添加用户数据。因此,这似乎不是用户帐户问题。

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

这是我设置Serilog的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)
            .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) =>

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

[UPDATE 1/23/19]

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

var connectionString 

而不是从Program.cs中获取

Configuration.GetConnectionString("MyConnectionString") 

,它开始记录到数据库。

因此看来,问题出在Program.cs能够从Azure App Service部署插槽的“应用程序设置”,“连接字符串”部分读取连接字符串。

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

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

1 个答案:

答案 0 :(得分:2)

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

Program.cs:

<!DOCTYPE html>
<html lang="en" ng-app="module">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div style="height: 100%" ng-controller="controller" ng-init="init()">
    <div id="wrapper">
      <div id="scroller">
        <div class="card" ng-repeat="i in list">{{i}}</div>
        <div id="loading" class="loading">Loading...</div>
      </div>
    </div>
  </div>
</body>
  <script src="angular.min.js"></script>
  <script src="main.js"></script>
</html>

Startup.cs:

html, body{
  margin: 0;
  padding: 0;
  height: 100%;
}
body{
  background-color: #eee;
}
#wrapper{
  height:calc(100% - 1px);
}
.card{
  background-color: #fff;
  height: 80px;
  line-height: 80px;
  text-align: center;
  margin-bottom: 15px;
}
.loading{
  height: 80px;
  background-color: #e6e6e6;
  line-height: 80px;
  text-align: center;
}