Qual è il modo più affidabile per creare un registro eventi e un'origine eventi personalizzati durante l'installazione di un servizio .Net

StackOverflow https://stackoverflow.com/questions/115488

Domanda

Ho difficoltà a creare / rimuovere in modo affidabile le origini degli eventi durante l'installazione del mio servizio .Net Windows.

Ecco il codice della mia classe 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 });

I metodi di facciata a cui si fa riferimento restituiscono solo le stringhe per il nome del registro, del servizio, ecc.

Questo codice funziona la maggior parte delle volte, ma recentemente dopo l'installazione ho iniziato a visualizzare le voci del mio registro nel registro dell'applicazione anziché nel registro personalizzato. E nel registro sono presenti anche i seguenti errori:

  

Impossibile trovare la descrizione per ID evento (0) in Origine (AutoCheckout). Il computer locale potrebbe non disporre delle informazioni di registro necessarie o dei file DLL dei messaggi per visualizzare i messaggi da un computer remoto. Potresti essere in grado di usare il flag / AUXSOURCE = per recuperare questa descrizione; consultare la Guida e supporto per i dettagli.

Per qualche motivo o non rimuove correttamente l'origine durante la disinstallazione o non la crea durante l'installazione.

Qualsiasi aiuto con le migliori pratiche qui è apprezzato.

Grazie!

Inoltre, ecco un esempio di come scrivo le eccezioni nel registro:

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

Per quanto riguarda la risposta di stephbu: il percorso consigliato è uno script di installazione e installutil o una routine di installazione di Windows.

Sto usando un progetto di installazione, che esegue l'installazione del servizio e imposta il registro. Sia che io usi installutil.exe o il progetto di installazione di Windows credo che entrambi chiamino la stessa classe ProjectInstaller che mostro sopra.

Vedo come lo stato della mia macchina di prova potrebbe causare l'errore se il registro non viene veramente rimosso fino al riavvio. Sperimenterò di più per vedere se questo risolve il problema.

Modifica Sono interessato a un modo sicuro per registrare l'origine e il nome del registro durante l'installazione del servizio. Pertanto, se il servizio fosse stato precedentemente installato, rimuoverebbe l'origine o riutilizzerebbe l'origine durante le installazioni successive.

Non ho ancora avuto l'opportunità di imparare WiX per provare quella rotta.

È stato utile?

Soluzione

La migliore raccomandazione sarebbe di non utilizzare il progetto di installazione in Visual Studio. Ha limitazioni molto gravi. Ho avuto ottimi risultati con WiX

Altri suggerimenti

La classe ServiceInstaller crea automaticamente un EventLogInstaller e lo inserisce nella propria raccolta Installer.

Prova questo codice:

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

Un paio di cose qui

La creazione di registri e fonti di eventi al volo è piuttosto disapprovata. principalmente a causa dei diritti richiesti per eseguire l'azione - non vuoi davvero benedire le tue applicazioni con quel potere.

Inoltre, se si elimina un registro eventi o un'origine, la voce viene veramente cancellata solo al riavvio del server, quindi è possibile entrare in stati strani se si eliminano e si ricreano le voci senza rimbalzare la casella. Ci sono anche un sacco di regole non scritte sui conflitti di denominazione dovuti al modo in cui i metadati sono archiviati nel registro.

Il percorso consigliato è uno script di installazione e installutil o una routine di installazione di Windows.

Devo essere d'accordo con stephbu riguardo agli "strani stati" in cui entra il registro eventi, l'ho già incontrato prima. Se dovessi indovinare, alcune delle tue difficoltà stanno lì.

Tuttavia, il modo migliore che conosco per eseguire la registrazione degli eventi nell'applicazione è in realtà con un TraceListener. Puoi configurarli tramite app.config del servizio:

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

Esiste una sezione al centro di quella pagina che descrive come utilizzare la proprietà EventLog per specificare l'EventLog in cui si desidera scrivere.

Spero che sia d'aiuto.

Ho anche seguito il suggerimento di helb , tranne per il fatto che ho sostanzialmente utilizzato le classi generate dal designer standard (gli oggetti predefiniti " ServiceProcessInstaller1 " e " ServiceInstaller1 "). Ho deciso di pubblicare questo dato che è una versione leggermente più semplice; e anche perché sto lavorando in VB e alle persone a volte piace vedere la VB-way.

Come diceva tartheode , non dovresti modificare la classe ProjectInstaller generata dal designer nel file ProjectInstaller.Designer.vb , ma puoi modificare il codice nel file ProjectInstaller.vb . Dopo aver creato un normale ProjectInstaller (utilizzando il meccanismo standard "Aggiungi programma di installazione"), l'unica modifica che ho apportato è stata nella New () della classe ProjectInstaller. Dopo il normale " InitializeComponent () " chiama, ho inserito questo codice:

  ' 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)

