c# - 将本机日志记录机制转发到 C# Microsoft.Extensions.Logging.ILogger
问题描述
我正在尝试设置本机记录器以调用 C# 函数。由于使用单例 (log4cpp) 设置了本机端的登录,因此我需要确保尽早设置侦听器(在任何其他本机调用之前)。所以我决定遵循以下建议:
因此,我编写了以下静态类(充当我的单身人士):
internal static class MyNativeService
{
private const string NativeLibraryName = "my.so.0";
private static Delegate _logDelegate; // to prevent garbage collection of C# delegate
static void LogMessage(int logLevel, string loggerName, string message)
{
Console.WriteLine($"{logLevel} - {loggerName} - {message}");
}
static MyNativeService()
{
_logDelegate = new LogDelegate(LogMessage);
IntPtr ptr = Marshal.GetFunctionPointerForDelegate(_logDelegate);
// my_listener_configure should be called before any other native calls (only once)
my_listener_configure(ptr);
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal delegate void LogDelegate(int logLevel,
[MarshalAs(UnmanagedType.LPUTF8Str)] string loggerName,
[MarshalAs(UnmanagedType.LPUTF8Str)] string logMessage);
[DllImport(NativeLibraryName, CallingConvention = CallingConvention.Cdecl)]
private static extern void my_listener_configure(IntPtr aCallback);
}
这按预期工作,但这并不是我想要的。我更愿意将消息转发到实际的ILogger
框架(Microsoft.Extensions.Logging
)。那么我应该如何重构上面的代码,以便我LogMessage
变成:
using Microsoft.Extensions.Logging;
internal class MyNativeLoggerListener
{
private readonly ILogger<MyNativeLoggerListener> _logger; // created somehow at startup
private void LogMessage(int logLevel, string loggerName, string message)
{
_logger.Log((LogLevel)logLevel, 0, $"{loggerName} - {message}", null, null);
}
}
由于我不能混合依赖注入和静态构造函数,我如何以及何时在我的 C# 应用程序中创建我的单例,以设置将本机消息转发到正确配置的记录器(在应用程序中定义)?
另一个天真的解决方案是在Startup
类中创建一个记录器,以便:
namespace Acme
{
public class Startup
{
private readonly ILogger<Startup> _logger;
private readonly Delegate _logDelegate; // to prevent garbage collection of C# delegate
public void LogMessage(int logLevel, string loggerName, string message)
{
_logger.Log((LogLevel)logLevel, 0, message, null, null);
}
public Startup(IConfiguration configuration, ILogger<Startup> logger)
{
Configuration = configuration;
_logger = logger;
_logDelegate = MyNativeService.my_listener_configure(LogMessage);
}
当然,这不会起作用,因为 ILogger 机制尚未设置。上面抛出一个:
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger`1[Acme.Startup]' while attempting to activate 'Acme.Startup'.
作为参考,这是我遵循的模式:
解决方案
静态上下文不适合依赖注入上下文。
不过,您有一些选择:
一个)
只需使用LoggerFactory并创建您的记录器。在下面的示例中,我创建了一个控制台记录器。然后,您可以将创建的记录器设置为静态类(或封装其构造),如:
using (var loggerFactory = LoggerFactory.Create(o => o.SetMinimumLevel(LogLevel.Trace).AddConsole()))
{
MyNativeService.Logger = loggerFactory.CreateLogger("foo");
}
二)
如果您仍想从 DI 容器中获取某些内容,请使用 Startup 类中的应用程序服务提供程序创建记录器,如下所示:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// Ommitted ConfigureServices... [...]
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// startup code [...]
var loggerFactory = app.ApplicationServices.GetService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger("foo"); // You can give your own name or nameof(MyNativeService), for instance
MyNativeService.Logger = logger;
}
}
C) 您还可以在静态类中设置 Services 属性,因此您可以访问全套 DI 容器服务,例如:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// Ommitted ConfigureServices... [...]
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// startup code [...]
MyNativeService.Services = app.ApplicationServices;
}
}
并且您的 LogMessage 实现可能是:
public static void LogMessage(int logLevel, string loggerName, string message)
{
var loggerFactory = Services.GetService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger(loggerName);
logger.Log((LogLevel)logLevel, 0, $"{loggerName} - {message}");
}
你不会摆脱一些初始化代码。在所有这些情况下,您可能希望保护您的静态类在初始化之前不被调用。
推荐阅读
- android - 如何设置提醒?
- shopify - 如何使用 Shopify Liquid 扫描订单 - 获取自定义发票
- r - 重新排序数据框中的单个列,而不重新排序整个表
- java - 将多个 TextView 合并到一个位图上 - 我应该使用画布吗?
- salt-stack - SaltStack 渲染 SLS 'base:tomee' 失败:Jinja 变量 'dict object' 没有属性 'app-server'
- nativescript - 我如何在 nativescript angular 中缓存图像
- python - 线程不会同时运行
- php - Goutte,如何发送正文
- git - Git 命令显示两次提交之间更改的文件
- javascript - 如何在javascript中为没有单位的svg设置字体大小