Подтвердить что ты не робот

Каков наиболее надежный способ создания настраиваемого журнала событий и источника событий во время установки службы .NET.

У меня возникают трудности с надежным созданием/удалением источников событий во время установки моей .Net-службы Windows.

Вот код из моего класса 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 });

Приведенные методы фасада возвращают строки для имени журнала, службы и т.д.

Этот код работает большую часть времени, но недавно после установки я начал получать свои записи в журнале приложений, а не в пользовательском журнале. В журнале также имеются следующие ошибки:

Невозможно найти описание для идентификатора события (0) в источнике (автоопределение). На локальном компьютере могут не отображаться необходимые данные реестра или файлы DLL сообщений, чтобы отображать сообщения с удаленного компьютера. Вы можете использовать флаг /AUXSOURCE = для получения этого описания; см. "Справка и поддержка".

По какой-то причине он либо неправильно удаляет источник во время удаления, либо не создает его во время установки.

Любая помощь с лучшими практиками здесь ценится.

Спасибо!

Кроме того, вот пример того, как я пишу исключения из журнала:

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

Относительно ответа stephbu: Рекомендуемый путь - это установщик script и installutil, или программа установки Windows.

Я использую проект установки, который выполняет установку службы и настраивает журнал. Я использую installutil.exe или проект установки Windows, я считаю, что оба они называют тот же самый класс ProjectInstaller, что и выше.

Я вижу, как состояние моего тестового компьютера может вызвать ошибку, если журнал не удаляется до перезагрузки. Я буду экспериментировать больше, чтобы понять, устраняет ли это проблему.

Edit: Я заинтересован в надежном способе регистрации источника и имени журнала во время установки службы. Поэтому, если служба ранее была установлена, она удаляла источник или повторно использовала источник во время последующих установок.

У меня еще не было возможности узнать WiX, чтобы попробовать этот маршрут.

4b9b3361

Ответ 1

Лучшей рекомендацией было бы не использовать проект установки в Visual Studio. Он имеет очень серьезные ограничения. У меня были очень хорошие результаты с WiX

Ответ 2

Класс ServiceInstaller автоматически создает EventLogInstaller и помещает его в собственную коллекцию инсталляторов.

Попробуйте этот код:

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

Ответ 3

Пара вещей здесь

Создание журналов событий и источников на лету довольно неодобрительно. прежде всего из-за прав, необходимых для выполнения действия - вы действительно не хотите благословлять свои приложения этой силой.

Кроме того, если вы удаляете журнал событий или источник, запись только правдоподобно удаляется при перезагрузке сервера, поэтому вы можете попасть в более странные состояния, если вы удаляете и воссоздаете записи без отскока окна. Существует также множество неписаных правил об именовании конфликтов из-за того, как метаданные хранятся в реестре.

Рекомендуемый путь - это установщик script и installutil, или программа установки Windows.

Ответ 4

Я должен согласиться с stephbu о "странных состояниях", в которые входит журнал событий, я столкнулся с этим раньше. Если бы я догадался, некоторые из ваших трудностей лежат там.

Тем не менее, лучший способ, который я знаю для ведения журнала событий в приложении, - это фактически с TraceListener. Вы можете настроить их через службу app.config:

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

В середине этой страницы есть раздел, в котором описывается, как использовать свойство EventLog, чтобы указать EventLog, который вы хотите записать.

Надеюсь, что это поможет.

Ответ 5

Я также воспользовался предложением helb, за исключением того, что в основном я использовал стандартные классы, сгенерированные дизайнером (объекты по умолчанию "ServiceProcessInstaller1" и "ServiceInstaller1" ). Я решил опубликовать это, поскольку это немного более простая версия; а также потому, что я работаю в VB, и людям иногда нравится видеть VB-путь.

Как сказал tartheode, вы не должны модифицировать созданный конструктором класс ProjectInstaller в файле ProjectInstaller.Designer.vb, но вы можете изменить код в ProjectInstaller.vb. > файл. После создания обычного ProjectInstaller (с использованием стандартного механизма "Добавить установщик" ) единственное изменение, которое я сделал, было в классе New() класса ProjectInstaller. После обычного вызова "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)

