Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8f02203
working otel exporter configured via dab-config.json
tommasodotNET Oct 30, 2024
f7160fd
set default value for otel service name to dab
tommasodotNET Oct 30, 2024
00caf7c
readded nuget config
tommasodotNET Nov 4, 2024
fe5518b
removes unnecessary code for otel
tommasodotNET Nov 4, 2024
c72e7b2
adds unit tests
tommasodotNET Nov 4, 2024
a35fe37
reorder traces
tommasodotNET Nov 4, 2024
74d2691
Merge branch 'main' into 2397-otel
tommasodotNET Nov 4, 2024
f904d76
default value name for otel service name in schema
tommasodotNET Nov 5, 2024
e44dceb
net8 specifc nugets in their own item group
tommasodotNET Nov 5, 2024
eacfce3
fixes syntax
tommasodotNET Nov 5, 2024
aed41fd
configurable otel protocol
tommasodotNET Nov 5, 2024
2528012
adding comments on tests
tommasodotNET Nov 7, 2024
59d6e5a
adds tests for open telemetry
tommasodotNET Nov 7, 2024
0341cb4
renames OtlpExportProtocol to ExporterProtocol
tommasodotNET Nov 14, 2024
c9bb417
exporter-protocol in dab draft schema
tommasodotNET Nov 15, 2024
1dd53e8
fixes formatting on usings
tommasodotNET Nov 15, 2024
8fdbf50
fixes if formatting
tommasodotNET Nov 16, 2024
96fa415
fix formatting
abhishekkumams Nov 25, 2024
2a2d591
fix formatting
abhishekkumams Nov 25, 2024
d06ca3b
Merge branch 'main' into 2397-otel
abhishekkumams Nov 25, 2024
fa5dc01
Update src/Cli/Commands/AddTelemetryOptions.cs
tommasodotNET Nov 25, 2024
9b73458
Update src/Cli/Commands/AddTelemetryOptions.cs
tommasodotNET Nov 25, 2024
c632865
appin-cs and otelendpoint not required by default
tommasodotNET Nov 25, 2024
c2b1c19
Merge branch '2397-otel' of https://github.com/tommasodotNET/data-api…
tommasodotNET Nov 25, 2024
4769896
fixes otelExProtocol
tommasodotNET Nov 25, 2024
1eeea23
fixes OpenTelemetryExportProtocol
tommasodotNET Nov 26, 2024
90d6884
Merge branch 'main' into 2397-otel
tommasodotNET Dec 2, 2024
2033f71
removes target build conditions
tommasodotNET Dec 2, 2024
acb3d7b
fix formatting
abhishekkumams Dec 3, 2024
f6f3209
Merge branch 'main' into 2397-otel
abhishekkumams Dec 3, 2024
d20cab1
fix parameters odering
abhishekkumams Dec 5, 2024
66f765a
Merge branch 'main' into 2397-otel
abhishekkumams Dec 5, 2024
cd39019
fix test
abhishekkumams Dec 5, 2024
e84b1a8
Merge branch '2397-otel' of https://github.com/tommasodotNET/data-api…
abhishekkumams Dec 5, 2024
52a8a6c
fix test
abhishekkumams Dec 5, 2024
20e824f
adds headers to addopenttelemetrytests
tommasodotNET Dec 5, 2024
c7d6f29
fixes correct required param for otel
tommasodotNET Dec 5, 2024
d3e3030
Merge branch '2397-otel' of https://github.com/tommasodotNET/data-api…
tommasodotNET Dec 5, 2024
88c01cf
adds license in oteloptions and tests
tommasodotNET Dec 10, 2024
3a4e330
specifies SetUpTelemetryInConfig is a helper function
tommasodotNET Dec 12, 2024
9727827
adds check file exists before deleting in oteltests
tommasodotNET Dec 12, 2024
5220a51
fixes AddTelemetryOptions cli help texts and comments
tommasodotNET Dec 12, 2024
db3dbbe
specifies params in AddTelemetryTests
tommasodotNET Dec 12, 2024
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
41 changes: 37 additions & 4 deletions schemas/dab.draft.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -357,11 +357,44 @@
"required": [
"connection-string"
]
},
"open-telemetry": {
"type": "object",
"additionalProperties": false,
"properties": {
"endpoint": {
"type": "string",
"description": "Open Telemetry connection string"
},
"headers": {
"type": "string",
"description": "Open Telemetry headers"
},
"service-name": {
"type": "string",
"description": "Open Telemetry service name",
"default": "dab"
},
"exporter-protocol": {
"type": "string",
"description": "Open Telemetry protocol",
"default": "grpc",
"enum": [
"grpc",
"httpprotobuf"
]
},
"enabled": {
"type": "boolean",
"description": "Allow enabling/disabling Open Telemetry.",
"default": true
}
},
"required": [
"endpoint"
]
}
},
"required": [
"application-insights"
]
}
}
}
},
Expand Down
166 changes: 166 additions & 0 deletions src/Cli.Tests/AddOpenTelemetryTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

