فصل ۱۳: Event Log، Performance Counter، Stopwatch و ابزارهای Cross-Platform

فصل ۱۳: Event Log، Performance Counter، Stopwatch و ابزارهای Cross-Platform

فصل ۱۳: Event Log، Performance Counter، Stopwatch و ابزارهای Cross-Platform

فصل ۱۳: Windows Event Log، Performance Counterها و ابزارهای Cross-Platform

ادامهٔ مثال نوشتن در Windows Event Log:

const string SourceName = "MyCompany.WidgetServer";

// CreateEventSource requires administrative permissions, so this would
// typically be done in application setup.
if (!EventLog.SourceExists (SourceName))
  EventLog.CreateEventSource (SourceName, "Application");

EventLog.WriteEntry (SourceName,
  "Service started; using configuration file=...",
  EventLogEntryType.Information);

EventLogEntryType می‌تواند Information، Warning، Error، SuccessAudit یا FailureAudit باشد. هرکدام با Icon متفاوتی در Windows Event Viewer نمایش داده می‌شوند. همچنین می‌توانید Category و Event ID اختیاری مشخص کنید ـ هرکدام Number انتخابی خودتان ـ و Binary Data اختیاری نیز ارائه دهید.

CreateEventSource اجازه می‌دهد Machine Name نیز مشخص کنید؛ برای نوشتن در Event Log Computer دیگری، در صورتی که Permission کافی داشته باشید.

خواندن Event Log

برای خواندن Event Log، Class با نام EventLog را با Name مربوط به Log موردنظر Instantiate کنید و به‌صورت اختیاری Name Computer دیگری را که Log روی آن قرار دارد بدهید. سپس هر Log Entry از طریق Property با نام Entries خوانده می‌شود:

EventLog log = new EventLog ("Application");

Console.WriteLine ("Total entries: " + log.Entries.Count);

EventLogEntry last = log.Entries [log.Entries.Count - 1];
Console.WriteLine ("Index:   " + last.Index);
Console.WriteLine ("Source: " + last.Source);
Console.WriteLine ("Type:    " + last.EntryType);
Console.WriteLine ("Time:    " + last.TimeWritten);
Console.WriteLine ("Message: " + last.Message);

می‌توانید همهٔ Logها را برای Computer فعلی یا Computer دیگری با Method استاتیک EventLog.GetEventLogs Enumerate کنید؛ برای دسترسی کامل Permission مدیریتی لازم است:

foreach (EventLog log in EventLog.GetEventLogs())
  Console.WriteLine (log.LogDisplayName);

این معمولاً حداقل Application، Security و System را چاپ می‌کند.

Monitoring Event Log

از طریق Event با نام EntryWritten می‌توانید هر زمان Entryای در Windows Event Log نوشته شد Alert دریافت کنید. این قابلیت برای Event Logهای Computer محلی کار می‌کند و مستقل از اینکه کدام Application Event را Log کرده است Fire می‌شود.

برای فعال‌کردن Monitoring مربوط به Log:

  1. یک EventLog Instantiate کنید و Property با نام EnableRaisingEvents را true قرار دهید.
  2. Event با نام EntryWritten را Handle کنید.

مثال:

using (var log = new EventLog ("Application"))
{
  log.EnableRaisingEvents = true;
  log.EntryWritten += DisplayEntry;
  Console.ReadLine();
}

void DisplayEntry (object sender, EntryWrittenEventArgs e)
{
  EventLogEntry entry = e.Entry;
  Console.WriteLine (entry.Message);
}

Performance Counterها

مکانیزم‌های Logging که تاکنون بررسی کردیم برای ثبت Information جهت Analysis آینده مفیدند. اما برای فهم State فعلی یک Application یا کل System، رویکرد Real-timeتری لازم است. راه‌حل Win32 برای این نیاز Infrastructure مربوط به Performance Monitoring است؛ شامل مجموعه‌ای از Performance Counterها که System و Applicationها Expose می‌کنند و Snap-inهای Microsoft Management Console یا MMC که برای Monitor کردن Real-time این Counterها استفاده می‌شوند.