Funzionava come previsto, in quanto il programma di installazione non ha creato l'origine evento nel registro dell'applicazione, ma piuttosto creato nel nuovo file di registro personalizzato.

Tuttavia, mi ero fregato abbastanza da avere un po 'di confusione su un server. Il problema con i registri personalizzati è che se il nome della fonte dell'evento esiste associato al file di registro sbagliato (ad es. Il registro "Applicazione" anziché il nuovo registro personalizzato), il nome della fonte deve essere prima eliminato ; quindi la macchina si è riavviata; quindi l'origine può essere creata con l'associazione al registro corretto. La Guida di Microsoft indica chiaramente (nella EventLogInstaller class description ):

  

Il metodo di installazione genera un'eccezione   se la proprietà Source corrisponde a   nome della fonte registrato per a   registro eventi diverso sul computer.

Pertanto, ho anche questa funzione nel mio servizio, che viene chiamata all'avvio del servizio:

   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

Se la funzione restituisce False, il codice di avvio del servizio interrompe semplicemente il servizio. Questa funzione assicura praticamente che alla fine otterrai il nome della sorgente evento corretta associato al file registro eventi corretto. Potrebbe essere necessario riavviare il computer una volta; e potresti dover provare ad avviare il servizio più di una volta.

Sto riscontrando gli stessi problemi. Nel mio caso sembra che Windows Installer stia aggiungendo automaticamente l'origine evento che ha lo stesso nome del mio servizio e questo sembra causare problemi. Stai usando lo stesso nome per il servizio Windows e per l'origine del registro? Prova a cambiarlo in modo che l'origine del registro eventi venga chiamata in modo diverso rispetto al nome del servizio.

Ho appena pubblicato una soluzione su questo nei forum MSDN che è stato quello di essere riuscito a aggirare questo usando un progetto MSI di installazione standard. Quello che ho fatto è stato aggiungere codice agli eventi PreInstall e Committed, il che significava che avrei potuto mantenere tutto il resto esattamente com'era:

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

Il codice potrebbe essere modificato un po 'di più per rimuovere solo le fonti di eventi che non esistevano già o crearle (anche se il nome di registro avrebbe dovuto essere memorizzato da qualche parte contro il programma di installazione) ma dal momento che il mio codice dell'applicazione crea effettivamente le fonti di eventi mentre corre quindi non ha senso per me. Se ci sono già eventi, dovrebbe esserci già una fonte di eventi. Per assicurarti che vengano creati, puoi semplicemente avviare automaticamente il servizio.

Ho riscontrato un comportamento strano simile perché ho provato a registrare un'origine evento con lo stesso nome del servizio che stavo iniziando.

Ho notato che hai anche DisplayName impostato sullo stesso nome della tua sorgente evento.

All'avvio del servizio, abbiamo riscontrato che Windows ha registrato " Il servizio è stato avviato correttamente " voce nel registro dell'applicazione, con origine come DisplayName. Questo sembra avere l'effetto di registrare Nome applicazione con il registro dell'applicazione.

Nella mia classe di registrazione eventi ho successivamente cercato di registrare Nome applicazione come origine con un registro eventi diverso, ma quando si trattava di aggiungere nuove voci nel registro eventi, venivano sempre aggiunti al registro applicazioni.

Ho anche ottenuto la descrizione di "Event ID (0) in Source " messaggio più volte.

Per aggirare, ho semplicemente registrato l'origine del messaggio con un nome leggermente diverso da DisplayName, e ha funzionato da allora. Vale la pena provare questo se non lo hai già fatto.

Il problema proviene da installutil che, per impostazione predefinita, registra un'origine evento con il nome del tuo servizio nella sezione "Applicazione" Registro eventi. Sto ancora cercando un modo per impedirgli di fare questa merda. Sarebbe davvero bello se si potesse influenzare il comportamento di installutil :(

Seguendo il suggerimento di helb ho risolto il problema per me. L'uccisione del programma di installazione del registro eventi predefinito, nel punto indicato nel suo esempio, ha impedito al programma di installazione di registrare automaticamente il mio servizio Windows nel registro eventi dell'applicazione.

È stato perso troppo tempo nel tentativo di risolvere questa strana frustrazione. Grazie mille!

FWIW, non ho potuto modificare il codice all'interno della mia classe ProjectInstaller generata dal designer senza causare VS a carp le mod. Ho eliminato il codice generato dal designer e inserito manualmente la classe.

L'aggiunta di una chiave di registro vuota a HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ services \ eventlog \ Application \ MY_CUSTOM_SOURCE_NAME_HERE sembra funzionare correttamente.

Un modo semplice per modificare il comportamento predefinito (ovvero che il programma di installazione del progetto crea un'origine registro eventi con il nome del servizio nel registro dell'applicazione) è modificare facilmente il costruttore del programma di installazione del progetto come segue:

[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;
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top