66 "runtime"
77 "slices"
88 "strconv"
9+ "strings"
910 "time"
1011
1112 "github.com/hashicorp/golang-lru/v2/expirable"
@@ -121,6 +122,11 @@ type PrometheusConfig struct {
121122 // Registry is only used for embedding Beyla within the Grafana Agent.
122123 // It must be nil when Beyla runs as standalone
123124 Registry * prometheus.Registry `yaml:"-"`
125+
126+ // ExtraResourceLabels adds extra metadata labels to Prometheus metrics from sources whose availability can't be known
127+ // beforehand. For example, to add the OTEL deployment.environment resource attribute as a Prometheus resource attribute,
128+ // you should add `deployment.environment`.
129+ ExtraResourceLabels []string `yaml:"extra_resource_attributes" env:"BEYLA_PROMETHEUS_EXTRA_RESOURCE_ATTRIBUTES" envSeparator:","`
124130}
125131
126132func (p * PrometheusConfig ) SpanMetricsEnabled () bool {
@@ -161,7 +167,8 @@ func (p *PrometheusConfig) Enabled() bool {
161167}
162168
163169type metricsReporter struct {
164- cfg * PrometheusConfig
170+ cfg * PrometheusConfig
171+ extraMetadataLabels []attr.Name
165172
166173 beylaInfo * Expirer [prometheus.Gauge ]
167174 httpDuration * Expirer [prometheus.Histogram ]
@@ -308,11 +315,13 @@ func newReporter(
308315 kubeEnabled := ctxInfo .K8sInformer .IsKubeEnabled ()
309316 // If service name is not explicitly set, we take the service name as set by the
310317 // executable inspector
318+ extraMetadataLabels := parseExtraMetadata (cfg .ExtraResourceLabels )
311319 mr := & metricsReporter {
312320 bgCtx : ctx ,
313321 ctxInfo : ctxInfo ,
314322 cfg : cfg ,
315323 kubeEnabled : kubeEnabled ,
324+ extraMetadataLabels : extraMetadataLabels ,
316325 hostID : ctxInfo .HostID ,
317326 clock : clock ,
318327 is : is ,
@@ -457,7 +466,7 @@ func newReporter(
457466 return NewExpirer [prometheus.Gauge ](prometheus .NewGaugeVec (prometheus.GaugeOpts {
458467 Name : TracesTargetInfo ,
459468 Help : "target service information in trace span metric format" ,
460- }, labelNamesTargetInfo (kubeEnabled )).MetricVec , clock .Time , cfg .TTL )
469+ }, labelNamesTargetInfo (kubeEnabled , extraMetadataLabels )).MetricVec , clock .Time , cfg .TTL )
461470 }),
462471 tracesHostInfo : optionalGaugeProvider (cfg .SpanMetricsEnabled () || cfg .ServiceGraphMetricsEnabled (), func () * Expirer [prometheus.Gauge ] {
463472 return NewExpirer [prometheus.Gauge ](prometheus .NewGaugeVec (prometheus.GaugeOpts {
@@ -500,7 +509,7 @@ func newReporter(
500509 targetInfo : NewExpirer [prometheus.Gauge ](prometheus .NewGaugeVec (prometheus.GaugeOpts {
501510 Name : TargetInfo ,
502511 Help : "attributes associated to a given monitored entity" ,
503- }, labelNamesTargetInfo (kubeEnabled )).MetricVec , clock .Time , cfg .TTL ),
512+ }, labelNamesTargetInfo (kubeEnabled , extraMetadataLabels )).MetricVec , clock .Time , cfg .TTL ),
504513 gpuKernelCallsTotal : optionalCounterProvider (is .GPUEnabled (), func () * Expirer [prometheus.Counter ] {
505514 return NewExpirer [prometheus.Counter ](prometheus .NewCounterVec (prometheus.CounterOpts {
506515 Name : attributes .GPUKernelLaunchCalls .Prom ,
@@ -597,6 +606,16 @@ func newReporter(
597606 return mr , nil
598607}
599608
609+ func parseExtraMetadata (labels []string ) []attr.Name {
610+ // first, we convert any metric in snake_format to dotted.format,
611+ // as it is the internal representation of metadata labels
612+ attrNames := make ([]attr.Name , len (labels ))
613+ for i , label := range labels {
614+ attrNames [i ] = attr .Name (strings .ReplaceAll (label , "_" , "." ))
615+ }
616+ return attrNames
617+ }
618+
600619func optionalHistogramProvider (enable bool , provider func () * Expirer [prometheus.Histogram ]) * Expirer [prometheus.Histogram ] {
601620 if ! enable {
602621 return nil
@@ -796,13 +815,17 @@ func (r *metricsReporter) labelValuesSpans(span *request.Span) []string {
796815 }
797816}
798817
799- func labelNamesTargetInfo (kubeEnabled bool ) []string {
818+ func labelNamesTargetInfo (kubeEnabled bool , extraMetadataLabelNames []attr. Name ) []string {
800819 names := []string {hostIDKey , hostNameKey , serviceKey , serviceNamespaceKey , serviceInstanceKey , serviceJobKey , telemetryLanguageKey , telemetrySDKKey , sourceKey }
801820
802821 if kubeEnabled {
803822 names = appendK8sLabelNames (names )
804823 }
805824
825+ for _ , mdn := range extraMetadataLabelNames {
826+ names = append (names , mdn .Prom ())
827+ }
828+
806829 return names
807830}
808831
@@ -823,6 +846,10 @@ func (r *metricsReporter) labelValuesTargetInfo(service svc.Attrs) []string {
823846 values = appendK8sLabelValuesService (values , service )
824847 }
825848
849+ for _ , k := range r .extraMetadataLabels {
850+ values = append (values , service .Metadata [k ])
851+ }
852+
826853 return values
827854}
828855
0 commit comments