| 118 | } |
| 119 | |
| 120 | func DiffMetrics(got []metricdata.ScopeMetrics, prefix, provider string, wantCalls []Call) string { |
| 121 | // OTel metric data is structured. We need to iterate through it to find the |
| 122 | // relevant metric data points and their attributes. |
| 123 | var diffs []string |
| 124 | gotTags := map[string]bool{} // map of canonicalized data point attributes |
| 125 | |
| 126 | // Helper to convert attribute.Set to a canonical string key |
| 127 | attrSetToCanonicalString := func(set attribute.Set) string { |
| 128 | // Get key-value pairs, sort them, and format into a stable string. |
| 129 | attrs := make([]attribute.KeyValue, 0, set.Len()) |
| 130 | iter := set.Iter() |
| 131 | for iter.Next() { |
| 132 | attrs = append(attrs, iter.Attribute()) |
| 133 | } |
| 134 | sort.Slice(attrs, func(i, j int) bool { |
| 135 | return string(attrs[i].Key) < string(attrs[j].Key) |
| 136 | }) |
| 137 | parts := make([]string, len(attrs)) |
| 138 | for i, attr := range attrs { |
| 139 | // Format value based on type - attribute.Value doesn't have a simple String() |
| 140 | // that's guaranteed to be consistent for comparison. Using fmt.Sprint is safer. |
| 141 | parts[i] = fmt.Sprintf("%s:%s", attr.Key, fmt.Sprint(attr.Value.AsInterface())) |
| 142 | } |
| 143 | return strings.Join(parts, ",") |
| 144 | } |
| 145 | |
| 146 | // Helper function to collect relevant attributes for tag comparison. |
| 147 | processAtrributes := func(attrSets ...attribute.Set) { |
| 148 | |
| 149 | var requiredAttributes []attribute.KeyValue |
| 150 | for _, attrSet := range attrSets { |
| 151 | for _, a := range attrSet.ToSlice() { |
| 152 | if a.Key == providerKey { |
| 153 | requiredAttributes = append(requiredAttributes, a) |
| 154 | } |
| 155 | |
| 156 | if a.Key == methodKey { |
| 157 | requiredAttributes = append(requiredAttributes, a) |
| 158 | } |
| 159 | |
| 160 | if a.Key == statusKey { |
| 161 | requiredAttributes = append(requiredAttributes, a) |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | if len(requiredAttributes) > 0 { |
| 167 | gotTags[attrSetToCanonicalString(attribute.NewSet(requiredAttributes...))] = true |
| 168 | } |
| 169 | |
| 170 | } |
| 171 | |
| 172 | // Iterate through all collected metrics to find relevant data points. |
| 173 | for _, sm := range got { |
| 174 | |
| 175 | for _, m := range sm.Metrics { |
| 176 | |
| 177 | // Using a switch will allow us accommodate other types of metrics. |