إضافة التسجيل إلى تطبيق Service Fabric الخاص بك

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

EventFlow

تسمح مجموعة مكتبة EventFlow للتطبيقات بتحديد بيانات التشخيص المراد تجميعها، والمكان الذي يجب إخراجها إليه. يمكن أن تكون بيانات التشخيص أي شيء بدءاً من عدادات الأداء وحتى تتبعات التطبيق. يشغّل في نفس العملية مثل التطبيق، لذلك يتم تقليل حمل الاتصال. لمزيد من المعلومات عن EventFlow وService Fabric، راجع Azure Service Fabric Event Aggregation مع EventFlow.

استخدام أحداث EventSource منظمة

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

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

[EventSource(Name = "MyCompany-VotingState-VotingStateService")]
internal sealed class ServiceEventSource : EventSource
{
    public static readonly ServiceEventSource Current = new ServiceEventSource();

    // The instance constructor is private to enforce singleton semantics.
    private ServiceEventSource() : base() { }

    ...

    // The ServiceTypeRegistered event contains a unique identifier, an event attribute that defined the event, and the code implementation of the event.
    private const int ServiceTypeRegisteredEventId = 3;
    [Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}", Keywords = Keywords.ServiceInitialization)]
    public void ServiceTypeRegistered(int hostProcessId, string serviceType)
    {
        WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType);
    }

    // The ServiceHostInitializationFailed event contains a unique identifier, an event attribute that defined the event, and the code implementation of the event.
    private const int ServiceHostInitializationFailedEventId = 4;
    [Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed", Keywords = Keywords.ServiceInitialization)]
    public void ServiceHostInitializationFailed(string exception)
    {
        WriteEvent(ServiceHostInitializationFailedEventId, exception);
    }

    ...

استخدام EventSource بشكل عام

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

[EventSource(Name = "MyCompany-VotingState-VotingStateService")]
internal sealed class ServiceEventSource : EventSource
{
    public static readonly ServiceEventSource Current = new ServiceEventSource();

    // The Instance constructor is private, to enforce singleton semantics.
    private ServiceEventSource() : base() { }

    ...

    private const int DebugEventId = 10;
    [Event(DebugEventId, Level = EventLevel.Verbose, Message = "{0}")]
    public void Debug(string msg)
    {
        WriteEvent(DebugEventId, msg);
    }

    private const int ErrorEventId = 11;
    [Event(ErrorEventId, Level = EventLevel.Error, Message = "Error: {0} - {1}")]
    public void Error(string error, string msg)
    {
        WriteEvent(ErrorEventId, error, msg);
    }

    ...

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

Microsoft.Extensions.Logging

يعد تسجيل ASP.NET Core (حزمة Microsoft.Extensions.Logging NuGet) إطار عمل تسجيل يوفر واجهة برمجة تطبيقات قياسية للتسجيل لتطبيقك. يمكن توصيل الدعم للخلفيات الخلفية الأخرى للتسجيل في تسجيل ASP.NET Core. هذا يمنحك مجموعة متنوعة من الدعم لإتمام معالجة تسجيل الدخول للتطبيق الخاص بك، دون الحاجة إلى تغيير الكثير من التعليمات البرمجية.

  1. أضف حزمة Microsoft.Extensions.Logging NuGet إلى المشروع الذي تريد استخدامه كأداة. أيضاً، أضف أي حزم مزود. لمزيد من المعلومات، راجع تسجيل الدخول ASP.NET Core .

  2. أضف توجيه استخدام لـMicrosoft.Extensions.Logging إلى ملف الخدمة الخاص بك.

  3. حدد متغيراً خاصاً ضمن فئة الخدمة الخاصة بك.

    private ILogger _logger = null;
    
  4. في مُنشئ فئة الخدمة الخاصة بك، أضف هذه التعليمة البرمجية:

    _logger = new LoggerFactory().CreateLogger<Stateless>();
    
  5. ابدأ بصياغة التعليمة البرمجية الخاصة بك أساليبك. وهنا بعض العينات:

    _logger.LogDebug("Debug-level event from Microsoft.Logging");
    _logger.LogInformation("Informational-level event from Microsoft.Logging");
    
    // In this variant, we're adding structured properties RequestName and Duration, which have values MyRequest and the duration of the request.
    // Later in the article, we discuss why this step is useful.
    _logger.LogInformation("{RequestName} {Duration}", "MyRequest", requestDuration);
    

استخدام موفري التسجيل الآخرين

يستخدم بعض موفري الجهات الخارجية الطريقة الموضحة في القسم السابق، بما في ذلك Serilog وNLog وLoggr. يمكنك توصيل كل هذا في تسجيل ASP.NET Core أو يمكنك استخدامها بشكل منفصل. Serilog لديه ميزة تثري جميع الرسائل المرسلة من المسجل. يمكن أن تكون هذه الميزة مفيدة لإخراج معلومات اسم الخدمة والنوع والقسم. لاستخدام هذه الإمكانية في البنية الأساسية لـASP.NET Core أجرِ الخطوات التالية:

  1. أضف حزم NuGet التالية إلى المشروع Serilog وSerilog.Extensions.Logging وSerilog.Sinks.Literate وSerilog.Sinks.Observable.

  2. أنشئ LoggerConfiguration ومثيل المسجل.

    Log.Logger = new LoggerConfiguration().WriteTo.LiterateConsole().CreateLogger();
    
  3. أضف وسيطة Serilog.ILogger لمنشئ الخدمة، ومرر أداة التسجيل المُنشأة حديثاً.

    ServiceRuntime.RegisterServiceAsync("StatelessType", context => new Stateless(context, Log.Logger)).GetAwaiter().GetResult();
    
  4. في مُنشئ الخدمة، يُنشئ أدوات إثراء للخصائص لـServiceTypeName وServiceName وPartitionId وInstanceId.

    public Stateless(StatelessServiceContext context, Serilog.ILogger serilog)
        : base(context)
    {
        PropertyEnricher[] properties = new PropertyEnricher[]
        {
            new PropertyEnricher("ServiceTypeName", context.ServiceTypeName),
            new PropertyEnricher("ServiceName", context.ServiceName),
            new PropertyEnricher("PartitionId", context.PartitionId),
            new PropertyEnricher("InstanceId", context.ReplicaOrInstanceId),
        };
    
        serilog.ForContext(properties);
    
        _logger = new LoggerFactory().AddSerilog(serilog.ForContext(properties)).CreateLogger<Stateless>();
    }
    
  5. ضع علامة التعليمة البرمجية كما لو كنت تستخدم ASP.NET Core دون Serilog.

    ملاحظة

    نوصي بعدم استخدام الثابت Log.Logger مع المثال السابق. يمكن أن يستضيف Service Fabric مثيلات متعددة من نفس نوع الخدمة في عملية واحدة. إذا كنت تستخدم الثابت Log.Logger، فسيعرض الكاتب الأخير لإثراء الخصائص قيماً لجميع المثيلات المُشغَّلة. هذا هو أحد الأسباب التي تجعل متغير _logger متغير عضو خاص لفئة الخدمة. أيضاً، يجب أن تجعل _logger متاحاً للتعليمة البرمجية العامة، والذي يمكن استخدامه عبر الخدمات.

الخطوات التالية