EasyNetQ icon indicating copy to clipboard operation
EasyNetQ copied to clipboard

Logging does not work when using Autofac DI

Open michaelnielsen1 opened this issue 3 years ago • 14 comments

Replacing the default NoopLogger, with e.g. a SerilogLoggerAdapter does not work if you use the RegisterEasyNetQ extension method from the EasyNetQ.DI.Autofac package for registering EasyNetQ in an Autofac container.

When running this example, the resolved IBus at the end will be using the NoopLogger instead of the SerilogLoggerAdapter, which was registered by calling EnableSerilogLogging:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterInstance(new LoggerConfiguration().CreateLogger()).As<ILogger>();
builder.RegisterEasyNetQ("<connectionstring>", sr => sr.EnableSerilogLogging());

using IContainer container = builder.Build();
IBus bus = container.Resolve<IBus>();

A similar example using the Microsoft DI integration instead works as expected, and the IBus is using the SerilogLoggerAdapter:

ServiceCollection services = new ServiceCollection();
services.AddSingleton<ILogger>(new LoggerConfiguration().CreateLogger());
services.RegisterEasyNetQ("<connectionstring>", sr => sr.EnableSerilogLogging());

await using ServiceProvider provider = services.BuildServiceProvider();
IBus bus = provider.GetRequiredService<IBus>();

The issue is related to this Autofac issue: https://github.com/autofac/Autofac/issues/958.

The IfNotRegistered method does not work for open generics, which is what the AutofacAdapter.TryRegister method attempts to do: https://github.com/EasyNetQ/EasyNetQ/blob/e7565c520fca67d3fd8edf02d977bc078293bedc/Source/EasyNetQ.DI.Autofac/AutofacAdapter.cs#L98-L103

The suggested fix from Autofac is to change the type passed into IfNotRegistered to be a closed generic instead, but that is not easy to do here, as the TryRegister method just has the Type objects.

Our workaround for now is to not use the Autofac DI integration.

Another workaround is to call EnableSerilogLogging after calling RegisterEasyNetQ, so that the Serilog logger gets registered last and then replaces the Noop logger.

EasyNetQ Version: 7.2.1

michaelnielsen1 avatar Oct 18 '22 13:10 michaelnielsen1

Hi, @michaelnielsen1 . @Pliner and I were those contributors who brought current design of DI integrations with Register/TryRegister semantics. Supporting all existing DI-containers under the same API set is a real PITA. Yes, we had problems with Autofac. And with StructureMap, and others... I can suggest another option - write you customized adapter as a workaround:

public static class MyEasyNetQContainerBuilderExtensions
{
    public static ContainerBuilder RegisterEasyNetQ(this ContainerBuilder containerBuilder, Func<IServiceResolver, ConnectionConfiguration> connectionConfigurationFactory, Action<IServiceRegister> registerServices)
    {
        if (containerBuilder == null) throw new ArgumentNullException(nameof(containerBuilder));

        var serviceRegister = new MyAutofacAdapter(containerBuilder);
        RabbitHutch.RegisterBus(serviceRegister, connectionConfigurationFactory, registerServices);
        return containerBuilder;
    }
}

public class MyAutofacAdapter : AutofacAdapter
{
 public override IServiceRegister TryRegister(Type serviceType, Type implementationType, Lifetime lifetime = Lifetime.Singleton)
 {
  if (implementationType == typeof(NoopLogger<>))
    return;
  return base.TryRegister(serviceType, implementationType, lifetime )
  }
}

But... as I see we have no virtual modifiers on methods in any DI adapter. @Pliner I suggest to add virtual to all those methods to be sure that in case of such problems one can quickly apply fixes like one above.

sungam3r avatar Oct 22 '22 19:10 sungam3r

I use easy.serilog,version 7.3.5, netcore6 it works well in windows,but when I publish to linux, and i make a problem in linux,problem like below: 1675648449787

