C# 在安装 .Net 服务期间创建自定义事件日志和事件源的最可靠方法是什么

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/115488/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 14:26:05  来源:igfitidea点击:

What is the most reliable way to create a custom event log and event source during the installation of a .Net Service

提问by Jason Stevenson

I am having difficulty reliably creating / removing event sources during the installation of my .Net Windows Service.

在安装 .Net Windows 服务期间,我无法可靠地创建/删除事件源。

Here is the code from my ProjectInstaller class:

这是我的 ProjectInstaller 类中的代码:

// Create Process Installer
ServiceProcessInstaller spi = new ServiceProcessInstaller();
spi.Account = ServiceAccount.LocalSystem;

// Create Service
ServiceInstaller si = new ServiceInstaller();
si.ServiceName = Facade.GetServiceName();
si.Description = "Processes ...";
si.DisplayName = "Auto Checkout";
si.StartType = ServiceStartMode.Automatic;

// Remove Event Source if already there
if (EventLog.SourceExists("AutoCheckout"))
    EventLog.DeleteEventSource("AutoCheckout");

// Create Event Source and Event Log     
EventLogInstaller log = new EventLogInstaller();
log.Source = "AutoCheckout";
log.Log = "AutoCheckoutLog";

Installers.AddRange(new Installer[] { spi, si, log });

The facade methods referenced just return the strings for the name of the log, service, etc.

引用的外观方法只返回日志、服务等名称的字符串。

This code works most of the time, but recently after installing I started getting my log entries showing up in the Application Log instead of the custom log. And the following errors are in the log as well:

这段代码大部分时间都有效,但最近安装后我开始让我的日志条目显示在应用程序日志中,而不是自定义日志中。并且日志中还有以下错误:

The description for Event ID ( 0 ) in Source ( AutoCheckout ) cannot be found. The local computer may not have the necessary registry information or message DLL files to display messages from a remote computer. You may be able to use the /AUXSOURCE= flag to retrieve this description; see Help and Support for details.

找不到 Source ( AutoCheckout ) 中事件 ID ( 0 ) 的描述。本地计算机可能没有必要的注册表信息或消息 DLL 文件来显示来自远程计算机的消息。您可以使用 /AUXSOURCE= 标志来检索此描述;有关详细信息,请参阅帮助和支持。

For some reason it either isn't properly removing the source during the uninstall or it isn't creating it during the install.

出于某种原因,它要么在卸载过程中没有正确删除源,要么在安装过程中没有创建它。

Any help with best practices here is appreciated.

感谢您对此处最佳实践的任何帮助。

Thanks!

谢谢!

In addition, here is a sample of how I am writing exceptions to the log:

此外,这里是我如何将异常写入日志的示例:

// Write to Log
EventLog.WriteEntry(Facade.GetEventLogSource(), errorDetails, EventLogEntryType.Error, 99);

Regarding stephbu's answer:The recommended path is an installer script and installutil, or a Windows Setup routine.

关于 stephbu 的回答:推荐的路径是安装程序脚本和 installutil,或 Windows 安装程序。

I am using a Setup Project, which performs the installation of the service and sets up the log. Whether I use the installutil.exe or the windows setup project I believe they both call the same ProjectInstaller class I show above.

我正在使用安装项目,它执行服务的安装并设置日志。无论我使用 installutil.exe 还是 windows setup 项目,我相信它们都调用我上面显示的同一个 ProjectInstaller 类。

I see how the state of my test machine could be causing the error if the log isn't truly removed until rebooting. I will experiment more to see if that solves the issue.

如果在重新启动之前没有真正删除日志,我会看到我的测试机器的状态如何导致错误。我将进行更多试验,看看是否能解决问题。

Edit:I'm interested in a sure fire way to register the source and the log name during the installation of the service. So if the service had previously been installed, it would remove the source, or reuse the source during subsequent installations.

编辑:我对在安装服务期间注册源和日志名称的可靠方法感兴趣。因此,如果先前已安装该服务,它将删除源,或在后续安装期间重新使用源。

I haven't yet had an opportunity to learn WiX to try that route.

我还没有机会学习 WiX 来尝试这条路线。

采纳答案by Albert

The best recommendation would be to not use the Setup Project in Visual Studio. It has very severe limitations. I had very good results with WiX

最好的建议是不要在 Visual Studio 中使用安装项目。它有非常严重的局限性。我用WiX取得了非常好的结果

回答by stephbu

