Skip to content

Commit 26db37e

Browse files
CopilotJerryNixonAniruddh25
authored
Add default OTEL telemetry settings to dab init config generation (#3162)
## Why make this change? `dab init` generates config with no telemetry section, requiring users to manually edit config or run `dab add-telemetry` to enable standard OpenTelemetry workflows. This is friction for .NET/Aspire and containerized scenarios where OTEL env vars are already set by the orchestrator. ## What is this change? Adds default OpenTelemetry config to `TryCreateRuntimeConfig()` using `@env()` references for standard OTEL environment variables. The generated config now includes: ```json "telemetry": { "open-telemetry": { "enabled": true, "endpoint": "@env('OTEL_EXPORTER_OTLP_ENDPOINT')", "headers": "@env('OTEL_EXPORTER_OTLP_HEADERS')", "service-name": "@env('OTEL_SERVICE_NAME')" } } ``` Additionally, runtime config loading and OTLP exporter setup are hardened to gracefully handle missing OTEL env vars: - **`src/Cli/ConfigGenerator.cs`** — Added `TelemetryOptions` with `OpenTelemetryOptions` to the `RuntimeConfig` constructed during init. - **`src/Config/FileSystemRuntimeConfigLoader.cs`** — Changed `TryLoadKnownConfig` to use `EnvironmentVariableReplacementFailureMode.Ignore` so missing `@env()` env vars don't crash config loading — they remain as literal `@env(...)` strings. - **`src/Service/Startup.cs`** — Added `Uri.TryCreate` validation in the OTLP exporter setup guard condition. If the endpoint is null, empty, or an unresolved `@env(...)` placeholder (not a valid URI), OTLP exporter setup is skipped entirely and a warning is logged. Updated warning message to reference "endpoint URI" instead of "connection string". - **`src/Service/Program.cs`** — Applied the same `Uri.TryCreate` validation for the logger factory OTLP setup, replacing the previous `!string.IsNullOrWhiteSpace` + `new Uri()` pattern. - **31 Verify snapshot files** — Updated to reflect the new telemetry section. - **`src/Cli.Tests/EndToEndTests.cs`** — E2E tests that deserialize init-generated config with `doReplaceEnvVar: true` now use `EnvironmentVariableReplacementFailureMode.Ignore` matching production behavior. `TestAddTelemetry` assertion updated since telemetry is no longer null after init. ## How was this tested? - [x] Integration Tests - [x] Unit Tests All 43 `InitTests` pass. `EndToEndTests.TestInitForCosmosDBNoSql` passes. E2E tests for `TestInitializingRestAndGraphQLGlobalSettings`, `TestEnablingMultipleCreateOperation`, and `TestAddTelemetry` updated to accommodate new defaults. ## Sample Request(s) ```bash dab init --database-type mssql --connection-string "Server=localhost;Database=mydb;" ``` Generated `dab-config.json` now includes the `telemetry.open-telemetry` section with `@env()` references. If the OTEL env vars are set at runtime (e.g., by Aspire or a container orchestrator), telemetry export works automatically. If unset, the `@env()` values remain as literal strings, the endpoint fails `Uri.TryCreate` validation, and OTLP setup is gracefully skipped. <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>dab init should automatically include default OTEL settings </issue_title> > <issue_description>## Problem > Currently, when running `dab init`, the generated `dab-config.json` does NOT include default configuration for OpenTelemetry telemetry using the standard OTEL_* environment variables (such as OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME, etc.). This makes DAB out-of-the-box telemetry support useless compared to typical .NET applications and Aspire scenarios. > > ## Proposal > When `dab init` is run in the CLI, if the telemetry section is being generated (or whenever the user chooses to include telemetry), automatically add the following default values to `dab-config.json`: > > ```json > "telemetry": { > "open-telemetry": { > "enabled": true, > "endpoint": "@env('OTEL_EXPORTER_OTLP_ENDPOINT')", > "headers": "@env('OTEL_EXPORTER_OTLP_HEADERS')", > "exporter-protocol": "@env('OTEL_EXPORTER_OTLP_PROTOCOL')", > "service-name": "@env('OTEL_SERVICE_NAME')" > } > } > ``` > > These defaults should follow the documented OTEL env variable mapping, so users can use standard .NET/OTEL workflows with container orchestrators and Aspire. If the env vars are not set at runtime, the config values should remain as-is or fall back gracefully. > > ## Benefit > - Aligns DAB telemetry configuration with the "expected" .NET/Aspire experience > - Simplifies Aspire integration and containerized telemetry export > - Reduces user error/misconfiguration > - DAB users don't need to hand-edit config to enable standard OTEL telemetry flows > > ## Implementation Guidance > - Extend CLI config template logic to include the above section when telemetry is requested/enabled > - Consider surfacing a CLI flag to toggle inclusion of OTEL env mappings > - Document the behavior in `dab init` help and in DAB docs > > --- > This is a feature request for improved developer out-of-the-box experience. See [OpenTelemetry config docs](https://learn.microsoft.com/en-us/azure/data-api-builder/concept/monitor/open-telemetry?tabs=bash) for reference.</issue_description> > > <agent_instructions>introduce as little code as possible. </agent_instructions> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > </comments> > </details> <!-- START COPILOT CODING AGENT SUFFIX --> - Fixes #3161 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey). --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: JerryNixon <1749983+JerryNixon@users.noreply.github.com> Co-authored-by: Aniruddh25 <3513779+Aniruddh25@users.noreply.github.com> Co-authored-by: Aniruddh Munde <anmunde@microsoft.com>
1 parent 883f4a1 commit 26db37e

48 files changed

Lines changed: 362 additions & 15 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/Cli.Tests/ConfigGeneratorTests.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,14 @@ public void TestSpecialCharactersInConnectionString()
176176
""provider"": ""AppService""
177177
},
178178
""mode"": ""production""
179+
},
180+
""telemetry"": {
181+
""open-telemetry"": {
182+
""enabled"": true,
183+
""endpoint"": ""@env('OTEL_EXPORTER_OTLP_ENDPOINT')"",
184+
""headers"": ""@env('OTEL_EXPORTER_OTLP_HEADERS')"",
185+
""service-name"": ""@env('OTEL_SERVICE_NAME')""
186+
}
179187
}
180188
},
181189
""autoentities"": {},