Performance Counterها در Categoryهایی مانند System، Processor، .NET CLR Memory و غیره Group می‌شوند. GUI Toolها گاهی این Categoryها را «Performance Object» می‌نامند. هر Category مجموعهٔ مرتبطی از Counterها را Group می‌کند که یک جنبه از System یا Application را Monitor می‌کنند. نمونه‌های Counter در Category با نام .NET CLR Memory عبارت‌اند از % Time in GC، # Bytes in All Heaps و Allocated bytes/sec.

هر Category می‌تواند به‌صورت اختیاری یک یا چند Instance داشته باشد که مستقل Monitor شوند. برای مثال Counter با نام % Processor Time در Category با نام Processor برای Monitor کردن CPU Utilization مفید است. روی Machine چندپردازنده‌ای، این Counter برای هر CPU یک Instance دارد تا Utilization هر CPU را جداگانه ببینید.

بخش‌های بعدی کارهای رایج مانند مشخص‌کردن Counterهای Exposeشده، Monitor کردن یک Counter و ساخت Counterهای خودتان برای Expose کردن Status Application را نشان می‌دهند.

Enumerate کردن Counterهای موجود

مثال زیر همهٔ Performance Counterهای موجود روی Computer را Enumerate می‌کند. برای Categoryهایی که Instance دارند، Counterهای هر Instance را نیز Enumerate می‌کند:

PerformanceCounterCategory[] cats =
  PerformanceCounterCategory.GetCategories();

foreach (PerformanceCounterCategory cat in cats)
{
  Console.WriteLine ("Category: " + cat.CategoryName);

  string[] instances = cat.GetInstanceNames();
  if (instances.Length == 0)
  {
    foreach (PerformanceCounter ctr in cat.GetCounters())
       Console.WriteLine (" Counter: " + ctr.CounterName);
  }
  else    // Dump counters with instances
  {
    foreach (string instance in instances)
    {
       Console.WriteLine (" Instance: " + instance);
       if (cat.InstanceExists (instance))
         foreach (PerformanceCounter ctr in cat.GetCounters (instance))
           Console.WriteLine ("    Counter: " + ctr.CounterName);
    }
  }
}

مثال بعد با LINQ فقط .NET Performance Counterها را می‌گیرد و Result را در XML File می‌نویسد:

var x =
  new XElement ("counters",
    from PerformanceCounterCategory cat in
         PerformanceCounterCategory.GetCategories()
    where cat.CategoryName.StartsWith (".NET")
    let instances = cat.GetInstanceNames()
    select new XElement ("category",
      new XAttribute ("name", cat.CategoryName),
      instances.Length == 0
      ?
        from c in cat.GetCounters()
        select new XElement ("counter",
          new XAttribute ("name", c.CounterName))
      :
        from i in instances
        select new XElement ("instance", new XAttribute ("name", i),
          !cat.InstanceExists (i)
          ?
            null
          :
            from c in cat.GetCounters (i)
            select new XElement ("counter",
              new XAttribute ("name", c.CounterName))
        )
    )
  );
x.Save ("counters.xml");

خواندن Data مربوط به Performance Counter

برای گرفتن Value یک Performance Counter، Object با نام PerformanceCounter را Instantiate و سپس Method با نام NextValue یا NextSample را فراخوانی کنید. NextValue یک Float ساده برمی‌گرداند؛ NextSample یک CounterSample می‌دهد که Propertyهای پیشرفته‌تری مانند CounterFrequency، TimeStamp، BaseValue و RawValue را Expose می‌کند.

Constructor مربوط به PerformanceCounter، Category Name، Counter Name و Instance اختیاری می‌گیرد. بنابراین برای نمایش Processor Utilization فعلی همهٔ CPUها:

using PerformanceCounter pc = new PerformanceCounter ("Processor",
                                                      "% Processor Time",
                                                      "_Total");
Console.WriteLine (pc.NextValue());

یا برای نمایش مصرف Memory «واقعی» ـ Private ـ Process فعلی:

string procName = Process.GetCurrentProcess().ProcessName;
using PerformanceCounter pc = new PerformanceCounter ("Process",
                                                      "Private Bytes",
                                                      procName);
Console.WriteLine (pc.NextValue());

PerformanceCounter Event با نام ValueChanged Expose نمی‌کند، بنابراین برای Monitor کردن Change باید Poll کنید. در مثال بعد هر 200 ms Poll می‌کنیم تا با یک EventWaitHandle Signal مربوط به Quit دریافت شود:

// need to import System.Threading as well as System.Diagnostics

static void Monitor (string category, string counter, string instance,
                     EventWaitHandle stopper)
{
  if (!PerformanceCounterCategory.Exists (category))
    throw new InvalidOperationException ("Category does not exist");

  if (!PerformanceCounterCategory.CounterExists (counter, category))
    throw new InvalidOperationException ("Counter does not exist");

  if (instance == null) instance = "";   // "" == no instance (not null!)
  if (instance != "" &&
      !PerformanceCounterCategory.InstanceExists (instance, category))
    throw new InvalidOperationException ("Instance does not exist");

  float lastValue = 0f;
  using (PerformanceCounter pc = new PerformanceCounter (category,
                                                       counter, instance))
    while (!stopper.WaitOne (200, false))
    {
      float value = pc.NextValue();
      if (value != lastValue)         // Only write out the value
      {                               // if it has changed.
        Console.WriteLine (value);
        lastValue = value;
      }
    }
}

این Method را می‌توان برای Monitor همزمان Activity مربوط به Processor و Hard Drive چنین استفاده کرد:

EventWaitHandle stopper = new ManualResetEvent (false);

new Thread (() =>
  Monitor ("Processor", "% Processor Time", "_Total", stopper)
).Start();

new Thread (() =>
  Monitor ("LogicalDisk", "% Idle Time", "C:", stopper)
).Start();

Console.WriteLine ("Monitoring - press any key to quit");
Console.ReadKey();
stopper.Set();

ساخت Counter و نوشتن Performance Data

پیش از نوشتن Performance Counter Data باید Performance Category و Counter بسازید. باید Performance Category را همراه با تمام Counterهای متعلق به آن در یک Step بسازید:

string category = "Nutshell Monitoring";

// We'll create two counters in this category:
string eatenPerMin = "Macadamias eaten so far";
string tooHard = "Macadamias deemed too hard";
if (!PerformanceCounterCategory.Exists (category))
{
  CounterCreationDataCollection cd = new CounterCreationDataCollection();

  cd.Add (new CounterCreationData (eatenPerMin,
          "Number of macadamias consumed, including shelling time",
          PerformanceCounterType.NumberOfItems32));

  cd.Add (new CounterCreationData (tooHard,
          "Number of macadamias that will not crack, despite much effort",
          PerformanceCounterType.NumberOfItems32));

  PerformanceCounterCategory.Create (category, "Test Category",
    PerformanceCounterCategoryType.SingleInstance, cd);
}

Counterهای جدید سپس در Windows Performance Monitoring Tool هنگام انتخاب Add Counters ظاهر می‌شوند. اگر بعداً بخواهید Counter بیشتری در همان Category تعریف کنید، ابتدا باید Category قدیمی را با PerformanceCounterCategory.Delete حذف کنید.

بعد از ساخت Counter، می‌توانید Value آن را با Instantiate کردن PerformanceCounter، قرار دادن ReadOnly=false و تنظیم RawValue Update کنید. همچنین Methodهای Increment و IncrementBy برای Update کردن Value موجود وجود دارند:

string category = "Nutshell Monitoring";
string eatenPerMin = "Macadamias eaten so far";