Это работало так, как ожидалось, в том, что установщик сделал не создание источника событий в журнале приложений, а скорее создан в новом пользовательском файле журнала.

Тем не менее, я был достаточно накручен, что у меня было немного беспорядка на одном сервере. Проблема с настраиваемыми журналами заключается в том, что если имя источника события существует, связанное с файлом журнала неправильный (например, журнал "Приложение" вместо нового настраиваемого журнала), тогда исходное имя должно быть сначала удалено; затем машина перезагрузилась; то источник может быть создан с привязкой к правильному журналу. В справке Microsoft четко указано (в описание класса EventLogInstaller):

Метод Install создает исключение если свойство Source соответствует имя источника, зарегистрированное для другой журнал событий на компьютере.

Поэтому у меня также есть эта функция в моей службе, которая вызывается при запуске службы:

   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, код запуска службы просто останавливает службу. Эта функция в значительной степени гарантирует, что вы в конечном итоге получите правильное имя источника события, связанное с правильным файлом журнала событий. Возможно, вам придется перезагрузить машину один раз; и вам, возможно, придется попробовать начать обслуживание более одного раза.

Ответ 6

Я только что опубликовал решение этой проблемы на форумах 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);
  }
}

Код может быть изменен немного дальше, чтобы удалить только источники событий, которые еще не существовали или не создавали их (хотя имя журнала должно храниться где-то напротив установщика), но поскольку код приложения фактически создает источники событий поскольку это бежит, тогда нет никакого смысла для меня. Если уже есть события, то уже должен быть источник события. Чтобы убедиться, что они созданы, вы можете просто автоматически запустить службу.

Ответ 7

Я испытал некоторое подобное странное поведение, потому что я пытался зарегистрировать источник событий с тем же именем, что и служба, которую я запускал.

Я заметил, что у вас также есть DisplayName с тем же именем, что и источник событий.

При запуске службы мы обнаружили, что Windows зарегистрировала запись "Служба успешно запущена" в журнале приложений, а источник - как DisplayName. Это, похоже, привело к регистрации имени приложения в журнале приложений.

В моем случае класс регистратора я позже попытался зарегистрировать имя приложения в качестве источника с другим журналом событий, но когда дело дошло до добавления новых записей журнала событий, они всегда добавлялись в журнал приложений.

Я также получил сообщение "Описание для события ID (0) в источнике" несколько раз.

Как работа, я просто зарегистрировал источник сообщений с немного другим именем в DisplayName, и он работал с тех пор. Было бы полезно попробовать это, если вы еще этого не сделали.

Ответ 8

У меня такие же проблемы. В моем случае кажется, что установщик Windows добавляет источник событий, который имеет то же имя, что и моя служба, и это, похоже, вызывает проблемы. Вы используете одно и то же имя для службы Windows и для источника журнала? Попробуйте изменить его, чтобы источник журнала событий вызывается по-разному, а затем имя службы.

Ответ 9

Проблема возникает из installutil, которая по умолчанию регистрирует источник событий с вашим именем службы в EventLog. Я все еще ищу способ остановить это, делая это дерьмо. Было бы очень приятно, если бы можно было повлиять на поведение installutil: (

Ответ 10

Следующее предложение helb's разрешило проблему для меня. Убивание установщика журнала событий по умолчанию в точке, указанной в его примере, не позволило установщику автоматически регистрировать мою службу Windows в журнале событий приложения.

Слишком много времени было потеряно, пытаясь решить эту расстроенную причуду. Спасибо миллион!

FWIW, я не смог изменить код в моем классе ProjectInstaller, создаваемом разработчиком, не вызывая VS для карпов о моде. Я отменил созданный конструктором код и вручную ввел класс.

Ответ 11

Добавление пустого раздела реестра в HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Application\MY_CUSTOM_SOURCE_NAME_HERE, кажется, работает нормально.

Ответ 12

Простой способ изменить поведение по умолчанию (то есть, что установщик проекта создает источник журнала событий с именем вашей службы в журнале приложений) заключается в том, чтобы легко изменить конструктор установщика проекта следующим образом:

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