namespace Cli.Tests
{
/// <summary>
/// Tests for verifying the functionality of adding OpenTelemetry to the config file.
/// </summary>
[TestClass]
public class AddOpenTelemetryTests
{
public string RUNTIME_SECTION_WITH_OPEN_TELEMETRY_SECTION = GenerateRuntimeSection(TELEMETRY_SECTION_WITH_OPEN_TELEMETRY);
public string RUNTIME_SECTION_WITH_EMPTY_TELEMETRY_SECTION = GenerateRuntimeSection(EMPTY_TELEMETRY_SECTION);
[TestInitialize]
public void TestInitialize()
{
ILoggerFactory loggerFactory = TestLoggerSupport.ProvisionLoggerFactory();

ConfigGenerator.SetLoggerForCliConfigGenerator(loggerFactory.CreateLogger<ConfigGenerator>());
Utils.SetCliUtilsLogger(loggerFactory.CreateLogger<Utils>());
}

/// <summary>
/// Testing to check OpenTelemetry options are correctly added to the config.
/// Verifying scenarios such as enabling/disabling OpenTelemetry and providing a valid/empty endpoint.
/// </summary>
[DataTestMethod]
[DataRow(CliBool.True, "", false, DisplayName = "Fail to add OpenTelemetry with empty endpoint.")]
[DataRow(CliBool.True, "http://localhost:4317", true, DisplayName = "Successfully adds OpenTelemetry with valid endpoint")]
[DataRow(CliBool.False, "http://localhost:4317", true, DisplayName = "Successfully adds OpenTelemetry but disabled")]
public void TestAddOpenTelemetry(CliBool isTelemetryEnabled, string endpoint, bool expectSuccess)
{
MockFileSystem fileSystem = FileSystemUtils.ProvisionMockFileSystem();
string configPath = "test-opentelemetry-config.json";
fileSystem.AddFile(configPath, new MockFileData(INITIAL_CONFIG));

// Initial State
Assert.IsTrue(fileSystem.FileExists(configPath));
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(fileSystem.File.ReadAllText(configPath), out RuntimeConfig? config));
Assert.IsNotNull(config);
Assert.IsNotNull(config.Runtime);
Assert.IsNull(config.Runtime.Telemetry);

// Add OpenTelemetry
bool isSuccess = ConfigGenerator.TryAddTelemetry(
new AddTelemetryOptions(openTelemetryEndpoint: endpoint, openTelemetryEnabled: isTelemetryEnabled, config: configPath),
new FileSystemRuntimeConfigLoader(fileSystem),
fileSystem);

// Assert after adding OpenTelemetry
Assert.AreEqual(expectSuccess, isSuccess);
if (expectSuccess)
{
Assert.IsTrue(fileSystem.FileExists(configPath));
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(fileSystem.File.ReadAllText(configPath), out config));
Assert.IsNotNull(config);
Assert.IsNotNull(config.Runtime);
Assert.IsNotNull(config.Runtime.Telemetry);
TelemetryOptions telemetryOptions = config.Runtime.Telemetry;
Assert.IsNotNull(telemetryOptions.OpenTelemetry);
Assert.AreEqual(isTelemetryEnabled is CliBool.True ? true : false, telemetryOptions.OpenTelemetry.Enabled);
Assert.AreEqual(endpoint, telemetryOptions.OpenTelemetry.Endpoint);
}
}

