Skip to content

[Logging] Make TagName and TagProvider attributes work when applied to properties - #7751

Open
LouisDeconinck wants to merge 2 commits into
dotnet:mainfrom
LouisDeconinck:fix/logging-tagname-tagprovider-on-properties
Open

LouisDeconinck wants to merge 2 commits into
dotnet:mainfrom
LouisDeconinck:fix/logging-tagname-tagprovider-on-properties

Conversation

@LouisDeconinck

@LouisDeconinck LouisDeconinck commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #6605

When a property of a [LogProperties] parameter was annotated with [TagName] or [TagProvider], the generated logging code ignored both attributes: generated tag names always used the property name and the provider method was never invoked. The parser already picked up both attributes; the emitter just never used them.

Changes

  • Emitter: use each property's TagName (the custom name when [TagName] is present) when composing generated tag names. The root chain element now uses the parameter name so a {Template} binding doesn't leak into property tag names.
  • Emitter: invoke property-level tag providers. The provider is called with LoggerMessageState.TagNamePrefix set to the full property path (e.g. param.Nested.Property), honoring OmitReferenceName on both the parameter ([LogProperties(OmitReferenceName = true)] drops the parameter prefix) and the property ([TagProvider(..., OmitReferenceName = true)] drops the property name itself). Properties handled by a provider no longer occupy reserved tag slots.
  • Emitter: when [LogProperties(SkipNullProperties = true)] is used, a null property value skips the provider call entirely, consistent with how other null properties are skipped. The provider call is also null-guarded when the property access can produce null but the provider signature doesn't accept it.
  • Attributes: removed [Conditional("CODE_GENERATION_ATTRIBUTES")] from TagNameAttribute and TagProviderAttribute so the attributes persist to metadata. This is what makes them work when the logged object is defined in a different assembly than the logging method — the same approach as [Logging] Fixes LogProperties and LogPropertyIgnore attributes to work if an object being logged resides in a different assembly than the logging method #6600 for LogPropertiesAttribute/LogPropertyIgnoreAttribute.
  • Parser: made the syntax reference access null-safe in ProcessTagProviderForProperty for attributes coming from metadata.

Tests

  • New runtime tests cover TagName, TagProvider (including nested and OmitReferenceName), SkipNullProperties, and the cross-assembly scenario via Microsoft.Gen.Logging.HelperLibrary.
  • Updated existing expectations for the pre-existing [TagProvider] on ClassToLog.AnotherStringProperty, which is now actually invoked.
  • New parser/emitter test asserts the generated source uses the custom tag name and calls the provider.

Microsoft.Gen.Logging.Unit.Tests: 368/368 pass. Microsoft.Gen.Logging.Generated.Tests: 106/106 pass (net8.0 and net10.0).

Microsoft Reviewers: Open in CodeFlow

Copilot AI lite review requested due to automatic review settings September 13, 2026 18:41
@LouisDeconinck
LouisDeconinck requested a review from a team as a code owner September 13, 2026 18:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Unresolved generated-string escaping, nested-prefix/nullability handling, and parameter tag-name preservation issues remain.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR enables property-level [TagName] and [TagProvider] support in generated logging code, including cross-assembly scenarios.

Changes:

  • Emits custom property tag names and invokes property tag providers.
  • Preserves attribute metadata and improves metadata parsing.
  • Adds parser, runtime, nested-property, null-handling, and cross-assembly tests.
File summaries
File Summary
test/Generators/Microsoft.Gen.Logging/Unit/ParserTests.TagProvider.cs Tests generated property tag and provider code.
test/Generators/Microsoft.Gen.Logging/TestClasses/TagProviderExtensions.cs Adds provider test fixtures.
test/Generators/Microsoft.Gen.Logging/TestClasses/TagAttributesOnPropertiesExtensions.cs Adds property-attribute logging fixtures.
test/Generators/Microsoft.Gen.Logging/HelperLibrary/ProvidedPropertyTagProvider.cs Adds a cross-assembly provider.
test/Generators/Microsoft.Gen.Logging/HelperLibrary/ProvidedProperty.cs Adds a cross-assembly test model.
test/Generators/Microsoft.Gen.Logging/HelperLibrary/ObjectToLogWithTagAttributes.cs Adds cross-assembly attributed objects.
test/Generators/Microsoft.Gen.Logging/Generated/TagProviderTests.cs Updates provider expectations.
test/Generators/Microsoft.Gen.Logging/Generated/LogPropertiesTests.cs Adds runtime coverage for property attributes.
src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Logging/TagProviderAttribute.cs Preserves provider metadata.
src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Logging/TagNameAttribute.cs Preserves tag-name metadata.
src/Generators/Microsoft.Gen.Logging/Parsing/Parser.TagProvider.cs Handles metadata-only provider attributes safely.
src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodParameterExtensions.cs Builds property traversal roots.
src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs Emits property tags and provider calls.
Review details

