Luc*_*asa 6 c# windows logging
我想要完成的是一个C#应用程序,它将从Windows事件日志中读取日志并将它们存储在其他位置。这必须很快,因为将要安装它的某些设备会生成大量的日志/秒。
到目前为止,我已经尝试了三种方法:
本地WMI:效果不好,有太多错误和异常,这些异常和异常是由需要加载的集合的大小引起的。EventLogReader:我虽然这是一个完美的解决方案,但是它允许您使用XPath表达式查询事件日志。问题是,当您想获取每个日志的消息内容时(通过调用FormatDescription()),对于长集合来说会花费太多时间。例如:如果我浏览它们,我可以在0.11s内读取12k日志。如果我添加一行来为每个日志存储消息,那么将花费近6分钟的时间来完成完全相同的操作,这对于如此低的日志数量是完全疯狂的。我不知道是否可以对EventLogReader进行某种优化以更快地获取消息,我无法
我还发现,您可以使用名为EventLog的类读取日志条目。但是,该技术不允许您输入任何类型的过滤器,因此您基本上必须将整个日志列表加载到内存中,然后根据需要将其过滤掉。这是一个例子:
EventLog eventLog = EventLog.GetEventLogs().FirstOrDefault(el => el.Log.Equals("Security", StringComparison.OrdinalIgnoreCase));
var newEntries = (from entry in eventLog.Entries.OfType()
orderby entry.TimeWritten ascending
where entry.TimeWritten > takefrom
select entry);
Run Code Online (Sandbox Code Playgroud)
尽管获取消息的速度更快,但是内存的使用可能会很高,我不想在将部署此解决方案的设备上引起任何问题。
有人可以帮我吗?我找不到实现这种目标的任何解决方法或方法。
谢谢!。
您可以尝试一下 EventLogReader 类。请参阅https://learn.microsoft.com/en-us/previous-versions/bb671200(v=vs.90)。
它比 EventLog 类更好,因为访问 EventLog.Entries 集合有一个令人讨厌的属性,即当您从中读取数据时,其计数可能会发生变化。更糟糕的是,读取发生在 IO 线程池线程上,这将使您的应用程序因未处理的异常而崩溃。至少几年前是这样。
EventLogReader 还使您能够提供查询字符串来过滤您感兴趣的事件。如果您编写新的应用程序,这就是要走的路。
这是一个应用程序,展示了如何并行阅读:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Eventing.Reader;
using System.Linq;
using System.Threading.Tasks;
namespace EventLogReading
{
class Program
{
static volatile bool myHasStoppedReading = false;
static void ParseEventsParallel()
{
var sw = Stopwatch.StartNew();
var query = new EventLogQuery("Application", PathType.LogName, "*");
const int BatchSize = 100;
ConcurrentQueue<EventRecord> events = new ConcurrentQueue<EventRecord>();
var readerTask = Task.Factory.StartNew(() =>
{
using (EventLogReader reader = new EventLogReader(query))
{
EventRecord ev;
bool bFirst = true;
int count = 0;
while ((ev = reader.ReadEvent()) != null)
{
if ( count % BatchSize == 0)
{
events.Enqueue(ev);
}
count++;
}
}
myHasStoppedReading = true;
});
ConcurrentQueue<KeyValuePair<string, EventRecord>> eventsWithStrings = new ConcurrentQueue<KeyValuePair<string, EventRecord>>();
Action conversion = () =>
{
EventRecord ev = null;
using (var reader = new EventLogReader(query))
{
while (!myHasStoppedReading || events.TryDequeue(out ev))
{
if (ev != null)
{
reader.Seek(ev.Bookmark);
for (int i = 0; i < BatchSize; i++)
{
ev = reader.ReadEvent();
if (ev == null)
{
break;
}
eventsWithStrings.Enqueue(new KeyValuePair<string, EventRecord>(ev.FormatDescription(), ev));
}
}
}
}
};
Parallel.Invoke(Enumerable.Repeat(conversion, 8).ToArray());
sw.Stop();
Console.WriteLine($"Got {eventsWithStrings.Count} events with strings in {sw.Elapsed.TotalMilliseconds:N3}ms");
}
static void ParseEvents()
{
var sw = Stopwatch.StartNew();
List<KeyValuePair<string, EventRecord>> parsedEvents = new List<KeyValuePair<string, EventRecord>>();
using (EventLogReader reader = new EventLogReader(new EventLogQuery("Application", PathType.LogName, "*")))
{
EventRecord ev;
while ((ev = reader.ReadEvent()) != null)
{
parsedEvents.Add(new KeyValuePair<string, EventRecord>(ev.FormatDescription(), ev));
}
}
sw.Stop();
Console.WriteLine($"Got {parsedEvents.Count} events with strings in {sw.Elapsed.TotalMilliseconds:N3}ms");
}
static void Main(string[] args)
{
ParseEvents();
ParseEventsParallel();
}
}
}
Run Code Online (Sandbox Code Playgroud)
Run Code Online (Sandbox Code Playgroud)Got 20322 events with strings in 19,320.047ms Got 20323 events with strings in 5,327.064ms
这提供了 4 倍的不错的加速。我需要使用一些技巧来获得更快的速度,因为出于某种奇怪的原因,类 ProviderMetadataCachedInformation 不是线程安全的,并且在 Format 方法周围使用内部锁(this),这会导致并行读取失败。关键技巧是再次打开转换线程中的事件日志,然后通过事件书签 Api 读取一堆查询事件。这样您就可以独立格式化字符串。
更新1
我在 .NET 5 中进行了一项更改,将性能提高了 3 至 20 倍。请参阅https://github.com/dotnet/runtime/issues/34568。您还可以从 .NET Core 复制 EventLogReader 类并使用此类,这将为您带来相同的加速。
我的博客文章描述了完整的传奇故事:https://aloiskraus.wordpress.com/2020/07/20/ms-performance-hud-analyze-eventlog-reading-performance-in-realtime/
我们在评论中讨论了一些有关阅读现有日志的信息,可以Security通过访问以下内容来访问 - 标记的日志:
var eventLog = new EventLog("Security");
for (int i = 0; i < eventLog.Entries.Count; i++)
{
Console.WriteLine($"{eventLog.Entries[i].Message}");
}
Run Code Online (Sandbox Code Playgroud)
这可能不是最干净的(性能方面的)方法,但我怀疑其他方法会更快,因为您自己已经通过尝试不同的技术发现了这一点。对 Alois 帖子的一个小编辑:EventLogReader开箱即用并不比 更快EventLog,特别是当使用for-loop上面代码块中显示的机制时,我认为EventLog更快 - 它只使用索引访问循环内的条目,集合Entries是只是一个引用,而在使用 时EventLogReader,它将首先执行查询并循环该结果,这应该会更慢。正如 Alois 帖子中评论的那样:如果您不需要使用查询选项,只需使用EventLog变体即可。如果您确实需要查询,请按EventLogReader原样使用,可以在比使用时更低的级别上查询EventLog(仅 LINQ 查询,这当然比执行查找时查询慢)。
为了防止您将来再次遇到这种麻烦,并且因为您说您正在运行服务,所以我将使用EventLog类的EntryWritten事件:
var eventLog = new EventLog("Security")
{
EnableRaisingEvents = true
};
eventLog.EntryWritten += EventLog_EntryWritten;
// .. read existing logs or do other work ..
private static void EventLog_EntryWritten(object sender, EntryWrittenEventArgs e)
{
Console.WriteLine($"received new entry: {e.Entry.Message}");
}
Run Code Online (Sandbox Code Playgroud)
请注意,您必须将EnableRaisingEvents设置为true,以便在记录新条目时触发该事件。启动(例如) Task也是一个很好的做法(也是性能方面的),这样系统在对事件的调用进行排队时就不会锁定自身。
如果您想检索所有新创建的事件,则此方法效果很好,如果您想检索新创建的事件但对这些事件使用查询(过滤器),您可以查看 EventLogWatcher类,但在您的情况下,当没有约束,我只使用该EntryWritten事件,因为您不需要过滤器并且为了简单。
| 归档时间: |
|
| 查看次数: |
540 次 |
| 最近记录: |