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
-
02-07-2019 - |
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.
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;
}
}
}