如何将原生日志机制转发到C# Microsoft.Extensions.Logging.ILogger
可行实现方案
1. 调整原生交互封装类
把原生配置相关的逻辑从静态构造函数移到可注入的服务类里,同时保留委托引用防止被GC回收,确保配置仅执行一次:
using System.Runtime.InteropServices; using Microsoft.Extensions.Logging; internal sealed class MyNativeLogListener : IDisposable { private const string NativeLibraryName = "my.so.0"; // 静态字段持有委托引用,防止GC回收 private static LogDelegate _logDelegate; // 标记是否已经完成配置,避免重复调用 private static bool _isConfigured; private readonly ILogger<MyNativeLogListener> _logger; public MyNativeLogListener(ILogger<MyNativeLogListener> logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); if (!_isConfigured) { _logDelegate = LogMessage; IntPtr funcPtr = Marshal.GetFunctionPointerForDelegate(_logDelegate); my_listener_configure(funcPtr); _isConfigured = true; } } private void LogMessage(int logLevel, string loggerName, string message) { _logger.Log((LogLevel)logLevel, "{LoggerName} - {Message}", loggerName, message); } [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)] private 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); public void Dispose() { // 如果原生层提供了清理回调的接口,可以在这里调用 _logDelegate = null; _isConfigured = false; } }
2. 注册服务并指定初始化时机
.NET 6+ 顶层语句(Program.cs)
在构建完WebApplication之后,启动服务之前手动解析一次监听器,触发配置,保证所有业务逻辑执行前原生配置已经完成:
var builder = WebApplication.CreateBuilder(args); // 注册日志监听器为单例 builder.Services.AddSingleton<MyNativeLogListener>(); // 你的其他服务注册、日志配置逻辑... var app = builder.Build(); // 关键:在启动服务前从根容器解析监听器,触发原生配置,此时ILogger已经完全初始化完成 _ = app.Services.GetRequiredService<MyNativeLogListener>(); // 你的中间件配置、路由配置... app.Run();
Startup类模式
不要在Startup构造函数里做配置,改为在Configure方法中注入监听器,此时DI容器已经构建完成,可以正常解析ILogger:
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddSingleton<MyNativeLogListener>(); // 其他服务注册... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env, MyNativeLogListener logListener) { // 此时logListener已经被容器构造完成,原生配置已经执行 // 其他中间件配置... } }
3. 之前报错的原因说明
你在Startup构造函数注入ILogger<Startup>报错,是因为Startup构造函数执行时,DI容器还未完成构建,日志服务也未完全初始化,只有容器构建完成后,才能正确解析到ILogger实例。
注意事项
- 确保所有原生调用都在上述初始化步骤之后执行,不要在服务注册阶段、Startup构造函数阶段调用任何原生方法
- 委托用静态字段持有,避免实例被GC回收后原生层调用空指针引发崩溃
- 如果有多个Host实例的场景,可以加锁保证配置仅执行一次
内容的提问来源于stack exchange,提问作者malat
相关产品推荐
相关产品推荐