Couple of things here

这里有几件事

Creating Event Logs and Sources on the fly is pretty frowned upon. primarily because of the rights required to perform the action - you don't really want to bless your applications with that power.

动态创建事件日志和源是非常不受欢迎的。主要是因为执行操作所需的权限 - 您并不真的想用这种权力来祝福您的应用程序。

Moreover if you delete an event log or source the entry is only truelydeleted when the server reboots, so you can get into wierd states if you delete and recreate entries without bouncing the box. There are also a bunch of unwritten rules about naming conflicts due to the way the metadata is stored in the registry.

此外,如果您删除事件日志或源,则只有在服务器重新启动时才会真正删除该条目,因此如果您删除并重新创建条目而不弹回该框,您可能会进入奇怪的状态。由于元数据在注册表中的存储方式,还有一堆关于命名冲突的不成文规则。

The recommended path is an installer script and installutil, or a Windows Setup routine.

推荐的路径是安装程序脚本和 installutil,或 Windows 安装例程。

回答by Zachary Yates

I have to agree with stephbu about the "weird states" that the event log gets into, I've run into that before. If I were to guess, some of your difficulties lie there.

我必须同意 stephbu 关于事件日志进入的“奇怪状态”,我以前遇到过。如果我猜的话,你的一些困难就在那里。

However, the best way that I know of to do event logging in the application is actually with a TraceListener. You can configure them via the service's app.config:

但是,我所知道的在应用程序中进行事件记录的最佳方法实际上是使用 TraceListener。您可以通过服务的 app.config 配置它们:

http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlogtracelistener.aspx

http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlogtracelistener.aspx

There is a section near the middle of that page that describes how to use the EventLog property to specify the EventLog you wish to write to.

该页面中间附近有一个部分,描述了如何使用 EventLog 属性来指定要写入的 EventLog。

Hope that helps.

希望有帮助。

回答by John Sibly

I experienced some similar weird behaviour because I tried to register an event source with the same name as the service I was starting.

我遇到了一些类似的奇怪行为,因为我试图注册一个与我启动的服务同名的事件源。

I notice that you also have the DisplayName set to the same name as your event Source.

我注意到您还将 DisplayName 设置为与您的事件源相同的名称。

On starting the service up, we found that Windows logged a "Service started successfully" entry in the Application log, with source as the DisplayName. This seemed to have the effect of registering Application Namewith the application log.

在启动服务时,我们发现 Windows 在应用程序日志中记录了“服务已成功启动”条目,源为 DisplayName。这似乎具有在应用程序日志中注册应用程序名称的效果。

In my event logger class I later tried to register Application Nameas the source with a different event log, but when it came to adding new event log entries they always got added to the Application log.

在我的事件记录器类中,我后来尝试将应用程序名称注册为具有不同事件日志的源,但是在添加新的事件日志条目时,它们总是被添加到应用程序日志中。

I also got the "The description for Event ID ( 0 ) in Source" message several times.

我还多次收到“源中事件 ID ( 0 ) 的描述”消息。

As a work around I simply registered the message source with a slightly different name to the DisplayName, and it's worked ever since. It would be worth trying this if you haven't already.

作为一种解决方法,我只是使用与 DisplayName 稍有不同的名称注册了消息源,此后一直有效。如果你还没有尝试这个,那将是值得的。

回答by John Sibly

I am having the same problems. In my case it seems that Windows installer is adding the event source which is of the same name as my service automatically and this seems to cause problems. Are you using the same name for the windows service and for the log source? Try changing it so that your event log source is called differently then the name of the service.

我有同样的问题。就我而言,Windows 安装程序似乎正在自动添加与我的服务同名的事件源,这似乎会导致问题。您是否对 Windows 服务和日志源使用相同的名称?尝试更改它,以便您的事件日志源的调用方式与服务名称不同。

回答by nojetlag

The problem comes from installutil which by default registers an event source with your services name in the "Application" EventLog. I'm still looking for a way to stop it doing this crap. It would be really nice if one could influence the behaviour of installutil :(

