Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Event counter listener #177

Closed
wants to merge 46 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
cf49964
Initial commit
hananiel Jan 20, 2022
3ca132b
formatting
hananiel Jan 20, 2022
7381f48
Add missing headers
twenzel Jan 21, 2022
79ce3c2
Remove not required packages
twenzel Jan 21, 2022
769ff52
Add example
twenzel Jan 21, 2022
a92627b
Rename project
twenzel Jan 21, 2022
3fb77d3
First implementation for options
twenzel Jan 22, 2022
86367ca
Working metrics
twenzel Jan 22, 2022
18c3507
Update readme
twenzel Jan 22, 2022
bffd200
Distinguish between different counters
twenzel Jan 22, 2022
69bd102
Better event source configuration
twenzel Jan 22, 2022
4f7ff3e
Support more metric types
twenzel Jan 23, 2022
a562375
Add tests
twenzel Jan 23, 2022
0619d33
Merge pull request #3 from twenzel/EventSourceConfiguration
Jan 24, 2022
6dbbf45
Test configuration via IConfiguration
twenzel Jan 24, 2022
1a9c341
Add NET 6 to PR build
twenzel Jan 24, 2022
d5abbfe
Create package-Instrumentation.EventCounters.yml
twenzel Jan 24, 2022
464dfe4
Merge pull request #4 from twenzel/EventSourceConfiguration
Jan 24, 2022
2e9350c
Revert example to Net5.0
hananiel Jan 25, 2022
a4e5b62
Markdown lint
hananiel Jan 25, 2022
97d9521
More MD lint
hananiel Jan 25, 2022
15f3a97
more markdown
hananiel Jan 25, 2022
f37cef3
Code review feedback: deleting example, removing well known counters,…
hananiel Jan 26, 2022
73a5ced
Remove old file
twenzel Jan 25, 2022
9b52ac4
Remove change of adding .NET 6
twenzel Jan 25, 2022
95542fb
Adds more tests
twenzel Jan 25, 2022
faeb150
Remove example project as wished by reviewer
twenzel Jan 25, 2022
85eb1c6
fix markdown lint
twenzel Jan 26, 2022
d72a15e
Fix typo
twenzel Jan 26, 2022
8540b42
Merge pull request #8 from twenzel/hananiel-EventCounterListener
Jan 26, 2022
b11b0b6
Merge branch 'main' into EventCounterListener
Jan 26, 2022
cfb4d0f
Lint issues and re-add the properties; not sure if we need them or not
hananiel Jan 26, 2022
07ac588
Add more tests
twenzel Jan 28, 2022
7f5bf0d
Use new Instrumentation type enum
twenzel Feb 4, 2022
35d6a6a
Better way to infer instrumentation type
twenzel Feb 4, 2022
d88c972
Fix markdown lint
twenzel Feb 4, 2022
6074cf1
Fix merge issue
twenzel Feb 4, 2022
c98619b
Merge pull request #10 from twenzel/EventCounterListener
Feb 4, 2022
85530dd
Fix sanity check
twenzel Feb 4, 2022
1f66660
Add more tests
twenzel Feb 4, 2022
188b919
Rename variables
twenzel Feb 4, 2022
b3a7d27
Merge pull request #12 from twenzel/EventCounterListener
Feb 4, 2022
16ceae7
Rename instrumentation types to reflect the created instrumentation
twenzel Feb 8, 2022
bb3a3d4
Merge pull request #14 from twenzel/EventCounterListener
Feb 8, 2022
02f3e19
Fix test
twenzel Feb 8, 2022
a32c005
Merge pull request #15 from twenzel/EventCounterListener
Feb 11, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<clear />
<add key="NuGet" value="https://api.nuget.org/v3/index.json" />
<add key="MyGet" value="https://www.myget.org/F/opentelemetry/api/v3/index.json" />
<add key="Dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can remove this, as we won't need any packages from non nuget.org/myget.org

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed ..