/// <summary>
/// Test to verify when Telemetry section is present in the config
/// It should add OpenTelemetry if telemetry section is empty
/// or overwrite the existing OpenTelemetry with the given OpenTelemetry options.
/// </summary>
[DataTestMethod]
[DataRow(true, DisplayName = "Add OpenTelemetry when telemetry section is empty.")]
[DataRow(false, DisplayName = "Overwrite OpenTelemetry when telemetry section already exists.")]
public void TestAddOpenTelemetryWhenTelemetryAlreadyExists(bool isEmptyTelemetry)
{
MockFileSystem fileSystem = FileSystemUtils.ProvisionMockFileSystem();
string configPath = "test-opentelemetry-config.json";
string runtimeSection = isEmptyTelemetry ? RUNTIME_SECTION_WITH_EMPTY_TELEMETRY_SECTION : RUNTIME_SECTION_WITH_OPEN_TELEMETRY_SECTION;
string configData = $"{{{SAMPLE_SCHEMA_DATA_SOURCE},{runtimeSection}}}";
fileSystem.AddFile(configPath, new MockFileData(configData));

// Initial State
Assert.IsTrue(fileSystem.FileExists(configPath));
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(fileSystem.File.ReadAllText(configPath), out RuntimeConfig? config));
Assert.IsNotNull(config);
Assert.IsNotNull(config.Runtime);
Assert.IsNotNull(config.Runtime.Telemetry);

if (isEmptyTelemetry)
{
Assert.IsNull(config.Runtime.Telemetry.OpenTelemetry);
}
else
{
Assert.IsNotNull(config.Runtime.Telemetry.OpenTelemetry);
Assert.AreEqual(true, config.Runtime.Telemetry.OpenTelemetry.Enabled);
Assert.AreEqual("http://localhost:4317", config.Runtime.Telemetry.OpenTelemetry.Endpoint);
}

// Add OpenTelemetry
bool isSuccess = ConfigGenerator.TryAddTelemetry(
new AddTelemetryOptions(openTelemetryEndpoint: "http://localhost:4318", openTelemetryEnabled: CliBool.False, config: configPath),
new FileSystemRuntimeConfigLoader(fileSystem),
fileSystem);

// Assert after adding OpenTelemetry
Assert.IsTrue(isSuccess);
Assert.IsTrue(fileSystem.FileExists(configPath));
Assert.IsTrue(RuntimeConfigLoader.TryParseConfig(fileSystem.File.ReadAllText(configPath), out config));
Assert.IsNotNull(config);
Assert.IsNotNull(config.Runtime);
Assert.IsNotNull(config.Runtime.Telemetry);
Assert.IsNotNull(config.Runtime.Telemetry.OpenTelemetry);
Assert.IsFalse(config.Runtime.Telemetry.OpenTelemetry.Enabled);
Assert.AreEqual("http://localhost:4318", config.Runtime.Telemetry.OpenTelemetry.Endpoint);
}

/// <summary>
/// Generates a JSON string representing a runtime section of the config, with a customizable telemetry section.
/// </summary>
private static string GenerateRuntimeSection(string telemetrySection)
{
return $@"
""runtime"": {{
""rest"": {{
""path"": ""/api"",
""enabled"": false
}},
""graphql"": {{
""path"": ""/graphql"",
""enabled"": false,
""allow-introspection"": true
}},
""host"": {{
""mode"": ""development"",
""cors"": {{
""origins"": [],
""allow-credentials"": false
}},
""authentication"": {{
""provider"": ""StaticWebApps""
}}
}},
{telemetrySection}
}},
""entities"": {{}}";
}

/// <summary>
/// Represents a JSON string for the telemetry section of the config, with Application Insights enabled and a specified connection string.
/// </summary>
private const string TELEMETRY_SECTION_WITH_OPEN_TELEMETRY = @"
""telemetry"": {
""open-telemetry"": {
""enabled"": true,
""endpoint"": ""http://localhost:4317""
}
}";

