in exporter/collector/googlemanagedprometheus/extra_metrics.go [149:257]
func (c Config) addTargetInfoMetric(m pmetric.Metrics) {
if !c.ExtraMetricsConfig.EnableTargetInfo {
return
}
ids := make(map[resourceID]struct{})
rms := m.ResourceMetrics()
// loop over input (original) resource metrics
for i := 0; i < rms.Len(); i++ {
rm := rms.At(i)
getResourceAttr := func(attr string) string {
if v, ok := rm.Resource().Attributes().Get(attr); ok {
return v.AsString()
}
return ""
}
// Keep track of the most recent time in this resource's metrics
// Use that time for the timestamp of the new metric
latestTime := time.Time{}
for j := 0; j < rm.ScopeMetrics().Len(); j++ {
for k := 0; k < rm.ScopeMetrics().At(j).Metrics().Len(); k++ {
metric := rm.ScopeMetrics().At(j).Metrics().At(k)
switch metric.Type() {
case pmetric.MetricTypeSum:
sum := metric.Sum()
points := sum.DataPoints()
for x := 0; x < points.Len(); x++ {
if latestTime.Before(points.At(x).Timestamp().AsTime()) {
latestTime = points.At(x).Timestamp().AsTime()
}
}
case pmetric.MetricTypeGauge:
gauge := metric.Gauge()
points := gauge.DataPoints()
for x := 0; x < points.Len(); x++ {
if latestTime.Before(points.At(x).Timestamp().AsTime()) {
latestTime = points.At(x).Timestamp().AsTime()
}
}
case pmetric.MetricTypeSummary:
summary := metric.Summary()
points := summary.DataPoints()
for x := 0; x < points.Len(); x++ {
if latestTime.Before(points.At(x).Timestamp().AsTime()) {
latestTime = points.At(x).Timestamp().AsTime()
}
}
case pmetric.MetricTypeHistogram:
hist := metric.Histogram()
points := hist.DataPoints()
for x := 0; x < points.Len(); x++ {
if latestTime.Before(points.At(x).Timestamp().AsTime()) {
latestTime = points.At(x).Timestamp().AsTime()
}
}
case pmetric.MetricTypeExponentialHistogram:
eh := metric.ExponentialHistogram()
points := eh.DataPoints()
for x := 0; x < points.Len(); x++ {
if latestTime.Before(points.At(x).Timestamp().AsTime()) {
latestTime = points.At(x).Timestamp().AsTime()
}
}
}
}
}
id := resourceID{
serviceName: getResourceAttr(semconv.AttributeServiceName),
serviceNamespace: getResourceAttr(semconv.AttributeServiceNamespace),
serviceInstanceID: getResourceAttr(semconv.AttributeServiceInstanceID),
}
if _, ok := ids[id]; ok {
// We've already added a resource with the same ID before, so skip this one.
continue
}
ids[id] = struct{}{}
// create the target_info metric as a Gauge with value 1
targetInfoMetric := rm.ScopeMetrics().AppendEmpty().Metrics().AppendEmpty()
targetInfoMetric.SetName("target_info")
dataPoint := targetInfoMetric.SetEmptyGauge().DataPoints().AppendEmpty()
dataPoint.SetIntValue(1)
dataPoint.SetTimestamp(pcommon.NewTimestampFromTime(latestTime))
// copy Resource attributes to the metric except for service.name, service.namespace, and service.instance.id
// because those three attributes (if present) will be copied to resource attributes.
// See https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#resource-attributes-1
// Also drop reserved GMP labels (location, cluster, namespace, job, service_namespace, instance).
// Other "fallback" attributes which could become `job` or `instance` in the absence of those three
// (such as k8s.pod.name or faas.name) will be duplicated as a resource attribute and metric label.
rm.Resource().Attributes().Range(func(k string, v pcommon.Value) bool {
if k != semconv.AttributeServiceName &&
k != semconv.AttributeServiceNamespace &&
k != semconv.AttributeServiceInstanceID &&
k != locationLabel &&
k != clusterLabel &&
k != namespaceLabel &&
k != jobLabel &&
k != serviceNamespaceLabel &&
k != instanceLabel {
dataPoint.Attributes().PutStr(k, v.AsString())
}
return true
})
}
}