Qual é a maneira mais confiável para criar um log de eventos personalizado e fonte de eventos durante a instalação de um serviço .NET

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

Pergunta

Estou tendo dificuldade confiável criando / remoção de fontes de eventos durante a instalação do meu .Net Windows Service.

Aqui está o código da minha 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 });

Os métodos de fachada referenciados apenas retornar as cordas para o nome do registo, serviço, etc.

Esse código funciona na maioria das vezes, mas, recentemente, após a instalação eu comecei a receber minhas entradas de log aparecendo no log do aplicativo em vez de log de costume. E os seguintes erros estão no log assim:

A descrição para Event ID (0) na origem (AutoCheckout) não pode ser encontrada. O computador local pode não ter as informações de registro ou DLL mensagem arquivos necessários para exibir mensagens de um computador remoto. Você pode ser capaz de usar o / auxsource = sinalizador para recuperar esta descrição; consulte a Ajuda e Suporte para obter mais detalhes.

Por alguma razão ele quer é não remover corretamente a fonte durante a desinstalação ou não é criá-lo durante a instalação.

Qualquer ajuda com as melhores práticas aqui é apreciado.

Obrigado!

Além disso, aqui está um exemplo de como eu estou escrevendo exceções ao log:

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

resposta Quanto de stephbu:. O caminho recomendado é um script de instalação e installutil, ou uma rotina de configuração do Windows

Eu estou usando uma configuração do projeto, que executa a instalação do serviço e conjuntos de backup do log. Se eu usar o installutil.exe ou o projeto de instalação do Windows Eu acredito que ambos chamada da mesma classe ProjectInstaller eu mostrar acima.

Eu vejo como o estado da minha máquina de teste poderia estar causando o erro se o registo não é realmente removido até que a reinicialização. Vou experimentar mais para ver se isso resolve o problema.

Editar: Estou interessado em uma certeza fogo maneira de registrar a fonte eo nome do log durante a instalação do serviço. Portanto, se o serviço tinha sido previamente instalado, ele iria remover a fonte, ou reutilizar a fonte durante instalações subseqüentes.

Eu ainda não tive a oportunidade de aprender WiX para tentar esse caminho.

Foi útil?

Solução

A melhor recomendação seria a não usar a configuração do projeto no Visual Studio. Ele tem limitações muito graves. Eu tive resultados muito bons com WiX

Outras dicas

A classe ServiceInstaller cria automaticamente um EventLogInstaller e coloca-lo dentro de sua própria coleção instaladores.

Tente este código:

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

Algumas coisas aqui

Criação de Logs e fontes de eventos em tempo real é muito mal visto. principalmente por causa dos direitos necessários para executar a ação -. você realmente não quer abençoar seus aplicativos com esse poder

Além disso, se você excluir um log de eventos ou fonte a entrada só é verdadeiramente excluído quando o servidor for reinicializado, para que possa entrar em estados estranhas se você excluir e entradas Recriar sem saltar da caixa. Há também um monte de regras não escritas sobre a nomeação de conflitos devido à forma como os metadados são armazenados no registro.

O caminho recomendado é um script de instalação e installutil, ou uma rotina de configuração do Windows.

Eu tenho que concordar com stephbu sobre os "estados estranho" que o log de eventos entra, eu tenho que correr para isso antes. Se eu fosse para adivinhar, algumas de suas dificuldades residem lá.

No entanto, a melhor maneira que eu conheço para fazer o registro de eventos na aplicação é na verdade com um TraceListener. Você pode configurá-los via app.config do serviço:

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

Há uma seção perto do meio da página que descreve como usar a propriedade EventLog para especificar o EventLog que você deseja gravar.

Espero que ajude.

Eu também seguiu sugestão O helb, exceto que eu basicamente usado as classes de designer padrão gerados (o padrão objetos "ServiceProcessInstaller1" e "ServiceInstaller1"). Eu decidi postar isso, pois é uma versão um pouco mais simples; e também porque eu estou trabalhando em VB e às vezes as pessoas gostam de ver o VB-forma.

As tartheode disse, você não deve modificar a classe ProjectInstaller gerado pelo designer no ProjectInstaller.Designer.vb arquivo, mas você pode modificar o código no ProjectInstaller.vb arquivo. Depois de criar um ProjectInstaller normal (utilizando a norma 'Adicionar Instalador' mecanismo), a única alteração foi feita I no Novo () da classe ProjectInstaller. Depois que o normal "InitializeComponent ()" chamada, I inserido este código:

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

