2008-09-22 50 views
25

在安裝.Net Windows服務期間,我無法可靠地創建/刪除事件源。在安裝.Net服務期間創建自定義事件日誌和事件源的最可靠方法是什麼

這裏是我的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 }); 

引用只返回字符串的日誌,服務的名稱門面方法等

此代碼工作的大部分時間,但最近安裝後,我開始讓我的日誌條目顯示在應用程序日誌中,而不是自定義日誌中。並且以下錯誤也出現在日誌中:

無法找到源(AutoCheckout)中事件ID(0)的說明。本地計算機可能沒有必要的註冊表信息或消息DLL文件來顯示來自遠程計算機的消息。您可以使用/ AUXSOURCE =標誌來檢索此說明;詳細信息請參閱幫助和支持。

由於某種原因,它不是在卸載過程中沒有正確刪除源代碼,或者在安裝過程中沒有創建它。

任何有關這裏的最佳做法的幫助表示讚賞。

謝謝!

另外,這裏是我如何寫例外日誌的一個示例:

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

關於stephbu的回答是:推薦路徑是安裝腳本和installutil,或Windows安裝程序例程。

我正在使用安裝項目,它執行服務的安裝並設置日誌。無論我使用installutil.exe還是Windows安裝項目,我都相信他們都會調用上面顯示的同一個ProjectInstaller類。

我看到如果日誌在重新啓動之前沒有真正刪除,我的測試機器的狀態會如何導致錯誤。我會試驗更多,看看是否解決了這個問題。

編輯: 我感興趣的是在安裝服務期間註冊源和日誌名稱的確定的方法。因此,如果該服務先前已安裝,則會在隨後的安裝過程中刪除源代碼或重新使用源代碼。

我還沒有機會學習WiX來嘗試這條路線。

+0

供參考:我仍然在尋找一個可靠的解決方案來解決這個問題。 – 2008-09-23 17:29:37

+0

Jason, 而不是刪除EventLogSource(如果存在)爲什麼不僅在日誌源和日誌不存在時執行安裝? 我還沒有嘗試過,因爲我發現你的問題是爲了迴應我自己的搜索。我有一個簡單的服務,我正在安裝一個單獨的服務安裝項目。 最好, 傑森 – Tinidian 2009-12-28 08:17:09

回答

5

最好的建議是不使用Visual Studio中的安裝項目。它有非常嚴格的限制。 我有很好的結果WiX

+0

謝謝阿爾貝我現在正在下載它,明天就試試吧! – 2008-10-01 22:51:16

7

這裏幾件事情

動態創建事件日誌和源是相當皺眉。主要是因爲執行該操作所需的權利 - 您並不真正想用這種權力來保護您的應用程序。如果刪除事件日誌或源項

而且只有忠實地刪除服務器重新啓動時,這樣你就可以進入狀態怪異,如果你刪除並重新創建條目沒有彈跳框。由於元數據存儲在註冊表中的方式,還有一堆關於命名衝突的不成文規則。

推薦的路徑是安裝程序腳本和installutil或Windows安裝例程。

+0

重啓還沒有解決問題,我已經使用InstallUtil和Windows安裝程序安裝程序,都沒有一個正確創建日誌源... – 2008-09-22 18:09:51

+1

事件源不是在飛行中創建,它們是在安裝過程中創建,作爲管理員執行。 – 2008-10-04 20:31:50

+1

這不是即時的 - 它是安裝程序的一部分,安裝程序應該以管理員身份運行。不過,同意不刪除。 – 2011-02-01 20:38:56

2

我不得不同意stephbu關於事件日誌進入的「奇怪狀態」,我之前遇到過。如果我猜測,你的一些困難在於此。

但是,我知道在應用程序中執行事件日誌記錄的最佳方式實際上是使用TraceListener。您可以通過服務的app.config配置它們:

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

有介紹如何使用事件日誌屬性來指定要寫入事件日誌該頁面中間附近的一個部分。

希望有所幫助。

0

我遇到了一些類似的怪異行爲,因爲我試圖註冊一個與我開始的服務具有相同名稱的事件源。

我注意到你也有DisplayName設置爲你的事件源相同的名稱。

在啓動服務後,我們發現Windows在應用程序日誌中記錄了「服務已成功啓動」條目,其中源爲DisplayName。這似乎具有在應用程序日誌中註冊應用程序名稱的效果。

在我的事件記錄器類後來我試圖註冊應用程序名稱與不同的事件日誌源,但是當它來增加新的事件日誌條目,他們總是得到添加到應用程序日誌。

