简体   繁体   中英

Log4Net New Eventlog not working

I have setup log4net to use a separate event log for my applicaiton. The event log exists and is visible (logging to that log used to even work!)

Name is "sbsysrst" isUat is true .

Expected LogName is SB3-UAT however it insists on logging to the Applicaiton log despite the log4net Debug messages below.

Method that returns the ILog object.

public static ILog GetLogger(string name, bool isUat = false)
{
    var eventLogAppender = new EventLogAppender
    {
        ApplicationName = name,
        Layout = new PatternLayout
        {
            ConversionPattern = "%message%newline%exception" 
        },
        LogName = "SB3" + (isUat ? "-" + UatSuffix : string.Empty),
        Threshold = Level.Info
    };

    BasicConfigurator.Configure(eventLogAppender, FileAppenders[isUat]);
    eventLogAppender.ActivateOptions();
    return LogManager.GetLogger(name);
}

Debug Output form log4net

log4net: Searched for existing files in [C:\Windows\...]
log4net: curSizeRollBackups starts at [0]
log4net: Opening file for writing [C:\Windows\...\SB3.log] append [True]
log4net: Searched for existing files in [C:\Windows\...]
log4net: curSizeRollBackups starts at [0]
log4net: Opening file for writing [C:\Windows\...\SB3.log] append [True]
log4net: Searched for existing files in [C:\Windows\...UAT]
log4net: curSizeRollBackups starts at [0]
log4net: Opening file for writing [C:\Windows\...UAT\SB3.log] append [True]
log4net: Searched for existing files in [C:\Windows\...UAT]
log4net: curSizeRollBackups starts at [0]
log4net: Opening file for writing [C:\Windows\...UAT\SB3.log] append [True]
log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Converter [message] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: Converter [newline] Option [] Format [min=-1,max=2147483647,leftAlign=False]
log4net: log4net assembly [log4net, Version=1.2.11.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a]. Loaded from [...]. (.NET Runtime [2.0.50727.5448] on Microsoft Windows NT 6.1.7601 Service Pack 1)
log4net: defaultRepositoryType [log4net.Repository.Hierarchy.Hierarchy]
log4net: Creating repository for assembly [..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]
log4net: Assembly [..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null] Loaded From [...]
log4net: Assembly [..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null] does not have a RepositoryAttribute specified.
log4net: Assembly [..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null] using repository [log4net-default-repository] and repository type [log4net.Repository.Hierarchy.Hierarchy]
log4net: log4net assembly [log4net, Version=1.2.11.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a]. Loaded from [...]. (.NET Runtime [2.0.50727.5448] on Microsoft Windows NT 6.1.7601 Service Pack 1)
log4net: defaultRepositoryType [log4net.Repository.Hierarchy.Hierarchy]
log4net: Creating repository for assembly [..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]
log4net: Assembly [..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null] Loaded From [...]
log4net: Assembly [..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null] does not have a RepositoryAttribute specified.
log4net: Assembly [..., Version=1.0.0.0, Culture=neutral, PublicKeyToken=null] using repository [log4net-default-repository] and repository type [log4net.Repository.Hierarchy.Hierarchy]
log4nlog4net: Creating repository [log4net-default-repository] using type [log4net.Repository.Hierarchy.Hierarchy]
et: Creating repository [log4net-default-repository] using type [log4net.Repository.Hierarchy.Hierarchy]
log4net: Changing event source [sbsysrst] from log [SB3] to log [SB3-UAT]
log4net: Source [sbsysrst] is registered to log [SB3-UAT]
log4net: Changing event source [sbsysrst] from log [SB3] to log [SB3-UAT]
log4net: Source [sbsysrst] is registered to log [SB3-UAT]

Update 1

Looking at the log4net source code, the code largely boils down to the followimg,

// Inside ActivateOptions()
using (SecurityContext.Impersonate(this))
{
    sourceAlreadyExists = EventLog.SourceExists(m_applicationName);
    if (sourceAlreadyExists)
    {
        currentLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
    }
}

