From 31bf4eb66cf56531c9138185f7f761c40e775d5a Mon Sep 17 00:00:00 2001 From: "Bowen Yang (from Dev Box)" Date: Fri, 4 Apr 2025 14:30:53 +0800 Subject: [PATCH 1/3] add CommTagAttribute --- .../Microsoft.Gen.Metrics/Emitter.cs | 21 ++++++-- .../Microsoft.Gen.Metrics/Model/MetricType.cs | 1 + .../Microsoft.Gen.Metrics/Parser.cs | 52 +++++++++++++++++++ .../Microsoft.Gen.Metrics/SymbolHolder.cs | 3 +- .../Microsoft.Gen.Metrics/SymbolLoader.cs | 5 +- .../Metrics/CommonTagAttribute.cs | 44 ++++++++++++++++ 6 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Metrics/CommonTagAttribute.cs diff --git a/src/Generators/Microsoft.Gen.Metrics/Emitter.cs b/src/Generators/Microsoft.Gen.Metrics/Emitter.cs index 0e6e7da0091..5616ac1d3c8 100644 --- a/src/Generators/Microsoft.Gen.Metrics/Emitter.cs +++ b/src/Generators/Microsoft.Gen.Metrics/Emitter.cs @@ -94,7 +94,7 @@ private void GenType(MetricType metricType, string nspace) OutLn(); } - GenInstrumentClass(metricMethod); + GenInstrumentClass(metricMethod, metricType.CommonTags); } } @@ -145,7 +145,8 @@ private void GenInstrumentCreateMethods(MetricType metricType, string nspace) } } - private void GenInstrumentClass(MetricMethod metricMethod) + private void GenInstrumentClass(MetricMethod metricMethod, List>? commonTags) + { const string CounterObjectName = "counter"; const string HistogramObjectName = "histogram"; @@ -180,7 +181,9 @@ private void GenInstrumentClass(MetricMethod metricMethod) } var tagListInit = metricMethod.TagKeys.Count != 0 || - metricMethod.StrongTypeConfigs.Count != 0; + metricMethod.StrongTypeConfigs.Count != 0 || + (commonTags != null && commonTags.Count != 0); + var accessModifier = metricMethod.MetricTypeModifiers.Contains("public") ? "public" @@ -231,7 +234,7 @@ private void GenInstrumentClass(MetricMethod metricMethod) Indent(); OutLn("var tagList = new global::System.Diagnostics.TagList"); OutOpenBrace(); - GenTagList(metricMethod); + GenTagList(metricMethod, commonTags); Unindent(); OutLn("};"); Unindent(); @@ -290,7 +293,7 @@ private void GenInstrumentCreateMethod(MetricMethod metricMethod, string nspace) OutLn(); } - private void GenTagList(MetricMethod metricMethod) + private void GenTagList(MetricMethod metricMethod, List>? commonTags) { if (string.IsNullOrEmpty(metricMethod.StrongTypeObjectName)) { @@ -322,6 +325,14 @@ private void GenTagList(MetricMethod metricMethod) OutLn($"new global::System.Collections.Generic.KeyValuePair(\"{config.TagName}\", {access}),"); } } + + if (commonTags != null) + { + foreach (var tag in commonTags) + { + OutLn($"new global::System.Collections.Generic.KeyValuePair(\"{tag.Key}\", \"{tag.Value}\"),"); + } + } } private void GenTagsParameters(MetricMethod metricMethod) diff --git a/src/Generators/Microsoft.Gen.Metrics/Model/MetricType.cs b/src/Generators/Microsoft.Gen.Metrics/Model/MetricType.cs index e96ef625fff..08886d69f79 100644 --- a/src/Generators/Microsoft.Gen.Metrics/Model/MetricType.cs +++ b/src/Generators/Microsoft.Gen.Metrics/Model/MetricType.cs @@ -14,4 +14,5 @@ internal sealed class MetricType public string Modifiers = string.Empty; public string Keyword = string.Empty; public MetricType? Parent; + public List>? CommonTags; } diff --git a/src/Generators/Microsoft.Gen.Metrics/Parser.cs b/src/Generators/Microsoft.Gen.Metrics/Parser.cs index da05c6a51ec..8903f3bd4e2 100644 --- a/src/Generators/Microsoft.Gen.Metrics/Parser.cs +++ b/src/Generators/Microsoft.Gen.Metrics/Parser.cs @@ -75,6 +75,8 @@ public IReadOnlyList GetMetricClasses(IEnumerable> GetCommonTags(TypeDeclarationSyntax classDeclaration, SymbolHolder symbols, SemanticModel semanticModel, CancellationToken cancellationToken) + { + List> commonTags = new(); + var attributes = classDeclaration.AttributeLists.SelectMany(al => al.Attributes); + + foreach (var attribute in attributes) + { + var attributeTypeInfo = semanticModel.GetTypeInfo(attribute, cancellationToken); + if (attributeTypeInfo.Type == null || !attributeTypeInfo.Type.Equals(symbols.CommonTagAttribute, SymbolEqualityComparer.Default)) + { + continue; + } + + if (attribute.ArgumentList != null) + { + KeyValuePair tag; + string tagKey = string.Empty; + string tagValue = string.Empty; + + foreach (var argument in attribute.ArgumentList.Arguments) + { + var key = argument.NameColon?.Name.ToString(); + var value = argument.Expression.ToString().Trim('"'); + if (key != null && !string.IsNullOrWhiteSpace(key)) + { + if (key == "tagName") + { + tagKey = value; + } + else if (key == "tagValue") + { + tagValue = value; + } + } + } + + if (!string.IsNullOrWhiteSpace(tagKey)) + { + tag = new KeyValuePair(tagKey, tagValue); + } + + commonTags.Add(tag); + } + } + + return commonTags; + } + private static void UpdateMetricKeywordIfRequired(TypeDeclarationSyntax? typeDeclaration, MetricType metricType) { if (typeDeclaration.IsKind(SyntaxKind.RecordStructDeclaration) && diff --git a/src/Generators/Microsoft.Gen.Metrics/SymbolHolder.cs b/src/Generators/Microsoft.Gen.Metrics/SymbolHolder.cs index dec31bcc7b9..2ee13fc5b9a 100644 --- a/src/Generators/Microsoft.Gen.Metrics/SymbolHolder.cs +++ b/src/Generators/Microsoft.Gen.Metrics/SymbolHolder.cs @@ -15,4 +15,5 @@ internal sealed record class SymbolHolder( INamedTypeSymbol? HistogramOfTAttribute, INamedTypeSymbol? GaugeAttribute, INamedTypeSymbol LongTypeSymbol, - INamedTypeSymbol? TagNameAttribute); + INamedTypeSymbol? TagNameAttribute, + INamedTypeSymbol? CommonTagAttribute); diff --git a/src/Generators/Microsoft.Gen.Metrics/SymbolLoader.cs b/src/Generators/Microsoft.Gen.Metrics/SymbolLoader.cs index 4c8bbfc31d8..3a8cd73c302 100644 --- a/src/Generators/Microsoft.Gen.Metrics/SymbolLoader.cs +++ b/src/Generators/Microsoft.Gen.Metrics/SymbolLoader.cs @@ -12,6 +12,7 @@ internal static class SymbolLoader internal const string GaugeAttribute = "Microsoft.Extensions.Diagnostics.Metrics.GaugeAttribute"; internal const string CounterAttribute = "Microsoft.Extensions.Diagnostics.Metrics.CounterAttribute"; internal const string HistogramAttribute = "Microsoft.Extensions.Diagnostics.Metrics.HistogramAttribute"; + internal const string CommonTagAttribute = "Microsoft.Extensions.Diagnostics.Metrics.CommonTagAttribute"; internal const string TagNameAttribute = "Microsoft.Extensions.Diagnostics.Metrics.TagNameAttribute"; internal const string MeterClass = "System.Diagnostics.Metrics.Meter"; @@ -33,6 +34,7 @@ internal static class SymbolLoader var histogramTAttribute = compilation.GetTypeByMetadataName(HistogramTAttribute); var gaugeAttribute = compilation.GetTypeByMetadataName(GaugeAttribute); var tagNameAttribute = compilation.GetTypeByMetadataName(TagNameAttribute); + var commonTagAttribute = compilation.GetTypeByMetadataName(CommonTagAttribute); var longType = compilation.GetSpecialType(SpecialType.System_Int64); return new( @@ -43,6 +45,7 @@ internal static class SymbolLoader histogramTAttribute, gaugeAttribute, longType, - tagNameAttribute); + tagNameAttribute, + commonTagAttribute); } } diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Metrics/CommonTagAttribute.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Metrics/CommonTagAttribute.cs new file mode 100644 index 00000000000..d462a0ac58f --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Metrics/CommonTagAttribute.cs @@ -0,0 +1,44 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.Extensions.Telemetry.Metrics; + +/// +/// Provides a way to add common tags to the all the metrics in this class. +/// +/// +/// +/// [CommonTag("Category", "Http")] +/// [CommonTag("MetricVersion", "v1")] +/// static partial class Metric +/// { +/// [Counter("RequestName", "RequestStatusCode")] +/// static partial RequestCounter CreateRequestCounter(Meter meter); +/// } +/// +/// +[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] +[Conditional("CODE_GENERATION_ATTRIBUTES")] +public sealed class CommonTagAttribute : Attribute +{ + public CommonTagAttribute(string tagName, string tagValue) + { + if (string.IsNullOrEmpty(tagName)) + { + throw new ArgumentException("tagName name cannot be null or empty.", nameof(tagName)); + } + + this.TagName = tagName; + this.TagValue = tagValue; + } + + public string TagName { get; } + public string TagValue { get; } +} From f30bdc8c159cbc6d5e590c803a6a3cc721a5033c Mon Sep 17 00:00:00 2001 From: "Bowen Yang (from Dev Box)" Date: Fri, 4 Apr 2025 15:02:29 +0800 Subject: [PATCH 2/3] fix warning --- .../Microsoft.Gen.Metrics/Emitter.cs | 2 -- .../Metrics/CommonTagAttribute.cs | 23 +++++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/Generators/Microsoft.Gen.Metrics/Emitter.cs b/src/Generators/Microsoft.Gen.Metrics/Emitter.cs index 5616ac1d3c8..e384632ca35 100644 --- a/src/Generators/Microsoft.Gen.Metrics/Emitter.cs +++ b/src/Generators/Microsoft.Gen.Metrics/Emitter.cs @@ -146,7 +146,6 @@ private void GenInstrumentCreateMethods(MetricType metricType, string nspace) } private void GenInstrumentClass(MetricMethod metricMethod, List>? commonTags) - { const string CounterObjectName = "counter"; const string HistogramObjectName = "histogram"; @@ -184,7 +183,6 @@ private void GenInstrumentClass(MetricMethod metricMethod, List + /// Initializes a new instance of the class. + /// + /// Tag name. + /// Tag value. public CommonTagAttribute(string tagName, string tagValue) { if (string.IsNullOrEmpty(tagName)) { - throw new ArgumentException("tagName name cannot be null or empty.", nameof(tagName)); + Throw.ArgumentException(nameof(tagName), "tagName name cannot be null or empty."); } - this.TagName = tagName; - this.TagValue = tagValue; + TagName = tagName; + TagValue = tagValue; } + /// + /// Gets the metric's tag name. + /// public string TagName { get; } + + /// + /// Gets the metric's tag value. + /// public string TagValue { get; } } From 3ce9f18c6833aacf7b56232b3e4536b798d158a0 Mon Sep 17 00:00:00 2001 From: "Bowen Yang (from Dev Box)" Date: Mon, 7 Apr 2025 09:18:09 +0800 Subject: [PATCH 3/3] fix build error --- .../Metrics/CommonTagAttribute.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Metrics/CommonTagAttribute.cs b/src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Metrics/CommonTagAttribute.cs index 33198184954..20f773b9fb5 100644 --- a/src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Metrics/CommonTagAttribute.cs +++ b/src/Libraries/Microsoft.Extensions.Telemetry.Abstractions/Metrics/CommonTagAttribute.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.Shared.Diagnostics; namespace Microsoft.Extensions.Telemetry.Metrics; @@ -23,6 +24,7 @@ namespace Microsoft.Extensions.Telemetry.Metrics; /// [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] [Conditional("CODE_GENERATION_ATTRIBUTES")] +[Experimental("EXPERIMENTAL_COMMON_TAG_ATTRIBUTE")] public sealed class CommonTagAttribute : Attribute { ///