"ClassName":"System.InvalidOperationException","Message":"Unable to resolve type: EasyNetQ.IBus, service name: ","Data":null,"InnerException":{"ClassName":"System.InvalidOperationException","Message":"Unresolved dependency [Target Type: EasyNetQ.RabbitBus], [Parameter: advanced(EasyNetQ.IAdvancedBus)], [Requested dependency: ServiceType:EasyNetQ.IAdvancedBus, ServiceName:]","Data":null,"InnerException":{"ClassName":"System.InvalidOperationException","Message":"Unresolved dependency [Target Type: EasyNetQ.RabbitAdvancedBus], [Parameter: logger(EasyNetQ.Logging.ILogger1[EasyNetQ.RabbitAdvancedBus])], [Requested dependency: ServiceType:EasyNetQ.Logging.ILogger1[EasyNetQ.RabbitAdvancedBus], ServiceName:]","Data":null,"InnerException":{"ClassName":"System.InvalidOperationException","Message":"Unresolved dependency [Target Type: EasyNetQ.Logging.Serilog.SerilogLoggerAdapter1[EasyNetQ.RabbitAdvancedBus]], [Parameter: logger(Serilog.ILogger)], [Requested dependency: ServiceType:Serilog.ILogger, ServiceName:]","Data":null,"InnerException":null,"HelpURL":null,"StackTraceString":" at EasyNetQ.LightInject.ServiceContainer.GetEmitMethodForDependency(Dependency dependency)\n at EasyNetQ.LightInject.ServiceContainer.EmitConstructorDependency(IEmitter emitter, Dependency dependency)\n at EasyNetQ.LightInject.ServiceContainer.EmitConstructorDependencies(ConstructionInfo constructionInfo, IEmitter emitter, Action1 decoratorTargetEmitter)\n at EasyNetQ.LightInject.ServiceContainer.EmitNewInstanceUsingImplementingType(IEmitter emitter, ConstructionInfo constructionInfo, Action1 decoratorTargetEmitMethod)\n at EasyNetQ.LightInject.ServiceContainer.EmitNewInstance(ServiceRegistration serviceRegistration, IEmitter emitter)\n at EasyNetQ.LightInject.ServiceContainer.EmitNewInstanceWithDecorators(ServiceRegistration serviceRegistration, IEmitter emitter)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass207_0.<ResolveEmitMethod>b__3(IEmitter e)\n at EasyNetQ.LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action1 serviceEmitter)\n at EasyNetQ.LightInject.ServiceContainer.CreateInstanceDelegateIndex(Action1 emitMethod)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass210_0.<EmitLifetime>b__3(ServiceRegistration _)\n at EasyNetQ.LightInject.LazyConcurrentDictionary2.<>c__DisplayClass2_1.<GetOrAdd>b__1()\n at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode)\n at System.Lazy1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)\n at System.Lazy1.CreateValue()\n at System.Lazy1.get_Value()\n at EasyNetQ.LightInject.LazyConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)\n at EasyNetQ.LightInject.ServiceContainer.EmitLifetime(ServiceRegistration serviceRegistration, Action1 emitMethod, IEmitter emitter)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass165_0.<CreateEmitMethodWrapper>b__0(IEmitter ms)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass165_0.<CreateEmitMethodWrapper>b__0(IEmitter ms)\n at EasyNetQ.LightInject.ServiceContainer.EmitConstructorDependency(IEmitter emitter, Dependency dependency)","RemoteStackTraceString":null,"RemoteStackIndex":0,"ExceptionMethod":null,"HResult":-2146233079,"Source":"EasyNetQ","WatsonBuckets":null},"HelpURL":null,"StackTraceString":" at EasyNetQ.LightInject.ServiceContainer.EmitConstructorDependency(IEmitter emitter, Dependency dependency)\n at EasyNetQ.LightInject.ServiceContainer.EmitConstructorDependencies(ConstructionInfo constructionInfo, IEmitter emitter, Action1 decoratorTargetEmitter)\n at EasyNetQ.LightInject.ServiceContainer.EmitNewInstanceUsingImplementingType(IEmitter emitter, ConstructionInfo constructionInfo, Action1 decoratorTargetEmitMethod)\n at EasyNetQ.LightInject.ServiceContainer.EmitNewInstance(ServiceRegistration serviceRegistration, IEmitter emitter)\n at EasyNetQ.LightInject.ServiceContainer.EmitNewInstanceWithDecorators(ServiceRegistration serviceRegistration, IEmitter emitter)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass207_0.<ResolveEmitMethod>b__3(IEmitter e)\n at EasyNetQ.LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action1 serviceEmitter)\n at EasyNetQ.LightInject.ServiceContainer.CreateInstanceDelegateIndex(Action1 emitMethod)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass210_0.<EmitLifetime>b__3(ServiceRegistration _)\n at EasyNetQ.LightInject.LazyConcurrentDictionary2.<>c__DisplayClass2_1.<GetOrAdd>b__1()\n at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode)\n at System.Lazy1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)\n at System.Lazy1.CreateValue()\n at System.Lazy1.get_Value()\n at EasyNetQ.LightInject.LazyConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)\n at EasyNetQ.LightInject.ServiceContainer.EmitLifetime(ServiceRegistration serviceRegistration, Action1 emitMethod, IEmitter emitter)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass165_0.<CreateEmitMethodWrapper>b__0(IEmitter ms)\n at EasyNetQ.LightInject.ServiceContainer.EmitConstructorDependency(IEmitter emitter, Dependency dependency)","RemoteStackTraceString":null,"RemoteStackIndex":0,"ExceptionMethod":null,"HResult":-2146233079,"Source":"EasyNetQ","WatsonBuckets":null},"HelpURL":null,"StackTraceString":" at EasyNetQ.LightInject.ServiceContainer.EmitConstructorDependency(IEmitter emitter, Dependency dependency)\n at EasyNetQ.LightInject.ServiceContainer.EmitConstructorDependencies(ConstructionInfo constructionInfo, IEmitter emitter, Action1 decoratorTargetEmitter)\n at EasyNetQ.LightInject.ServiceContainer.EmitNewInstanceUsingImplementingType(IEmitter emitter, ConstructionInfo constructionInfo, Action1 decoratorTargetEmitMethod)\n at EasyNetQ.LightInject.ServiceContainer.EmitNewInstance(ServiceRegistration serviceRegistration, IEmitter emitter)\n at EasyNetQ.LightInject.ServiceContainer.EmitNewInstanceWithDecorators(ServiceRegistration serviceRegistration, IEmitter emitter)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass207_0.<ResolveEmitMethod>b__3(IEmitter e)\n at EasyNetQ.LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action1 serviceEmitter)\n at EasyNetQ.LightInject.ServiceContainer.CreateInstanceDelegateIndex(Action1 emitMethod)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass210_0.<EmitLifetime>b__3(ServiceRegistration _)\n at EasyNetQ.LightInject.LazyConcurrentDictionary2.<>c__DisplayClass2_1.<GetOrAdd>b__1()\n at System.Lazy1.ViaFactory(LazyThreadSafetyMode mode)\n at System.Lazy1.ExecutionAndPublication(LazyHelper executionAndPublication, Boolean useDefaultConstructor)\n at System.Lazy1.CreateValue()\n at System.Lazy1.get_Value()\n at EasyNetQ.LightInject.LazyConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)\n at EasyNetQ.LightInject.ServiceContainer.EmitLifetime(ServiceRegistration serviceRegistration, Action1 emitMethod, IEmitter emitter)\n at EasyNetQ.LightInject.ServiceContainer.<>c__DisplayClass165_0.<CreateEmitMethodWrapper>b__0(IEmitter ms)\n at EasyNetQ.LightInject.ServiceContainer.CreateDynamicMethodDelegate(Action1 serviceEmitter)\n at EasyNetQ.LightInject.ServiceContainer.CreateDelegate(Type serviceType, String serviceName, Boolean throwError)","RemoteStackTraceString":null,"RemoteStackIndex":0,"ExceptionMethod":null,"HResult":-2146233079,"Source":"EasyNetQ","WatsonBuckets":null},"HelpURL":null,"StackTraceString":" at EasyNetQ.LightInject.ServiceContainer.CreateDelegate(Type serviceType, String serviceName, Boolean throwError)\n at EasyNetQ.LightInject.ServiceContainer.CreateDefaultDelegate(Type serviceType, Boolean throwError)\n at EasyNetQ.LightInject.ServiceContainer.GetInstance(Type serviceType)\n at EasyNetQ.LightInject.ServiceFactoryExtensions.GetInstance[TService](IServiceFactory factory)\n at EasyNetQ.RabbitHutch.CreateBus(Func2 connectionConfigurationFactory, Action1 registerServices)\n at EasyNetQ.RabbitHutch.CreateBus(ConnectionConfiguration connectionConfiguration, Action`1 registerServices)\n at Microsoft.Extensions.DependencyInjection.MessageTransportsCollectionExtensions.AddEasyNetQModuleService(IServiceCollection services) in

