Log4net writing custom object to sql database using custom appender?

馋奶兔 提交于 2019-12-10 10:57:15

问题


Using SQL Server 2012 here is my table:

CREATE TABLE [dbo].[Test]
(
    [One] [VARCHAR](50) NOT NULL,
    [Two] [VARCHAR](50) NOT NULL
) ON [PRIMARY]

Here is my appender:

<appender name="TestAppender" type="log4net.Appender.AdoNetAppender">
    <bufferSize value="1" />
    <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    <connectionString value="data source=localhost;initial catalog=ApplicationLog;integrated security=false;persist security info=True;User ID=someUser;Password=somePassword" />
    <commandText value="INSERT INTO [dbo].[Test] ([One],[Two]) VALUES (@one, @two)" />
    <parameter>
        <parameterName value="@one"/>
        <dbType value="String"/>
        <size value="50"/>
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%one"/>
        </layout>
    </parameter>
    <parameter>
        <parameterName value="@two"/>
        <dbType value="String"/>
        <size value="50"/>
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%two"/>
        </layout>
    </parameter>
</appender>

Here is how I get the instance of the logger in code and try to write to it:

private static readonly ILog TestLogger = LogManager.GetLogger("TestAppender");
TestLogger.Info(new Test {One = "someOne", Two = "someTwo"});

Here is my test class:

public class Test
{
    public string One { get; set; }
    public string Two { get; set; }
}

After stepping through this, I have a record in my table and the contents of the columns are this:

One: "one" Two: "12wo"

What the heck is "12wo"? I know I'm missing something here. I think my conversion patterns are wrong. I've tried this instead:

<conversionPattern value="%property{one}"/>

..but that doesn't work either. Do I have to write a custom pattern layout or something? Thanks.


回答1:


This site pointed me in the right direction.

I had to create a custom LayoutPattern and PatternConverter in order to write my object to the log successfully. Turns out the weird "12wo" text I was getting in the database was because the conversion pattern uses printf c style syntax. Anyhow, here's some code.

public class TestLayoutPattern : PatternLayout
{
    public TestLayoutPattern()
    {
        AddConverter(new ConverterInfo
        {
            Name = "test",
            Type = typeof (TestConverter)
        });
    }
}
public class TestConverter : PatternConverter
{
    protected override void Convert(System.IO.TextWriter writer, object state)
    {
        if (state == null)
        {
            writer.Write(SystemInfo.NullText);
            return;
        }

        var loggingEvent = state as LoggingEvent;
        if (loggingEvent == null)
            throw new NullReferenceException("loggingEvent");

        var test = loggingEvent.MessageObject as Test;

        if (test == null)
        {
            writer.Write(SystemInfo.NullText);
        }
        else
        {
            switch (Option.ToLower())
            {
                case "one":
                    writer.Write(test.One);
                    break;
                case "two":
                    writer.Write(test.Two);
                    break;                    
                default:
                    writer.Write(SystemInfo.NullText);
                    break;
            }
        }
    }
}

Here is how to get an instance of the logger by name:

private static readonly ILog TestLogger = LogManager.GetLogger("TestLogger");

Here is how to write a Test object to the log.

TestLogger.Info(new Test {One = "field one", Two = "field two"});

Here is how a parameter should be defined in the web.config.

<parameter>
  <parameterName value="@one" />
  <dbType value="String" />
  <size value="50" />
  <layout type="MyApp.TestLayoutPattern">
    <conversionPattern value="%test{one}" />
  </layout>
</parameter>

Another thing to note is the root and logger sections of the web.config. In the root section is where the default logger is defined with its level set. I can define my custom TestLogger in a logger section which will reference the appender as shown below. This allows me to access the TestLogger by name as shown above.

<root>
  <level value="ALL"/>
  <appender-ref ref="ADONetAppender"/>
</root>
<logger additivity="false" name="TestLogger">
  <level value="ALL"/>
  <appender-ref ref="TestAppender" />
</logger>

I also found if you wanted to just add a few properties to the default ADONetAppender (and add a few fields to the table), you could instead use the log4net.ThreadContext to set those properties like so:

log4net.ThreadContext.Properties["MyCustomPrperty"] = value;

Then in the web.config under the parameter section you can access that property like this:

<parameter>
  <parameterName value="@myCustomProperty"/>
  <dbType value="String"/>
  <layout type="log4net.Layout.RawPropertyLayout">
    <key value="MyCustomProperty" />
  </layout>
</parameter>


来源:https://stackoverflow.com/questions/35323545/log4net-writing-custom-object-to-sql-database-using-custom-appender

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!