Base solution for your next web application
Open Closed

Convert log4net config from file to config in code (programmatically) #12463


User avatar
0
csona created

ASP.Net Zero Version: v10.2 ASP.Net Core + Angular

Hello,

We are using log4net.config file to configure the logger with following code in Startup.cs

 `options.IocManager.IocContainer.AddFacility<LoggingFacility>(
     f => f.LogUsing(new Log4NetLoggerFactory("log4net.config"))
 );`

log4net.config is as follows

`<?xml version="1.0" encoding="utf-8" ?>
<log4net>
  <appender name="Console" type="log4net.Appender.ConsoleAppender">
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%-5level %date [%-5.5thread] %-40.40logger - %message%newline" />
    </layout>
  </appender>
  <appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender" >
    <file value="App_Data/Logs/Logs.txt" />
    <encoding value="utf-8"/>
    <appendToFile value="true" />
    <rollingStyle value="Size" />
    <maxSizeRollBackups value="10" />
    <maximumFileSize value="10000KB" />
    <staticLogFileName value="true" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%-5level %date [%-5.5thread] %-40.40logger - %message%newline" />
    </layout>
  </appender>
  <root>
    <appender-ref ref="Console" />
    <appender-ref ref="RollingFileAppender" />
    <level value="DEBUG" />
  </root>
</log4net>`

Can you please help us to convert this configuration from file-based to programmatically configure log4net in application code?

Thanks, Chaitanya

Markdown is supported
Copy & paste or drag & drop images (max 30 MB per image)