bingtianyiyan avatar Feb 06 '23 02:02 bingtianyiyan

Did you register the result of RabbitBus.CreateHutch() in your IServiceCollection (as a singleton)?

zidad avatar Feb 06 '23 08:02 zidad

Did you register the result of RabbitBus.CreateHutch() in your IServiceCollection (as a singleton)?

Did you register the result of RabbitBus.CreateHutch() in your IServiceCollection (as a singleton)?

yes,i sure is register result bus as singleton.it works well in local when debug,but it will fail i publish program in linux env,when program startup it will fail when execute createbus fail,error .below is my register Screenshot_20230206_193236

bingtianyiyan avatar Feb 06 '23 11:02 bingtianyiyan

Did you register the result of RabbitBus.CreateHutch() in your IServiceCollection (as a singleton)?

yes,i have register ibus.

///

/// EasyNetQ Client

///

///

///

///

public static IServiceCollection AddEasyNetQModuleService(this IServiceCollection services)

{

if (services == null) throw new ArgumentNullException(nameof(services));

var setting = EngineContext.Current.Configuration.

GetSection(RabbitMqOptions.CAPRabbitMqSettings);

if (setting == null)

{

return services;

}

var op = setting.Get<RabbitMqOptions>();

if (op == null)

{

throw new ArgumentNullException("RabbitMq配置文件不能为空");

}

services.AddOptions<RabbitMqOptions>()

.Bind(setting);

var addressList = op.HostName.Split(",");

List<HostConfiguration> hostList = new List<HostConfiguration>();

addressList.ForEach(item =>

{

hostList.Add(new HostConfiguration()

{

Host = item,

Port = (ushort)op.Port

});

});

ConnectionConfiguration connection = new ConnectionConfiguration();

connection.Port = (ushort)op.Port;

connection.Password = op.Password;

connection.UserName = op.UserName;

connection.VirtualHost = op.VirtualHost;

connection.PublisherConfirms = op.PublishConfirms;

connection.Hosts = hostList;

var bus = RabbitHutch.CreateBus(connection, x =>

{

x.EnableLegacyConventions();

// x.EnableMicrosoftLogging()

//.Register<IConsumerErrorStrategy>((sp) => new ExtendedConsumerErrorStrategy(

// sp.Resolve<ILogger<DefaultConsumerErrorStrategy>>(),

// sp.Resolve<IConsumerConnection>(),

// sp.Resolve<ISerializer>(),

// sp.Resolve<IConventions>(),

// sp.Resolve<ITypeNameSerializer>(),

// sp.Resolve<IErrorMessageSerializer>(),

// connection

// ))

;

//.Register(typeof(EasyNetQ.Logging.ILogger<>), typeof(EasyNetQCusLogger<>))

// .Register<IConsumerErrorStrategy>(_ => new AlwaysRequeueErrorStrategy())//默认返回原有队列

#if DEBUG

x.EnableConsoleLogger();

#endif

});

// var bus = RabbitHutch.CreateBus($"host={op.HostName};port={op.Port};virtualHost={op.VirtualHost};username={op.UserName};password={op.Password};requestedHeartbeat=10;publisherConfirms={op.PublishConfirms}");

services.AddSingleton(bus);

services.AddSingleton<IEasyNetQPublish, EasyNetQPublish>();

//注册IConsume的实现类,EasyNetQ会自动扫描实现该接口的类

// AddEasyNetQConsumerRegisterAuto(services);

if (op.AddConsumer)

{

services.AddHostedService<EasyNetQConsumerService>();

}

return services;

}