using (SecurityContext.Impersonate(this))
{
    if (sourceAlreadyExists && currentLogName != m_logName)
    {
        //
        // Re-register this to the current application if the user has changed
        // the application / logfile association
        //
        EventLog.DeleteEventSource(m_applicationName, m_machineName);
        CreateEventSource(m_applicationName, m_logName, m_machineName);

        registeredLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
    }
    else if (!sourceAlreadyExists)
    {
        CreateEventSource(m_applicationName, m_logName, m_machineName);

        registeredLogName = EventLog.LogNameFromSourceName(m_applicationName, m_machineName);
    }
}

// Inside Append(LoggingEvent loggingEvent)
using(SecurityContext.Impersonate(this))
{
    EventLog.WriteEntry(m_applicationName, eventTxt, entryType, eventID, category);
}

The crucial piece is EventLog.LogNameFromSourceName(m_applicationName, m_machineName); does return SB3-UAT as expected.

Update 2

I have written my own EventLogAppender that overrides the log4net functionality by using instance Eventlog objects that are set to the log and source name.

To my immense surprise that did not work either, so I am leaning increasingly towards a machine problem, or something to do with the SecurityContext.Impersonate() code in log4net .

Update 3

So this is not a log4net problem, nor even a dotnet problem. Below is code that uses the Windows API directly and it still fails. The fact that the source name was ever registered with the Application log is tripping things up.

I am looking at the appropriate WinAPIs to see if I can clear up this confusion. Certainly looking at registry keys this should not be a problem.

const string SourceName = "sbsysrst";
const string TextMessage = "Test";
const string MachineName = ".";

// This code is essentially the watered down version behind `EventLog`
// in dotnet.  There are two main WinAPI calls and just boiler code around
// passing parameters to them as expected.

var message = new[] { TextMessage };
SafeHandle eventLogHandle = null;
var numArray = new IntPtr[message.Length];
var gcHandleArray = new GCHandle[mesage.Length];
GCHandle gcHandle = GCHandle.Alloc(numArray, GCHandleType.Pinned);
try
{
    eventLogHandle = SafeEventLogWriteHandle
        .RegisterEventSource(MachineName, SourceName);
    for (int index = 0; index < message.Length; index++)
    {
        gcHandleArray[index] = GCHandle
            .Alloc(message[index], GCHandleType.Pinned);
        numArray[index] = gcHandleArray[index]
            .AddrOfPinnedObject();
    }

    if (!ReportEvent(
        eventLogHandle,
        0x0004,           // Information type
        0,                // Category
        0,                // EventID
        null,             // user id
        message.Length,   // size of text array
        0,                // size of raw data array
        new HandleRef(this, gcHandle.AddrOfPinnedObject()),
        new byte[0]))     // raw data (none)
    {
        throw new Win32Exception(Marshal.GetLastWin32Error());
    }

}
finally
{
    for (int index = 0; index < message.Length; ++index)
    {
        if (gcHandleArray[index].IsAllocated)
        {
            gcHandleArray[index].Free();
        }
    }

    gcHandle.Free();
    if (eventLogHandle != null)
    {
        eventLogHandle.Dispose();
    }
}

// RegisterEventSource cannot return an abstract object.
internal sealed class SafeEventLogWriteHandle :
    SafeHandleZeroOrMinusOneIsInvalid
{
    internal SafeEventLogWriteHandle() : base(true)
    {
    }

    [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    internal static extern SafeEventLogWriteHandle RegisterEventSource(
        string uncServerName, string sourceName);

    [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool DeregisterEventSource(IntPtr hEventLog);

    protected override bool ReleaseHandle()
    {
        return DeregisterEventSource(this.handle);
    }
}

The problem was down to the fact the source name was registered under a different EventLog in the past. Whilst the source registration has changed (in the registry), it appears like the EventLog service caches the registration. On rebooting the machine and attempting to log once again (you cannot restart the Eventlog service), the event entries went into the correct Eventlog.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM