天天看點

.NET跨平台之旅:在Linux上将ASP.NET 5運作日志寫入檔案

在前一篇博文中,我們遇到了這樣一個問題:雖然有一些.NET日志元件已經開始支援.NET Core,但目前隻支援控制台輸出日志,不支援将日志寫入檔案;這就意味着我們在Linux上運作的示例ASP.NET 5站點無法将日志寫入檔案,給排查問題造成很大的麻煩,比如現在示例站點經常挂掉的問題。

在前一篇博文(增加檔案日志功能遇到的挫折)中,我們遇到了這樣一個問題:雖然有一些.NET日志元件(比如Serilog, NLog)已經開始支援.NET Core,但目前隻支援控制台輸出日志,不支援将日志寫入檔案;這就意味着我們在Linux上運作的示例ASP.NET 5站點無法将日志寫入檔案,給排查問題造成很大的麻煩,比如現在示例站點經常挂掉的問題。

面對這個問題我們沒有善罷甘休,不想因為這個問題影響.NET跨平台之旅的步伐,我們要解決它,而且希望先用簡單的方法解決,不想從頭實作一個日志元件。

在上一篇博文釋出後,一位同僚給出了這樣一個提示:既然Serilog記錄日志時是直接将日志資訊寫入控制台的輸出流( .WriteTo.TextWriter(Console.Out) ),那麼應該也可以用它寫入檔案的寫入流,都是Stream嘛。

好主意!可行!但有一個前提是corefx中的檔案操作類庫要支援Linux,也就是System.IO.FileSystem實作了跨平台。

那.NET Core中的檔案操作類庫是否已經跨平台了呢?我們的檔案日志問題是否可以通過它解決呢?碼一下,就會知道。

于是将Startup.cs中的 .WriteTo.TextWriter(Console.Out) 改為下面的代碼:

.WriteTo.TextWriter(new StreamWriter(logFilePath))      

運作 dnx kestrel 指令卻出現下面的錯誤:

DNXCore,Version=v5.0 error CS1503: Argument 1: cannot convert from 'string' to 'System.IO.Stream'      

出現這個錯誤是因為corefx中實作的StreamWriter的構造函數不支援檔案路徑作為參數,.NET Framework中是支援的。是以需要修改一下代碼,将FileStream類型的參數傳遞給StreamWriter的構造函數,代碼如下:

.WriteTo.TextWriter(new StreamWriter(new FileStream(logFilePath, FileMode.OpenOrCreate)))      

這樣改了之後,站點成功啟動。

Logging to /data/git/AboutUs/logs/log.txt
Hosting environment: Production
Now listening on: http://*:8001
Application started. Press Ctrl+C to shut down.      

然後用浏覽器通路一下站點,并用tail指令看一下日志是否成功寫入到了日志檔案中?

# tail log.txt
11/22/2015 14:08:58 +08:00 [Debug] The view '"Intro"' was found.
11/22/2015 14:08:58 +08:00 [Information] Executing ViewResult, running view at path "/Views/About/Intro.cshtml".
11/22/2015 14:08:58 +08:00 [Information] User profile is available. Using '"/root/.aspnet/DataProtection-Keys"' as key repository; keys will not be encrypted at rest.
11/22/2015 14:09:01 +08:00 [Debug] Data Source=xxx
11/22/2015 14:09:03 +08:00 [Information] Microsoft.Data.Entity.Storage.DbCommandLogData
11/22/2015 14:09:03 +08:00 [Debug] Data Source=xxx
11/22/2015 14:09:03 +08:00 [Information] Executed action "CNBlogs.AboutUs.Web.AboutController.Intro" in 0.6192ms
11/22/2015 14:09:03 +08:00 [Information] Request finished in 0.6196ms 200 text/html; charset=utf-8
11/22/2015 14:09:16 +08:00 [Error] Connection id "4" disconnected.
11/22/2015 14:09:16 +08:00 [Error] Connection id "3" disconnected.      

成功!在Linux上記錄ASP.NET 5站點的運作日志到檔案的問題就這麼臨時解決了。

順便分享一下目前Startup.cs中的最新代碼:

using System;
using System.IO;
using System.Linq;
using Microsoft.AspNet.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Data.Entity;
using CNBlogs.AboutUs.Data;
using Microsoft.Dnx.Runtime;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Extensions.Configuration;
using System.Data.SqlClient;
using Microsoft.Extensions.Logging;
using Serilog;

namespace CNBlogs.AboutUs.Web
{
    public class Startup
    {
        public Startup(IApplicationEnvironment appEnv)
        {
            IConfigurationBuilder builder = new ConfigurationBuilder()
                .SetBasePath(appEnv.ApplicationBasePath)
                .AddJsonFile("config.json", false);
            Configuration = builder.Build();

            var logFilePath = Path.Combine(appEnv.ApplicationBasePath,"logs/log.txt");
            Console.WriteLine("Logging to "+ logFilePath);
            Log.Logger = new LoggerConfiguration()
                    .MinimumLevel.Debug()
                    .WriteTo.TextWriter(new StreamWriter(
                        new FileStream(logFilePath, FileMode.OpenOrCreate)))
                    .CreateLogger();    
        }

        public IConfiguration Configuration { get; set; }

        public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
        {
            loggerFactory
                .AddSerilog()
                .AddConsole();

            app.UseDeveloperExceptionPage();
            app.UseMvcWithDefaultRoute();            
            app.UseStaticFiles();            
            app.UseRuntimeInfoPage();
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
            services.AddEntityFramework()
                .AddSqlServer()
                .AddDbContext<EfDbContext>(options =>
                {
                    options.UseSqlServer(Configuration["data:ConnectionString"]);
                });
            services.AddTransient<ITabNavRepository, TabNavRepository>();
        }
    }
}