using (PerformanceCounter pc = new PerformanceCounter (category,
                                                       eatenPerMin, ""))
{
  pc.ReadOnly = false;
  pc.RawValue = 1000;
  pc.Increment();
  pc.IncrementBy (10);
  Console.WriteLine (pc.NextValue());    // 1011
}

Class با نام Stopwatch

Class با نام Stopwatch مکانیزم راحتی برای اندازه‌گیری Execution Time فراهم می‌کند. Stopwatch از بالاترین Resolution موجود در OS و Hardware استفاده می‌کند که معمولاً کمتر از یک Microsecond است. در مقابل، DateTime.Now و Environment.TickCount Resolution حدود 15 ms دارند.

برای استفاده از Stopwatch، StartNew را فراخوانی کنید؛ این Method یک Stopwatch می‌سازد و شروع به Tick کردن می‌کند. به‌عنوان جایگزین می‌توانید دستی Instantiate و سپس Start را فراخوانی کنید. Property با نام Elapsed Interval سپری‌شده را به‌صورت TimeSpan برمی‌گرداند:

Stopwatch s = Stopwatch.StartNew();
System.IO.File.WriteAllText ("test.txt", new string ('*', 30000000));
Console.WriteLine (s.Elapsed);       // 00:00:01.4322661

Stopwatch همچنین ElapsedTicks را Expose می‌کند که تعداد Tickهای سپری‌شده را به‌صورت long برمی‌گرداند. برای تبدیل Tick به Second، بر StopWatch.Frequency تقسیم کنید. Property با نام ElapsedMilliseconds نیز وجود دارد که اغلب راحت‌ترین گزینه است.

فراخوانی Stop مقدار Elapsed و ElapsedTicks را Freeze می‌کند. Stopwatch در حالت «Running» هیچ Background Activity ایجاد نمی‌کند، بنابراین Call کردن Stop اختیاری است.

ابزارهای Diagnostic چندسکویی (Cross-Platform Diagnostic Tools)

در این بخش Toolهای Cross-platform Diagnostic در .NET را به‌اختصار معرفی می‌کنیم:

dotnet-counters
نمای کلی از State یک Application در حال اجرا ارائه می‌کند.
dotnet-trace
برای Performance Monitoring و Event Monitoring با Detail بیشتر.
dotnet-dump
برای گرفتن Memory Dump به‌صورت On-demand یا پس از Crash.

این Toolها به Administrative Elevation نیاز ندارند و برای هر دو Environment با نام Development و Production مناسب‌اند.

dotnet-counters

Tool با نام dotnet-counters مصرف Memory و CPU یک .NET Process را Monitor و Data را در Console یا File می‌نویسد.

برای Install کردن Tool، Command زیر را از Command Prompt یا Terminalای که dotnet در Path آن است اجرا کنید:

dotnet tool install --global dotnet-counters

سپس می‌توانید Process را Monitor کنید:

dotnet-counters monitor System.Runtime --process-id <<ProcessID>>

System.Runtime یعنی می‌خواهیم همهٔ Counterهای Category با نام System.Runtime را Monitor کنیم. می‌توانید Category یا Counter Name مشخص کنید؛ Command با نام dotnet-counters list همهٔ Categoryها و Counterهای موجود را List می‌کند.

Output به‌صورت مداوم Refresh می‌شود و شبیه زیر است:

Press p to pause, r to resume, q to quit.
    Status: Running

[System.Runtime]
    # of Assemblies Loaded                                                  63
    % Time in GC (since last GC)                                             0
    Allocation Rate (Bytes / sec)                                      244,864
    CPU Usage (%)                                                            6
    Exceptions / sec                                                         0
    GC Heap Size (MB)                                                        8
    Gen 0 GC / sec                                                           0
    Gen 0 Size (B)                                                     265,176
    Gen 1 GC / sec                                                           0
    Gen 1 Size (B)                                                     451,552
    Gen 2 GC / sec                                                           0
    Gen 2 Size (B)                                                          24
    LOH Size (B)                                                     3,200,296
    Monitor Lock Contention Count / sec                                      0
    Number of Active Timers                                                  0
    ThreadPool Completed Work Items / sec                                   15
    ThreadPool Queue Length                                                  0
    ThreadPool Threads Count                                                 9
    Working Set (MB)                                                        52

