Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

log4net conversion pattern not working with Asp.net Core

I am using log4net-core-preview(2.0.6) in Asp.Net Web API Core 2.0, and my log4net config layout is

 <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%-5p %d{hh:mm:ss} %logger [%M %line] %message%newline" />
 </layout>

and at Startup.ConfigureServices

//log4net stuff goes here ...
var logRepo = LogManager.GetRepository(Assembly.GetEntryAssembly());
XmlConfigurator.Configure(logRepo, File.OpenRead("log4net.config"));
ILog log = LogManager.GetLogger(this.GetType());
log.Info("App is starting");

but I don't know why my logs look like this

DEBUG 01:29:10 MyNameSpace.Controllers.XYZController [? ?] , somemessage

Can anyone tell me what's wrong here

thanks

like image 986
Bahram Avatar asked Mar 08 '23 09:03

Bahram


1 Answers

A little bit late but just run into this issue recently, turns out that some patterns are not supported under .NET Standard framework.

According to Apache's documentation, these features are not supported:

  • the ADO.NET appender
  • anything related to ASP.NET (trace appender and several pattern converters)
  • .NET Remoting
  • the colored console appender
  • the event log appender
  • The NetSendAppender
  • The SMTP appender
  • DOMConfigurator
  • stack trace patterns
  • access to appSettings (neither the log4net section itself nor using the AppSettingsPatternConverter)
  • Access to "special paths" using the EnvironmentFolderPathPatternConverter
  • Impersonation of Windows accounts

There are two workarounds:

  1. Using C# 5.0's Caller Info attributes with custom log wrapper, some good examples here:
    • Improve Logging using C# 5.0 Caller Info Attributes
    • Learn How To Use log4net Logging in .NET
  2. Creating custom PatternConverter with StackTrace (.NET Standard 2.0 and later required) shown as below

StackTraceConverter.cs

namespace MyWebApp
{
    public class StackTraceConverter : PatternConverter
    {
        protected override void Convert(TextWriter writer, object state)
        {
            try
            {
                StackTrace st = new StackTrace(true);
                int idx = 1;
                while (st.GetFrame(idx).GetMethod().DeclaringType.Assembly == typeof(LogManager).Assembly)
                    idx++;
                //I have to create a new StackTrace object in order to get the line number.
                var targetSt = new StackTrace(st.GetFrame(idx));
                writer.Write($"{targetSt.GetFrame(0).GetMethod().DeclaringType.FullName}.{targetSt.GetFrame(0).GetMethod().Name}({targetSt.GetFrame(0).GetFileLineNumber()})");
            }
            catch (Exception)
            {
            }
        }
    }
}

StackTracePatternLayout.cs

namespace MyWebApp
{
    public class StackTracePatternLayout : PatternLayout
    {
        public StackTracePatternLayout()
        {
            AddConverter(new ConverterInfo
            {
                Name = "stack_trace", //your pattern name here
                Type = typeof(StackTraceConverter)
            }
        );
        }
    }
}

And log4net.config

<appender name="RollingFileAppenderCustomPattern" type="log4net.Appender.RollingFileAppender">
    ...
    <layout type="MyWebApp.StackTracePatternLayout, MyWebApp">
        <conversionPattern value="%-5p %date{yyyy/MM/dd HH:mm:ss} [tread-%t] %stack_trace - %m%n" />
    </layout>
    ...
</appender>
<root>
    <level value="ALL" />
    <appender-ref ref="RollingFileAppenderCustomPattern" />
</root>

Then log as usual

public class HomeController : Controller
{
    ...
    public IActionResult LogForNetTest()
    {
        if (Logger.IsDebugEnabled)
            Logger.Debug($"Log4Net is ready to log!");
        ...
    }
    ...
}

The result is something like this

DEBUG 2018/07/02 15:30:59 [tread-9] MyWebApp.Controllers.HomeController.LogForNetTest(46) - Log4Net is ready to log!

Haven't fully tested yet but both methods work well so far, though there are some differences between them:

  • Caller info attribute can only get file path, StackTrace can get AssemblyQualifiedName or it's namespace with DeclaringType.FullName, providing more information.
  • Caller info attribute may have better performance

Hope this will help.

Timmy.

like image 191
Chi-Wei Shih Avatar answered Mar 19 '23 11:03

Chi-Wei Shih