<add key="Dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
</packageSources>
<disabledPackageSources />
</configuration>
14 changes: 14 additions & 0 deletions opentelemetry-dotnet-contrib.sln
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Examples.Owin", "examples\o
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.Instrumentation.Owin.Tests", "test\OpenTelemetry.Contrib.Instrumentation.Owin.Tests\OpenTelemetry.Contrib.Instrumentation.Owin.Tests.csproj", "{D52558C8-B7BF-4F59-A0FA-9AA629E68012}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.EventCounterListener", "src\OpenTelemetry.Contrib.EventCounterListener\OpenTelemetry.Contrib.EventCounterListener.csproj", "{8487C723-DCAE-4EF5-A29F-ADB9BCDF9DCD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Contrib.EventCounterListener.Tests", "test\OpenTelemetry.Contrib.EventCounterListener.Tests\OpenTelemetry.Contrib.EventCounterListener.Tests.csproj", "{2872634A-581E-494C-B3ED-01E0F3D304ED}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -306,6 +310,14 @@ Global
{D52558C8-B7BF-4F59-A0FA-9AA629E68012}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D52558C8-B7BF-4F59-A0FA-9AA629E68012}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D52558C8-B7BF-4F59-A0FA-9AA629E68012}.Release|Any CPU.Build.0 = Release|Any CPU
{8487C723-DCAE-4EF5-A29F-ADB9BCDF9DCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8487C723-DCAE-4EF5-A29F-ADB9BCDF9DCD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8487C723-DCAE-4EF5-A29F-ADB9BCDF9DCD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8487C723-DCAE-4EF5-A29F-ADB9BCDF9DCD}.Release|Any CPU.Build.0 = Release|Any CPU
{2872634A-581E-494C-B3ED-01E0F3D304ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2872634A-581E-494C-B3ED-01E0F3D304ED}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2872634A-581E-494C-B3ED-01E0F3D304ED}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2872634A-581E-494C-B3ED-01E0F3D304ED}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -353,6 +365,8 @@ Global
{8D11A34C-D0EF-4DE1-8230-32168E67044D} = {B75EE478-97F7-4E9F-9A5A-DB3D0988EDEA}
{6B3AA3F2-89A7-433F-918A-1E5E6AAF8423} = {8D11A34C-D0EF-4DE1-8230-32168E67044D}
{D52558C8-B7BF-4F59-A0FA-9AA629E68012} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{8487C723-DCAE-4EF5-A29F-ADB9BCDF9DCD} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{2872634A-581E-494C-B3ED-01E0F3D304ED} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B0816796-CDB3-47D7-8C3C-946434DE3B66}
Expand Down
23 changes: 23 additions & 0 deletions src/OpenTelemetry.Contrib.EventCounterListener/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// <copyright file="AssemblyInfo.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System.Runtime.CompilerServices;

#if SIGNED
[assembly: InternalsVisibleTo("OpenTelemetry.Contrib.EventCounterListener.Tests, PublicKey=")]
#else
[assembly: InternalsVisibleTo("OpenTelemetry.Contrib.EventCounterListener.Tests")]
#endif
5 changes: 5 additions & 0 deletions src/OpenTelemetry.Contrib.EventCounterListener/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## Unreleased

* Initial release
37 changes: 37 additions & 0 deletions src/OpenTelemetry.Contrib.EventCounterListener/EventCounter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// <copyright file="MySqlDataInstrumentationOptions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Collections.Concurrent;
using System.Data;
using System.Diagnostics;
using System.Text.RegularExpressions;

using OpenTelemetry.Trace;

namespace OpenTelemetry.Contrib.Instrumentation.EventCounterListener
{

/// <summary>
/// The Event Counter to listen to
/// </summary>
public class EventCounter
{
public string Name { get; set; }

public string Type { get; set; }
}
}
135 changes: 135 additions & 0 deletions src/OpenTelemetry.Contrib.EventCounterListener/EventCounterListener.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Diagnostics.Tracing;
using System.Reflection;
using Microsoft.Diagnostics.Monitoring.EventPipe;
using OpenTelemetry.Contrib.EventCounterListener.EventPipe;