/// <summary>
/// Represents a JSON string for the empty telemetry section of the config.
/// </summary>
private const string EMPTY_TELEMETRY_SECTION = @"
""telemetry"": {}";
}
}
6 changes: 3 additions & 3 deletions src/Cli.Tests/AddTelemetryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void TestAddApplicationInsightsTelemetry(CliBool isTelemetryEnabled, stri

// Add Telemetry
bool isSuccess = ConfigGenerator.TryAddTelemetry(
new AddTelemetryOptions(appInsightsConnString, isTelemetryEnabled, configPath),
new AddTelemetryOptions(appInsightsConnString: appInsightsConnString, appInsightsEnabled: isTelemetryEnabled, config: configPath),
new FileSystemRuntimeConfigLoader(fileSystem),
fileSystem);

Expand Down Expand Up @@ -101,7 +101,7 @@ public void TestAddAppInsightsTelemetryWhenTelemetryAlreadyExists(bool isEmptyTe

// Add Telemetry
bool isSuccess = ConfigGenerator.TryAddTelemetry(
new AddTelemetryOptions("InstrumentationKey=11111-1111-111-11-1", CliBool.False, configPath),
new AddTelemetryOptions(appInsightsConnString: "InstrumentationKey=11111-1111-111-11-1", appInsightsEnabled: CliBool.False, config: configPath),
new FileSystemRuntimeConfigLoader(fileSystem),
fileSystem);

Expand All @@ -119,7 +119,7 @@ public void TestAddAppInsightsTelemetryWhenTelemetryAlreadyExists(bool isEmptyTe

/// <summary>
/// Generates a JSON string representing a runtime section of the config, with a customizable telemetry section.
/// </summary>
/// </summary>
private static string GenerateRuntimeSection(string telemetrySection)
{
return $@"
Expand Down
4 changes: 2 additions & 2 deletions src/Cli.Tests/EndToEndTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,8 @@ public void TestAddTelemetry(string? appInsightsEnabled, string appInsightsConnS
Assert.IsNotNull(updatedConfig.Runtime.Telemetry);
Assert.IsNotNull(updatedConfig.Runtime.Telemetry.ApplicationInsights);

// if --app-insights-enabled is not provided, it will default to true
Assert.AreEqual(appInsightsEnabled is null ? true : Boolean.Parse(appInsightsEnabled), updatedConfig.Runtime.Telemetry.ApplicationInsights.Enabled);
// if --app-insights-enabled is not provided, it will default to false
Assert.AreEqual(appInsightsEnabled is null ? false : Boolean.Parse(appInsightsEnabled), updatedConfig.Runtime.Telemetry.ApplicationInsights.Enabled);
Assert.AreEqual("InstrumentationKey=00000000", updatedConfig.Runtime.Telemetry.ApplicationInsights.ConnectionString);
}

Expand Down
49 changes: 42 additions & 7 deletions src/Cli/Commands/AddTelemetryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Cli.Constants;
using CommandLine;
using Microsoft.Extensions.Logging;
using OpenTelemetry.Exporter;
using static Cli.Utils;

namespace Cli.Commands
Expand All @@ -18,20 +19,54 @@ namespace Cli.Commands
[Verb("add-telemetry", isDefault: false, HelpText = "Add telemetry for Data Api builder Application", Hidden = false)]
public class AddTelemetryOptions : Options
{
public AddTelemetryOptions(string appInsightsConnString, CliBool appInsightsEnabled, string? config) : base(config)
public AddTelemetryOptions(
string? appInsightsConnString = null,
CliBool? appInsightsEnabled = null,
string? openTelemetryEndpoint = null,
CliBool? openTelemetryEnabled = null,
string? openTelemetryHeaders = null,
OtlpExportProtocol? openTelemetryExportProtocol = null,
string? openTelemetryServiceName = null,
string? config = null) : base(config)
{
AppInsightsConnString = appInsightsConnString;
AppInsightsEnabled = appInsightsEnabled;
OpenTelemetryEndpoint = openTelemetryEndpoint;
OpenTelemetryEnabled = openTelemetryEnabled;
OpenTelemetryHeaders = openTelemetryHeaders;
OpenTelemetryExportProtocol = openTelemetryExportProtocol;
OpenTelemetryServiceName = openTelemetryServiceName;
}

// Connection string for the Application Insights resource to which telemetry data should be sent.
// This option is required and must be provided with a valid connection string.
[Option("app-insights-conn-string", Required = true, HelpText = "Connection string for the Application Insights resource for telemetry data")]
public string AppInsightsConnString { get; }
// This option is required and must be provided with a valid connection string when using app insights.
[Option("app-insights-conn-string", Required = false, HelpText = "Connection string for the Application Insights resource for telemetry data")]
public string? AppInsightsConnString { get; }

// To specify whether Application Insights telemetry should be enabled. This flag is optional and default value is true.
[Option("app-insights-enabled", Default = CliBool.True, Required = false, HelpText = "(Default: true) Enable/Disable Application Insights")]
public CliBool AppInsightsEnabled { get; }
// To specify whether Application Insights telemetry should be enabled. This flag is optional and default value is false.
[Option("app-insights-enabled", Default = CliBool.False, Required = false, HelpText = "(Default: false) Enable/Disable Application Insights")]
public CliBool? AppInsightsEnabled { get; }

// Connection string for the Open Telemetry resource to which telemetry data should be sent.
// This option is required and must be provided with a valid connection string when using open telemetry.
[Option("otel-endpoint", Required = false, HelpText = "Endpoint for Open Telemetry for telemetry data")]
public string? OpenTelemetryEndpoint { get; }

// To specify whether Open Telemetry telemetry should be enabled. This flag is optional and default value is false.
[Option("otel-enabled", Default = CliBool.False, Required = false, HelpText = "(Default: false) Enable/Disable OTEL")]
public CliBool? OpenTelemetryEnabled { get; }

// Headers for the Open Telemetry resource to which telemetry data should be sent.
[Option("otel-headers", Required = false, HelpText = "Headers for Open Telemetry for telemetry data")]
public string? OpenTelemetryHeaders { get; }

// Specify the Open Telemetry protocol. This flag is optional and default value is grpc.
[Option("otel-protocol", Default = OtlpExportProtocol.Grpc, Required = false, HelpText = "(Default: grpc) Accepted: grpc/httpprotobuf")]
public OtlpExportProtocol? OpenTelemetryExportProtocol { get; }

// Service Name for the Open Telemetry resource to which telemetry data should be sent. This flag is optional and default value is dab.
[Option("otel-service-name", Default = "dab", Required = false, HelpText = "(Default: dab) Headers for Open Telemetry for telemetry data")]
public string? OpenTelemetryServiceName { get; }

public int Handler(ILogger logger, FileSystemRuntimeConfigLoader loader, IFileSystem fileSystem)
{
Expand Down
29 changes: 26 additions & 3 deletions src/Cli/ConfigGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -619,8 +619,8 @@ private static bool TryUpdateConfiguredDataSourceOptions(

/// <summary>
/// Adds CosmosDB-specific options to the provided database options dictionary.
/// This method checks if the CosmosDB-specific options (database, container, and schema) are provided in the
/// configuration options. If they are, it converts their names using the provided naming policy and adds them
/// This method checks if the CosmosDB-specific options (database, container, and schema) are provided in the
/// configuration options. If they are, it converts their names using the provided naming policy and adds them
/// to the database options dictionary.
/// </summary>
/// <param name="dbOptions">The dictionary to which the CosmosDB-specific options will be added.</param>
Expand Down Expand Up @@ -1939,17 +1939,40 @@ public static bool TryAddTelemetry(AddTelemetryOptions options, FileSystemRuntim
return false;
}

if (string.IsNullOrWhiteSpace(options.AppInsightsConnString))
if (options.AppInsightsEnabled is CliBool.True && string.IsNullOrWhiteSpace(options.AppInsightsConnString))
{
_logger.LogError("Invalid Application Insights connection string provided.");
return false;
}

if (options.OpenTelemetryEnabled is CliBool.True && string.IsNullOrWhiteSpace(options.OpenTelemetryEndpoint))
{
_logger.LogError("Invalid OTEL endpoint provided.");
return false;
}

ApplicationInsightsOptions applicationInsightsOptions = new(
Enabled: options.AppInsightsEnabled is CliBool.True ? true : false,
ConnectionString: options.AppInsightsConnString
);

OpenTelemetryOptions openTelemetryOptions = new(
Enabled: options.OpenTelemetryEnabled is CliBool.True ? true : false,
Endpoint: options.OpenTelemetryEndpoint,
Headers: options.OpenTelemetryHeaders,
ExporterProtocol: options.OpenTelemetryExportProtocol,
ServiceName: options.OpenTelemetryServiceName
);

runtimeConfig = runtimeConfig with
{
Runtime = runtimeConfig.Runtime with
{
Telemetry = runtimeConfig.Runtime.Telemetry is null
? new TelemetryOptions(ApplicationInsights: applicationInsightsOptions, OpenTelemetry: openTelemetryOptions)
: runtimeConfig.Runtime.Telemetry with { ApplicationInsights = applicationInsightsOptions, OpenTelemetry = openTelemetryOptions }
}
};
runtimeConfig = runtimeConfig with
{
Runtime = runtimeConfig.Runtime with
Expand Down
1 change: 1 addition & 0 deletions src/Config/Azure.DataApiBuilder.Config.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<PackageReference Include="System.IdentityModel.Tokens.Jwt" />
<PackageReference Include="Humanizer" />
<PackageReference Include="Npgsql" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" />
</ItemGroup>

<ItemGroup>
Expand Down
12 changes: 12 additions & 0 deletions src/Config/ObjectModel/OpenTelemetryOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using OpenTelemetry.Exporter;

namespace Azure.DataApiBuilder.Config.ObjectModel;

/// <summary>
/// Represents the options for configuring Open Telemetry.
/// </summary>
public record OpenTelemetryOptions(bool Enabled = false, string? Endpoint = null, string? Headers = null, OtlpExportProtocol? ExporterProtocol = null, string? ServiceName = null)
{ }
Loading