Skip to content

Commit e93246b

Browse files
anna-gitandrewlock
andcommitted
Apply documentation suggestions from Andrew's code review
Co-authored-by: Andrew Lock <[email protected]>
1 parent d980f7a commit e93246b

File tree

73 files changed

+443
-365
lines changed

Some content is hidden

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

73 files changed

+443
-365
lines changed

Datadog.Trace.Minimal.slnf

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
"path": "Datadog.Trace.sln",
44
"projects": [
55
"tracer\\src\\Datadog.Trace.ClrProfiler.Managed.Loader\\Datadog.Trace.ClrProfiler.Managed.Loader.csproj",
6-
"tracer\\src\\Datadog.Tracer.Native\\Datadog.Tracer.Native.DLL.vcxproj",
7-
"tracer\\src\\Datadog.Tracer.Native\\Datadog.Tracer.Native.vcxproj",
86
"tracer\\src\\Datadog.Trace.MSBuild\\Datadog.Trace.MSBuild.csproj",
9-
"tracer\\src\\Datadog.Trace.Tools.Analyzers\\Datadog.Trace.Tools.Analyzers.csproj",
7+
"tracer\\src\\Datadog.Trace.SourceGenerators\\Datadog.Trace.SourceGenerators.csproj",
108
"tracer\\src\\Datadog.Trace.Tools.Analyzers.CodeFixes\\Datadog.Trace.Tools.Analyzers.CodeFixes.csproj",
9+
"tracer\\src\\Datadog.Trace.Tools.Analyzers\\Datadog.Trace.Tools.Analyzers.csproj",
10+
"tracer\\src\\Datadog.Tracer.Native\\Datadog.Tracer.Native.DLL.vcxproj",
11+
"tracer\\src\\Datadog.Tracer.Native\\Datadog.Tracer.Native.vcxproj",
1112
"tracer\\src\\Datadog.Trace\\Datadog.Trace.csproj",
12-
"tracer\\src\\Datadog.Trace.SourceGenerators\\Datadog.Trace.SourceGenerators.csproj"]
13+
"tracer\\test\\Datadog.Trace.SourceGenerators.Tests\\Datadog.Trace.SourceGenerators.Tests.csproj"
14+
]
1315
}
1416
}

docs/development/Configuration/AddingConfigurationKeys.md

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@ This guide explains how to add new configuration keys to the .NET Tracer. Config
2222

2323
Configuration keys in the .NET Tracer are defined in two source files:
2424

25-
- **`tracer/src/Datadog.Trace/Configuration/supported-configurations.json`** - Defines the configuration keys, their environment variable names, and fallback chains
26-
- **`tracer/src/Datadog.Trace/Configuration/supported-configurations-docs.yaml`** - Contains XML documentation for each key
25+
- **`tracer/src/Datadog.Trace/Configuration/supported-configurations.json`** - Defines the configuration keys, their
26+
environment variable names, and optional fallbacks.
27+
- **`tracer/src/Datadog.Trace/Configuration/supported-configurations-docs.yaml`** - Contains XML documentation for
28+
each key. We're using yaml here as it makes it easier for some of the long documentation summaries and formatting.
2729

2830
Two source generators read these files at build time:
2931

@@ -38,7 +40,10 @@ Two source generators read these files at build time:
3840

3941
### 1. Add the Configuration Key Definition
4042