bingtianyiyan avatar Feb 06 '23 11:02 bingtianyiyan

it works well in windows,but when I publish to linux,

I think OS is not the core reason here. изображение Second Enable call replaces serilog logging adapter. Try to run locally in RELEASE mode. You should get the same error. Let's check to understand that we are moving in the right direction.

sungam3r avatar Feb 07 '23 06:02 sungam3r

you are right,is not os planet problem,is EnableConsoleLogger() recover when in debug mode haha,may this easynetq has not correct hander log in DI

bingtianyiyan avatar Feb 08 '23 01:02 bingtianyiyan

Replace semantics works here by design, it's OK but the naming may confuse.

@bingtianyiyan I suggest to open new issue transferring all needed information there and do not continue here because initial post is about Autofac. ping @Pliner

sungam3r avatar Feb 08 '23 06:02 sungam3r

ok,i open new issue with this problem。thanks for you reply

bingtianyiyan avatar Feb 08 '23 08:02 bingtianyiyan

#1622 hello,i now open new issue,

bingtianyiyan avatar Feb 08 '23 09:02 bingtianyiyan

The same question, TryRegister does not work when using Autofac DI.

@sungam3r Has the problem been solved?

jerviscui avatar Mar 31 '23 05:03 jerviscui

I made this suggestion https://github.com/EasyNetQ/EasyNetQ/issues/1502#issuecomment-1287890940

sungam3r avatar Apr 05 '23 20:04 sungam3r

I made this suggestion #1502 (comment)

I'm sorry for my confusion. Can we implement this suggestion at this moment, or do we need to add the virtual modifiers first?

BluePositive avatar May 18 '23 17:05 BluePositive

@BluePositive My suggestion was to add virtual modifiers first.

sungam3r avatar Jul 07 '23 14:07 sungam3r