我有一个 .NET 4.8 WindowService,它使用 Autofac 进行依赖注入并使用 Serilog 进行日志记录。
我的 Serilog 记录器注册为
builder.Register<ILogger>((c, p) =>
{
return new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.Logger(logger => logger
.Filter.ByIncludingOnly(e => e.Level == Serilog.Events.LogEventLevel.Verbose || e.Level == Serilog.Events.LogEventLevel.Debug)
.WriteTo.Async(asyncLog => asyncLog.File(ApplicationConstants.ApplicationLogPath + "\\Trace\\Trace-.txt", Serilog.Events.LogEventLevel.Verbose, ApplicationConstants.ApplicationLogTemplate, rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, fileSizeLimitBytes: 10000000, shared: true)))
.WriteTo.Logger(logger => logger
.Filter.ByExcluding(e => e.Level == Serilog.Events.LogEventLevel.Verbose && e.Level == Serilog.Events.LogEventLevel.Debug)
.WriteTo.Map("LogContext", "Log",
(LogContext, wt) => wt.Async(asyncLog => asyncLog.File(ApplicationConstants.ApplicationLogPath + $".\\Logs\\{LogContext}-.txt", Serilog.Events.LogEventLevel.Information, ApplicationConstants.ApplicationLogTemplate, rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, fileSizeLimitBytes: 10000000, shared: true))))
.CreateLogger();
});
我使用工厂模式来解析使用相同接口的多个类。他们的依赖关系注册如下
builder.RegisterType<HandleA>().As<IHandler>().Keyed<IHandler>("HandleA");
builder.RegisterType<HandleB>().As<IHandler>().Keyed<IHandler>("HandleB");
服务不断处理数据,然后根据类型解决对运行时的依赖关系,如下所示
case "A":
return ContainerManager.Container.ResolveKeyed<IHandler>("HandleA");
case "B":
return ContainerManager.Container.ResolveKeyed<IHandler>("HandleB");
其中 ContainerManager.Container 是一个静态 IContainer 变量,当所有依赖项都在服务启动时构建时,该变量包含容器。
现在接口IHandler的类的实现是
public class HandleA: IHandler
{
private ILogger _fileLogger;
public HandleA(ILogger fileLogger)
{
_fileLogger = fileLogger;
}
}
此时解析,代码抛出错误
An exception was thrown while activating Service.HandleA
-> ?:Serilog.ILogger.An exception was thrown by a TaskScheduler.Exception of type 'System.OutOfMemoryException' was thrown.
at Autofac.Core.Resolving.Middleware.ActivatorErrorHandlingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext ctxt)
at Autofac.Core.Resolving.Middleware.RegistrationPipelineInvokeMiddleware.Execute(ResolveRequestContext context, Action`1 next)
at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
at Autofac.Core.Resolving.Middleware.SharingMiddleware.Execute(ResolveRequestContext context, Action`1 next)
at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
at Autofac.Core.Resolving.Middleware.ScopeSelectionMiddleware.Execute(ResolveRequestContext context, Action`1 next)
at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
at Autofac.Core.Resolving.Middleware.CircularDependencyDetectorMiddleware.Execute(ResolveRequestContext context, Action`1 next)
at Autofac.Core.Resolving.Pipeline.ResolvePipelineBuilder.<>c__DisplayClass14_0.<BuildPipeline>b__1(ResolveRequestContext ctxt)
at Autofac.Core.Pipeline.ResolvePipeline.Invoke(ResolveRequestContext ctxt)
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, ResolveRequest request)
at Autofac.Core.Resolving.ResolveOperation.ExecuteOperation(ResolveRequest request)
at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(ResolveRequest request)
at Autofac.Core.Container.ResolveComponent(ResolveRequest request)
at Autofac.ResolutionExtensions.TryResolveService(IComponentContext context, Service service, IEnumerable`1 parameters, Object& instance)
at Autofac.ResolutionExtensions.ResolveService(IComponentContext context, Service service, IEnumerable`1 parameters)
at Autofac.ResolutionExtensions.ResolveKeyed[TService](IComponentContext context, Object serviceKey, IEnumerable`1 parameters)
注意: 当偶尔解决依赖关系时,这种情况不会每次都会发生,并且只会在服务器上重现(8333 次中的 690 次),而不是在本地。
由于其偶发性,我无法持续监控任务管理器来检查内存使用情况。
服务器有 32 个核心和 64-128GB RAM,并且托管有多个服务和 API,所以问题肯定是在我这边。
其中一个非常可疑的地方是您注册记录器的方式。默认情况下,Autofac 使用 Instance Per Dependency 策略,因此您将为每个依赖项创建一个记录器,据我所知,这不是 Serilog 应该使用的方式。
考虑在启动时构建一次记录器并将其注册为实例:
var logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.Logger(logger => logger
.Filter.ByIncludingOnly(e => e.Level == Serilog.Events.LogEventLevel.Verbose || e.Level == Serilog.Events.LogEventLevel.Debug)
.WriteTo.Async(asyncLog => asyncLog.File(ApplicationConstants.ApplicationLogPath + "\\Trace\\Trace-.txt", Serilog.Events.LogEventLevel.Verbose, ApplicationConstants.ApplicationLogTemplate, rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, fileSizeLimitBytes: 10000000, shared: true)))
.WriteTo.Logger(logger => logger
.Filter.ByExcluding(e => e.Level == Serilog.Events.LogEventLevel.Verbose && e.Level == Serilog.Events.LogEventLevel.Debug)
.WriteTo.Map("LogContext", "Log",
(LogContext, wt) => wt.Async(asyncLog => asyncLog.File(ApplicationConstants.ApplicationLogPath + $".\\Logs\\{LogContext}-.txt", Serilog.Events.LogEventLevel.Information, ApplicationConstants.ApplicationLogTemplate, rollingInterval: RollingInterval.Day, rollOnFileSizeLimit: true, fileSizeLimitBytes: 10000000, shared: true))))
.CreateLogger();
// ...
builder.RegisterInstance<ILogger>(logger);
或者更好地考虑使用类似 Serilog.Extensions.Autofac.DependencyInjection.