locked
Custom Logging - Circular Dependency Injection / Infinite Loop problem RRS feed

  • Question

  • User-798016373 posted

    I tried to write a customer ILogger / ILoggerProvider that has a dependency on a service that has a dependency on ILogger :)  That leaves me with a circular reference and an infinite loop.

    How could I modify this code to avoid the circular reference?


    class Program
        {
            static void Main(string[] args)
            {
                var configuration = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json").Build();
    
                var serviceCollection = new ServiceCollection();
    
                serviceCollection.AddSingleton<IDependentOnLoggerService, MyService>();
    
                serviceCollection.AddLogging(loggingBuilder =>
                {
                    loggingBuilder.AddConfiguration(configuration.GetSection("Logging"));
                    loggingBuilder.AddConsole();
                });
    
                serviceCollection.AddCustomLogger();
    
                using (var serviceProvider = serviceCollection.BuildServiceProvider())
                {
                    var loggerFactory = serviceProvider.GetService<ILoggerFactory>();
                    var logger = loggerFactory.CreateLogger<Program>();
    
                    logger.LogInformation("hello world");
                }
            }
        }
    
        public interface IDependentOnLoggerService
        {
            void DoWork();
        }
    
        public class MyService : IDependentOnLoggerService
        {
            private ILogger logger;
    
            public MyService(ILoggerFactory loggerFactory)
            {
                this.logger = loggerFactory.CreateLogger<MyService>();
                this.logger.LogInformation("MyService create");
            }
    
            public void DoWork()
            {
                this.logger.LogInformation("Doing work");
            }
        }
        
        public static class ServiceCollectionExtensions
        {
            public static void AddCustomLogger(this IServiceCollection serviceCollection)
            {
                serviceCollection.AddLogging(loggingBuilder =>
                {
                    loggingBuilder.AddCustomLogger();
                });
            }
    
            private static ILoggingBuilder AddCustomLogger(this ILoggingBuilder builder)
            {
                if (builder == null)
                {
                    throw new ArgumentNullException(nameof(builder));
                }
    
                builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, CustomLoggerProvider>(
                    serviceProvider =>
                    {
                        // this causes circulation dependency injection, resolving IDependentOnLoggerService will try to resolve an ILogger which will bring it right back here
                        var loggerDependentService = serviceProvider.GetService<IDependentOnLoggerService>(); 
    
                        return new CustomLoggerProvider(loggerDependentService);
                    }));
    
                return builder;
            }
        }
    
        public class CustomLoggerProvider : ILoggerProvider
        {
            IDependentOnLoggerService dependentOnLoggerService;
    
            public CustomLoggerProvider(IDependentOnLoggerService dependentOnLoggerService)
            {
                this.dependentOnLoggerService = dependentOnLoggerService;
            }
    
            public ILogger CreateLogger(string categoryName)
            {
                return new CustomLogger(this.dependentOnLoggerService);
            }
    
            public void Dispose() { }
        }
    
        public class CustomLogger : ILogger
        {
            IDependentOnLoggerService dependentOnLoggerService;
    
            public CustomLogger(IDependentOnLoggerService dependentOnLoggerService)
            {
                this.dependentOnLoggerService = dependentOnLoggerService;
            }
    
            public IDisposable BeginScope<TState>(TState state)
            {
                return null;
            }
    
            public bool IsEnabled(LogLevel logLevel)
            {
                return true;
            }
    
            public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
            {
                this.dependentOnLoggerService.DoWork();
                Console.WriteLine(state.ToString());
            }
        }

    Wednesday, June 17, 2020 3:23 AM

All replies

  • User-17257777 posted

    Hi Mark,

    I have answered the same question on SO, You can have a look.

    Best Regards,

    Jiadong Meng

    Friday, June 19, 2020 5:45 AM