prometheus-net
98 строк · 3.4 Кб
1using Grpc.AspNetCore.Server;2using Microsoft.AspNetCore.Http;3
4namespace Prometheus;5
6// Modeled after HttpRequestMiddlewareBase, just with gRPC specific functionality.
7internal abstract class GrpcRequestMiddlewareBase<TCollector, TChild>8where TCollector : class, ICollector<TChild>9where TChild : class, ICollectorChild10{
11/// <summary>12/// The set of labels from among the defaults that this metric supports.13///14/// This set will be automatically extended with labels for additional15/// route parameters when creating the default metric instance.16/// </summary>17protected abstract string[] DefaultLabels { get; }18
19/// <summary>20/// Creates the default metric instance with the specified set of labels.21/// Only used if the caller does not provide a custom metric instance in the options.22/// </summary>23protected abstract TCollector CreateMetricInstance(string[] labelNames);24
25/// <summary>26/// The factory to use for creating the default metric for this middleware.27/// Not used if a custom metric is alreaedy provided in options.28/// </summary>29protected MetricFactory MetricFactory { get; }30
31private readonly TCollector _metric;32
33protected GrpcRequestMiddlewareBase(GrpcMetricsOptionsBase? options, TCollector? customMetric)34{35MetricFactory = Metrics.WithCustomRegistry(options?.Registry ?? Metrics.DefaultRegistry);36
37if (customMetric != null)38{39_metric = customMetric;40ValidateNoUnexpectedLabelNames();41}42else43{44_metric = CreateMetricInstance(DefaultLabels);45}46}47
48protected TChild? CreateChild(HttpContext context)49{50var metadata = context.GetEndpoint()?.Metadata?.GetMetadata<GrpcMethodMetadata>();51if (metadata == null)52{53// Not a gRPC request54return null;55}56
57if (!_metric.LabelNames.Any())58{59return _metric.Unlabelled;60}61
62return CreateChild(context, metadata);63}64
65protected TChild CreateChild(HttpContext context, GrpcMethodMetadata metadata)66{67var labelValues = new string[_metric.LabelNames.Length];68
69for (var i = 0; i < labelValues.Length; i++)70{71switch (_metric.LabelNames[i])72{73case GrpcRequestLabelNames.Service:74labelValues[i] = metadata.Method.ServiceName;75break;76case GrpcRequestLabelNames.Method:77labelValues[i] = metadata.Method.Name;78break;79default:80// Should never reach this point because we validate in ctor.81throw new NotSupportedException($"Unexpected label name on {_metric.Name}: {_metric.LabelNames[i]}");82}83}84
85return _metric.WithLabels(labelValues);86}87
88/// <summary>89/// If we use a custom metric, it should not have labels that are neither defaults nor additional route parameters.90/// </summary>91private void ValidateNoUnexpectedLabelNames()92{93var unexpected = _metric.LabelNames.Except(DefaultLabels);94
95if (unexpected.Any())96throw new ArgumentException($"Provided custom gRPC request metric instance for {GetType().Name} has some unexpected labels: {string.Join(", ", unexpected)}.");97}98}