41-
Add your new configuration key to `tracer/src/Datadog.Trace/Configuration/supported-configurations.json`.
43+
Add your new configuration key to `tracer/src/Datadog.Trace/Configuration/supported-configurations.json`, specifying
44+
an arbitrary version string (e.g. `"A"`, as shown below). and specifying the product if required. Any product name
45+
is allowed, but try to reuse the existing ones (see [Common products](#common-products)) if it makes sense, as they will create another partial class, ie
46+
ConfigurationKeys.ProductName.cs. Without a product name, the keys will go in the main class, ConfigurationKeys.cs.
4247

4348
**Example:**
4449
```json
@@ -77,13 +82,13 @@ OTEL_EXPORTER_OTLP_LOGS_TIMEOUT: |
7782
- **Do NOT include `<summary>` tags** - the source generator automatically wraps your documentation in `<summary>` tags
7883
- You can include `<seealso>` and `<see>` tags directly in the content - the source generator will extract `<seealso>` tags and place them outside the `<summary>` section as needed
7984

80-
### 3. (Optional) Add Fallback Keys (Aliases)
85+
### 3. (Optional) Add Aliases
8186

82-
Configuration keys can have **fallback keys** (also called aliases) that are checked in order of appearance when the primary key is not found. Add them to the `fallbacks` section in `supported-configurations.json`:
87+
Configuration keys can have **aliases** that are checked in order of appearance when the primary key is not found. Add them to the `aliases` section in `supported-configurations.json`:
8388

8489
```json
8590
{
86-
"fallbacks": {
91+
"aliases": {
8792
"OTEL_EXPORTER_OTLP_LOGS_TIMEOUT": [
8893
"OTEL_EXPORTER_OTLP_TIMEOUT"
8994
]
@@ -93,9 +98,11 @@ Configuration keys can have **fallback keys** (also called aliases) that are che
9398

9499
**How it works:**
95100
1. The configuration system first looks for `OTEL_EXPORTER_OTLP_LOGS_TIMEOUT`
96-
2. If not found, it checks `OTEL_EXPORTER_OTLP_TIMEOUT` (the fallback)
101+
2. If not found, it automatically checks `OTEL_EXPORTER_OTLP_TIMEOUT` (the alias)
97102
3. If still not found, it uses the default value
98103

104+
The `ConfigKeyAliasesSwitcherGenerator` source generator automatically generates the alias resolution logic from this section. No additional code is needed - just use the primary configuration key constant and the aliases are handled transparently.
105+
99106
**Use cases:**
100107
- **Specific → General fallback:** A specific key (e.g., logs timeout) falls back to a general key (e.g., overall timeout)
101108
- **Backward compatibility:** Renamed keys can fall back to their old names to maintain compatibility
@@ -119,7 +126,7 @@ If you need to explicitly control the constant name (e.g., for backward compatib
119126

120127
### 5. Build to Generate Code
121128

122-
Build the `Datadog.Trace` project to run the source generator:
129+
Build the `Datadog.Trace` project to run the source generator, either using Nuke or by building the project directly from the command line or your IDE:
123130

124131
```bash
125132
# From repository root
@@ -147,6 +154,18 @@ var timeout = source.GetInt32(ConfigurationKeys.OpenTelemetry.ExporterOtlpLogsTi
147154

148155
**Note:** The generated constants are in the `Datadog.Trace.Configuration` namespace.
149156

157+
#### Syntax Analyzers
158+
159+
The codebase includes Roslyn analyzers that enforce the use of configuration keys from the `ConfigurationKeys` classes:
160+
161+
- **`ConfigurationBuilderWithKeysAnalyzer`** - Enforces that `ConfigurationBuilder.WithKeys()` method calls only accept string constants from `ConfigurationKeys` or `PlatformKeys` classes, not hardcoded strings or variables.
162+
163+
**Diagnostic rules:**
164+
- **DD0007**: Triggers when hardcoded string literals are used instead of configuration key constants
165+
- **DD0008**: Triggers when variables or expressions are used instead of configuration key constants
166+
167+
These analyzers help prevent typos and ensure consistency across the codebase by enforcing compile-time validation of configuration keys.
168+
150169
### 7. Add to Telemetry Normalization Rules
151170

152171
Configuration keys are reported in telemetry with normalized names. Add your key to the normalization rules:
@@ -190,7 +209,8 @@ Use the `product` field to organize related keys into nested classes:
190209

191210
Generates: `ConfigurationKeys.OpenTelemetry.ExporterOtlpEndpoint`
192211

193-
**Common products:**
212+
#### Common products
213+
194214
- `OpenTelemetry` - OpenTelemetry-related keys
195215
- `CIVisibility` - CI Visibility keys
196216
- `Telemetry` - Telemetry configuration
@@ -230,7 +250,7 @@ DD_TRACE_SAMPLE_RATE: |
230250
var rate = source.GetDouble(ConfigurationKeys.GlobalSamplingRate);
231251
```
232252

233-
### Example 2: Configuration Key with Fallback
253+
### Example 2: Configuration Key with Aliases
234254

235255
**supported-configurations.json:**
236256
```json
@@ -245,7 +265,7 @@ var rate = source.GetDouble(ConfigurationKeys.GlobalSamplingRate);
245265
"product": "OpenTelemetry"
246266
}
247267
},
248-
"fallbacks": {
268+
"aliases": {
249269
"OTEL_EXPORTER_OTLP_LOGS_TIMEOUT": [
250270
"OTEL_EXPORTER_OTLP_TIMEOUT"
251271
]
@@ -257,22 +277,20 @@ var rate = source.GetDouble(ConfigurationKeys.GlobalSamplingRate);
257277
```yaml
258278
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT: |
259279
Configuration key for the timeout in milliseconds for OTLP logs export.
260-
Falls back to <see cref="ConfigurationKeys.OpenTelemetry.ExporterOtlpTimeoutMs"/> if not set.
280+
Falls back to <see cref="ConfigurationKeys.OpenTelemetry.ExporterOtlpTimeout"/> if not set.
261281
Default value is 10000ms.
262282
<seealso cref="Datadog.Trace.Configuration.TracerSettings.OtlpLogsTimeoutMs"/>
263283
264284
OTEL_EXPORTER_OTLP_TIMEOUT: |
265285
Configuration key for the general OTLP export timeout in milliseconds.
266-
Used as fallback for specific timeout configurations.
286+
Used as alias for specific timeout configurations.
267287
Default value is 10000ms.
268288
```
269289

270290
**Usage:**
271291
```csharp
272-
// Reads OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, falls back to OTEL_EXPORTER_OTLP_TIMEOUT
273-
var timeout = source.GetInt32(
274-
ConfigurationKeys.OpenTelemetry.ExporterOtlpLogsTimeout,
275-
ConfigurationKeys.OpenTelemetry.ExporterOtlpTimeout);
292+
// Reads OTEL_EXPORTER_OTLP_LOGS_TIMEOUT, automatically falls back to OTEL_EXPORTER_OTLP_TIMEOUT
293+
var timeout = source.GetInt32(ConfigurationKeys.OpenTelemetry.ExporterOtlpLogsTimeout);
276294
```
277295

278296
### Example 3: Feature Flag
@@ -336,17 +354,19 @@ dotnet build tracer/src/Datadog.Trace/Datadog.Trace.csproj
336354
3. XML tags are properly closed
337355
4. Rebuild after YAML changes
338356

339-
### Fallback not working
357+
### Aliases not working
340358

341359
**Check:**
342-
1. Fallback key is defined in `configurations` section
343-
2. Fallback array is in correct order (first fallback is tried first)
344-
3. Using the correct overload of `GetXxx()` method that accepts fallback keys
360+
1. Alias key is defined in `supportedConfigurations` section
361+
2. Alias array is in correct order (first alias is tried first)
362+
3. Both `ConfigurationKeysGenerator` and `ConfigKeyAliasesSwitcherGenerator` ran successfully during build
345363

346364
## Related Files
347365

348-
- **Source generator:** `tracer/src/Datadog.Trace.SourceGenerators/Configuration/ConfigurationKeysGenerator.cs`
366+
- **Source generators:**
367+
- `tracer/src/Datadog.Trace.SourceGenerators/Configuration/ConfigurationKeysGenerator.cs` - Generates configuration key constants
368+
- `tracer/src/Datadog.Trace.SourceGenerators/Configuration/ConfigKeyAliasesSwitcherGenerator.cs` - Generates alias resolution logic
349369
- **Configuration source:** `tracer/src/Datadog.Trace/Configuration/supported-configurations.json`
350370
- **Documentation source:** `tracer/src/Datadog.Trace/Configuration/supported-configurations-docs.yaml`
351371
- **Telemetry rules:** `tracer/test/Datadog.Trace.Tests/Telemetry/config_norm_rules.json`
352-
- **Generated output:** `tracer/src/Datadog.Trace/Generated/<tfm>/Datadog.Trace.SourceGenerators/ConfigurationKeysGenerator/`
372+
- **Generated output:** `tracer/src/Datadog.Trace/Generated/<tfm>/Datadog.Trace.SourceGenerators/`

tracer/src/Datadog.Trace.SourceGenerators/Configuration/ConfigKeyAliasesSwitcherGenerator.cs

Lines changed: 47 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@
1313
using Datadog.Trace.SourceGenerators;
1414
using Datadog.Trace.SourceGenerators.Helpers;
1515
using Microsoft.CodeAnalysis;
16-
using Microsoft.CodeAnalysis.CSharp;
17-
using Microsoft.CodeAnalysis.CSharp.Syntax;
1816
using Microsoft.CodeAnalysis.Text;
19-
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
2017

2118
/// <summary>
2219
/// Source generator that reads supported-configurations.json and generates a switch case
@@ -69,8 +66,7 @@ private static void Execute(SourceProductionContext context, Result<Configuratio
6966

7067
// Generate source code even if there are errors (use empty configuration as fallback)
7168
var configurationAliases = result.Value ?? new ConfigurationAliases(new Dictionary<string, string[]>());
72-
var compilationUnit = GenerateConfigurationKeyMatcher(configurationAliases);
73-
var generatedSource = compilationUnit.NormalizeWhitespace(eol: "\n").ToFullString();
69+
var generatedSource = GenerateConfigurationKeyMatcher(configurationAliases);
7470
context.AddSource($"{ClassName}.g.cs", SourceText.From(generatedSource, Encoding.UTF8));
7571
}
7672

@@ -197,97 +193,58 @@ private static DiagnosticInfo CreateDiagnosticInfo(string id, string title, stri
197193
return new DiagnosticInfo(descriptor, Location.None);
198194
}
199195

200-
private static CompilationUnitSyntax GenerateConfigurationKeyMatcher(ConfigurationAliases configurationAliases)
196+
private static string GenerateConfigurationKeyMatcher(ConfigurationAliases configurationAliases)
201197
{
202-
var getAliasesMethod = GenerateGetAliasesMethod(configurationAliases);
203-
204-
var classDeclaration = ClassDeclaration(ClassName)
205-
.WithModifiers(TokenList(Token(SyntaxKind.InternalKeyword), Token(SyntaxKind.StaticKeyword), Token(SyntaxKind.PartialKeyword)))
206-
.WithLeadingTrivia(
207-
Comment(Constants.FileHeader),
208-
Comment("/// <summary>"),
209-
Comment("/// Generated configuration key matcher that handles main keys and aliases."),
210-
Comment("/// This file is auto-generated from supported-configurations.json and supported-configurations-docs.yaml. Do not edit this file directly. The source generator will regenerate it on build."),
211-
Comment("/// </summary>"))
212-
.WithMembers(
213-
List<MemberDeclarationSyntax>(
214-
[
215-
getAliasesMethod
216-
]));
217-
218-
var namespaceDeclaration = FileScopedNamespaceDeclaration(
219-
QualifiedName(
220-
QualifiedName(
221-
IdentifierName("Datadog"),
222-
IdentifierName("Trace")),
223-
IdentifierName("Configuration")))
224-
.WithMembers(SingletonList<MemberDeclarationSyntax>(classDeclaration));
225-
226-
return CompilationUnit()
227-
.WithUsings(
228-
SingletonList(
229-
UsingDirective(IdentifierName("System"))))
230-
.WithMembers(SingletonList<MemberDeclarationSyntax>(namespaceDeclaration));
231-
}
232-
233-
private static MethodDeclarationSyntax GenerateGetAliasesMethod(ConfigurationAliases configurationAliases)
234-
{
235-
var switchArms = new List<SwitchExpressionArmSyntax>();
236-
237-
// Add cases for keys that have aliases
238-
foreach (var alias in configurationAliases.Aliases.OrderBy(a => a.Key))
198+
var sb = new StringBuilder();
199+
200+
// File header
201+
sb.Append(Constants.FileHeader);
202+
203+
// Namespace
204+
sb.AppendLine("namespace Datadog.Trace.Configuration;");
205+
sb.AppendLine();
206+
207+
// Class XML documentation
208+
sb.AppendLine("/// <summary>");
209+
sb.AppendLine("/// Generated configuration key matcher that handles main keys and aliases.");
210+
sb.AppendLine("/// Do not edit this file directly as it is auto-generated from supported-configurations.json and supported-configurations-docs.yaml.");
211+
sb.AppendLine("/// For more info, see docs/development/Configuration/AddingConfigurationKeys.md");
212+
sb.AppendLine("/// </summary>");
213+
214+
// Class declaration
215+
sb.AppendLine("internal static partial class ConfigKeyAliasesSwitcher");
216+
sb.AppendLine("{");
217+
218+
// Method XML documentation
219+
sb.AppendLine(" /// <summary>");
220+
sb.AppendLine(" /// Gets all aliases for the given configuration key.");
221+
sb.AppendLine(" /// </summary>");
222+
sb.AppendLine(" /// <param name=\"mainKey\">The configuration key.</param>");
223+
sb.AppendLine(" /// <returns>An array of aliases for the key, or empty array if no aliases exist.</returns>");
224+
225+
// Method signature
226+
sb.AppendLine(" public static string[] GetAliases(string mainKey) => mainKey switch");
227+
sb.AppendLine(" {");
228+
229+
// Generate switch cases for each alias
230+
foreach (var kvp in configurationAliases.Aliases.OrderBy(a => a.Key))
239231
{
240-
var mainKey = alias.Key;
241-
var aliasKeys = alias.Value;
232+
var mainKey = kvp.Key;
233+
var aliases = kvp.Value;
242234

243-
// Create collection expression elements
244-
var collectionElements = aliasKeys
245-
.OrderBy(a => a)
246-
.Select(aliasKey => ExpressionElement(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(aliasKey))))
247-
.Cast<CollectionElementSyntax>()
248-
.ToArray();
249-
250-
// Create collection expression [ "alias1", "alias2" ]
251-
var collectionExpression = CollectionExpression(SeparatedList(collectionElements));
235+
// Build the collection expression
236+
var aliasesStr = string.Join(", ", aliases.Select(a => $"\"{a}\""));
237+
sb.AppendLine($" \"{mainKey}\" => [{aliasesStr}],");
238+
}
252239

253-
// Create switch arm: "DD_AGENT_HOST" => [ "alias1", "alias2" ],
254-
var switchArm = SwitchExpressionArm(
255-
ConstantPattern(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(mainKey))),
256-
collectionExpression);
240+
// Default case
241+
sb.AppendLine(" _ => []");
257242

258-
switchArms.Add(switchArm);
259-
}
243+
// Close method and class
244+
sb.AppendLine(" };");
245+
sb.AppendLine("}");
260246

261-
// Add default case: _ => []
262-
var defaultArm = SwitchExpressionArm(
263-
DiscardPattern(),
264-
CollectionExpression());
265-
266-
switchArms.Add(defaultArm);
267-
268-
// Create switch expression: mainKey switch { ... }
269-
var switchExpression = SwitchExpression(
270-
IdentifierName(MainKeyParamName),
271-
SeparatedList(switchArms));
272-
273-
return MethodDeclaration(
274-
ArrayType(PredefinedType(Token(SyntaxKind.StringKeyword)))
275-
.WithRankSpecifiers(SingletonList(ArrayRankSpecifier(SingletonSeparatedList<ExpressionSyntax>(OmittedArraySizeExpression())))),
276-
"GetAliases")
277-
.WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword), Token(SyntaxKind.StaticKeyword)))
278-
.WithParameterList(
279-
ParameterList(
280-
SingletonSeparatedList(
281-
Parameter(Identifier(MainKeyParamName))
282-
.WithType(PredefinedType(Token(SyntaxKind.StringKeyword))))))
283-
.WithLeadingTrivia(
284-
Comment("/// <summary>"),
285-
Comment("/// Gets all aliases for the given configuration key."),
286-
Comment("/// </summary>"),
287-
Comment($"/// <param name=\"{MainKeyParamName}\">The configuration key.</param>"),
288-
Comment("/// <returns>An array of aliases for the key, or empty array if no aliases exist.</returns>"))
289-
.WithExpressionBody(ArrowExpressionClause(switchExpression))
290-
.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
247+
return sb.ToString();
291248
}
292249

293250
private sealed class ConfigurationAliases(Dictionary<string, string[]> aliases) : IEquatable<ConfigurationAliases>

tracer/src/Datadog.Trace.SourceGenerators/Configuration/ConfigurationKeysGenerator.cs

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -288,27 +288,21 @@ private static Dictionary<string, ConfigEntry> ParseConfigurationEntries(JsonEle
288288
return configurations;
289289
}
290290

291-
private static void AppendFileHeader(StringBuilder sb)
292-
{
293-
sb.Append(Constants.FileHeader);
294-
sb.Append(Constants.ConfigurationGeneratorComment);
295-
}
296-
297291
private static string GenerateProductPartialClass(string product, List<KeyValuePair<string, ConfigEntry>> entries, Dictionary<string, ConstantNameMapping>? nameMapping)
298292
{
299293
var sb = new StringBuilder();
300-
301-
AppendFileHeader(sb);
294+
sb.Append(Constants.FileHeader);
302295
sb.Append("namespace ").Append(Namespace).AppendLine(";");
303296
sb.AppendLine();
297+
sb.AppendLine("/// <summary>");
298+
sb.AppendLine("/// String constants for standard Datadog configuration keys.");
299+
sb.AppendLine("/// Do not edit this file directly as it's auto-generated from supported-configurations.json and supported-configurations-docs.yaml");
300+
sb.AppendLine("/// For more info, see docs/development/Configuration/AddingConfigurationKeys.md");
301+
sb.AppendLine("/// </summary>");
304302

305303
if (string.IsNullOrEmpty(product))
306304
{
307305
// Generate main class without nested product class
308-
sb.AppendLine("/// <summary>");
309-
sb.AppendLine("/// String constants for standard Datadog configuration keys.");
310-
sb.AppendLine("/// Auto-generated from supported-configurations.json and supported-configurations-docs.yaml");
311-
sb.AppendLine("/// </summary>");
312306
sb.AppendLine($"internal static partial class {GeneratedClassName}");
313307
sb.AppendLine("{");
314308

0 commit comments

Comments
 (0)