65.9K
CodeProject 正在变化。 阅读更多。
Home

实时事件日志读取器

2018年5月4日

CPOL

3分钟阅读

viewsIcon

36747

downloadIcon

1445

RealTimeEventLogReader 将向您展示如何在 C# 中读取事件日志,只要它们被写入 Windows 事件日志中。

引言

RealTime Event Log Reader 可用于实时读取 Windows 事件日志,即,只要日志被写入 Windows 事件日志中,就立即读取。

考虑这样一种情况:某些其他应用程序正快速写入事件日志。在这种情况下,如果我们使用 EventLog.EntryWritten Event 来读取最新的日志,那么只要条目被添加到 Windows 事件日志中,此事件就会被触发。这会减慢应用程序的速度或导致其挂起。

此问题的解决方案就在这里。 我使用 EventLogQueryEventLogReader 类来读取最近的事件。

LogReader 类计算自上次读取事件以来经过的时间。 然后,使用创建时间上的过滤器,它会读取在经过的时间内写入的日志。

然后,这些记录将发布到主窗口以在网格上显示。

运行 RealTimeEventLogReader 时,在将某些内容写入 Windows 事件日志之前,它不会显示任何日志。

RealTimeEventLogReader 将不会读取在运行 RealTimeEventLogReader 之前 Windows 事件日志中已存在的现有日志。

  • 在下面的窗口中输入日志名称,然后单击“确定”。

    Log Name

  • 单击“确定”按钮后,如果它是一个有效的日志名称,将显示以下窗口。

    Main Window

  • 通过启动/停止某些服务来生成一些日志。
  • 双击网格中的任何条目。

    Details

Using the Code

在此处下载源代码 - 

https://codeproject.org.cn/KB/tips/1242641/RealTimeEventLogReader.zip

EventLogRecord.cs

public EventLogRecord(EventRecord eventdetail)
{
            eventID = eventdetail.Id;
            detailsXML = eventdetail.ToXml();
            taskCategory = eventdetail.Task.Value;
            timestamp = eventdetail.TimeCreated.Value;
            source = eventdetail.ProviderName;
            //level = (LogLevel)eventdetail.Level.Value;
            levelString = eventdetail.LevelDisplayName;
            logName = eventdetail.LogName;
            eventData = GetEventData(eventdetail);
}

CtorEventRecord 对象作为参数,并创建 EventLogRecord 对象。

EventLogRecord 用于在 Grid 中显示日志。

LogReader.cs

        public delegate void AddRecordDelegate(EventLogRecord record);

        public AddRecordDelegate AddRecord;

AddRecordDelegate 用于将 EventRecord 对象发布到主窗口。

        private Thread readerThread = null;
        private DateTime _lastReadTime = DateTime.UtcNow;
        private const string TimeFormatString = "yyyy-MM-ddTHH:mm:ss.fffffff00K";
        private const string EventReaderQuery = "*[System/TimeCreated/@SystemTime >='{0}']";
  • readerThread 是一个线程,用于及时读取事件日志。
  • _lastReadTime 用于计算经过的时间。
  • TimeFormatString 用于将本地时间格式化为事件记录格式。
  • EventReaderQuery 是基于创建时间读取事件的查询。

线程主循环

private void ReadLogs()
        {
            while (!Stop)
            {
                // 1. Calculate elapsed time since previous read.
                double elapsedTimeSincePreviousRead = (DateTime.UtcNow - _lastReadTime).TotalSeconds;
                DateTime timeSpanToReadEvents = 
                    DateTime.UtcNow.AddSeconds(-elapsedTimeSincePreviousRead);
                string strTimeSpanToReadEvents = 
                    timeSpanToReadEvents.ToString(TimeFormatString, CultureInfo.InvariantCulture);
                string query = string.Format(EventReaderQuery, strTimeSpanToReadEvents);
                int readEventCount = 0;

                // 2. Create event log query using elapsed time.
                // 3. Read the record using EventLogReader.
                EventLogQuery eventsQuery = new EventLogQuery(LogName, PathType.LogName, query) 
                                            { ReverseDirection = true };
                EventLogReader logReader = new EventLogReader(eventsQuery);

                // 4. Set lastReadTime to Date.Now
                _lastReadTime = DateTime.UtcNow;

                for (EventRecord eventdetail = logReader.ReadEvent(); 
                           eventdetail != null; eventdetail = logReader.ReadEvent())
                {
                    byte[] bytes = null;
                    if (eventdetail.Properties.Count >= 2)
                    {
                        bytes = (byte[])eventdetail.Properties[eventdetail.Properties.Count-1].Value;
                    }
                    EventLogRecord record = new EventLogRecord(eventdetail);

                    // 5. Post record read using event log query.
                    // if (parser.IsValid(temporaryRecord))
                    {
                        PostDetail(record);
                    }
                    // 6. Post only latest InternalLogLimit records, 
                    // if result of event log query is more than InternalLogLimit.
                    if (++readEventCount >= LogLimit)
                    {
                        break;
                    }
                }
                Thread.Sleep(ReadInterval);
            }          
        }

ReadLogs 是线程启动方法。 它用于使用 EventLogQueryEventLogReader 读取事件日志。

  • 上面的方法首先计算自上次读取以来经过的时间,并创建查询字符串以读取事件。
  • 使用查询字符串创建 EventLogQuery 对象。
  • 使用 EventLogQuery 对象,使用 EventLogReader 读取事件。
  • 使用 for 循环,使用 PostDetail 方法发布记录。

PostDetail() 方法

private void PostDetail(params EventLogRecord[] records)
        {
            if (AddRecord != null && records != null)
            {
                //Return each record in the list to the viewer using AddRecord
                foreach (EventLogRecord record in records)
                {
                    if (record != null)
                    {
                        AddRecord.BeginInvoke((EventLogRecord)record.Clone(), null, null);
                    }
                }
            }
        }

上面的方法调用 AddRecord 委托。 此方法在 *MainWindow.cs* 中处理。

MainWindow.cs

AddRecord() 方法

private void AddRecord(EventLogRecord record)
        {
            eventLogRecordList.Add(record);
            toolStripStatusLabelNumCount.Text = eventLogRecordList.Count.ToString();
            toolStripStatusLabelStatusString.Text = "Started";
            if (eventLogRecordList.Count >= reader.LogLimit)
            {
                eventLogRecordList.RemoveAt(0);
            }
            gvLogs.FirstDisplayedScrollingRowIndex = gvLogs.RowCount - 1;

        }

eventLogRecordList 用于维护 EventLogRecord 的列表。 还可以绑定到网格以在网格中显示记录。

实时事件日志读取器在网格中显示最近的 5000 条记录。 一旦达到限制,它就会开始从列表底部删除项目。 这可以防止实时事件日志读取器发生内存泄漏。

关注点

如果事件生成速度太快,则使用 EventLog.EntryWritten Event 实时读取事件可能会导致应用程序挂起。

我们需要适当地使用列表,如果我们忘记从列表中清理旧记录,那么经过很长时间后,会导致内存泄漏。

 

历史

2018年5月8日 - 添加了下载源代码的链接。

 

© . All rights reserved.