问题来自 installutil,它默认在“应用程序”事件日志中使用您的服务名称注册事件源。我仍在寻找一种方法来阻止它做这种废话。如果可以影响 installutil 的行为,那就太好了:(

回答by helb

The ServiceInstallerclass automatically creates an EventLogInstallerand puts it inside its own Installers collection.

ServiceInstaller级自动创建一个EventLogInstaller并把它自己的安装程序集合中。

Try this code:

试试这个代码:

ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
serviceProcessInstaller.Password = null;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;

// serviceInstaller
ServiceInstaller serviceInstaller = new ServiceInstaller();
serviceInstaller.ServiceName = "MyService";
serviceInstaller.DisplayName = "My Service";
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Description = "My Service Description";
// kill the default event log installer
serviceInstaller.Installers.Clear(); 

// Create Event Source and Event Log     
EventLogInstaller logInstaller = new EventLogInstaller();
logInstaller.Source = "MyService"; // use same as ServiceName
logInstaller.Log = "MyLog";

// Add all installers
this.Installers.AddRange(new Installer[] {
   serviceProcessInstaller, serviceInstaller, logInstaller
});

回答by helb

Following helb'ssuggestion resolved the problem for me. Killing the default event log installer, at the point indicated in his example, prevented the installer from automatically registering my Windows Service under the Application Event log.

按照helb的建议为我解决了这个问题。在他的示例中指出的点处,杀死默认事件日志安装程序会阻止安装程序在应用程序事件日志下自动注册我的 Windows 服务。

Far too much time was lost attempting to resolve this frustrating quirk. Thanks a million!

试图解决这个令人沮丧的怪癖浪费了太多时间。太感谢了!

FWIW, I could not modify the code within my designer-generated ProjectInstaller class without causing VS to carp about the mods. I scrapped the designer-generated code and manually entered the class.

FWIW,我无法修改我的设计器生成的 ProjectInstaller 类中的代码,而不会导致 VS 对 mod 嗤之以鼻。我废弃了设计器生成的代码并手动输入了课程。

回答by Tod Flak

I also followed helb'ssuggestion, except that I basically used the standard designer generated classes (the default objects "ServiceProcessInstaller1" and "ServiceInstaller1"). I decided to post this since it is a slightly simpler version; and also because I am working in VB and people sometimes like to see the VB-way.

我也遵循了helb 的建议,只是我基本上使用了标准设计器生成的类(默认对象“ServiceProcessInstaller1”和“ServiceInstaller1”)。我决定发布这个,因为它是一个稍微简单的版本;也因为我在 VB 中工作,人们有时喜欢看到 VB 的方式。

As tartheodesaid, you should not modify the designer-generated ProjectInstaller class in the ProjectInstaller.Designer.vbfile, but you canmodify the code in the ProjectInstaller.vbfile. After creating a normal ProjectInstaller (using the standard 'Add Installer' mechanism), the only change I made was in the New() of the ProjectInstaller class. After the normal "InitializeComponent()" call, I inserted this code:

正如tartheode所说,您不应该修改ProjectInstaller.Designer.vb文件中设计器生成的ProjectInstaller类,但可以修改ProjectInstaller.vb文件中的代码 。创建一个普通的 ProjectInstaller(使用标准的“添加安装程序”机制)后,我所做的唯一更改是在 ProjectInstaller 类的 New() 中。在正常的“InitializeComponent()”调用之后,我插入了以下代码:

  ' remove the default event log installer 
  Me.ServiceInstaller1.Installers.Clear()

  ' Create an EventLogInstaller, and set the Event Source and Event Log      
  Dim logInstaller As New EventLogInstaller
  logInstaller.Source = "MyServiceName"
  logInstaller.Log = "MyCustomEventLogName"

  ' Add the event log installer
  Me.ServiceInstaller1.Installers.Add(logInstaller)

This worked as expected, in that the installer did notcreate the Event Source in the Application log, but rather created in the new custom log file.

这种预期一样,则在安装程序并没有在应用程序日志中创建事件源,但在新的自定义日志文件而创建的。

However, I had screwed around enough that I had a bit of a mess on one server. The problem with the custom logs is that if the event source name exists associated to the wronglog file (e.g. the 'Application' log instead of your new custom log), then the source name must first be deleted; then the machine rebooted; then the source can be created with association to the correct log. The Microsoft Help clearly states (in the EventLogInstaller class description):

但是,我已经搞砸了,以至于我在一台服务器上有点混乱。自定义日志的问题在于,如果存在与错误日志文件相关联的事件源名称(例如“应用程序”日志而不是新的自定义日志),则必须首先删除源名称;然后机器重新启动;然后可以创建与正确日志相关联的源。Microsoft 帮助明确说明(在EventLogInstaller 类说明中):

The Install method throws an exception if the Source property matches a source name that is registered for a different event log on the computer.

如果 Source 属性与为计算机上不同事件日志注册的源名称匹配,则 Install 方法将引发异常。

Therefore, I also have this function in my service, which is called when the service starts:

因此,我的服务中也有这个函数,在服务启动时调用:

   Private Function EventLogSourceNameExists() As Boolean
      'ensures that the EventSource name exists, and that it is associated to the correct Log 

      Dim EventLog_SourceName As String = Utility.RetrieveAppSetting("EventLog_SourceName")
      Dim EventLog_LogName As String = Utility.RetrieveAppSetting("EventLog_LogName")

      Dim SourceExists As Boolean = EventLog.SourceExists(EventLog_SourceName)
      If Not SourceExists Then
         ' Create the source, if it does not already exist.
         ' An event log source should not be created and immediately used.
         ' There is a latency time to enable the source, it should be created
         ' prior to executing the application that uses the source.
         'So pass back a False to cause the service to terminate.  User will have 
         'to re-start the application to make it work.  This ought to happen only once on the 
         'machine on which the service is newly installed

         EventLog.CreateEventSource(EventLog_SourceName, EventLog_LogName)  'create as a source for the SMRT event log
      Else
         'make sure the source is associated with the log file that we want
         Dim el As New EventLog
         el.Source = EventLog_SourceName
         If el.Log <> EventLog_LogName Then
            el.WriteEntry(String.Format("About to delete this source '{0}' from this log '{1}'.  You may have to kill the service using Task Manageer.  Then please reboot the computer; then restart the service two times more to ensure that this event source is created in the log {2}.", _
            EventLog_SourceName, el.Log, EventLog_LogName))

            EventLog.DeleteEventSource(EventLog_SourceName)
            SourceExists = False  'force a close of service
         End If
      End If
      Return SourceExists
   End Function

If the function returns False, the service startup code simply stops the service. This function pretty much ensures that you will eventually get the correct Event Source name associated to the correct Event Log file. You may have to reboot the machine once; and you may have to try starting the service more than once.

如果函数返回 False,则服务启动代码只是停止服务。此功能可确保您最终获得与正确事件日志文件关联的正确事件源名称。您可能需要重新启动机器一次;并且您可能需要多次尝试启动该服务。

回答by netniV

I just posted a solution to this over on MSDN forums which was to that I managed to get around this using a standard setup MSI project. What I did was to add code to the PreInstall and Committed events which meant I could keep everything else exactly as it was:

我刚刚在 MSDN 论坛上发布了一个解决方案,我设法使用标准设置 MSI 项目解决了这个问题。我所做的是向 PreInstall 和 Committed 事件添加代码,这意味着我可以保持其他所有内容完全一样:

SortedList<string, string> eventSources = new SortedList<string, string>();
private void serviceProcessInstaller_BeforeInstall(object sender, InstallEventArgs e)
{
  RemoveServiceEventLogs();
}

private void RemoveServiceEventLogs()
{
  foreach (Installer installer in this.Installers)
    if (installer is ServiceInstaller)
    {
      ServiceInstaller serviceInstaller = installer as ServiceInstaller;
      if (EventLog.SourceExists(serviceInstaller.ServiceName))
      {
        eventSources.Add(serviceInstaller.ServiceName, EventLog.LogNameFromSourceName(serviceInstaller.ServiceName, Environment.MachineName));
        EventLog.DeleteEventSource(serviceInstaller.ServiceName);
      }
    }
}

private void serviceProcessInstaller_Committed(object sender, InstallEventArgs e)
{
  RemoveServiceEventLogs();
  foreach (KeyValuePair<string, string> eventSource in eventSources)
  {
    if (EventLog.SourceExists(eventSource.Key))
      EventLog.DeleteEventSource(eventSource.Key);

    EventLog.CreateEventSource(eventSource.Key, eventSource.Value);
  }
}

The code could be modified a bit further to only remove the event sources that didn't already exist or create them (though the logname would need to be stored somewhere against the installer) but since my application code actually creates the event sources as it runs then there's no point for me. If there are already events then there should already be an event source. To ensure that they are created, you can just automatically start the service.

可以进一步修改代码以仅删除不存在的事件源或创建它们(尽管日志名需要存储在安装程序的某处)但因为我的应用程序代码实际上在运行时创建了事件源那么我就没有意义了。如果已经有事件,那么应该已经有一个事件源。为了确保它们被创建,你可以自动启动服务。