2 Answer(s)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @csona

    You need to make the changes specified below for this situation. You will need to customize some of the classes provided by ABP. Once you follow the steps below, you will no longer need the log4net.config file. I will create an issue for the necessary improvements on the ABP side.

    Add ConfigurableProgrammaticLog4NetLoggerFactory.cs

    public class Log4NetConfiguration
    {
        public string LogFileName { get; set; } = "App_Data/Logs/Logs.txt";
        public string ProductionLogFileName { get; set; } = "App_Data/Logs/Production.txt";
        public string LogPattern { get; set; } = "%-5level %date [%-5.5thread] %-40.40logger - %message%newline";
        public int MaxSizeRollBackups { get; set; } = 10;
        public string MaximumFileSize { get; set; } = "10000KB";
        public Level LogLevel { get; set; } = Level.Debug;
        public bool EnableConsoleLogging { get; set; } = true;
        public bool EnableFileLogging { get; set; } = true;
    }
    
    public class ConfigurableProgrammaticLog4NetLoggerFactory : AbstractLoggerFactory
    {
        private readonly IWebHostEnvironment _environment;
        private readonly Log4NetConfiguration _config;
        private ILoggerRepository _loggerRepository;
        public ConfigurableProgrammaticLog4NetLoggerFactory() : this(null)
        {
                
        }
        public ConfigurableProgrammaticLog4NetLoggerFactory(
            IWebHostEnvironment environment,
            Log4NetConfiguration config = null)
        {
            _environment = environment;
            _loggerRepository = LogManager.CreateRepository(
                    typeof(Log4NetLoggerFactory).GetAssembly(),
                    typeof(log4net.Repository.Hierarchy.Hierarchy)
                );
            _config = config ?? new Log4NetConfiguration();
            ConfigureLog4Net();
        }
    
        public override Castle.Core.Logging.ILogger Create(string name)
        {
            return new CustomLog4NetLogger(LogManager.GetLogger(_loggerRepository.Name, name).Logger, this);
        }
    
        public override Castle.Core.Logging.ILogger Create(string name, LoggerLevel level)
        {
            throw new NotSupportedException("Logger levels cannot be set at runtime. Please review your configuration file.");
        }
    
        private void ConfigureLog4Net()
        {
            var appenders = new List<IAppender>();
    
            if (_config.EnableConsoleLogging)
            {
                var consoleAppender = new ConsoleAppender
                {
                    Name = "Console",
                    Layout = new PatternLayout(_config.LogPattern)
                };
                consoleAppender.ActivateOptions();
                appenders.Add(consoleAppender);
            }
    
            if (_config.EnableFileLogging)
            {
                var fileAppender = CreateFileAppender();
                appenders.Add(fileAppender);
            }
    
            BasicConfigurator.Configure(_loggerRepository, appenders.ToArray());
    
            var hierarchy = (log4net.Repository.Hierarchy.Hierarchy)_loggerRepository;
            hierarchy.Root.Level = _config.LogLevel;
            hierarchy.RaiseConfigurationChanged(EventArgs.Empty);
        }
    
        private RollingFileAppender CreateFileAppender()
        {
            var logFileName = _environment?.IsDevelopment() == true
                ? _config.LogFileName
                : _config.ProductionLogFileName;
    
            var finalLogPath = Path.Combine(_environment.ContentRootPath, logFileName);
    
            var fileAppender = new RollingFileAppender
            {
                Name = "RollingFileAppender",
                File = finalLogPath,
                Encoding = System.Text.Encoding.UTF8,
                AppendToFile = true,
                RollingStyle = RollingFileAppender.RollingMode.Size,
                MaxSizeRollBackups = _config.MaxSizeRollBackups,
                MaximumFileSize = _config.MaximumFileSize,
                StaticLogFileName = true,
                Layout = new PatternLayout(_config.LogPattern)
            };
    
            fileAppender.ActivateOptions();
            return fileAppender;
        }
    }
    

    Update Startup.cs

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        //Configure Abp and Dependency Injection
        return services.AddAbp<YourProjectNameWebHostModule>(options =>
        {
            //Configure Log4Net logging
            options.IocManager.IocContainer.AddFacility<LoggingFacility>(
                f => f.LogUsing(new ConfigurableProgrammaticLog4NetLoggerFactory(_hostingEnvironment))
            );
        
            options.PlugInSources.AddFolder(Path.Combine(_hostingEnvironment.WebRootPath, "Plugins"),
                SearchOption.AllDirectories);
        });
    }
    

    Add CustomLog4NetLogger (Log4NetLogger)

    [Serializable]
    public class CustomLog4NetLogger :
         MarshalByRefObject,
         ILogger
    {
        private static readonly Type DeclaringType = typeof(CustomLog4NetLogger);
    
        public CustomLog4NetLogger(log4net.Core.ILogger logger, ILoggerFactory factory)
        {
            Logger = logger;
            Factory = factory;
        }
    
        internal CustomLog4NetLogger()
        {
        }
    
        internal CustomLog4NetLogger(ILog log, Log4NetLoggerFactory factory)
            : this(log.Logger, factory)
        {
        }
    
        protected internal ILoggerFactory Factory { get; set; }
    
        protected internal log4net.Core.ILogger Logger { get; set; }
    
        //All the other remaining methods are the same as used in Log4NetLogger, you can copy them from there. (We will make the necessary changes for easy override on the abp side.)
    }
    

    Update YourProjectNameWebHostModule.cs

    public class YourProjectNameWebHostModule : AbpModule
    {
        public override void PreInitialize()
        {
            IocManager.Register<Log4NetConfiguration>(DependencyLifeStyle.Singleton);
            IocManager.Register<ILoggerFactory, ConfigurableProgrammaticLog4NetLoggerFactory>(DependencyLifeStyle.Singleton);
        }
    }
    

    After applying these changes you will no longer need the log4net.config file. We will make the necessary changes for a more flexible change on the ABP side.

    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)
  • User Avatar
    0
    csona created

    Hi @oguzhanagir

    Thanks for the detailed answer. After making these changes the issue has been resolved.

    Just as additional context, we requested this change because after deploying the changes to Azure App Service we were getting following error

    `Unhandled exception. System.IO.FileNotFoundException: Could not find file 'C:\Program Files (x86)\SiteExtensions\ApplicationInsightsAgent\2.8.45\ExtensionManager\log4net.config'.
    File name: 'C:\Program Files (x86)\SiteExtensions\ApplicationInsightsAgent\2.8.45\ExtensionManager\log4net.Production.config'
       at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
       at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize)
       at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize)
       at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize)
       at System.IO.Strategies.FileStreamHelpers.ChooseStrategy(FileStream fileStream, String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, Int64 preallocationSize)
       at System.IO.File.OpenRead(String path)
       at Abp.Castle.Logging.Log4Net.Log4NetLoggerFactory..ctor(String configFileName)
       at lambda_method2(Closure , Object[] )
       at Castle.Core.Internal.ReflectionUtil.Instantiate(ConstructorInfo ctor, Object[] ctorArgs)
       at Castle.Core.Internal.ReflectionUtil.Instantiate[TBase](Type subtypeofTBase, Object[] ctorArgs)
       at Castle.Core.Internal.ReflectionUtil.CreateInstance[TBase](Type subtypeofTBase, Object[] ctorArgs)
       at Castle.Facilities.Logging.LoggingFacility.CreateProperLoggerFactory(LoggerImplementation loggerApi)
       at Castle.Facilities.Logging.LoggingFacility.ReadConfigurationAndCreateLoggerFactory()
       at Castle.Facilities.Logging.LoggingFacility.Init()
       at Castle.MicroKernel.Facilities.AbstractFacility.Castle.MicroKernel.IFacility.Init(IKernel kernel, IConfiguration facilityConfig)
       at Castle.MicroKernel.DefaultKernel.AddFacility(IFacility facility)
       at Castle.MicroKernel.DefaultKernel.AddFacility[T](Action`1 onCreate)
       at Castle.Windsor.WindsorContainer.AddFacility[T](Action`1 onCreate)
       at GrowthPlan.Web.Startup.Startup.&lt;ConfigureServices&gt;b__4_7(AbpBootstrapperOptions options) in E:\Projects\GrowthPlan\GP-ANZ-v11.2.1\aspnet-core\src\GrowthPlan.Web.Host\Startup\Startup.cs:line 223
       at Abp.AbpBootstrapper..ctor(Type startupModule, Action`1 optionsAction)
       at Abp.AbpBootstrapper.Create[TStartupModule](Action`1 optionsAction)
       at Abp.AspNetCore.AbpServiceCollectionExtensions.AddAbpBootstrapper[TStartupModule](IServiceCollection services, Action`1 optionsAction)
       at Abp.AspNetCore.AbpServiceCollectionExtensions.AddAbp[TStartupModule](IServiceCollection services, Action`1 optionsAction, Boolean removeConventionalInterceptors)
       at GrowthPlan.Web.Startup.Startup.ConfigureServices(IServiceCollection services) in E:\Projects\GrowthPlan\GP-ANZ-v11.2.1\aspnet-core\src\GrowthPlan.Web.Host\Startup\Startup.cs:line 220
       at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
       at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
       at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.InvokeCore(Object instance, IServiceCollection services)
       at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.<>c__DisplayClass9_0.g__Startup|0(IServiceCollection serviceCollection)
       at Microsoft.AspNetCore.Hosting.StartupLoader.ConfigureServicesDelegateBuilder`1.&lt;&gt;c__DisplayClass15_0.&lt;BuildStartupServicesFilterPipeline&gt;g__RunPipeline|0(IServiceCollection services)
       at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.Invoke(Object instance, IServiceCollection services)
       at Microsoft.AspNetCore.Hosting.ConfigureServicesBuilder.&lt;&gt;c__DisplayClass8_0.&lt;Build&gt;b__0(IServiceCollection services)
       at Microsoft.AspNetCore.Hosting.StartupLoader.ConfigureServicesDelegateBuilder`1.<>c__DisplayClass14_0.g__ConfigureServicesWithContainerConfiguration|0(IServiceCollection services)
       at Microsoft.AspNetCore.Hosting.ConventionBasedStartup.ConfigureServices(IServiceCollection services)
       at Microsoft.AspNetCore.Hosting.WebHost.EnsureApplicationServices()
       at Microsoft.AspNetCore.Hosting.WebHost.Initialize()
       at Microsoft.AspNetCore.Hosting.WebHostBuilder.Build()
       at GrowthPlan.Web.Startup.Program.Main(String[] args) in E:\Projects\GrowthPlan\GP-ANZ-v11.2.1\aspnet-core\src\GrowthPlan.Web.Host\Startup\Program.cs:line 13`
    
    Markdown is supported
    Copy & paste or drag & drop images (max 30 MB per image)