Commandهای موجود:

Commandهای dotnet-counters
Commandکاربرد
listList مربوط به Counter Nameها را همراه Description هرکدام نمایش می‌دهد.
psList مربوط به dotnet Processهای واجد شرایط Monitoring را نمایش می‌دهد.
monitorValueهای Counterهای انتخاب‌شده را نمایش می‌دهد و دوره‌ای Refresh می‌کند.
collectCounter Information را در File ذخیره می‌کند.

Parameterها و Optionهای پشتیبانی‌شده:

Optionها و Argumentهای dotnet-counters
Option / Argumentکاربرد
--versionVersion مربوط به dotnet-counters را نمایش می‌دهد.
-h, --helpHelp برنامه را نمایش می‌دهد.
-p, --process-idID مربوط به dotnet Process برای Monitor؛ در Commandهای monitor و collect کاربرد دارد.
--refresh-intervalRefresh Interval موردنظر را بر حسب Second تعیین می‌کند؛ برای monitor و collect.
-o, --outputOutput File Name را تعیین می‌کند؛ برای collect.
--formatOutput Format را تعیین می‌کند؛ Valueهای معتبر csv یا json هستند؛ برای collect.

dotnet-trace

Traceها Recordهای Timestamped از Eventهای Program شما هستند؛ مانند فراخوانی یک Method یا Query شدن Database. Traceها می‌توانند Performance Metric و Custom Event نیز داشته باشند و Context محلی مانند Value مربوط به Local Variable را شامل شوند. به‌طور سنتی .NET Framework و Frameworkهایی مانند ASP.NET از ETW استفاده می‌کردند. در .NET 5، Application Traceها روی Windows در ETW و روی Linux در LTTng نوشته می‌شوند.

برای Install:

dotnet tool install --global dotnet-trace

برای شروع Recording Eventهای Program:

dotnet-trace collect --process-id <<ProcessId>>

این Command، dotnet-trace را با Profile پیش‌فرض اجرا می‌کند که CPU و .NET Runtime Eventها را Collect و در File با نام trace.nettrace می‌نویسد. می‌توانید Profileهای دیگر را با Switch با نام --profile مشخص کنید: gc-verbose Garbage Collection و Sampled Object Allocation را Track می‌کند و gc-collect Garbage Collection را با Overhead کم Track می‌کند. Switch با نام -o اجازه می‌دهد Output Filename متفاوتی بدهید.

Output پیش‌فرض File با Format مربوط به .netperf است که روی Windows مستقیماً با Tool با نام PerfView قابل Analysis است. همچنین می‌توانید به dotnet-trace بگویید File سازگار با Speedscope بسازد؛ Speedscope یک سرویس رایگان Online Analysis است. آدرس متن منبع: https://speedscope.app. برای ساخت File با پسوند .speedscope.json از Option با نام --format speedscope استفاده کنید.

Commandهای پشتیبانی‌شده:

Commandهای dotnet-trace
Commandکاربرد
collectRecording مربوط به Counter/Event Information را در File شروع می‌کند.
psList مربوط به dotnet Processهای واجد شرایط Monitoring را نشان می‌دهد.
list-profilesProfileهای آمادهٔ Tracing را همراه Description مربوط به Providerها و Filterهای هرکدام List می‌کند.
convert <file>Format با نام nettrace یا .netperf را به Format جایگزین تبدیل می‌کند؛ در حال حاضر فقط speedscope Target است.

Custom Trace Eventها

Application شما می‌تواند با تعریف Custom EventSource Event سفارشی Emit کند:

[EventSource (Name = "MyTestSource")]
public sealed class MyEventSource : EventSource
{
  public static MyEventSource Instance = new MyEventSource ();
  MyEventSource() : base (EventSourceSettings.EtwSelfDescribingEventFormat)
  {
  }

  public void Log (string message, int someNumber)
  {
    WriteEvent (1, message, someNumber);
  }
}

Method با نام WriteEvent Overload شده تا ترکیب‌های مختلفی از Typeهای ساده ـ عمدتاً String و Integer ـ بگیرد. سپس می‌توانید چنین فراخوانی کنید:

MyEventSource.Instance.Log ("Something", 123);

هنگام فراخوانی dotnet-trace باید Name هر Custom Event Sourceای را که می‌خواهید Record شود مشخص کنید:

dotnet-trace collect --process-id <<ProcessId>> --providers MyTestSource

dotnet-dump

Dump که گاهی Core Dump نامیده می‌شود Snapshot از State مربوط به Virtual Memory یک Process است. می‌توانید از Process در حال اجرا به‌صورت On-demand Dump بگیرید یا OS را Configure کنید تا هنگام Crash Application Dump بسازد.

در Ubuntu Linux، Command زیر Core Dump هنگام Crash Application را فعال می‌کند؛ Stepهای لازم بین Flavorهای مختلف Linux می‌تواند متفاوت باشد:

ulimit -c unlimited

در Windows از regedit.exe برای ساخت یا ویرایش Key زیر در Local Machine Hive استفاده کنید:

SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps

زیر آن Keyای با همان Name مربوط به Executable خود، مثلاً foo.exe، اضافه کنید و زیر آن Key موارد زیر را بسازید:

  • DumpFolder با Type با نام REG_EXPAND_SZ و Value مربوط به Path مقصد Dump Fileها.
  • DumpType با Type با نام REG_DWORD و Value برابر 2 برای درخواست Full Dump.
  • به‌صورت اختیاری DumpCount با Type با نام REG_DWORD برای تعیین حداکثر تعداد Dump File پیش از حذف قدیمی‌ترین File.

برای Install Tool:

dotnet tool install --global dotnet-dump

بعد از Install می‌توانید بدون پایان‌دادن Process به‌صورت On-demand Dump بگیرید:

dotnet-dump collect --process-id <<YourProcessId>>

Command زیر Interactive Shell را برای Analysis یک Dump File آغاز می‌کند:

dotnet-dump analyze <<dumpfile>>

اگر Exception باعث Down شدن Application شده باشد، می‌توانید از Command با نام printexceptions ـ کوتاه‌شده pe ـ برای نمایش Detailهای آن Exception استفاده کنید. Shell مربوط به dotnet-dump Commandهای اضافی زیادی دارد که با Command با نام help می‌توانید آن‌ها را List کنید.

پایان محتوای تخصیص‌یافته از فایل PDF برای این مقاله.

امتیاز کاربران به این مقاله

☆☆☆☆☆

0 نفر امتیاز داده اند. میانگین: 0.0 از 5

 

0 نظر

نظر محترم شما در مورد مقاله های وب سایت برنامه نویسی و پایگاه داده

نظرات محترم شما در خدمات رسانی بهتر ما را یاری می نمایند. لطفا اگر مایل بودید یک نظر ما را مهمان فرمائید. آدرس ایمیل و وب سایت شما نمایش داده نخواهد شد.

0 / 500

اطلاعات تماس

  • آدرس:اصفهان-خیابان ام کلثوم غربی - بعد خیابان تخم چی - بیست متر بعد از پیتزا ننه شب - کوچه تعمیر گاه سمار زغالی - پلاک 354 - درب مشکی - طبقه هفتم
  • آدرس ایمیل:najafzade@gmail.com
  • وب سایت:http://www.a00b.com/
  • تلفن ثابت:(+98)9131253620
  • تلفن همراه:09131253620