Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4e4c16a
feat(csharp/src/Telemetry): Add support for adding and configuring OT…
birschick-bq Jun 10, 2025
cafec58
fix lint issue
birschick-bq Jun 10, 2025
860bf78
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Jun 10, 2025
15d13ce
update windows os version for csharp build
birschick-bq Jun 10, 2025
3b22e73
update REAME.md with license
birschick-bq Jun 10, 2025
aa07ef6
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Jun 10, 2025
93c65fe
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Jun 11, 2025
33d95cb
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Jul 3, 2025
10faea7
lint: fix spelling mistakes
birschick-bq Jul 3, 2025
75e29ef
Updates for PR comments and new tests for concurrency.
birschick-bq Jul 9, 2025
52a1140
Updates for PR comments #2
birschick-bq Jul 9, 2025
cb05ece
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Jul 11, 2025
11e70e6
change to use BatchActivityExportProcessor - potential performance im…
birschick-bq Jul 15, 2025
386393e
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Jul 15, 2025
6d53d1f
PR review comment updates - builder and retry
birschick-bq Jul 18, 2025
31fa33d
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Jul 23, 2025
d8ddf3f
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Jul 29, 2025
23204e9
File enumerate is now async. Try/catch.
birschick-bq Aug 6, 2025
9ab9147
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Aug 6, 2025
23a147d
Merge branch 'main' into dev/birschick-bq/csharp-otel-exporters
birschick-bq Aug 13, 2025
57f317e
Update for code review comments.
birschick-bq Aug 15, 2025
9523b3b
Fix trailing space.
birschick-bq Aug 15, 2025
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
31 changes: 31 additions & 0 deletions csharp/Apache.Arrow.Adbc.sln
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Arrow.Adbc.Drivers.D
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Arrow.Adbc.Tests.Drivers.Databricks", "test\Drivers\Databricks\Apache.Arrow.Adbc.Tests.Drivers.Databricks.csproj", "{BA07EB2C-5246-EB72-153C-493C7E7412D2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Telemetry", "Telemetry", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Traces", "Traces", "{22EF23A3-1566-446F-B696-9323F3B6F56C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exporters", "Exporters", "{4A0C233A-90A0-42ED-8D8A-F21E7D5BB7EB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Arrow.Adbc.Telemetry.Traces.Exporters", "src\Telemetry\Traces\Exporters\Apache.Arrow.Adbc.Telemetry.Traces.Exporters.csproj", "{85D2EC10-F461-02F4-83B6-E4FC320C244F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Telemetry", "Telemetry", "{9FE39661-2A39-4E9F-A5F2-11FB0D54CB42}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Traces", "Traces", "{B74532A7-8A78-4AD9-9B2E-584765491E48}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exporters", "Exporters", "{43910445-FEC4-4AEC-A698-6A48327600C3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Apache.Arrow.Adbc.Tests.Telemetry.Traces.Exporters", "test\Telemetry\Traces\Exporters\Apache.Arrow.Adbc.Tests.Telemetry.Traces.Exporters.csproj", "{1558BC4B-6E76-434B-8877-6C49B1460544}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -112,6 +128,14 @@ Global
{BA07EB2C-5246-EB72-153C-493C7E7412D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA07EB2C-5246-EB72-153C-493C7E7412D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA07EB2C-5246-EB72-153C-493C7E7412D2}.Release|Any CPU.Build.0 = Release|Any CPU
{85D2EC10-F461-02F4-83B6-E4FC320C244F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{85D2EC10-F461-02F4-83B6-E4FC320C244F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{85D2EC10-F461-02F4-83B6-E4FC320C244F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{85D2EC10-F461-02F4-83B6-E4FC320C244F}.Release|Any CPU.Build.0 = Release|Any CPU
{1558BC4B-6E76-434B-8877-6C49B1460544}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1558BC4B-6E76-434B-8877-6C49B1460544}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1558BC4B-6E76-434B-8877-6C49B1460544}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1558BC4B-6E76-434B-8877-6C49B1460544}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -133,6 +157,13 @@ Global
{BAF2CF14-BA77-429E-AF54-A34B978E9F5C} = {5BD04C26-CE52-4893-8C1A-479705195CEF}
{25042111-6B86-8B75-7EF6-5BFAA36F72B1} = {FEB257A0-4FD3-495E-9A47-9E1649755445}
{BA07EB2C-5246-EB72-153C-493C7E7412D2} = {C7290227-E925-47E7-8B6B-A8B171645D58}
{22EF23A3-1566-446F-B696-9323F3B6F56C} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{4A0C233A-90A0-42ED-8D8A-F21E7D5BB7EB} = {22EF23A3-1566-446F-B696-9323F3B6F56C}
{85D2EC10-F461-02F4-83B6-E4FC320C244F} = {4A0C233A-90A0-42ED-8D8A-F21E7D5BB7EB}
{9FE39661-2A39-4E9F-A5F2-11FB0D54CB42} = {5BD04C26-CE52-4893-8C1A-479705195CEF}
{B74532A7-8A78-4AD9-9B2E-584765491E48} = {9FE39661-2A39-4E9F-A5F2-11FB0D54CB42}
{43910445-FEC4-4AEC-A698-6A48327600C3} = {B74532A7-8A78-4AD9-9B2E-584765491E48}
{1558BC4B-6E76-434B-8877-6C49B1460544} = {43910445-FEC4-4AEC-A698-6A48327600C3}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4795CF16-0FDB-4BE0-9768-5CF31564DC03}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
<Nullable>enable</Nullable>
<PackageReadmeFile>readme.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<Content Include="readme.md">
<Pack>true</Pack>
<PackagePath>\</PackagePath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.12.0" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Apache.Arrow.Adbc\Apache.Arrow.Adbc.csproj" />
</ItemGroup>

