Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
hananiel committed Jan 20, 2022
1 parent 9373eb5 commit cf49964
Show file tree
Hide file tree
Showing 18 changed files with 740 additions and 0 deletions.
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" />
<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
{
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));
}

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

0 comments on commit cf49964

Please sign in to comment.