Skip to content

Commit

Permalink
Adding a Geneva Exporter - Exports to local ETW or UDS (open-telemetr…
Browse files Browse the repository at this point in the history
  • Loading branch information
mic-max authored Apr 13, 2022
1 parent ccc0f42 commit af31ca7
Show file tree
Hide file tree
Showing 47 changed files with 8,281 additions and 1 deletion.
2 changes: 1 addition & 1 deletion .github/workflows/dotnet-core-cov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
run: dotnet build --configuration Release --no-restore

- name: dotnet test
run: dotnet test --collect:"Code Coverage" --results-directory:"TestResults" --configuration Release --no-build -- RunConfiguration.DisableAppDomain=true
run: dotnet test --collect:"Code Coverage" --results-directory:"TestResults" --configuration Release --no-build --filter "Platform=Any|Platform=Windows" -- RunConfiguration.DisableAppDomain=true

- name: Process code coverage
run: .\build\process-codecoverage.ps1
Expand Down
27 changes: 27 additions & 0 deletions opentelemetry-dotnet-contrib.sln
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Extensions.Tests", "test\OpenTelemetry.Extensions.Tests\OpenTelemetry.Extensions.Tests.csproj", "{2117F4E3-6612-4E4D-A757-27271EEB7783}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Geneva", "src\OpenTelemetry.Exporter.Geneva\OpenTelemetry.Exporter.Geneva.csproj", "{1105C814-31DA-4214-BEA8-6DB5FC12C808}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Geneva.Benchmark", "test\OpenTelemetry.Exporter.Geneva.Benchmark\OpenTelemetry.Exporter.Geneva.Benchmark.csproj", "{F53FD7F5-DBC0-4FA5-83BA-B4C07A5BD248}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Geneva.Stress", "test\OpenTelemetry.Exporter.Geneva.Stress\OpenTelemetry.Exporter.Geneva.Stress.csproj", "{F632DFB6-38AD-4356-8997-8CCC0492619C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Exporter.Geneva.UnitTest", "test\OpenTelemetry.Exporter.Geneva.UnitTest\OpenTelemetry.Exporter.Geneva.UnitTest.csproj", "{A3EB4E60-256C-45EC-92EE-68FD035CAD11}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.Hangfire", "src\OpenTelemetry.Instrumentation.Hangfire\OpenTelemetry.Instrumentation.Hangfire.csproj", "{BE5FFBBB-D73F-4071-92F4-F1694881604F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenTelemetry.Instrumentation.Hangfire.Tests", "test\OpenTelemetry.Instrumentation.Hangfire.Tests\OpenTelemetry.Instrumentation.Hangfire.Tests.csproj", "{ED774FC3-C1C0-44CD-BA41-686C04BEB3E5}"
Expand Down Expand Up @@ -339,6 +346,22 @@ Global
{2117F4E3-6612-4E4D-A757-27271EEB7783}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2117F4E3-6612-4E4D-A757-27271EEB7783}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2117F4E3-6612-4E4D-A757-27271EEB7783}.Release|Any CPU.Build.0 = Release|Any CPU
{1105C814-31DA-4214-BEA8-6DB5FC12C808}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1105C814-31DA-4214-BEA8-6DB5FC12C808}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1105C814-31DA-4214-BEA8-6DB5FC12C808}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1105C814-31DA-4214-BEA8-6DB5FC12C808}.Release|Any CPU.Build.0 = Release|Any CPU
{F53FD7F5-DBC0-4FA5-83BA-B4C07A5BD248}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F53FD7F5-DBC0-4FA5-83BA-B4C07A5BD248}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F53FD7F5-DBC0-4FA5-83BA-B4C07A5BD248}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F53FD7F5-DBC0-4FA5-83BA-B4C07A5BD248}.Release|Any CPU.Build.0 = Release|Any CPU
{F632DFB6-38AD-4356-8997-8CCC0492619C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F632DFB6-38AD-4356-8997-8CCC0492619C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F632DFB6-38AD-4356-8997-8CCC0492619C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F632DFB6-38AD-4356-8997-8CCC0492619C}.Release|Any CPU.Build.0 = Release|Any CPU
{A3EB4E60-256C-45EC-92EE-68FD035CAD11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A3EB4E60-256C-45EC-92EE-68FD035CAD11}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A3EB4E60-256C-45EC-92EE-68FD035CAD11}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A3EB4E60-256C-45EC-92EE-68FD035CAD11}.Release|Any CPU.Build.0 = Release|Any CPU
{BE5FFBBB-D73F-4071-92F4-F1694881604F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BE5FFBBB-D73F-4071-92F4-F1694881604F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BE5FFBBB-D73F-4071-92F4-F1694881604F}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down Expand Up @@ -398,6 +421,10 @@ Global
{6AE92AAD-CF08-4E60-98EF-A7F762DAAB4D} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{42B3FB71-BB42-46E3-9CEC-56620CB76BD9} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{2117F4E3-6612-4E4D-A757-27271EEB7783} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{1105C814-31DA-4214-BEA8-6DB5FC12C808} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{F53FD7F5-DBC0-4FA5-83BA-B4C07A5BD248} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{F632DFB6-38AD-4356-8997-8CCC0492619C} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{A3EB4E60-256C-45EC-92EE-68FD035CAD11} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
{BE5FFBBB-D73F-4071-92F4-F1694881604F} = {22DF5DC0-1290-4E83-A9D8-6BB7DE3B3E63}
{ED774FC3-C1C0-44CD-BA41-686C04BEB3E5} = {2097345F-4DD3-477D-BC54-A922F9B2B402}
EndGlobalSection
Expand Down
16 changes: 16 additions & 0 deletions src/OpenTelemetry.Exporter.Geneva/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Runtime.CompilerServices;

[assembly: CLSCompliant(false)]
[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Geneva.Benchmark" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Geneva.UnitTest" + AssemblyInfo.PublicKey)]
[assembly: InternalsVisibleTo("OpenTelemetry.Exporter.Geneva.Stress" + AssemblyInfo.PublicKey)]

internal static class AssemblyInfo
{
#if SIGNED
public const string PublicKey = ", PublicKey=002400000480000094000000060200000024000052534131000400000100010051C1562A090FB0C9F391012A32198B5E5D9A60E9B80FA2D7B434C9E5CCB7259BD606E66F9660676AFC6692B8CDC6793D190904551D2103B7B22FA636DCBB8208839785BA402EA08FC00C8F1500CCEF28BBF599AA64FFB1E1D5DC1BF3420A3777BADFE697856E9D52070A50C3EA5821C80BEF17CA3ACFFA28F89DD413F096F898";
#else
public const string PublicKey = "";
#endif
}
3 changes: 3 additions & 0 deletions src/OpenTelemetry.Exporter.Geneva/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Changelog

## Unreleased
212 changes: 212 additions & 0 deletions src/OpenTelemetry.Exporter.Geneva/ConnectionStringBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.Globalization;

namespace OpenTelemetry.Exporter.Geneva
{
internal enum TransportProtocol
{
Etw,
Tcp,
Udp,
Unix,
Unspecified,
}

internal class ConnectionStringBuilder
{
private readonly Dictionary<string, string> _parts = new Dictionary<string, string>(StringComparer.Ordinal);

public ConnectionStringBuilder(string connectionString)
{
if (string.IsNullOrWhiteSpace(connectionString))
{
throw new ArgumentNullException(nameof(connectionString), $"{nameof(connectionString)} is invalid.");
}

const char Semicolon = ';';
const char EqualSign = '=';
foreach (var token in connectionString.Split(Semicolon))
{
if (string.IsNullOrWhiteSpace(token))
{
continue;
}

var index = token.IndexOf(EqualSign);
if (index == -1 || index != token.LastIndexOf(EqualSign))
{
continue;
}

var pair = token.Trim().Split(EqualSign);

var key = pair[0].Trim();
var value = pair[1].Trim();
if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(value))
{
throw new ArgumentException("Connection string cannot contain empty keys or values.");
}

this._parts[key] = value;
}

if (this._parts.Count == 0)
{
throw new ArgumentNullException(nameof(connectionString), $"{nameof(connectionString)} is invalid.");
}
}

public string EtwSession
{
get => this.ThrowIfNotExists<string>(nameof(this.EtwSession));
set => this._parts[nameof(this.EtwSession)] = value;
}

public string Endpoint
{
get => this.ThrowIfNotExists<string>(nameof(this.Endpoint));
set => this._parts[nameof(this.Endpoint)] = value;
}

public TransportProtocol Protocol
{
get
{
try
{
// Checking Etw first, since it's preferred for Windows and enables fail fast on Linux
if (this._parts.ContainsKey(nameof(this.EtwSession)))
{
return TransportProtocol.Etw;
}

if (!this._parts.ContainsKey(nameof(this.Endpoint)))
{
return TransportProtocol.Unspecified;
}

var endpoint = new Uri(this.Endpoint);
if (Enum.TryParse(endpoint.Scheme, true, out TransportProtocol protocol))
{
return protocol;
}

throw new ArgumentException("Endpoint scheme is invalid.");
}
catch (UriFormatException ex)
{
throw new ArgumentException($"{nameof(this.Endpoint)} value is malformed.", ex);
}
}
}

public string ParseUnixDomainSocketPath()
{
try
{
var endpoint = new Uri(this.Endpoint);
return endpoint.AbsolutePath;
}
catch (UriFormatException ex)
{
throw new ArgumentException($"{nameof(this.Endpoint)} value is malformed.", ex);
}
}

public int TimeoutMilliseconds
{
get
{
if (!this._parts.TryGetValue(nameof(this.TimeoutMilliseconds), out string value))
{
return UnixDomainSocketDataTransport.DefaultTimeoutMilliseconds;
}

try
{
int timeout = int.Parse(value, CultureInfo.InvariantCulture);
if (timeout <= 0)
{
throw new ArgumentException(
$"{nameof(this.TimeoutMilliseconds)} should be greater than zero.",
nameof(this.TimeoutMilliseconds));
}

return timeout;
}
catch (ArgumentException)
{
throw;
}
catch (Exception ex)
{
throw new ArgumentException(
$"{nameof(this.TimeoutMilliseconds)} is malformed.",
nameof(this.TimeoutMilliseconds),
ex);
}
}
set => this._parts[nameof(this.TimeoutMilliseconds)] = value.ToString(CultureInfo.InvariantCulture);
}

public string Host
{
get
{
try
{
var endpoint = new Uri(this.Endpoint);
return endpoint.Host;
}
catch (UriFormatException ex)
{
throw new ArgumentException($"{nameof(this.Endpoint)} value is malformed.", ex);
}
}
}

public int Port
{
get
{
try
{
var endpoint = new Uri(this.Endpoint);
if (endpoint.IsDefaultPort)
{
throw new ArgumentException($"Port should be explicitly set in {nameof(this.Endpoint)} value.");
}

return endpoint.Port;
}
catch (UriFormatException ex)
{
throw new ArgumentException($"{nameof(this.Endpoint)} value is malformed.", ex);
}
}
}

public string Account
{
get => this.ThrowIfNotExists<string>(nameof(this.Account));
set => this._parts[nameof(this.Account)] = value;
}

public string Namespace
{
get => this.ThrowIfNotExists<string>(nameof(this.Namespace));
set => this._parts[nameof(this.Namespace)] = value;
}

private T ThrowIfNotExists<T>(string name)
{
if (!this._parts.TryGetValue(name, out var value))
{
throw new ArgumentException($"'{name}' value is missing in connection string.");
}

return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture);
}
}
}
77 changes: 77 additions & 0 deletions src/OpenTelemetry.Exporter.Geneva/EtwDataTransport.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.Diagnostics.Tracing;

namespace OpenTelemetry.Exporter.Geneva
{
[EventSource(Name = "OpenTelemetry")]
internal class EtwEventSource : EventSource
{
public EtwEventSource(string providerName)
: base(providerName, EventSourceSettings.EtwManifestEventFormat)
{
}

public enum EtwEventId
{
TraceEvent = 100,
}

[Event((int)EtwEventId.TraceEvent, Version = 1, Level = EventLevel.Informational)]
public void InformationalEvent()
{
}

[NonEvent]
public unsafe void SendEvent(int eventId, byte[] data, int size)
{
EventData* dataDesc = stackalloc EventData[1];
fixed (byte* ptr = data)
{
dataDesc[0].DataPointer = (IntPtr)ptr;
dataDesc[0].Size = (int)size;
this.WriteEventCore(eventId, 1, dataDesc);
}
}
}

internal class EtwDataTransport : IDataTransport, IDisposable
{
public EtwDataTransport(string providerName)
{
this.m_eventSource = new EtwEventSource(providerName);
}

public void Send(byte[] data, int size)
{
this.m_eventSource.SendEvent((int)EtwEventSource.EtwEventId.TraceEvent, data, size);
}

public bool IsEnabled()
{
return this.m_eventSource.IsEnabled();
}

private EtwEventSource m_eventSource;
private bool m_disposed;

public void Dispose()
{
this.Dispose(true);
}

protected virtual void Dispose(bool disposing)
{
if (this.m_disposed)
{
return;
}

if (disposing)
{
this.m_eventSource.Dispose();
}

this.m_disposed = true;
}
}
}
Loading

0 comments on commit af31ca7

Please sign in to comment.