假設在某個框架中有以下服務:
public interface ICalculationService { int Add(int x, int y); } public class CalculationService : ICalculationService { private readonly ILogger logger; public CalculationService(ILogger<CalculationService> logger) => this.logger = logger; public int Add(int x, int y) { var result = x + y; this.logger.LogDebug($"{x}+{y}={result}"); return result; } }
該服務很簡單,就是計算兩個整數的和,並在返回結果的時候打印一條日志。如果在ASP.NET Core應用程序中使用這個服務,方法非常簡單,只需要在Startup.cs中注冊服務,然后在Controller中直接通過構造器注入即可:
// Startup.cs public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddSingleton<ICalculationService, CalculationService>(); } // Controller [Route("api/[controller]")] [ApiController] public class CalculationController : ControllerBase { private readonly ICalculationService service; public CalculationController(ICalculationService service) { this.service = service; } [HttpGet("{x}/{y}")] public ActionResult<int> Get(int x, int y) => this.service.Add(x, y); }
執行起來可以看到,日志可被正常輸出:
那么,如果希望在一個控制台應用程序中使用這個CalculationService服務,又該如何提供這個ILogger構造函數的參數呢?
在.NET Core控制台應用程序中,如需使用標准的日志機制(Microsoft.Extensions.Logging),最合適的做法就是使用依賴注入,通過依賴注入框架來完成ILogger對象的解析。使用標准的日志機制的一個最大好處是,你可以選擇不同的日志解決方案,來決定你的應用程序的日志輸出方式。比如,Serilog提供了非常強大的擴展,可以很方便地將Serilog集成到應用程序中,以快速地實現將日志輸出到幾十種不同的日志服務平台。
下面,我使用dotnet CLI和Visual Studio Code來展示如何在.NET Core控制台應用程序中,使用上面的CalculationService服務。
首先,在本地目錄中,使用dotnet new命令創建一個控制台應用程序:
dotnet new console -n CalcServiceApp
然后,使用dotnet add命令,添加NuGet包的引用:
dotnet add package Microsoft.Extensions.DependencyInjection dotnet add package Microsoft.Extensions.Configuration dotnet add package Microsoft.Extensions.Configuration.Json dotnet add package Microsoft.Extensions.Logging dotnet add package Microsoft.Extensions.Logging.Configuration dotnet add package Microsoft.Extensions.Logging.Console
現在,基於CalcServiceApp目錄,啟動Visual Studio Code,如果已經安裝了OmniSharp插件,Visual Studio Code會自動安裝C#依賴項、更新代碼引用,並提示會在當前目錄下新建一個.vscode的隱藏目錄,以存放項目調試的配置信息。我們暫時還不涉及到調試的問題,所以可以暫時放一放。
下面要做的就是在Program類的Main方法中,使用CalculationService提供的服務。Main方法的代碼如下:
static void Main(string[] args) { // 從appsettings.json文件中讀入日志的配置信息 var configuration = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .Build(); using (var serviceProvider = new ServiceCollection() .AddLogging(loggingBuilder => { loggingBuilder.AddConfiguration(configuration.GetSection("Logging")); loggingBuilder.AddConsole(); // 將日志輸出到控制台 }) .AddSingleton<ICalculationService, CalculationService>() .BuildServiceProvider()) { var calcService = serviceProvider.GetService<ICalculationService>(); Console.WriteLine(calcService.Add(3, 4)); } }
上面的代碼中,通過向ServiceCollection對象添加Logging配置,指定日志輸出的配置文件將采用appsettings.json文件,同時使用該文件中的Logging節點所提供的配置信息。目前為了測試,appsettings.json文件內容如下:
{ "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } }
接下來,就是要確保appsettings.json文件在代碼編譯時,能夠正確復制到輸出目錄,以便程序運行時能夠找到該文件,因此,可以修改項目的csproj文件,指定appsettings.json文件能夠在編譯的時候復制到輸出目錄:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp2.2</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" /> </ItemGroup> <ItemGroup> <None Update="appsettings.json"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </None> </ItemGroup> </Project>
OK,在命令行通過dotnet run命令啟動應用程序,可以看到日志能夠被正常輸出:
細心的朋友會發現,為什么日志的輸出會在計算結果輸出之后,不是應該在計算的時候才輸出日志,而此時計算結果還沒有返回嗎?沒錯,看起來像是日志的輸出會在單獨的線程中進行。如果在Main函數中沒有使用using來合理釋放serviceProvider的資源,那么日志的輸出內容會非常奇怪,有時候甚至看不到日志信息的輸出。有關這一問題可以參考這篇帖子:https://github.com/aspnet/Logging/issues/631,解決方案就是需要dispose serviceProvider對象,但日志的輸出仍然是異步的。
好了,有關在.NET Core控制台應用程序中使用日志,就介紹這么多,有關日志配置和使用的詳細信息,可以參考微軟的官方文檔。微軟官方文檔內容非常給力,微軟也花了很大的功夫啟動了Docs項目,來推動文檔本地化的工作進程。本文另一個目的是為了展示使用Visual Studio Code進行.NET Core應用程序的開發,為了撰寫博客方便,我是在Windows下使用的Visual Studio Code和.NET Core SDK。事實上,在Linux或者MacOS下,都可以很好地使用Visual Studio Code進行.NET應用程序的開發(之前我也有一篇英文文章介紹這個話題:https://sunnycoding.cn/2018/11/07/developing-asp-net-core-apps-by-using-visual-studio-code/)。最后,貼一張Visual Studio Code的圖,供大家參考: