ما هي الطريقة الأكثر موثوقية لإنشاء سجل أحداث مخصص ومصدر حدث أثناء تثبيت خدمة .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);

فيما يتعلق بإجابة ستيفبو: المسار الموصى به هو البرنامج النصي للمثبت وinstallutil، أو روتين إعداد Windows.

أنا أستخدم مشروع الإعداد، الذي يقوم بتثبيت الخدمة وإعداد السجل.سواء كنت أستخدم ملف installutil.exe أو مشروع إعداد Windows، فأعتقد أن كلاهما يستدعي نفس فئة ProjectInstaller التي أعرضها أعلاه.

أرى كيف يمكن أن تتسبب حالة جهاز الاختبار الخاص بي في حدوث الخطأ إذا لم تتم إزالة السجل بالفعل حتى إعادة التشغيل.سأجرب المزيد لمعرفة ما إذا كان ذلك سيحل المشكلة.

يحرر:أنا مهتم بطريقة مؤكدة لتسجيل المصدر واسم السجل أثناء تثبيت الخدمة.لذا، إذا تم تثبيت الخدمة مسبقًا، فستؤدي إلى إزالة المصدر أو إعادة استخدام المصدر أثناء عمليات التثبيت اللاحقة.

لم تتح لي الفرصة بعد لتعلم WiX لتجربة هذا الطريق.

هل كانت مفيدة؟

المحلول

أفضل توصية هي عدم استخدام مشروع الإعداد في Visual Studio.لديها قيود شديدة للغاية.لقد حصلت على نتائج جيدة جدًا مع ويكس

نصائح أخرى

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

بضعة أشياء هنا

يعد إنشاء سجلات الأحداث والمصادر أثناء التنقل أمرًا مستهجنًا جدًا.في المقام الأول بسبب الحقوق المطلوبة لتنفيذ الإجراء - فأنت لا تريد حقًا أن تمنح تطبيقاتك هذه القوة.

علاوة على ذلك، إذا قمت بحذف سجل أحداث أو مصدر، فسيتم إدخال الإدخال فقط حقا يتم حذفه عند إعادة تشغيل الخادم، حتى تتمكن من الدخول في حالات غريبة إذا قمت بحذف الإدخالات وإعادة إنشائها دون ارتداد الصندوق.هناك أيضًا مجموعة من القواعد غير المكتوبة حول تعارضات الأسماء نظرًا لطريقة تخزين البيانات التعريفية في السجل.

المسار الموصى به هو البرنامج النصي للمثبت وinstallutil، أو روتين إعداد Windows.

يجب أن أتفق مع ستيفبو بشأن "الحالات الغريبة" التي يدخل فيها سجل الأحداث، وقد واجهت ذلك من قبل.إذا كان لي أن أخمن، فإن بعض الصعوبات التي تواجهك تكمن هناك.

ومع ذلك، فإن أفضل طريقة أعرفها لتسجيل الأحداث في التطبيق هي في الواقع باستخدام TraceListener.يمكنك تكوينها عبر app.config الخاص بالخدمة:

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

يوجد قسم بالقرب من منتصف تلك الصفحة يصف كيفية استخدام خاصية سجل الأحداث لتحديد سجل الأحداث الذي ترغب في الكتابة إليه.

امل ان يساعد.

لقد تابعت أيضا مساعدة اقتراح، باستثناء أنني استخدمت بشكل أساسي الفئات التي أنشأها المصمم القياسي (الكائنات الافتراضية "ServiceProcessInstaller1" و"ServiceInstaller1").قررت نشر هذا لأنه إصدار أبسط قليلاً؛وأيضًا لأنني أعمل في VB ويحب الناس أحيانًا رؤية طريقة VB.

مثل com.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):

تطرح طريقة التثبيت استثناءً إذا كانت خاصية المصدر تتطابق مع اسم مصدر مسجل في سجل حدث مختلف على الكمبيوتر.

لذلك، لدي أيضًا هذه الوظيفة في خدمتي، والتي يتم استدعاؤها عند بدء الخدمة:

   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

إذا قامت الدالة بإرجاع خطأ، فإن رمز بدء تشغيل الخدمة يوقف الخدمة ببساطة.تضمن هذه الوظيفة إلى حد كبير أنك ستحصل في النهاية على اسم مصدر الحدث الصحيح المرتبط بملف سجل الأحداث الصحيح.قد تضطر إلى إعادة تشغيل الجهاز مرة واحدة؛وقد تضطر إلى محاولة بدء الخدمة أكثر من مرة.

أواجه نفس المشاكل.في حالتي، يبدو أن مثبت 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 قام بتسجيل إدخال "بدء الخدمة بنجاح" في سجل التطبيق، مع المصدر باسم DisplayName.يبدو أن هذا له تأثير التسجيل اسم التطبيق مع سجل التطبيق.

في فصل مسجل الأحداث الخاص بي حاولت التسجيل لاحقًا اسم التطبيق كمصدر بسجل أحداث مختلف، ولكن عندما يتعلق الأمر بإضافة إدخالات سجل أحداث جديدة، تتم إضافتها دائمًا إلى سجل التطبيق.

لقد تلقيت أيضًا رسالة "وصف معرف الحدث (0) في المصدر" عدة مرات.

كحل بديل، قمت ببساطة بتسجيل مصدر الرسالة باسم مختلف قليلاً عن DisplayName، وقد نجح الأمر منذ ذلك الحين.سيكون من المفيد تجربة هذا إذا لم تكن قد قمت بذلك بالفعل.

تأتي المشكلة من installutil الذي يقوم افتراضيًا بتسجيل مصدر حدث باسم خدماتك في سجل أحداث "التطبيق".ما زلت أبحث عن طريقة لمنعه من القيام بهذا الهراء.سيكون من الرائع حقًا أن يتمكن المرء من التأثير على سلوك installutil :(

التالي مساعدة اقتراح حل المشكلة بالنسبة لي.أدى قتل مثبت سجل الأحداث الافتراضي، عند النقطة المشار إليها في مثاله، إلى منع المثبت من تسجيل خدمة Windows الخاصة بي تلقائيًا ضمن سجل أحداث التطبيق.

لقد ضاع الكثير من الوقت في محاولة حل هذه المشكلة المحبطة.مليون شكرا!

FWIW، لم أتمكن من تعديل الكود ضمن فئة 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