</Project>
211 changes: 211 additions & 0 deletions csharp/src/Telemetry/Traces/Exporters/ExportersBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/

using System;
using System.Collections.Generic;
using Apache.Arrow.Adbc.Telemetry.Traces.Exporters.FileExporter;
using OpenTelemetry;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

namespace Apache.Arrow.Adbc.Telemetry.Traces.Exporters
{
public class ExportersBuilder
{
private static readonly IReadOnlyDictionary<string, Func<string, string?, TracerProvider?>> s_tracerProviderFactoriesDefault;

private readonly string _sourceName;
private readonly string? _sourceVersion;
private readonly IReadOnlyDictionary<string, Func<string, string?, TracerProvider?>> _tracerProviderFactories;

static ExportersBuilder()
{
var defaultProviders = new Dictionary<string, Func<string, string?, TracerProvider?>>
{
[ExportersOptions.Exporters.None] = NewNoopTracerProvider,
[ExportersOptions.Exporters.Otlp] = NewOtlpTracerProvider,
[ExportersOptions.Exporters.Console] = NewConsoleTracerProvider,
[ExportersOptions.Exporters.AdbcFile] = NewAdbcFileTracerProvider,
};
s_tracerProviderFactoriesDefault = defaultProviders;
}

private ExportersBuilder(Builder builder)
{
_sourceName = builder.SourceName;
_sourceVersion = builder.SourceVersion;
_tracerProviderFactories = builder.TracerProviderFactories;
}

/// <summary>
/// Build an <see cref="ExportersBuilder"/> from different possible Exporters. Use the Add* functions to add
/// one or more <see cref="TracerProvider"/> factories.
/// </summary>
/// <param name="sourceName">The name of the source that the exporter will filter on.</param>
/// <param name="sourceVersion">The (optional) version of the source that the exporter will filter on.</param>
/// <returns>A <see cref="Builder"/> object.</returns>
/// <exception cref="ArgumentNullException"></exception>
public static Builder Build(string sourceName, string? sourceVersion = default, bool addDefaultExporters = false)
{
if (string.IsNullOrWhiteSpace(sourceName))
{
throw new ArgumentNullException(nameof(sourceName));
}
return new Builder(sourceName, sourceVersion, addDefaultExporters);
}

/// <summary>
/// <para>
/// Attempts to activate an exporter based on the dictionary of <see cref="TracerProvider"/> factories
/// added to the <see cref="Builder"/>. If the value of exporterOption is not null and not empty, it will be
/// used as the key to the dictionary. If exporterOption is null or empty, then the exporter option will
/// check the environment variable identified by environmentName (default <see cref="ExportersOptions.Environment.Exporter"/>).
/// If both the exporterOption and the environment variable value are null or empty, then this function will return null
/// and no exporeter will be activated.
/// </para>
/// <para>
/// If the exporterOption or the value of the environment variable are not null and not empty, then
/// if a matching factory delegate is found, it is called to activate the exporter. If no exception is thrown,
/// the result of the factory method returns the result which may be null. If a matching function is found, the expoertName is set.
/// If the factory delegate throws an exception it is not caught.
/// </para>
/// </summary>
/// <param name="exporterOption">The value (name) of the exporter option, typically passed as option <see cref="ExportersOptions.Exporter"/>.</param>
/// <param name="exporterName">The actual exporter name when successfully activated.</param>
/// <param name="environmentName">The (optional) name of the environment variable to test for the exporter name. Default: <see cref="ExportersOptions.Environment.Exporter"/></param>
/// <exception cref="AdbcException" />
/// <returns>The a non-null <see cref="TracerProvider"/> when successfully activated. Note: this object must be explicitly disposed when no longer necessary.</returns>
public TracerProvider? Activate(
string? exporterOption,
out string? exporterName,
string environmentName = ExportersOptions.Environment.Exporter)
{
TracerProvider? tracerProvider = null;
exporterName = null;

if (string.IsNullOrWhiteSpace(exporterOption))
{
// Fall back to check the environment variable
exporterOption = Environment.GetEnvironmentVariable(environmentName);
}
if (string.IsNullOrWhiteSpace(exporterOption))
{
// Neither option or environment variable is set - no tracer provider will be activated.
return null;
}

if (!_tracerProviderFactories.TryGetValue(exporterOption!, out Func<string, string?, TracerProvider?>? factory))
{
// Requested option has not been added via the builder
throw AdbcException.NotImplemented($"Exporter option '{exporterOption}' is not implemented.");
}

tracerProvider = factory.Invoke(_sourceName, _sourceVersion);
if (tracerProvider != null)
{
exporterName = exporterOption;
}
return tracerProvider;
}

public static TracerProvider NewAdbcFileTracerProvider(string sourceName, string? sourceVersion) =>
Sdk.CreateTracerProviderBuilder()
.AddSource(sourceName)
.ConfigureResource(resource =>
resource.AddService(
serviceName: sourceName,
serviceVersion: sourceVersion))
.AddAdbcFileExporter(sourceName)
.Build();

public static TracerProvider NewConsoleTracerProvider(string sourceName, string? sourceVersion) =>
Sdk.CreateTracerProviderBuilder()
.AddSource(sourceName)
.ConfigureResource(resource =>
resource.AddService(
serviceName: sourceName,
serviceVersion: sourceVersion))
.AddConsoleExporter()
.Build();

public static TracerProvider NewOtlpTracerProvider(string sourceName, string? sourceVersion) =>
Sdk.CreateTracerProviderBuilder()
.AddSource(sourceName)
.ConfigureResource(resource =>
resource.AddService(
serviceName: sourceName,
serviceVersion: sourceVersion))
.AddOtlpExporter()
.Build();

public static TracerProvider? NewNoopTracerProvider(string sourceName, string? sourceVersion) =>
null;

public class Builder
{
private readonly string _sourceName;
private readonly string? _sourceVersion;
private readonly Dictionary<string, Func<string, string?, TracerProvider?>> _tracerProviderFactories;

internal string SourceName => _sourceName;

internal string? SourceVersion => _sourceVersion;

internal IReadOnlyDictionary<string, Func<string, string?, TracerProvider?>> TracerProviderFactories => _tracerProviderFactories;

public Builder(string sourceName, string? sourceVersion, bool addDefaultExporters = false)
{
_sourceName = sourceName;
_sourceVersion = sourceVersion;
_tracerProviderFactories = [];
if (addDefaultExporters)
{
AddDefaultExporters();
}
}

public ExportersBuilder Build()
{
if (_tracerProviderFactories.Count == 0)
{
throw new InvalidOperationException("No options provided. Please add one or more exporter.");
}
return new ExportersBuilder(this);
}

public Builder AddExporter(string exporterName, Func<string, string?, TracerProvider?> tracerProviderFactory)
{
if (string.IsNullOrWhiteSpace(exporterName))
{
throw new ArgumentNullException(nameof(exporterName));
}

_tracerProviderFactories.Add(exporterName, tracerProviderFactory);
return this;
}

private Builder AddDefaultExporters()
{
foreach (KeyValuePair<string, Func<string, string?, TracerProvider?>> item in s_tracerProviderFactoriesDefault)
{
_tracerProviderFactories[item.Key] = item.Value;
}
return this;
}
}
}
}
37 changes: 37 additions & 0 deletions csharp/src/Telemetry/Traces/Exporters/ExportersOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/

namespace Apache.Arrow.Adbc.Telemetry.Traces.Exporters
{
public class ExportersOptions
{
public const string Exporter = "adbc.traces.exporter";

public static class Environment
{
public const string Exporter = "OTEL_TRACES_EXPORTER";
}

public static class Exporters
{
public const string None = "none";
public const string Otlp = "otlp";
public const string Console = "console";
public const string AdbcFile = "adbcfile";
}
}
}
Loading
Loading