autofac / Autofac.ServiceFabric

Autofac integration for Azure Service Fabric. Provides service factory implementations for Actors, Stateful Services and Stateless Services.
MIT License
26 stars 27 forks source link

Servicefabric context for logging #25

Closed saisworld closed 5 years ago

saisworld commented 6 years ago

My logging library requires ServiceContext to be injected into it for logging Telemetry information.

var loggerFactory = new LoggerFactoryBuilder(context).CreateLoggerFactory(applicationInsightsKey); logger = loggerFactory.CreateLogger<MyStateless>();

Before implementing Autofac I was able to send the Context like this -

   ServiceRuntime.RegisterServiceAsync("MyStatelessType",
                    context =>
                    {
                        var applicationInsightsKey = FabricRuntime.GetActivationContext()
                            .GetConfigurationPackageObject("Config")
                            .Settings
                            .Sections["ConfigurationSection"]
                            .Parameters["ApplicationInsightsKey"]
                            .Value;

                        var loggerFactory = new LoggerFactoryBuilder(context).CreateLoggerFactory(applicationInsightsKey);
                        logger = loggerFactory.CreateLogger<MyStateless>();

                        return new MyStateless(context, logger);
                    }).GetAwaiter().GetResult();

However, I'm not sure how to get hold of Servicecontext now after implementing Autofac. Will be servicecontext be available only after builder.build()? In which case how can I register my logging library to Autofac. Please advise.

Thanks Sai

pshrosbree commented 6 years ago

You will see in StatelessServiceFactoryRegistration.cs and, mutatis mutandis, in the stateful and actor versions, that ServiceContext is registered as follows:

builder.RegisterInstance(context)
    .As<StatelessServiceContext>()
    .As<ServiceContext>();

so you should be able to resolve ServiceContext or its derived types from the container without any additional action. Make sure you;re using a current version, because this registration was not in some earlier versions.

saisworld commented 6 years ago

@pshrosbree , I understand we can access ServiceContext from the Container after its build. But my question is, can I access it before building the container builder.build(). if so how? How can I register my Logger to the Container with ServiceContext as parameter.?

pshrosbree commented 6 years ago

The first time the service context is available is when the Service Fabric runtime invokes the lambda passed to the service registration, and that happens during the the build of the container, which after the container is configured. What you want is to access to the service context during Build(), rather than before .Build(). That is, after you call .Build() but before it returns.

The way this is done is to resolve the logger from the container context in the registration callback with something like (I am guessing some type names):

builder
    .Register(c => FabricRuntime.GetActivationContext().GetConfigurationPackageObject("Config").Settings)
    .As<ConfigurationSettings>();

builder
    .Register(c => c.Resolve<ConfigurationSettings>.Sections["ConfigurationSection"])
    .Named<ConfigurationSection>("ConfigurationSection");

builder
    .Register(c => c.ResolveNamed<ConfigurationSection>("ConfigurationSection").Parameters["ApplicationInsightsKey"].Value)
    .Named<string>("ApplicationInsightsKey");

builder
    .Register(c =>
    {
        var serviceContext = c.Resolve<StatelessServiceContext>();
        var applicationInsightsKey = c.ResolveNamed<string>("ApplicationInsightsKey");
        return new LoggerFactoryBuilder(serviceContext)
            .CreateLoggerFactory(applicationInsightsKey);
    })
    .As<ILoggerFactory>();

then use the factory to pass in a parameter when you register the service:

builder
    .RegisterStatelessService<MyService>("MyServiceType")
    .WithParameter(
        (p, c) => p.ParameterType == typeof(ILogger),
        (p, c) => c.Resolve<ILoggerFactory>().CreateLogger<MyService>());

and let the container resolve everything for you during Build(). You can use this technique for other registrations that require a contextual logger.

Alternatively, once you have the logger factory registered, you can attach to the OnActivating build callback and contextualize the logger for all registrations without having to specify it each time. This is how Serilog Autofac integration works, so you can use that as an example of how to implement it yourself.

You may also want to look at decomposing configuration access further to make access to configuration a little less clunky.

tillig commented 5 years ago

I think @pshrosbree has this nailed. Closing.