Isso funcionou como esperado, em que o instalador fez não criar a origem de evento no registo de aplicações, mas sim criado no novo arquivo de log personalizado.

No entanto, eu tinha ferrado ao redor o suficiente para que eu tinha um pouco de uma confusão em um servidor. O problema com os logs personalizados é que, se o nome da fonte de evento existe associado à errado arquivo de log (por exemplo, a 'aplicação' log em vez de seu log personalizado novo), então o nome da fonte deve primeiro ser excluído ; em seguida, a máquina reinicializado; em seguida, a fonte pode ser criado com a associação para o registro correto. A Ajuda do Microsoft afirma claramente (na EventLogInstaller descrição da classe ):

O método de instalação gera uma exceção se a propriedade Fonte corresponde a um nome da fonte que está registrado para um log de eventos diferente no computador.

Por isso, eu também tenho essa função no meu serviço, que é chamado quando o serviço é iniciado:

   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 a função retorna False, o código de inicialização do serviço simplesmente interrompe o serviço. Esta função praticamente garante que você vai, eventualmente, obter o nome Origem do evento correto associado ao arquivo de log de eventos correta. Você pode ter que reiniciar a máquina uma vez; e você pode ter que tentar iniciar o serviço mais de uma vez.

Estou tendo os mesmos problemas. No meu caso, parece que instalador do Windows está adicionando a origem do evento que é do mesmo nome que o meu serviço automaticamente e este parece causar problemas. Você está usando o mesmo nome para o serviço de janelas e para a fonte de log? Tente mudá-lo de modo a que a sua fonte de log de eventos é chamado de forma diferente, em seguida, o nome do serviço.

Acabei de publicar uma solução para este Mais em fóruns do MSDN que era para que eu consegui contornar esta usando um projeto padrão MSI setup. O que fiz foi adicionar código para os eventos pré-instalar e comprometido o que significava que eu poderia manter tudo exatamente como 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);
  }
}

O código pode ser modificado um pouco mais para remover apenas as fontes de eventos que já não existem ou criá-los (embora o logname teriam de ser armazenados em algum lugar contra o instalador), mas desde que meu código de aplicativo na verdade, cria as fontes de eventos como ele é executado, em seguida, não há nenhum ponto para mim. Se já houver eventos, em seguida, já deve ser uma fonte de evento. Para garantir que eles são criados, você pode apenas iniciar automaticamente o serviço.

Eu experimentei algum comportamento estranho semelhante porque eu tentou registrar uma fonte de evento com o mesmo nome que o serviço estava começando.

Eu noto que você também tem o conjunto DisplayName para o mesmo nome que o seu evento Fonte.

Ao iniciar o serviço, nós descobrimos que o Windows registrado um "Serviço iniciado com êxito" entrada no registo de aplicações, com origem como o DisplayName. Este parecia ter o efeito de registrar Nome do aplicativo com o log de aplicação.

Na minha classe de evento logger I depois tentou registrar Nome do aplicativo como a origem com um registo de eventos diferentes, mas quando chegou a adição de novas entradas de log de eventos que sempre foi adicionados ao registo de aplicações.

Eu também tenho o "A descrição para Event ID (0) na fonte" mensagem várias vezes.

Como um trabalho em torno I simplesmente registrou a origem da mensagem com um nome ligeiramente diferente para o DisplayName, e trabalhou desde então. Seria vale a pena tentar isso se você não tiver já.

O problema vem de installutil que por registros padrão uma fonte de evento com seu nome serviços no "Application" EventLog. Eu ainda estou procurando uma maneira de pará-lo fazendo essa porcaria. Seria muito bom se alguém pudesse influenciar o comportamento de installutil: (

A seguir O helb sugestão resolvido o problema para mim. Matar o instalador log de eventos padrão, no ponto indicado no seu exemplo, impediu o instalador de registrar automaticamente o meu serviço Windows em log de eventos do aplicativo.

Far muito tempo foi perdido tentando resolver esse truque frustrante. Graças um milhão!

FWIW, eu não poderia modificar o código dentro do meu classe ProjectInstaller gerado pelo designer sem causar VS a carpa sobre os mods. I desfeito o código gerado pelo designer e manualmente entrou na classe.

Adicionando uma chave de registro vazio para HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Services \ EventLog \ Application \ MY_CUSTOM_SOURCE_NAME_HERE parece funcionar bem.

Uma maneira fácil de alterar o comportamento padrão (isto é, que o instalador do projeto cria uma fonte de log de eventos com o nome de seu serviço no log do aplicativo) é facilmente modificar o construtor do instalador do projeto da seguinte forma:

[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;
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top