Log4Net Logging of two different levels to two different appenders for the same logger

55,986

Solution 1

You should be able to set the threshold property of each appender separately and include them in the same root.

<appender name="filelogAppender" type="log4net.Appender.RollingFileAppender">
  <threshold value="Error" />
</appender>
<appender name="dblogAppender" type="log4net.Appender.AdoNetAppender">
  <threshold value="Info" />
</appender>
<root>
  <appender-ref ref="filelogAppender" />
  <appender-ref ref="dblogAppender" />
</root>

reference

Solution 2

You could specify a LevelRangeFilter for each of the appenders, and define each appender in the root to log at different levels.

<appender name="filelogAppender" type="log4net.Appender.RollingFileAppender" >
    <!--File Details/Layout Options-->
    <filter type="log4net.Filter.LevelRangeFilter">
        <levelMin value="INFO"/>
    </filter>
</appender>
<appender name="dbLogAppender" type="log4net.Appender.AdoNetAppender">
    <!--SQL Options-->      
    <filter type="log4net.Filter.LevelRangeFilter">
        <levelMin value="ERROR"/>
    </filter>
</appender>
<root>
    <level value="INFO"/>
    <appender-ref ref="filelogAppender" /> <!--Prints info, warn, error, or fatal logs. -->
    <appender-ref ref="dbLogAppender" />   <!--Prints only error or fatal logs. -->
</root>

Solution 3

Two <root> elements are not allowed.

From documentation:

root     Optional element, maximum of one allowed. Defines the configuration of the root logger.

You can however have a root logger like you do, and then have a separate logger specified by a namespace.

<root>
    <level value="Error"/>
    <appender-ref ref="filelogAppender"/>
</root>
<logger name="MyCompany.MyApp.Namespace">
    <level value="Info"/>
    <appender-ref ref="dbLogAppender"/>
</logger>
Share:
55,986

Related videos on Youtube

graney
Author by

graney

Technologist Experience; Python, PHP, Javascript, AWS, Solr [And: Java, .NET, Pascal, Delphi, C, C++, previously]

Updated on February 21, 2020

Comments

  • graney
    graney about 4 years

    We have two different asp.net applications with Log4net logging enabled. They both have the same version of Log4Net, 1.2.10.0.

    We have added the log4net.Appender.AdoNetAppender logger to both of them and want to log Info level to it for the root logger, but also want to log to the Error level for a root logger to a file appender. Our configuration is as follows;

    <?xml version="1.0" encoding="utf-8"?>
    
    <log4net>
        <appender name="filelogAppender" type="log4net.Appender.RollingFileAppender" >
            <file value="..\logs\app.debug.log" />
            <encoding value="utf-8" />
            <staticLogFileName value="true" />
            <datePattern value=".yyyyMMdd.'log'" />
            <rollingStyle value="Composite" />
            <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
            <appendToFile value="true" />
            <maximumFileSize value="1MB" />
            <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%date [%3thread] %-5level %property{log4net:HostName} [%property{Revision}] %logger %message%n" />
            </layout>
        </appender>
        <!--    
            use [DB]
            GO
            CREATE TABLE [dbo].[Log] (
                [Id] [int] IDENTITY (1, 1) NOT NULL,
                [Date] [datetime] NOT NULL,
                [Application][varchar] (255) NOT NULL,
                [Thread] [varchar] (255) NOT NULL,
                [Level] [varchar] (50) NOT NULL,
                [Logger] [varchar] (255) NOT NULL,
                [Server][varchar](255) NOT NULL,
                [Revision][varchar](50) NOT NULL,
                [Message] [varchar] (4000) NOT NULL,
                [Exception] [varchar] (2000) NULL
            )
        -->
        <appender name="dbLogAppender" type="log4net.Appender.AdoNetAppender" xdt:Transform="InsertBefore(/log4net/root)">
            <bufferSize value="100" />
            <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
            <connectionString value="data source=sqlserver;initial catalog=DB;integrated security=false;persist security info=True;User ID=USER;Password=PASSWORD" />
            <commandText value="INSERT INTO Log ([Date],[Application],[Thread],[Level],[Logger],[Server],[Revision],[Message],[Exception]) VALUES (@log_date,'WebApp1', @thread, @log_level, @logger, @server, @revision, @message, @exception)" />
            <parameter>
                <parameterName value="@log_date" />
                <dbType value="DateTime" />
                <layout type="log4net.Layout.RawTimeStampLayout" />
            </parameter>
            <parameter>
                <parameterName value="@thread" />
                <dbType value="String" />
                <size value="255" />
                <layout type="log4net.Layout.PatternLayout">
                    <conversionPattern value="%thread" />
                </layout>
            </parameter>
            <parameter>
                <parameterName value="@log_level" />
                <dbType value="String" />
                <size value="50" />
                <layout type="log4net.Layout.PatternLayout">
                    <conversionPattern value="%level" />
                </layout>
            </parameter>
            <parameter>
                <parameterName value="@logger" />
                <dbType value="String" />
                <size value="255" />
                <layout type="log4net.Layout.PatternLayout">
                    <conversionPattern value="%logger" />
                </layout>
            </parameter>
            <parameter>
                <parameterName value="@server" />
                <dbType value="String" />
                <size value="255" />
                <layout type="log4net.Layout.PatternLayout">
                    <conversionPattern value="%property{log4net:HostName}"/>
                </layout>
            </parameter>
            <parameter>
                <parameterName value="@revision" />
                <dbType value="String" />
                <size value="50" />
                <layout type="log4net.Layout.PatternLayout">
                    <conversionPattern value="%property{Revision}"/>
                </layout>
            </parameter>
            <parameter>
                <parameterName value="@message" />
                <dbType value="String" />
                <size value="4000" />
                <layout type="log4net.Layout.PatternLayout">
                    <conversionPattern value="%message" />
                </layout>
            </parameter>
            <parameter>
                <parameterName value="@exception" />
                <dbType value="String" />
                <size value="2000" />
                <layout type="log4net.Layout.ExceptionLayout" />
            </parameter>
        </appender>
        <root>
            <level value="Error"/>
            <appender-ref ref="filelogAppender"/>
        </root>
        <root>
            <level value="Info"/>
            <appender-ref ref="dbLogAppender"/>
        </root>
    </log4net>  
    

    The issue we are seeing is that for one application we see entries in the DB, but for the other we do not.

    Revision is GlobalContext property we set in Application_Start() in both applications. And the only difference between the two configurations is that we have a different hardcoded value for Application.

    Can we have two root loggers like this? Could this be the source of our issue that we are seeing in one application?

  • graney
    graney over 10 years
    However, we want to log root to two locations. We do not want to constrain logging a specific namespace. Hence our attempt at using two root loggers. [Which strangely works on one of the apps] Otherwise we will need to create a ton of loggers for each possible root namespace with additivity set to false.
  • graney
    graney over 10 years
    A great answer! And provides flexibility I was totally unaware of. I think I will use @barry answer though as I need not define a range just a threshold.
  • Anssssss
    Anssssss over 6 years
    For some reason the "threshold" and "evaluator" nodes didn't work for me, but this did (I specified levelMin and levelMax as the same value).
  • Honza P.
    Honza P. over 4 years
    I just had a trouble with fact that once levelMin is used in appender, it has to be specified in all appenders otherwise filtration did not work properly in my case and only root level was taken into account.