我還多次獲得「源中的事件ID(0)的描述」消息。

作爲解決方法,我只是將註冊消息源的名稱與DisplayName略有不同,並且從此開始工作。如果你還沒有,那麼值得一試。

0

我有同樣的問題。在我看來,Windows安裝程序似乎是自動添加與我的服務同名的事件源,這似乎會導致問題。您是否使用Windows服務和日誌源的相同名稱?嘗試改變它,以便您的事件日誌源被稱爲不同,然後服務的名稱。

0

問題來自installutil,默認情況下,它在「應用程序」事件日誌中註冊一個帶有服務名稱的事件源。我仍在尋找一種方法來阻止它做這種廢話。如果可以影響installutil的行爲,這將非常好:(

25

ServiceInstaller類會自動創建一個EventLogInstaller並將其放入其自己的Installers集合中。

試試這個代碼:

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 
}); 
0

HELB的建議解決了這個問題對我來說。在他的示例中指出的那樣,終止默認事件日誌安裝程序會阻止安裝程序在應用程序事件日誌下自動註冊我的Windows服務。

試圖解決這個令人沮喪的怪癖失去了太多的時間。太感謝了!

FWIW,我無法修改我的設計器生成的ProjectInstaller類中的代碼,而不會導致VS鯉魚關於mods。我放棄了設計師生成的代碼並手動輸入了課程。

2

我也跟着HELB的建議,除了我基本上使用標準設計器生成的類(默認對象「ServiceProcessInstaller1」和「ServiceInstaller1」)。我決定發佈這個,因爲它是一個稍微簡單的版本;也因爲我在VB工作,有時候人們喜歡看VB方式。

由於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) 

這和預期一樣,在安裝程序沒有在應用程序日誌中創建事件源,但在新的而創建自定義日誌文件。

但是,我搞砸了,我有一個服務器上有點混亂。自定義日誌的問題在於,如果事件源名稱與錯誤的日誌文件(例如「應用程序」日誌,而不是新的自定義日誌)相關聯,則必須先刪除源名稱;然後機器重新啓動;那麼可以創建源並關聯到正確的日誌。微軟幫助(在EventLogInstaller class description)中明確規定:如果源屬性相匹配是一個註冊爲計算機上的 不同的事件日誌 源名稱

安裝方法拋出一個異常 。

因此,我也有這個功能在我的服務,在服務啓動時調用:

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 

如果函數返回False,該服務啓動代碼簡單地停止服務。該功能非常確保您最終將獲得與正確的事件日誌文件關聯的正確的事件源名稱。您可能必須重新啓動機器一次;你可能不得不嘗試多次啓動服務。

1

我剛剛在MSDN論壇上發佈了一個解決方案,這是我設法使用標準安裝MSI項目解決這個問題。我所做的就是將代碼添加到這意味着我能保持一切,正是因爲它是預安裝,並承諾事項:

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); 
    } 
} 

的代碼可以進一步修改了一下,只除去事件源沒有已經存在或創建它們(儘管日誌名稱需要存儲在安裝程序的某處),但由於我的應用程序代碼實際上是在創建事件源時運行的,所以對我來說沒有意義。如果已經有事件,那麼應該已經有一個事件源。爲了確保它們被創建,您可以自動啓動該服務。

0

將一個空的註冊表項添加到HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ services \ eventlog \ Application \ MY_CUSTOM_SOURCE_NAME_HERE似乎工作正常。

0

更改默認行爲(即,項目安裝程序使用應用程序日誌中的服務名稱創建事件日誌源)的一種簡單方法是輕鬆修改項目安裝程序的構造函數,如下所示:

[RunInstaller(true)] 
public partial class ProjectInstaller : System.Configuration.Install.Installer 
{ 
    public ProjectInstaller() 
    { 
     InitializeComponent(); 

     //Skip through all ServiceInstallers. 
     foreach(ServiceInstaller ThisInstaller in Installers.OfType<ServiceInstaller>()) 
     { 
      //Find the first default EventLogInstaller. 
      EventLogInstaller ThisLogInstaller = ThisInstaller.Installers.OfType<EventLogInstaller>().FirstOrDefault(); 
      if(ThisLogInstaller == null) 
       continue; 

      //Modify the used log from "Application" to the same name as the source name. This creates a source in the "Applications and Services log" which separates your service logs from the default application log. 
      ThisLogInstaller.Log = ThisLogInstaller.Source; 
     } 
    } 
} 
相關問題