-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added EtwEventCounter NLog target (#63)
- Loading branch information
Showing
12 changed files
with
301 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
#if NETFRAMEWORK | ||
using Microsoft.Diagnostics.Tracing; | ||
#else | ||
using System.Diagnostics.Tracing; | ||
#endif | ||
using System; | ||
using System.Collections.Concurrent; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using System.Collections; | ||
|
||
namespace NLog.Etw.Tests | ||
{ | ||
internal sealed class EventSourceEnumerator : EventListener | ||
{ | ||
public ConcurrentDictionary<string, List<string>> Events { get; } = new ConcurrentDictionary<string, List<string>>(); | ||
|
||
public EventSourceEnumerator() | ||
{ | ||
EventSourceCreated += OnEventSourceCreated; | ||
EventWritten += OnEventWritten; | ||
} | ||
|
||
#if NETFRAMEWORK | ||
protected override void OnEventWritten(EventWrittenEventArgs eventData) | ||
{ | ||
OnEventWritten(this, eventData); | ||
} | ||
#endif | ||
|
||
private void OnEventWritten(object sender, EventWrittenEventArgs e) | ||
{ | ||
var events = Events[e.EventSource.Name]; | ||
if (events != null) | ||
{ | ||
var message = e.Message; | ||
if (string.IsNullOrEmpty(message) && e.Payload?.Count > 0) | ||
{ | ||
for (int i = 0; i < e.Payload?.Count; ++i) | ||
{ | ||
if (!string.IsNullOrEmpty(message)) | ||
message += "|"; | ||
|
||
if (e.Payload[i] is IEnumerable complexPayload && !(complexPayload is string)) | ||
{ | ||
foreach (var item in complexPayload) | ||
message += item.ToString() + " ,"; | ||
} | ||
else | ||
{ | ||
message += e.Payload[i].ToString(); | ||
} | ||
} | ||
} | ||
events.Add(message); | ||
} | ||
} | ||
|
||
private void OnEventSourceCreated(object sender, EventSourceCreatedEventArgs e) | ||
{ | ||
if (e.EventSource is null) | ||
return; | ||
|
||
if (!Events.ContainsKey(e.EventSource.Name)) | ||
{ | ||
var args = new Dictionary<string, string> { ["EventCounterIntervalSec"] = "1" }; | ||
EnableEvents(e.EventSource, EventLevel.LogAlways, EventKeywords.All, args); | ||
Events.TryAdd(e.EventSource.Name, new List<string>()); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using NLog.Config; | ||
using NLog.Layouts; | ||
using NLog.Targets; | ||
using Xunit; | ||
|
||
namespace NLog.Etw.Tests | ||
{ | ||
public class EventSourceEnumeratorTest | ||
{ | ||
[Fact] | ||
public void EtwEventSourceTargetTest() | ||
{ | ||
// setup NLog configuration | ||
var loggingConfiguration = new LoggingConfiguration(); | ||
var etwTarget = new EtwEventSourceTarget() { ProviderName = nameof(EventSourceEnumeratorTest), Layout = Layout.FromString("${uppercase:${level}}|${message}") }; | ||
loggingConfiguration.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, etwTarget)); | ||
loggingConfiguration.AddTarget("etw", etwTarget); | ||
LogManager.Configuration = loggingConfiguration; | ||
|
||
using (var eventSourceEnumerator = new EventSourceEnumerator()) | ||
{ | ||
// send events to session | ||
var logger = LogManager.GetLogger("A"); | ||
logger.Debug("test-debug"); | ||
logger.Info("test-info"); | ||
logger.Warn("test-warn"); | ||
logger.Error("test-error"); | ||
logger.Fatal("test-fatal"); | ||
|
||
Assert.NotEmpty(eventSourceEnumerator.Events); | ||
Assert.True(eventSourceEnumerator.Events.ContainsKey(nameof(EventSourceEnumeratorTest))); | ||
Assert.NotEmpty(eventSourceEnumerator.Events[nameof(EventSourceEnumeratorTest)]); | ||
Assert.Contains("test-fatal", eventSourceEnumerator.Events[nameof(EventSourceEnumeratorTest)].LastOrDefault()); | ||
} | ||
} | ||
|
||
[Fact] | ||
public void EtwEventCounterTargetTest() | ||
{ | ||
// setup NLog configuration | ||
var loggingConfiguration = new LoggingConfiguration(); | ||
var etwTarget = new EtwEventCounterTarget() { ProviderName = nameof(EventSourceEnumeratorTest), CounterName = nameof(EtwEventCounterTargetTest) }; | ||
loggingConfiguration.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, etwTarget)); | ||
loggingConfiguration.AddTarget("etw", etwTarget); | ||
LogManager.Configuration = loggingConfiguration; | ||
|
||
using (var eventSourceEnumerator = new EventSourceEnumerator()) | ||
{ | ||
// send events to session | ||
var logger = LogManager.GetLogger("A"); | ||
logger.Debug("test-debug"); | ||
logger.Info("test-info"); | ||
logger.Warn("test-warn"); | ||
logger.Error("test-error"); | ||
logger.Fatal("test-fatal"); | ||
|
||
System.Threading.Thread.Sleep(10000); | ||
|
||
Assert.NotEmpty(eventSourceEnumerator.Events); | ||
Assert.True(eventSourceEnumerator.Events.ContainsKey(nameof(EventSourceEnumeratorTest))); | ||
Assert.NotEmpty(eventSourceEnumerator.Events[nameof(EventSourceEnumeratorTest)]); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
#if !NET46 | ||
using System; | ||
using System.Collections.Concurrent; | ||
#if NET45 | ||
using Microsoft.Diagnostics.Tracing; | ||
#else | ||
using System.Diagnostics.Tracing; | ||
#endif | ||
using NLog.Config; | ||
using NLog.Layouts; | ||
using NLog.Targets; | ||
|
||
namespace NLog.Etw | ||
{ | ||
/// <summary> | ||
/// NLog Target to ETW EventCounter. Supporting dynamic EventSource name | ||
/// </summary> | ||
[Target("EtwEventCounter")] | ||
public class EtwEventCounterTarget : Target | ||
{ | ||
private static readonly ConcurrentDictionary<string, EventCounter> EventCounters = new ConcurrentDictionary<string, EventCounter>(StringComparer.OrdinalIgnoreCase); | ||
|
||
private EventCounter _eventCounter; | ||
|
||
/// <summary> | ||
/// Name used for the <see cref="EventSource" />-contructor | ||
/// </summary> | ||
[RequiredParameter] | ||
public Layout ProviderName { get; set; } | ||
|
||
/// <summary> | ||
/// Name used for the <see cref="EventCounter" />-contructor | ||
/// </summary> | ||
[RequiredParameter] | ||
public Layout CounterName { get; set; } | ||
|
||
/// <summary> | ||
/// The value by which to increment the counter. | ||
/// </summary> | ||
public Layout<int> MetricValue { get; set; } = 1; | ||
|
||
/// <inheritdoc/> | ||
protected override void InitializeTarget() | ||
{ | ||
var providerName = RenderLogEvent(ProviderName, LogEventInfo.CreateNullEvent())?.Trim(); | ||
if (string.IsNullOrEmpty(providerName)) | ||
{ | ||
throw new NLogConfigurationException("EtwEventCounterTarget - ProviderName must be configured"); | ||
} | ||
|
||
var counterName = RenderLogEvent(CounterName, LogEventInfo.CreateNullEvent())?.Trim(); | ||
if (string.IsNullOrEmpty(counterName)) | ||
{ | ||
throw new NLogConfigurationException("EtwEventCounterTarget - CounterName must be configured"); | ||
} | ||
|
||
if (!EtwEventSourceTarget.EventSources.TryGetValue(providerName, out var eventSource)) | ||
{ | ||
eventSource = new EventSource(providerName, EventSourceSettings.EtwSelfDescribingEventFormat); | ||
if (eventSource.ConstructionException != null) | ||
{ | ||
NLog.Common.InternalLogger.Error("EtwEventCounterTarget(Name={0}): EventSource({1}) constructor threw exception: {2}", Name, providerName, eventSource.ConstructionException); | ||
} | ||
if (!EtwEventSourceTarget.EventSources.TryAdd(providerName, eventSource)) | ||
{ | ||
eventSource = EtwEventSourceTarget.EventSources[providerName]; | ||
} | ||
} | ||
|
||
if (!EventCounters.TryGetValue(counterName, out _eventCounter)) | ||
{ | ||
_eventCounter = new EventCounter(counterName, eventSource); | ||
if (!EventCounters.TryAdd(counterName, _eventCounter)) | ||
{ | ||
_eventCounter = EventCounters[counterName]; | ||
} | ||
} | ||
|
||
base.InitializeTarget(); | ||
} | ||
|
||
/// <summary> | ||
/// Write LogEvent to ETW. | ||
/// </summary> | ||
/// <param name="logEvent">event to be written.</param> | ||
protected override void Write(LogEventInfo logEvent) | ||
{ | ||
int metricValue = RenderLogEvent(MetricValue, logEvent); | ||
_eventCounter?.WriteMetric(metricValue); | ||
} | ||
} | ||
} | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.