مدخل إلى سجلّ أحداث Windows وETW ── وضع سجلّات تطبيق الأعمال على آليّة نظام التشغيل القياسيّة
· آخر تحديث: · غو كومورا · CSharp, .NET, سجل الأحداث, ETW, EventSource, تصميم السجلّات, تحقيق الأعطال, تطوير Windows
سجل التعديلات (2 تحديثات، آخر تحديث 2 Sep، 2026)
سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.
- أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
- أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
- النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621652)
هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.
غو كومورا (2026). مدخل إلى سجلّ أحداث Windows وETW ── وضع سجلّات تطبيق الأعمال على آليّة نظام التشغيل القياسيّة. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621652 https://comcomponent.com/ar/blog/windows-eventlog-etw-structured-logging/
- DOI (أحدث نسخة)
- 10.5281/zenodo.21621652
- DOI (هذه النسخة)
- 10.5281/zenodo.22241016
عند تطوير تطبيقات أعمال أو خدمات Windows بلغة C#، غالباً ما تُخرَج السجلّات اليوميّة عبر مسجّل ملفّات ذاتي الصنع أو مكتبة مثل Serilog وNLog. فهل يعني ذلك أنّ سجلّ أحداث Windows وETW (Event Tracing for Windows) لم يعودا ضروريّين؟ ليس كذلك. فهاتان الوسيلتان ليستا «بديلاً لسجلّ الملفّات»، بل سجلّ طبقة مختلفة يراه القائمون على التشغيل وأدوات نظام التشغيل القياسيّة.
يرتب هذا المقال التمييز بين سجلّ الملفّات وسجلّ الأحداث وETW، وطرق التنفيذ في .NET، وأدنى ما يلزم فهمه عن ETW، وممارسة التجميع والتحقيق، وحتّى المطبّات التي يقع فيها العمل الفعلي كثيراً.
الإصدارات المستهدَفة: عيّنات الشيفرة في هذا المقال مفترَضة على .NET 8 (Windows). الفرقان الرئيسيان عن حالة استخدام .NET Framework 4.8 هما التاليان. الأوّل أنّ System.Diagnostics.EventLog مضمَّن في BCL في .NET Framework، بينما يلزم في .NET (عائلة Core) الإشارة صراحة إلى حزمة NuGet بالاسم نفسه. الثاني وجهة EventSource. في .NET Framework الوجهة ETW فقط، أمّا في .NET (عائلة Core) فتسري إلى ETW وإلى EventPipe أيضاً (آليّة تتبّع مضمَّنة في وقت التشغيل)، لذا يمكن التجميع بلا امتيازات مدير عبر أدوات مثل dotnet-trace.12
1. الخلاصة أوّلاً
- الوسائل الثلاث ليست بدائل، بل توزيع أدوار. سجلّ الملفّات تسجيل يتتبّع به المطوّر التفاصيل لاحقاً، وسجلّ الأحداث تسجيل يلاحظ به القائم على التشغيل وعارض الأحداث القياسي وأدوات المراقبة الشذوذ، وETW تتبّع عالي التواتر يُفعَّل عند الطلب لتحليل الأداء أو الأعطال صعبة التكرار. الأساس هو الجمع، مع تغيير كمّ المعلومات المكتوبة في كلّ منها.
- إلى سجلّ الأحداث اكتب «البدء والإيقاف والخطأ القاتل» فقط. إذا أسرت كلّ السجلّات إلى سجلّ الأحداث أيضاً، دُفِنت الشذوذات التي ينبغي أن يراها القائم على التشغيل حقّاً. أبقِ سجلّ الملفّات تفصيليّاً كما هو، وضيّق سجلّ الأحداث.
- تسجيل مصدر الأحداث يحتاج امتيازات مدير. لا يمكن استدعاء
EventLog.CreateEventSourceمنذ Windows Vista بلا امتيازات مدير. تجنّب تصميماً يحاول التسجيل عند أوّل وصول وقت التشغيل، وسجّل مسبقاً في المثبّت.3 - ETW ليس «تجميعاً دائماً». حتّى إن عرّفت الأحداث بـ
EventSource، لا يُسجَّل شيء ما لم يوجد مشترِك (dotnet-trace، PerfView، أدوات قائمة على ETW). الاستخدام الأساسي هو التجميع مع تحديد المزوّد المستهدَف فقط عند تحليل الأداء أو تحقيق عطل معيّن.4 - اكتب الرسائل بصيغة قالب، لا بوصل نصوص ولا باستيفاء نصوص. إذا كتبت
logger.LogInformation("Order {OrderId} failed", orderId)، أمكن البحث عن OrderId كسجلّ مهيكل باسم العنصر النائب. الوصل والاستيفاء يفقدان معلومات البنية هذه.5 - تضخّم السجلّ كثيراً ما سببه صغر القيمة الافتراضيّة. الحجم الأقصى الافتراضي لسجلّ الأحداث 512KB فقط. إذا استخدم تطبيق الأعمال سجلّ الأحداث يوميّاً فوسّعه صراحة، وحدّد أيضاً عند التصميم السلوك عند بلوغ الحدّ (الكتابة فوق القديم، أو الإتلاف).6
فيما يلي جدول الحكم.
| الزاوية | سجلّ الملفّات | سجلّ الأحداث | ETW |
|---|---|---|---|
| القارئ الرئيسي | المطوّر ومسؤول الصيانة | القائم على التشغيل، أدوات المراقبة، عارض الأحداث القياسي في نظام التشغيل | مسؤول تحقيق الأداء، مهندس الدعم |
| مؤشّر كمّ الإخراج | تفصيلي (يجوز تضمين Trace/Debug) | قليل (البدء والإيقاف والخطأ القاتل فقط) | كمّ كبير عند التفعيل فقط. افتراضيّاً لا يُسجَّل شيء |
| العمر والاحتفاظ | يسهل الاحتفاظ الطويل بحسب ضبط التدوير | عند بلوغ حدّ الحجم يُكتَب فوق القديم أو يُتلَف | لكلّ جلسة. بعد انتهاء التجميع يُحفَظ كملفّ .etl / .nettrace |
| التكامل مع أدوات نظام التشغيل القياسيّة | لا يوجد (يلزم عارض خاص) | يُرى قياسياً عبر عارض الأحداث وwevtutil وGet-WinEvent |
يُرى عبر dotnet-trace وPerfView وWPR/WPA |
| الصلاحيات | صلاحية الكتابة لمستخدم تشغيل التطبيق فقط | تسجيل المصدر يحتاج امتيازات مدير (الكتابة نفسها جائزة لمستخدم قياسي إن كان المصدر مسجَّلاً) | بدء التجميع كثيراً ما يحتاج امتيازات مدير |
في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 18، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle
2. أساسيات سجلّ أحداث Windows
في Windows ثلاثة سجلّات افتراضيّة: Application وSystem وSecurity، وSecurity للقراءة فقط. تطبيقات الأعمال والخدمات تكتب في سجلّ Application، أو في سجلّ (قناة) مخصّص للتطبيق. اعتياد برامج تشغيل الأجهزة أن تكتب في سجلّ System.7
وحدة الكتابة هي «مصدر الأحداث». المصدر اسم يميّز التطبيق، ويُسجَّل مسبقاً بـ EventLog.CreateEventSource. منذ Windows Vista، تسجيل المصدر يحتاج امتيازات مدير. السبب أنّ التحقّق من فرادة اسم المصدر يستلزم البحث في كلّ سجلّات الأحداث بما فيها سجلّ Security، والمستخدم القياسي لا يملك حقّ الوصول إلى سجلّ Security.3
using System.Diagnostics;
// インストーラーやセットアップ処理の中で、管理者権限がある間に実行する
const string SourceName = "KomuraSoft.OrderService";
const string LogName = "Application";
if (!EventLog.SourceExists(SourceName))
{
EventLog.CreateEventSource(SourceName, LogName);
}
إذا حاولت إنشاء المصدر عند أوّل وصول وقت التشغيل، فشل هذا الاستدعاء في تشغيل يعمل فيه التطبيق بمستخدم قياسي. العموم عن متى تلزم امتيازات المدير مرتَّب في «متى يصبح Windows admin privilege ضرورياً»، وتسجيل مصدر الأحداث من هذا النمط تماماً. التصميم الآمن هو التسجيل في المثبّت، وأن يكتب أصل التطبيق فقط إلى مصدر مسجَّل مسبقاً.
يحمل الحدث معلومتي تعريف: «المستوى» و«معرّف الحدث». المستوى هو EventLogEntryType (Information، Warning، Error، SuccessAudit، FailureAudit)، ويُستخدَم في أيقونات Event Viewer والمرشّحات الافتراضيّة. معرّف الحدث عدد صحيح يعرّفه التطبيق، ويصبح مفتاحاً للبحث والتجميع لاحقاً لنوع الحدث نفسه.
3. الكتابة إلى سجلّ الأحداث من .NET
مسارا الكتابة إلى سجلّ الأحداث من .NET اثنان كبيران. نرتّب أوّلاً فروق الشروط المسبقة.
| الزاوية | 3.1 استخدام System.Diagnostics.EventLog مباشرة |
3.2 ILogger + AddEventLog |
|---|---|---|
| لزوم تسجيل مصدر الأحداث | لازم. اجعل اسم المصدر الممرَّر إلى WriteEntry مسجَّلاً مسبقاً بـ CreateEventSource في جهة المثبّت3 |
لازم. سجّل الاسم المحدَّد في SourceName كذلك في المثبّت. إن حُذِف أصبح الاسم العام .NET Runtime8 |
| الصلاحية اللازمة للتسجيل | امتيازات مدير (عند التسجيل فقط. الكتابة إلى مصدر مسجَّل جائزة لمستخدم قياسي) | كذلك |
| مرجع إضافي لازم | في .NET (عائلة Core) حزمة System.Diagnostics.EventLog |
حزمة Microsoft.Extensions.Logging.EventLog (مفعَّلة افتراضيّاً على Windows في Generic Host)9 |
| البنود التي يمكن تحديدها | المستوى ومعرّف الحدث، إضافة إلى الفئة وإرفاق بيانات ثنائيّة بدقّة | المستوى ومعرّف الحدث وقالب الرسالة. لا يُعالَج إرفاق الفئة أو البيانات الثنائيّة |
| تضييق كمّ الإخراج | تحكم بنفسك في كلّ موضع استدعاء | يمكن رفع المستوى لسجلّ الأحداث وحده عبر settings.Filter |
| المشهد المناسب | خدمة صغيرة بلا بنية سجلّات، أو ترحيل من بناء أقرب إلى Win32 | تطبيق بنى سجلّات مهيكلة مسبقاً بـ ILogger |
في كلا المسارين، الفرضية أنّ اسم المصدر المحدَّد مسجَّل مسبقاً. إذا كتبت باسم مصدر غير مسجَّل، دخل الحدث نفسه إلى سجلّ Application، لكن عارض الأحداث لا يستطيع عرض نصّ الشرح لأنّ مورد الرسالة المقابل غير موجود.10
3.1 استخدام System.Diagnostics.EventLog مباشرة
واجهة منخفضة المستوى، تناسب عند الحاجة إلى تحكّم دقيق (الفئة، إرفاق بيانات ثنائيّة، وما شابه). في .NET (غير .NET Framework) يلزم الإشارة إلى حزمة NuGet System.Diagnostics.EventLog.
using System.Diagnostics;
public sealed class OrderServiceEventLog
{
private const string SourceName = "KomuraSoft.OrderService";
public void WriteServiceStarted()
{
EventLog.WriteEntry(SourceName, "شُغِّل OrderService.",
EventLogEntryType.Information, eventID: 1000);
}
public void WriteFatalError(Exception ex)
{
EventLog.WriteEntry(SourceName,
$"حدث خطأ فادح ولا يمكن مواصلة المعالجة. راجع سجلّ الملف للتفاصيل.{ex.GetType().Name}: {ex.Message}",
EventLogEntryType.Error, eventID: 1999);
}
}
3.2 استخدام ILogger + AddEventLog
إذا كنت قد بنيت سجلّات مهيكلة بـ ILogger، فإنّ AddEventLog في حزمة Microsoft.Extensions.Logging.EventLog يركب بسلاسة على مسار السجلّات القائم. في Worker Service القائم على ASP.NET Core أو Generic Host، يكون مزوّد EventLog مفعَّلاً افتراضيّاً عند التشغيل على Windows.9
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.EventLog;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddEventLog(settings =>
{
settings.SourceName = "KomuraSoft.OrderService";
settings.LogName = "Application";
// 既定のカテゴリフィルターに加えて、イベントログへは
// Warning以上だけを流す(詳細はファイルログ側に任せる)
settings.Filter = (_, level) => level >= LogLevel.Warning;
});
using IHost host = builder.Build();
await host.RunAsync();
إذا حذفت EventLogSettings، يصبح LogName افتراضيّاً "Application" وSourceName افتراضيّاً ".NET Runtime".8 إذا بقي اسم المصدر العام .NET Runtime، يصعب في عارض الأحداث تمييز سجلّ تطبيقك من سجلّات تطبيقات .NET أخرى، لذا حدّد SourceName دائماً صراحة لتطبيق شركتك.
في معالجة دفعيّة تعمل كخدمة Windows أو عبر Task Scheduler، التوازن العملي هو كتابة «البدء» و«الإيقاف» و«الخطأ القاتل» فقط إلى سجلّ الأحداث، وترك التفاصيل الأخرى لسجلّ الملفّات. بناء الخدمة وتشغيلها في «كيفيّة إنشاء خدمات Windows وتشغيلها»، ومتاعب التنفيذ عبر Task Scheduler في «لماذا لا تُنفَّذ مهامّ Task Scheduler أو تنتهي بالرمز 0x1». في كليهما كثيراً ما يكون أوّل ما يراه القائم على التشغيل عند الشذوذ هو سجلّ الأحداث، وبقاء النقاط الجوهريّة هنا يغيّر سرعة التحقيق الأوّلي.
3.3 التحقّق من أنّ الكتابة نجحت
بعد كتابة الشيفرة، مرّ مرّة واحدة حتّى تظهر فعلاً في عارض الأحداث. الخطوات كالتالي.
- نفّذ
eventvwr.mscمنWin + Rوافتح عارض الأحداث. - في الجزء الأيسر افتح عارض الأحداث (المحلّي) > سجلات Windows > Application (في البيئة اليابانية قد يظهر الاسم «アプリケーション»). إذا أنشأت سجلّاً مخصّصاً، يظهر اسم سجلّك تحت سجلات التطبيقات والخدمات.
- إذا كثرت العناصر ودُفِنت، افتح في الجزء الأيمن تصفية السجلّ الحالي، وضع اسم مصدرك في «مصدر الأحداث»، والمعرّف المراد في «كلّ معرّفات الأحداث»، ثمّ ضيّق.
مقابلة القيم المحدَّدة في الشيفرة مع الحقول في عارض الأحداث كالتالي. ضبط هذا يبيّن أين تنظر عندما «كتبت لكنّك لا تجده».
| التحديد في الشيفرة | العرض في عارض الأحداث |
|---|---|
الوسيط الأوّل لـ WriteEntry / settings.SourceName |
عمود «المصدر» |
وسيط eventID / EventId في ILogger |
عمود «معرّف الحدث» |
EventLogEntryType (Information، Warning، Error) / LogLevel |
عمود «المستوى» (معلومات، تحذير، خطأ) |
النصّ الممرَّر إلى WriteEntry / الرسالة بعد توسيع القالب |
أسفل القائمة، نصّ الشرح في علامة التبويب «عام» |
LogName (الافتراضي Application) |
تحت أيّ سجلّ يظهر |
إذا أردت التحقّق من سطر الأوامر، يسهل التعامل مع Get-WinEvent في PowerShell. مرّر اسم السجلّ واسم المزوّد (= اسم المصدر) إلى -FilterHashtable لتحصل على الأحداث التي كتبها ذلك المصدر فقط.11
# 自分のソースが書いた直近10件を、日時・ID・レベル・本文だけに絞って表示する
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'KomuraSoft.OrderService' } -MaxEvents 10 |
Select-Object TimeCreated, Id, LevelDisplayName, Message
إذا لم يُرجَع أيّ عنصر، فالسبب عادة أحد: «اختلاف إملاء اسم المصدر» أو «المصدر غير مسجَّل» أو «فشلت الكتابة أصلاً وأُخمِد الاستثناء». يمكن التحقّق من تسجيل المصدر بـ [System.Diagnostics.EventLog]::SourceExists('KomuraSoft.OrderService').
4. ما هو ETW ── بنية تتبّع تخترق نظام التشغيل بأكمله
Event Tracing for Windows (ETW) آليّة تتبّع يمكن بها قياس من النواة إلى تطبيقات وضع المستخدم على أساس مشترك. أكبر ميزة أنّ أحداث النواة (إدخال/إخراج القرص، إنشاء العمليّة، وما شابه) وأحداث التطبيق الخاصّة يمكن تسجيلها وتحليلها معاً على الخطّ الزمني نفسه.12
في .NET يمكن تعريف مزوّد ETW خاص بإنشاء صنف يرث System.Diagnostics.Tracing.EventSource.1 يعمل ETW بأسلوب Publish-Subscribe، وما لم يوجد مشترِك لا تُسجَّل الأحداث. أي أنّ تنفيذ EventSource وحده لا يفعل شيئاً، ولا تُسجَّل الأحداث إلّا بعد التفعيل الصريح بأداة تجميع.4
الفرق الذي كتبناه في جدول حكم الفصل 1 «سجلّ الأحداث = دائم وللتشغيل» و«ETW = عند الطلب وللتحقيق» يأتي من وجود هذا الاشتراك من عدمه. إذا رسمناه يصبح كالتالي.
flowchart LR
APP["تطبيق الأعمال"]
APP -->|"البدء والإيقاف والخطأ القاتل فقط"| EL["سجلّ الأحداث<br/>يُسجَّل دائماً، ونظام التشغيل يثبّته"]
EL --> EV["عارض الأحداث<br/>wevtutil / Get-WinEvent<br/>يراه القائم على التشغيل في أيّ وقت"]
APP -->|"قياس عبر EventSource"| SRC["المزوّد<br/>يعرّف أحداثاً عالية التواتر"]
SRC -->|"عند غياب مشترِك"| NONE["لا يُسجَّل في أيّ موضع<br/>والتكلفة شبه صفر"]
SRC -->|"عند بدء التجميع"| SES["الجلسة<br/>تُفعَّل أثناء التحقيق فقط"]
SES --> TOOL["المستهلك<br/>dotnet-trace / PerfView / WPA<br/>يحلّل بفترة محدودة"]
الشكل 1: سجلّ الأحداث تسجيل دائم ومثبَّت، أمّا ETW فلا يُسجَّل إلّا عندما يكتمل المزوّد ثم الجلسة ثم المستهلك.
using System.Diagnostics.Tracing;
[EventSource(Name = "KomuraSoft-OrderService")]
internal sealed class OrderServiceEventSource : EventSource
{
public static readonly OrderServiceEventSource Log = new();
[Event(1, Level = EventLevel.Informational, Message = "بدأ معالجة الطلب. OrderId={0}")]
public void OrderProcessingStart(string orderId)
{
if (IsEnabled())
{
WriteEvent(1, orderId);
}
}
[Event(2, Level = EventLevel.Informational, Message = "اكتملت معالجة الطلب. OrderId={0}")]
public void OrderProcessingStop(string orderId)
{
if (IsEnabled())
{
WriteEvent(2, orderId);
}
}
}
جهة الاستدعاء تستخدمه كالتالي. التحقّق بـ IsEnabled() قبل الكتابة الفعلية للحدث يتجنّب تكلفة زائدة عندما لا يكون التجميع جارياً.
OrderServiceEventSource.Log.OrderProcessingStart(order.Id);
try
{
ProcessOrder(order);
}
finally
{
OrderServiceEventSource.Log.OrderProcessingStop(order.Id);
}
لتجميع هذه الأحداث، أسهل طريق هو أداة dotnet-trace العابرة للمنصّات.13
dotnet-trace collect --providers KomuraSoft-OrderService -- OrderService.exe
ما ينبغي ضبطه هنا أنّ ما يستخدمه dotnet-trace ليس ETW نفسه، بل مساراً آخر اسمه EventPipe. EventPipe آليّة تتبّع مضمَّنة في وقت تشغيل .NET، وتستطيع جمع أحداث EventSource كما يفعل ETW. الفروق ثلاثة. EventPipe يعمل بالطريقة نفسها على كلّ المنصّات التي يدعمها .NET، بينما ETW خاص بـ Windows. بدء تجميع ETW يحتاج امتيازات مدير، بينما EventPipe لا يحتاجها إن شُغِّل بمستخدم التطبيق المستهدَف نفسه. ونطاق EventPipe مقصور على الشيفرة المُدارة ووقت التشغيل، فلا يأخذ أحداث نظام التشغيل والنواة ولا مكدّس الاستدعاءات الأصلي.2 الحاجة إلى ETW (PerfView، WPR/WPA) عندما تريد التتبّع بما فيه النواة سببها هذا القيد الثالث.
ملفّ .nettrace المجموع يُفتَح في Visual Studio أو PerfView للتأكّد من محتواه. لتحقيق أداء أجدّ، مثلاً تحليل يشمل أحداث النواة، يصبح التكوين استخدام Windows Performance Recorder (WPR) المضمَّن في Windows Assessment and Deployment Kit (ADK)، والنظر عبر Windows Performance Analyzer (WPA).14 غير أنّ نطاق هذا المقال يقف عند «ماذا يستطيع ETW ومتى يُستخدَم»، ولا يدخل إجراءات التحليل التفصيلي في PerfView أو WPA. من زاوية تطوير تطبيقات الأعمال وصيانتها، يكفي ضبط أن تستطيع تعريف مزوّدك بـ EventSource وتجميعه بـ dotnet-trace.
5. ممارسة السجلّات المهيكلة
يُكتَب قالب رسالة ILogger كنصّ ثابت يتضمّن عنصراً نائباً مسمّى {PlaceHolder}. هذا ليس مجرّد أسلوب مظهر، بل يعني أنّ تمرير الوسائط وحدها دون تغيير القالب نفسه يتيح لبنية السجلّات الاحتفاظ بعلاقة أسماء العناصر النائبة بالقيم (بيانات مهيكلة).15
// موصى به: القالب ثابت، والوسائط تُمرَّر منفصلة
logger.LogWarning("فشل حجز المخزون للطلب {OrderId}. المستودع={WarehouseId}", orderId, warehouseId);
// غير موصى به: وصل السلاسل أو الاستيفاء يكسر علاقة القالب بالقيم
logger.LogWarning("Order " + orderId + " فشل حجز المخزون. المستودع=" + warehouseId);
logger.LogWarning($"Order {orderId} فشل حجز المخزون. المستودع={warehouseId}");
قاعدة محلّل الشيفرة CA2254 تكتشف هذا النمط غير الموصى به (كتابة يتغيّر فيها القالب في كلّ استدعاء).5 في المسار الساخن عالي تواتر الاستدعاء، يمكن أيضاً تجنّب تكلفة boxing وتحليل القالب باستخدام توليد المصدر عبر LoggerMessageAttribute.16
using Microsoft.Extensions.Logging;
internal static partial class Log
{
[LoggerMessage(
EventId = 2001,
Level = LogLevel.Warning,
Message = "فشل حجز المخزون للطلب {OrderId}. المستودع={WarehouseId}")]
public static partial void StockReservationFailed(
this ILogger logger, string orderId, string warehouseId);
}
// جانب الاستدعاء
logger.StockReservationFailed(orderId, warehouseId);
بهذا التكوين يمكن، من استدعاء السجلّ نفسه، توزيع التفاصيل إلى سجلّ الملفّات (وجهة مثل Serilog/NLog)، والمحتوى المضيَّق إلى سجلّ الأحداث، والتتبّع منخفض التكلفة إلى ETW عبر EventSourceLoggerProvider. الشكل الأساسي هو تسجيل عدّة ILoggerProvider وتغيير مستوى الإخراج لكلّ مزوّد عبر Filter. الحدّ الأدنى لمسجّل ذاتي الصنع في «حين لا مفرّ من بناء logger خاصّ بك»، وكيف تربط السجلّ والتفريغ عند الانهيار في «كيف نُبقي سجلّات الأعطال في تطبيقات Windows».
6. التجميع والتحقيق ── حديث جهة قراءة السجلّ المكتوب
في عارض الأحداث يمكن استخراج مصدر معيّن أو مستوى أو معرّف حدث فقط من قائمة السجلّ عبر «تصفية» أو «عرض مخصّص». حفظ الشروط المستخدمة كثيراً كعرض مخصّص يسرّع التحقيق في المرّات التالية. العرض المخصّص معرَّف باستعلام XPath، مثلاً استعلام يستخرج اسماً معيّناً للحدث يأخذ شكلاً كالتالي.17
<QueryList>
<Query Id="0" Path="Application">
<Select Path="Application">
*[System[Provider[@Name='KomuraSoft.OrderService'] and (Level=2 or Level=3)]]
</Select>
</Query>
</QueryList>
موضع لصق هذا XML هو: الجزء الأيمن في عارض الأحداث «إنشاء عرض مخصّص» > علامة تبويب «XML» > ضع علامة «تحرير الاستعلام يدوياً». بعد الحفظ يصطفّ تحت «عروض مخصّصة» في الجزء الأيسر، وبعد ذلك يكفي النقر لفتح قائمة أخطاء (Level=2) وتحذيرات (Level=3) كتبها KomuraSoft.OrderService فقط. Level في مخطّط الحدث هو القيمة نفسها في عمود «المستوى» في عارض الأحداث، لذا إن أردت تضمين مستوى المعلومات فأضف Level=4 إلى الشرط. التضييق المصنوع في حوار التصفية يمكن أيضاً التحقّق منه كـ XPath بفتح علامة تبويب «XML» في الحوار نفسه، لذا الطريق المختصر أن تضيّق أوّلاً بالواجهة ثمّ تنسخ XML.
من سطر الأوامر يمكن تصدير السجلّ وتغيير الإعدادات بـ wevtutil. يُستخدَم أيضاً في تشغيل من نوع تصدير سجلّ الأحداث حول لحظة الحدوث وإرساله إلى الدعم كجزء من تحقيق العطل.18
:: Applicationログを丸ごとevtxファイルにエクスポートする
wevtutil epl Application C:\logs\application_20260707.evtx
:: 特定のソースのイベントだけを表示する(直近10件)
wevtutil qe Application /q:"*[System[Provider[@Name='KomuraSoft.OrderService']]]" /c:10 /rd:true /f:text
ما يُمرَّر إلى /q: هو محتوى عنصر Select في XPath نفسه المستخدم في العرض المخصّص أعلاه. /f: تحديد لصيغ الإخراج: text إن أراد إنسان تتبّعه بالعين، وxml إن عالجته شيفرة آليّاً. إذا اخترت xml خرج المخطّط نفسه المستخدم في XML العرض المخصّص، فتلتقط Provider وEventID بأسماء العناصر. ملفّ .evtx المصدَّر يُفتَح كما هو على جهاز آخر من «فتح سجلّ محفوظ» في عارض الأحداث.
في تحقيق الانهيار، الأساس مقابلة الثلاثة: سجلّ الأحداث وسجلّ الملفّات وتفريغ الانهيار، بالوقت. انطلاقاً من الطابع الزمني لـ«خطأ قاتل» المتبقّي في سجلّ الأحداث، تقابل تفاصيل سجلّ الملفّات في اللحظة نفسها والتفريغ المأخوذ بـ WER/ProcDump. طريقة أخذ التفريغ في «جمع crash dumps لتطبيقات Windows»، وإجراءات التحليل الفعلي للتفريغ المأخوذ في «قراءة ملفّات تفريغ الأعطال عبر WinDbg + SOS».
7. المطبّات
- الفشل عند الكتابة ومصدر غير مسجَّل. إذا استدعيت ما يعادل
RegisterEventSourceباسم مصدر غير مسجَّل، كُتِب الحدث نفسه في سجلّ Application، لكن لأنّ DLL مورد الرسالة المقابل غير موجود، يعرض عارض الأحداث خطأ بدل نصّ الشرح.10 إضافة إلى ذلك، التصميم الذي يحاول التسجيل التلقائي بـCreateEventSourceوقت التشغيل كثيراً ما يسقط باستثناء عند أوّل بدء لعمليّة تعمل بمستخدم غير مدير. أتمم تسجيل المصدر حتماً في جهة المثبّت. - ارتباك «يمكن الكتابة باسم مصدر آخر». سجلّ الأحداث يستطيع، ما دامت صلاحية الكتابة موجودة، الكتابة حتّى باسم مصدر آخر لا يتذكّر التطبيق أنّه سجّله. يلزم أن يكون اسم المصدر فريداً على الحاسوب، لكن لا توجد آليّة في نظام التشغيل تحفظ ذلك، بل يُترَك لانضباط جهة التطبيق.7 اختيار اسم مصدر عام أكثر من اللازم (اسم قصير لا يتضمّن اسم الشركة أو المنتج) يصبح سبباً للتصادم مع تطبيقات مورّدين آخرين، أو لاستخدام اسم مصدر تطبيق آخر دون قصد.
- تضخّم السجلّ وسياسة الاحتفاظ. الحجم الأقصى الافتراضي لسجلّ الأحداث 512KB فقط.6 إذا استخدم تطبيق الأعمال سجلّ الأحداث يوميّاً، فوسّع الحجم صراحة بـ
wevtutil slأو في المثبّت، واضبط بوعي عند بلوغ الحدّ إن كنت «تكتب فوق الأقدم» أو «تتلف الجديد». انتبه أيضاً إلى أنّOverwriteOlderأصبح غير موصى به، وقد يتصرّف عمليّاً كـ«لا تكتب فوق» حتّى إن حُدِّد.19 - عرض الرسائل في بيئة متعدّدة اللغات. إذا استخدمت تكويناً بملفّ موارد رسائل مترجَمة (
MessageResourceFile)، وعرضت السجلّ في بيئة لا يوجد فيها DLL المورد ذلك أو يختلف إصداره، ظهر عرض «تعذّر اكتشاف شرح معرّف الحدث كذا». مشكلة سهلة الحدوث عند النظر إلى سجلّ أحداث منقول على خادم آخر، أو عند بقاء السجلّ وحده بعد إلغاء تثبيت التطبيق. التكوين الذي يكتب النصّ مباشرة بـWriteEntry(بلا ملفّ موارد) يتجنّب إدخال هذا النوع من المشكلات أصلاً. - حالات يتعذّر فيها الكتابة لنقص الصلاحية. يُساء فهمه كثيراً، لكن تسجيل المصدر يحتاج امتيازات مدير، بينما الكتابة إلى مصدر مسجَّل جائزة لمستخدم قياسي. قبل أن تبادر بتشغيل التطبيق كلّه كمدير ظنّاً أنّ «لا يعمل بلا امتيازات مدير»، افصل أيّ عملية تحتاج الصلاحية فعلاً.
8. الخلاصة
هيكل هذا المقال يُجمَع في ثلاث نقاط.
- الوسائل الثلاث توزيع أدوار. سجلّ الملفّات ليتتبّع المطوّر التفاصيل، وسجلّ الأحداث ليلاحظ القائم على التشغيل وأدوات نظام التشغيل القياسيّة الشذوذ، وETW/EventPipe لحفر تحقيقات الأداء والأعطال صعبة التكرار. الشكل الأساسي أن تكتب إلى سجلّ الأحداث البدء والإيقاف والخطأ القاتل فقط، وتترك التفاصيل لسجلّ الملفّات (الفصل 1).
- تسجيل مصدر الأحداث عمل المثبّت. التسجيل يحتاج امتيازات مدير، بينما الكتابة إلى مصدر مسجَّل جائزة لمستخدم قياسي. التصميم الذي يحاول التسجيل التلقائي بـ
CreateEventSourceوقت التشغيل يولّد فشل أوّل بدء في بيئة مستخدم قياسي (الفصول 2 و3 و7). - ETW لا يُسجَّل إلّا بوجود مشترِك. كتابة
EventSourceوحدها لا تفعل شيئاً. بالمقابل، تكلفة زرع القياس شبه صفر، ويمكنك إعداد حالة يمكن التقاطها بـdotnet-traceعندما تلزم (الفصل 4).
إذا تحرّكت يداك تالياً، فهذا الترتيب واقعي.
- اجرد إخراج سجلّ الأحداث في التطبيق القائم، وتحقّق ممّا إذا كان المحتوى الساري إلى سجلّ الأحداث مضيَّقاً إلى «البدء والإيقاف والخطأ القاتل». إن كانت السجلّات التفصيليّة تسري أيضاً، فضيّق بـ
FilterفيAddEventLog(الفصل 3). - حدّد قاعدة ترقيم معرّفات الأحداث (نطاق لكلّ مجال وظيفي، وعدم تغيير معنى معرّف صُرِف مرّة) واتركها في وثيقة (الفصل 2).
- تحقّق ممّا إذا كان في المثبّت معالجة تسجيل مصدر الأحداث، وممّا إذا وُسِّع الحجم الأقصى للسجلّ صراحة من 512KB الافتراضي (الفصل 7).
- إذا كنت تضيّق بالشرط نفسه في كلّ تحقيق عطل، فاحفظ ذلك XPath كعرض مخصّص (الفصل 6).
- في معالجة يُتوقَّع أن تظهر فيها استشارة أداء، ازرع مسبقاً حدثي البدء والانتهاء فقط بـ
EventSource. التكلفة أثناء عدم التجميع شبه صفر (الفصل 4).
مقالات ذات صلة
- متى يصبح Windows admin privilege ضرورياً - UAC والمناطق المحميّة وكيفيّة التمييز على مستوى التصميم
- كيفيّة إنشاء خدمات Windows وتشغيلها ── من التمييز عن جدولة المهام إلى تحويل BackgroundService إلى خدمة
- لماذا لا تُنفَّذ مهامّ Task Scheduler أو تنتهي بالرمز 0x1 ── تشخيص الأسباب وتصميم تشغيل آمن
- حين لا مفرّ من بناء logger خاصّ بك، ما الحدّ الأدنى الذي تحتاجه فعلاً؟
- كيف نُبقي سجلّات الأعطال في تطبيقات Windows حتّى عند الموت بسبب أخطاء برمجية
- جمع crash dumps لتطبيقات Windows: متى تبدأ بـ WER أو ProcDump أو WinDbg
- قراءة ملفّات تفريغ الأعطال عبر WinDbg + SOS ── مدخل عمليّ للتحليل بعد الجمع
مجالات الاستشارة ذات الصلة
تتعامل شركة كومورا سوفت ذ.م.م. مع استشارات تقنيّة حول تصميم بنية السجلّات وسجلّ الأحداث وETW لتطبيقات Windows للأعمال، وتصميم نقاط الرصد استعداداً لتحقيق الأعطال.
روابط مرجعية
-
Microsoft Learn, EventSource. حول أنّ EventSource آليّة سجلّات مهيكلة سريعة مضمَّنة في .NET تدعم ETW وEventPipe. ↩ ↩2
-
Microsoft Learn, EventPipe. حول أنّ EventPipe آليّة تتبّع مضمَّنة في وقت تشغيل .NET تشبه ETW وperf_events، وأنّها تستطيع جمع أحداث EventSource، وأنّ ETW خاص بـ Windows ويحتاج امتيازات مدير بينما EventPipe عابر للمنصّات ولا يحتاج امتيازات مدير، وأنّ نطاق EventPipe مقصور على الشيفرة المُدارة ووقت التشغيل فلا يأخذ أحداث النواة ولا مكدّس الاستدعاءات الأصلي. ↩ ↩2
-
Microsoft Learn, EventLog.CreateEventSource Method. حول سبب احتياج إنشاء مصدر الأحداث منذ Windows Vista إلى امتيازات مدير (لأنّه يستلزم البحث في كلّ السجلّات بما فيها سجلّ Security). ↩ ↩2 ↩3
-
Microsoft Learn, Getting Started with EventSource. حول أنّ EventSource يعمل بنمط Publish-Subscribe ولا تُسجَّل الأحداث ما لم يوجد مشترِك (ETW أو EventPipe)، وحول طريقة التجميع بـ dotnet-trace collect. ↩ ↩2
-
Microsoft Learn, CA2254: Template should be a static expression. حول فقدان علاقة أسماء العناصر النائبة بالقيم عند استخدام وصل النصوص أو استيفاء النصوص في قالب رسالة السجلّ. ↩ ↩2
-
Microsoft Learn, EventLog.MaximumKilobytes Property. حول أنّ الحجم الأقصى الافتراضي لسجلّ الأحداث 512 كيلوبايت. ↩ ↩2
-
Microsoft Learn, EventLog Class. حول السجلّات الثلاثة الافتراضيّة Application/System/Security، ولزوم فرادة المصدر على حاسوب واحد، وآليّة إمكان الكتابة بأيّ اسم مصدر مسجَّل ما دامت صلاحية الكتابة موجودة. ↩ ↩2
-
Microsoft Learn, EventLogSettings Class. حول القيم الافتراضيّة لـ AddEventLog (LogName هو “Application”، وSourceName هو “.NET Runtime”). ↩ ↩2
-
Microsoft Learn, Logging providers in .NET. حول أنّ مزوّدات السجلّات الافتراضيّة في Generic Host تتضمّن Console وDebug وEventSource وEventLog (Windows فقط). ↩ ↩2
-
Microsoft Learn, Event Sources. حول أنّ الكتابة باسم مصدر غير مسجَّل تسقط احتياطيّاً إلى سجلّ Application، لكن عارض الأحداث لا يستطيع عرض نصّ الشرح ويظهر خطأ لأنّ ملفّ الرسالة غير موجود. ↩ ↩2
-
Microsoft Learn, Get-WinEvent. حول إمكان تضييق الأحداث بتمرير مفاتيح مثل
LogNameوProviderNameإلى-FilterHashtable، وأنّ الأحداث الحاصلة تحمل خصائص مثلTimeCreatedوIdوLevelDisplayNameوMessage. ↩ -
Microsoft Learn, Event Tracing. حول أنّ Event Tracing for Windows (ETW) آليّة تبدأ أحداث تتبّع التطبيقات وتوقفها وتقيسها وتستهلكها. ↩
-
Microsoft Learn, dotnet-trace performance analysis utility. حول نظرة عامّة على أداة التجميع العابرة للمنصّات dotnet-trace القائمة على EventPipe، وأمر collect. ↩
-
Microsoft Learn, Introduction to WPR. حول أنّ Windows Performance Recorder (WPR) أداة توسّع ETW وتوفّر تسجيلاً تفصيليّاً للنظام والتطبيق. ↩
-
Microsoft Learn, Logging in C# and .NET. حول صياغة {PlaceHolder} في قالب رسالة السجلّ، وآليّة أن تصبح أسماء المفاتيح المتضمّنة في القالب أسماء خصائص السجلّ. ↩
-
Microsoft Learn, High-performance logging in .NET. حول أنّ سجلّ توليد المصدر عبر LoggerMessageAttribute يتجنّب تكلفة boxing وتحليل القالب. ↩
-
Microsoft Learn, Comparison of ETW and EventLog logger functionality. حول طريقة إنشاء عرض مخصّص في عارض الأحداث باستعلام XPath حسب اسم الحدث وما شابه. ↩
-
Microsoft Learn, wevtutil. حول عمليات سطر الأوامر مثل تصدير سجلّ الأحداث (epl) والاستعلام (qe) وتغيير الإعدادات (sl). ↩
-
Microsoft Learn, OverflowAction Enum. حول السلوك عند بلوغ سجلّ الأحداث حدّ الحجم (الكتابة فوق، الإتلاف)، وأنّ OverwriteOlder أصبح غير موصى به. ↩
مقالات ذات صلة
أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.
تحديد سبب «البطء» بـ PerfView وdotnet-trace ── مدخل عملي لتحقيق أداء .NET
عندما يصبح تطبيق الأعمال «بطيئاً» أو «يلتصق بالمعالج»، أي أداة تستخدم وماذا تنظر؟ نرتّب توزيع الأدوار بين PerfView وdotnet-trace، وقراءة ...
انتهاء خدمة برامج تشغيل الطابعات في Windows ── كيف تستعد تطبيقات الأعمال لطباعة التقارير والملصقات
تُنهى Microsoft تدريجيّاً خدمة برامج تشغيل الطابعات v3/v4، ومن تمّوز/يوليو 2026 يُفضَّل برنامج تشغيل فئة IPP. نرتّب في جداول قرار ما يختف...
تعدد اللغات في تطبيقات WinForms/WPF ── الممارسة العملية لملفات resx وتجميعات الأقمار الصناعية وتبديل الثقافة
نرتّب تعدد اللغات في تطبيقات سطح المكتب على Windows: الفرق بين CurrentCulture وCurrentUICulture، وآلية resx وتجميعات الأقمار الصناعية، وا...
ملف CSV ليس «مجرد نص» ── الممارسة العملية لـ CSV في تطبيقات C# للأعمال (ترميز الأحرف وتوافق Excel والحماية من الحقن)
نرتّب الحوادث النموذجية لإدخال وإخراج CSV في تطبيقات الأعمال — التحليل اليدوي بـ Split(',')، وتشوه النصوص في Excel بسبب UTF-8 بلا BOM، وس...
لا تُحِط HttpClient بـ using ── الممارسة العملية لاتصال HTTP في تطبيقات C# للأعمال (أنماط الإنشاء والمهلة وإعادة المحاولة)
إنشاء HttpClient داخل using في كل مرة يستنزف المقابس، وجعله static يمنعه من متابعة تغيّر DNS. نرتّب نمط الإنشاء الصحيح عبر PooledConnecti...
أين يتصل هذا الموضوع
ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.
تطوير تطبيقات ويندوز
تصميم بنية السجلّات وسجلّ الأحداث وETW يدخل ضمن نطاق استشارات تطوير تطبيقات Windows.
التحقيق في الأخطاء وتحليل السبب الجذري
تصميم السجلّات بهدف التحقيق في الأعطال يتّصل مباشرة باستشارات التحقيق في الأعطال وتحليل الأسباب.
الأسئلة الشائعة
أسئلة شائعة حول موضوع هذه المقالة.
- إن وُجد سجلّ ملفّات، فهل نحن بغنى عن سجلّ الأحداث؟
- لأغراض التحقيق التفصيلي، غالباً ما يكفي سجلّ الملفّات وحده. لكن إن أردت تمكين القائمين على التشغيل من ملاحظة أعطال التطبيق عبر عارض الأحداث القياسي في نظام التشغيل أو أدوات المراقبة (كإشعارات فشل جدولة المهام أو SCOM)، فالجمع بين ذلك وإخراج نقاط جوهريّة فقط إلى سجلّ الأحداث خيار عملي. لا تنظر إلى الوسيلتين كبديلتين لبعضهما، بل اعتبرهما سجلّين في طبقتين مختلفتين لقارئين مختلفين.
- كيف نحدّد قاعدة ترقيم معرّفات الأحداث (Event ID)؟
- لا توجد إجابة قطعيّة، لكنّ الالتزام بثلاث نقاط عمليّاً يقلّل الحوادث: «تقسيم النطاقات حسب المجال الوظيفي (المجال 1000 لأحداث التشغيل، والمجال 2000 لأحداث الاتّصال، وهكذا)»، و«عدم تغيير معنى معرّف صُرِف مرّة، وعدم إعادة استخدامه»، و«تقبّل وجود أرقام مفقودة». بما أنّ EventId في ILogger يُستخدَم أيضاً كمفتاح بحث في السجلّات المهيكلة، فإنّ نظام ترقيم يسهل توثيقه لاحقاً يفيد عند الصيانة.
- نستخدم Serilog أو NLog، فما علاقة ذلك بمحتوى هذا المقال؟
- Serilog وNLog هما أحد أشكال التنفيذ التي توزّع نفس السجلّ، تحت ILogger، إلى عدّة وجهات (sinks) مثل الملفّات وسجلّ الأحداث وETW وSaaS خارجي. سياسة التصميم التي تناولها هذا المقال ── «إخراج النقاط الجوهريّة فقط إلى سجلّ الأحداث» و«عدم كسر قالب الرسالة (message template)» ── تنطبق بالتساوي سواء استخدمت ILogger مباشرة أو عبر Serilog/NLog. أمّا استخدام وجهة (sink) مخصّصة لسجلّ الأحداث (مثل Serilog.Sinks.EventLog) أو استخدام AddEventLog القياسي فهو خيار تنفيذي.
- إلى أيّ مدى ينبغي تعلّم ETW؟
- إن كان الهدف الرئيسي تطوير تطبيقات الأعمال وصيانتها، يكفي إتقان إنشاء مزوّد (provider) خاص عبر EventSource، وتجميع أحداث ذلك المزوّد عبر dotnet-trace. أمّا تحليل مكدّس الاستدعاءات (call stack) عبر PerfView وجمع أحداث النواة عبر WPR فمجال يكفي تعلّمه عند التخصّص في تحقيقات الأداء، وقد استُبعِد عمداً من نطاق هذا المقال.
الملف الشخصي للمؤلف
صفحة الملف الشخصي لمؤلف المقالة.
غو كومورا
مؤسّس شركة كومورا سوفت ذ.م.م.
يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.