Suppressed comments (2)

src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs:545

  • This null guard uses member.IsNullable rather than the provider method's second-parameter nullability. ValidateTagProvider permits a nullable provider argument for a non-nullable property, so when a parent in the property chain is null and SkipNullProperties is false, a valid provider such as Provide(..., Child? value) is incorrectly skipped instead of being invoked with null. Track the provider parameter's nullability and guard only when it cannot accept null; keep the SkipNullProperties guard independent.
                        var canProduceNull = member.PotentiallyNull || propertyChain.Any(static x => x.PotentiallyNull);
                        if (canProduceNull && (!member.IsNullable || p.SkipNullProperties))

src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodParameterExtensions.cs:20

  • This discards an explicit [TagName] on a [LogProperties] parameter. Before this change, parameter.TagName was the root of every property path; only the synthesized template name needs to be avoided here. For [TagName("payload")] [LogProperties] MyType value, this changes payload.Property to value.Property. Preserve the custom tag name unless parameter.UsedAsTemplate is true.
            TagName = parameter.ParameterName,
  • Files reviewed: 11/13 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs Outdated
Comment thread src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs
@LouisDeconinck

Copy link
Copy Markdown
Contributor Author

Addressed the latest review feedback in e06c48c:

  • Escaping: tag names emitted into generated string literals now go through EscapeMessageString (SymbolDisplay.FormatLiteral), including the TagNamePrefix assignment for property-level providers and the tag name literals emitted for TagArray/ClassifiedTagArray/AddTag/AddClassifiedTag calls. Custom [TagName] values containing quotes, backslashes, etc. now produce compilable generated code.
  • Transitive coverage: added runtime tests for a [TagProvider] on a transitively nested leaf property (LogPropertiesSupportsTagProviderOnTransitivelyNestedProperty[WhenOmittingParameterName]), covering all four combinations of parameter-level LogProperties(OmitReferenceName = true) and property-level TagProvider(OmitReferenceName = true).
  • Unit tests: TagNameAndTagProviderOnPropertiesAreEscapedInGeneratedSource verifies that tag names containing a quote and a backslash are escaped in the emitted literals and that the generated source compiles, and TagProviderOnTransitivelyNestedProperty asserts the composed TagNamePrefix values for the nested OmitReferenceName combinations.

All Microsoft.Gen.Logging tests pass: 370 unit + 108 generated (net8.0/net9.0/net10.0).

…o properties

When a property of a [LogProperties] parameter was annotated with
[TagName] or [TagProvider], the generated logging code ignored both
attributes: tag names used the property name and the provider method
was never invoked.

- Emit the property's custom tag name in generated property chains
- Invoke property-level tag providers with the property path as the
  tag name prefix, honoring OmitReferenceName on both the parameter
  and the property
- Skip null property values for providers when SkipNullProperties is
  enabled
- Remove [Conditional("CODE_GENERATION_ATTRIBUTES")] from
  TagNameAttribute and TagProviderAttribute so the attributes are
  persisted to metadata, matching LogPropertiesAttribute and
  LogPropertyIgnoreAttribute, and enabling the attributes to work
  when the logged object is defined in a different assembly
- Guard against a missing syntax reference when the attribute comes
  from metadata

Fixes dotnet#6605
- Emit tag name string literals through EscapeMessageString so custom
  [TagName] values containing characters like quotes or backslashes
  produce compilable generated code
- Add runtime tests for a [TagProvider] on a transitively nested leaf
  property, covering the parameter-level and property-level
  OmitReferenceName combinations
- Add unit tests asserting the escaped literals in the generated
  source and that the generated code compiles
@LouisDeconinck
LouisDeconinck force-pushed the fix/logging-tagname-tagprovider-on-properties branch from e06c48c to c9e8309 Compare September 14, 2026 05:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Logging] TagName and TagProvider attributes don't work when applied to properties

2 participants