src/Cli.Tests/EndToEndTests.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
using Azure.DataApiBuilder.Config.Converters;
45
using Azure.DataApiBuilder.Product;
56
using Cli.Constants;
67
using Microsoft.Data.SqlClient;
@@ -116,7 +117,7 @@ public void TestInitializingRestAndGraphQLGlobalSettings()
116117
string[] args = { "init", "-c", TEST_RUNTIME_CONFIG_FILE, "--connection-string", SAMPLE_TEST_CONN_STRING, "--database-type", "mssql", "--rest.path", "/rest-api", "--rest.enabled", "false", "--graphql.path", "/graphql-api" };
117118
Program.Execute(args, _cliLogger!, _fileSystem!, _runtimeConfigLoader!);
118119

119-
DeserializationVariableReplacementSettings replacementSettings = new(azureKeyVaultOptions: null, doReplaceEnvVar: true, doReplaceAkvVar: true);
120+
DeserializationVariableReplacementSettings replacementSettings = new(azureKeyVaultOptions: null, doReplaceEnvVar: true, doReplaceAkvVar: true, envFailureMode: EnvironmentVariableReplacementFailureMode.Ignore);
120121
Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(
121122
TEST_RUNTIME_CONFIG_FILE,
122123
out RuntimeConfig? runtimeConfig,
@@ -196,7 +197,7 @@ public void TestEnablingMultipleCreateOperation(CliBool isMultipleCreateEnabled,
196197

197198
Program.Execute(args.ToArray(), _cliLogger!, _fileSystem!, _runtimeConfigLoader!);
198199

199-
DeserializationVariableReplacementSettings replacementSettings = new(azureKeyVaultOptions: null, doReplaceEnvVar: true, doReplaceAkvVar: true);
200+
DeserializationVariableReplacementSettings replacementSettings = new(azureKeyVaultOptions: null, doReplaceEnvVar: true, doReplaceAkvVar: true, envFailureMode: EnvironmentVariableReplacementFailureMode.Ignore);
200201
Assert.IsTrue(_runtimeConfigLoader!.TryLoadConfig(
201202
TEST_RUNTIME_CONFIG_FILE,
202203
out RuntimeConfig? runtimeConfig,
@@ -273,7 +274,7 @@ public void TestAddTelemetry(string? appInsightsEnabled, string appInsightsConnS
273274
// Perform assertions on various properties.
274275
Assert.IsNotNull(runtimeConfig);
275276
Assert.IsNotNull(runtimeConfig.Runtime);
276-
Assert.IsNull(runtimeConfig.Runtime.Telemetry);
277+
Assert.IsNotNull(runtimeConfig.Runtime.Telemetry);
277278

278279
string[] addTelemetryArgs;
279280
if (appInsightsEnabled is null)

src/Cli.Tests/EnvironmentTests.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,7 @@ public async Task FailureToStartEngineWhenEnvVarNamedWrong()
165165
string? output = await process.StandardError.ReadLineAsync();
166166
Assert.AreEqual("Deserialization of the configuration file failed during a post-processing step.", output);
167167
output = await process.StandardError.ReadToEndAsync();
168-
StringAssert.Contains(output, "Environmental Variable, "
169-
+ expectedEnvVarName + ", not found.", StringComparison.Ordinal);
168+
StringAssert.Contains(output, "A valid Connection String should be provided.", StringComparison.Ordinal);
170169
process.Kill();
171170
}
172171

src/Cli.Tests/Snapshots/EndToEndTests.TestAddingStoredProcedureWithRestMethodsAndGraphQLOperations.verified.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
Authentication: {
4848
Provider: AppService
4949
}
50+
},
51+
Telemetry: {
52+
OpenTelemetry: {
53+
Enabled: true,
54+
Endpoint: @env('OTEL_EXPORTER_OTLP_ENDPOINT'),
55+
Headers: @env('OTEL_EXPORTER_OTLP_HEADERS'),
56+
ServiceName: @env('OTEL_SERVICE_NAME')
57+
}
5058
}
5159
},
5260
Entities: [

src/Cli.Tests/Snapshots/EndToEndTests.TestConfigGeneratedAfterAddingEntityWithSourceAsStoredProcedure.verified.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
Authentication: {
4848
Provider: AppService
4949
}
50+
},
51+
Telemetry: {
52+
OpenTelemetry: {
53+
Enabled: true,
54+
Endpoint: @env('OTEL_EXPORTER_OTLP_ENDPOINT'),
55+
Headers: @env('OTEL_EXPORTER_OTLP_HEADERS'),
56+
ServiceName: @env('OTEL_SERVICE_NAME')
57+
}
5058
}
5159
},
5260
Entities: [

src/Cli.Tests/Snapshots/EndToEndTests.TestConfigGeneratedAfterAddingEntityWithSourceWithDefaultType.verified.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
Authentication: {
4848
Provider: AppService
4949
}
50+
},
51+
Telemetry: {
52+
OpenTelemetry: {
53+
Enabled: true,
54+
Endpoint: @env('OTEL_EXPORTER_OTLP_ENDPOINT'),
55+
Headers: @env('OTEL_EXPORTER_OTLP_HEADERS'),
56+
ServiceName: @env('OTEL_SERVICE_NAME')
57+
}
5058
}
5159
},
5260
Entities: [

src/Cli.Tests/Snapshots/EndToEndTests.TestConfigGeneratedAfterAddingEntityWithoutIEnumerables.verified.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@
4848
Provider: AppService
4949
},
5050
Mode: Production
51+
},
52+
Telemetry: {
53+
OpenTelemetry: {
54+
Enabled: true,
55+
Endpoint: @env('OTEL_EXPORTER_OTLP_ENDPOINT'),
56+
Headers: @env('OTEL_EXPORTER_OTLP_HEADERS'),
57+
ServiceName: @env('OTEL_SERVICE_NAME')
58+
}
5159
}
5260
},
5361
Entities: [

src/Cli.Tests/Snapshots/EndToEndTests.TestInitForCosmosDBNoSql.verified.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@
5353
Provider: AppService
5454
},
5555
Mode: Production
56+
},
57+
Telemetry: {
58+
OpenTelemetry: {
59+
Enabled: true,
60+
Endpoint: @env('OTEL_EXPORTER_OTLP_ENDPOINT'),
61+
Headers: @env('OTEL_EXPORTER_OTLP_HEADERS'),
62+
ServiceName: @env('OTEL_SERVICE_NAME')
63+
}
5664
}
5765
},
5866
Entities: []

