Question

I'm trying to configure a Topshelf-based Windows service to log to a custom event log using Topshelf.Log4Net and log4net. This works fine if I run the application in command-line mode. When I try to install the service with BillsTestService.exe install, I get:

INFO  Topshelf v3.1.107.0, .NET Framework v4.0.30319.18052
DEBUG Attempting to install 'BillsTestService'
Running a transacted installation.
...
Service BillsTestService has been successfully installed.
Creating EventLog source BillsTestService in log Application...

An exception occurred during the Install phase.
System.ArgumentException: Source BillsTestService already exists on the local computer.
...
   at System.Diagnostics.EventLog.CreateEventSource(EventSourceCreationData sourceData)

I've tried running EventLog.DeleteEventSource("BillsTestService"); in LINQPad before installing; that succeeds, but a subsequent service install still fails.

My log4net appender configuration is:

<appender name="ErrorEventLogAppender" type="log4net.Appender.EventLogAppender" >
  <threshold value="ERROR" />
  <logName value="MyCompanyServices" />
  <applicationName value="BillsTestService" />
  <layout type="log4net.Layout.PatternLayout">
    <conversionPattern value="%-5level %logger - %message%newline" />
  </layout>
</appender>

What am I doing wrong?

The intent is to have multiple services log errors to the same log name (with different application names); the log would be created by Operations.

Was it helpful?

Solution

Part of the issue is that Topshelf automatically creates an eventlog source named after the service when you install. Since the log4net appender applicationName is also used as an eventlog source, that cannot be the actual application/service name. The source must be unique on the local computer. I added a "Source" suffix to the name in the log4net configuration.

The other part is that the service does not have rights to create the log. It can create a new source, but not a new log. One way to do this is in code (I used LINQPad):

EventLog.CreateEventSource("FOODEBUG", "MyCoSvc");

EventLog mylog = new EventLog("MyCoSvc");
mylog.Source = "FOODEBUG";

mylog.WriteEntry("This is a test.");

EventLog.DeleteEventSource("FOODEBUG");

I'm not positive if you actually have to write to the log to create it; after spending over two days on this, I'd rather be safe.

Also note that log names are limited to 8 characters; you can go longer, but the system only considers the first 8 characters as significant.

There's no need to move the log4net initialization as Chris Patterson suggested. Simply including

configurator.DependsOnEventLog();
configurator.UseLog4Net("MyService.exe.config");

in the HostFactory.Run delegate is sufficient. (I'm using Topshelf.Log4Net.)

Finally, I'm reasonably sure that the entire Windows event logging system is flaky. Event Viewer's refresh doesn't work in all cases, at one point my Application log entries disappeared, and I believe I've seen different results after a reboot.

OTHER TIPS

Move your log4net initialization to the ConstructUsing() configuration delegate for the service instead of specifying for use during install/uninstall which doesn't require the service class to be instantiated.

Or, only use the event log appender when the actual service is running, by either adding the appender outside of the config file or modifying the configuration to eliminate the event log appender unless an ERROR or FATAL event occurs.

My guess is the DEBUG/INFO level events are trying to log to the appender, and the source does not exist yet.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top