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

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

Вопрос

У меня возникли трудности с надежным созданием/удалением источников событий во время установки службы .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) в источнике (AutoCheckout).На локальном компьютере может отсутствовать необходимая информация реестра или файлы DLL сообщений для отображения сообщений с удаленного компьютера.Вы можете использовать флаг /AUXSOURCE= для получения этого описания;подробности см. в разделе «Справка и поддержка».

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

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

Спасибо!

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

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

Что касается ответа Stephbu: Рекомендуемый путь — сценарий установки и утилита установки или программа установки Windows.

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

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

Редактировать:Меня интересует надежный способ зарегистрировать источник и имя журнала во время установки службы.Таким образом, если служба была установлена ​​ранее, она удалит источник или повторно использует его во время последующих установок.

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

Это было полезно?

Решение

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

Другие советы

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

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

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

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

Рекомендуемый путь — сценарий установки и утилита установки или программа установки Windows.

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

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

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

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

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

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

Как тартеод сказал, что вам не следует изменять созданный дизайнером класс 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):

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

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

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

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

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

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

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

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

При запуске службы мы обнаружили, что Windows зарегистрировала запись «Служба запущена успешно» в журнале приложений с источником в качестве отображаемого имени.Похоже, это имело эффект регистрации Имя приложения с журналом приложения.

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

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

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

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

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

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

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

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

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

[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;
        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top