src/Cli.Tests/Snapshots/EndToEndTests.TestUpdatingStoredProcedureWithRestMethods.verified.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
Authentication: {
4848
Provider: AppService
4949
}
50+
},
51+
Telemetry: {
52+
OpenTelemetry: {
53+
Enabled: true,
54+
Endpoint: @env('OTEL_EXPORTER_OTLP_ENDPOINT'),
55+
Headers: @env('OTEL_EXPORTER_OTLP_HEADERS'),
56+
ServiceName: @env('OTEL_SERVICE_NAME')
57+
}
5058
}
5159
},
5260
Entities: [

src/Cli.Tests/Snapshots/EndToEndTests.TestUpdatingStoredProcedureWithRestMethodsAndGraphQLOperations.verified.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@
4747
Authentication: {
4848
Provider: AppService
4949
}
50+
},
51+
Telemetry: {
52+
OpenTelemetry: {
53+
Enabled: true,
54+
Endpoint: @env('OTEL_EXPORTER_OTLP_ENDPOINT'),
55+
Headers: @env('OTEL_EXPORTER_OTLP_HEADERS'),
56+
ServiceName: @env('OTEL_SERVICE_NAME')
57+
}
5058
}
5159
},
5260
Entities: [

0 commit comments

Comments
 (0)