namespace OpenTelemetry.Contrib.Instrumentation.EventCounterListener
{
/// <summary>
/// EventCounterListener that subscribes to EventSource Events.
/// </summary>
public class EventCounterListener : EventListener
hananiel marked this conversation as resolved.
Show resolved Hide resolved
{
internal static readonly AssemblyName AssemblyName = typeof(EventCounterListener).Assembly.GetName();
internal static readonly string InstrumentationName = AssemblyName.Name;
internal static readonly string InstrumentationVersion = AssemblyName.Version.ToString();

private readonly string eventSourceName = "System.Runtime"; // TODO : Get from options
private readonly string eventName = "EventCounters";
private readonly Meter meter;
private readonly EventCounterListenerOptions options;

private ConcurrentDictionary<MetricKey, double> metericStore = new();

/// <summary>
/// Initializes a new instance of the <see cref="EventCounterListener"/> class.
/// </summary>
/// <param name="options">Options to configure the EventCounterListener</param>
public EventCounterListener(EventCounterListenerOptions options)
{
this.options = options;
this.meter = new Meter(InstrumentationName, InstrumentationVersion);
}

/// <summary>
/// Processes a new EventSource event.
/// </summary>
/// <param name="eventData">Event to process.</param>
protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
if (eventData == null)
{
throw new ArgumentNullException(nameof(eventData));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does anyone catch this?

}

try
{
if (eventData.EventName.Equals(this.eventName, StringComparison.OrdinalIgnoreCase))
{
this.ExtractAndRecordMetric(eventData);
}
}
catch (Exception ex)
{
EventCounterListenerEventSource.Log.ErrorEventCounter(this.eventName, ex.ToString());
}
}

/// <inheritdoc/>
protected override void OnEventSourceCreated(EventSource eventSource)
{
if (eventSource == null)
{
throw new ArgumentNullException(nameof(eventSource));
}

if (this.eventSourceName.Equals(eventSource.Name, StringComparison.OrdinalIgnoreCase))
{
var refreshInterval = new Dictionary<string, string>() { { "EventCounterIntervalSec", "1" } }; // TODO: Get from configuration
try
{
this.EnableEvents(eventSource, EventLevel.Verbose, EventKeywords.All, refreshInterval);
}
catch (Exception ex)
{
// TODO: Log to eventSource
}
}
}

private static bool CompareMetrics(ICounterPayload first, ICounterPayload second)
{
return string.Equals(first.Name, second.Name);
}

private double ObserveValue(MetricKey key)
{
// return last value
return this.metericStore[key];
}

private void ExtractAndRecordMetric(EventWrittenEventArgs eventWrittenEventArgs)
{
eventWrittenEventArgs.TryGetCounterPayload(out var eventPayload);
var metricKey = new MetricKey(eventPayload);
if (!this.metericStore.ContainsKey(metricKey))
{
this.meter.CreateObservableGauge<double>(eventPayload.Name, () => this.ObserveValue(metricKey), eventPayload.DisplayName);
}

this.metericStore[metricKey] = eventPayload.Value;
}

private sealed class MetricKey
{
private ICounterPayload metric;

public MetricKey(ICounterPayload metric)
{
this.metric = metric;
}

public override int GetHashCode()
{
HashCode code = default(HashCode);
code.Add(this.metric.Provider);
code.Add(this.metric.Name);
return code.ToHashCode();
}

public override bool Equals(object obj)
{
if (obj is MetricKey metricKey)
{
return CompareMetrics(this.metric, metricKey.metric);
}

return false;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// <copyright file="MySqlDataInstrumentationEventSource.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System.Diagnostics.Tracing;

namespace OpenTelemetry.Contrib.Instrumentation.EventCounterListener
{
/// <summary>
/// EventSource events emitted from the project.
/// </summary>
[EventSource(Name = "OpenTelemetry-Instrumentation-MySqlData")]
internal class EventCounterListenerEventSource : EventSource
{
public static readonly EventCounterListenerEventSource Log = new EventCounterListenerEventSource();

[Event(2, Message = "Error accured while processing eventCounter, EventCounter: {0}, Exception: {2}", Level = EventLevel.Error)]
public void ErrorEventCounter(string counterName, string exception)
{
this.WriteEvent(1, counterName, exception);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace OpenTelemetry.Contrib.Instrumentation.EventCounterListener
{
internal class EventCounterListenerInstrumentation : IDisposable
{

private readonly EventCounterListener eventCounterListener;

public EventCounterListenerInstrumentation(EventCounterListenerOptions options)
{
this.eventCounterListener = new EventCounterListener(options);
}

public void Dispose()
{
this.eventCounterListener.Dispose();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// <copyright file="MySqlDataInstrumentationOptions.cs" company="OpenTelemetry Authors">
// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// </copyright>

using System;
using System.Collections.Concurrent;
using System.Data;
using System.Diagnostics;
using System.Text.RegularExpressions;

using OpenTelemetry.Trace;

namespace OpenTelemetry.Contrib.Instrumentation.EventCounterListener
{
/// <summary>
/// Options for <see cref="EventCounterListener"/>.
/// </summary>
public class EventCounterListenerOptions
{
/// <summary>
/// Gets or sets event Counters to listen to.
/// </summary>
public EventCounter[] Sources { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace OpenTelemetry.Contrib.EventCounterListener.EventPipe
{
internal class CounterPayload : ICounterPayload
{
public CounterPayload(DateTime timestamp,
string provider,
string name,
string displayName,
string unit,
double value,
CounterType counterType,
float interval)
{
Timestamp = timestamp;
Name = name;
DisplayName = displayName;
Unit = unit;
Value = value;
CounterType = counterType;
Provider = provider;
Interval = interval;
}

public string Namespace { get; }

public string Name { get; }

public string DisplayName { get; }

public string Unit { get; }

public double Value { get; }

public DateTime Timestamp { get; }

public float Interval { get; }

public CounterType CounterType { get; }

public string Provider { get; }
}
}
Loading