Skip to content

Commit 8bc8dd1

Browse files
committed
Introduce Labels
1 parent 847aecd commit 8bc8dd1

File tree

15 files changed

+545
-111
lines changed

15 files changed

+545
-111
lines changed
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
/*-
2+
* #%L
3+
* Elastic APM Java agent
4+
* %%
5+
* Copyright (C) 2018 - 2019 Elastic and contributors
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package co.elastic.apm.agent.metrics;
21+
22+
import co.elastic.apm.agent.objectpool.Recyclable;
23+
24+
import javax.annotation.Nullable;
25+
import java.util.ArrayList;
26+
import java.util.Collections;
27+
import java.util.List;
28+
import java.util.Map;
29+
import java.util.Objects;
30+
31+
public class Labels implements Recyclable {
32+
33+
private static final Labels EMPTY = Labels.of().immutableCopy();
34+
private final List<String> keys = new ArrayList<>();
35+
private final List<CharSequence> values = new ArrayList<>();
36+
private final boolean immutable;
37+
@Nullable
38+
private CharSequence transactionName;
39+
@Nullable
40+
private String transactionType;
41+
@Nullable
42+
private String spanType;
43+
private int cachedHash;
44+
45+
public Labels() {
46+
this(Collections.<String>emptyList(), Collections.<CharSequence>emptyList(), false);
47+
}
48+
49+
private Labels(List<String> keys, List<? extends CharSequence> values, boolean immutable) {
50+
this.keys.addAll(keys);
51+
this.values.addAll(values);
52+
this.immutable = immutable;
53+
}
54+
55+
public static Labels of() {
56+
return new Labels();
57+
}
58+
59+
public static Labels of(String key, CharSequence value) {
60+
final Labels labels = new Labels();
61+
labels.add(key, value);
62+
return labels;
63+
}
64+
65+
public static Labels of(Map<String, ? extends CharSequence> labelMap) {
66+
Labels labels = new Labels();
67+
for (Map.Entry<String, ? extends CharSequence> entry : labelMap.entrySet()) {
68+
labels.add(entry.getKey(), entry.getValue());
69+
}
70+
return labels;
71+
}
72+
73+
public static Labels empty() {
74+
return EMPTY;
75+
}
76+
77+
public Labels add(String key, CharSequence value) {
78+
assertMutable();
79+
keys.add(key);
80+
values.add(value);
81+
return this;
82+
}
83+
84+
public Labels transactionName(CharSequence transactionName) {
85+
assertMutable();
86+
this.transactionName = transactionName;
87+
return this;
88+
}
89+
90+
public Labels transactionType(String transactionType) {
91+
assertMutable();
92+
this.transactionType = transactionType;
93+
return this;
94+
}
95+
96+
public Labels spanType(String spanType) {
97+
assertMutable();
98+
this.spanType = spanType;
99+
return this;
100+
}
101+
102+
private void assertMutable() {
103+
if (immutable) {
104+
throw new UnsupportedOperationException("This Labels instance is immutable");
105+
}
106+
}
107+
108+
@Nullable
109+
public CharSequence getTransactionName() {
110+
return transactionName;
111+
}
112+
113+
@Nullable
114+
public String getTransactionType() {
115+
return transactionType;
116+
}
117+
118+
@Nullable
119+
public String getSpanType() {
120+
return spanType;
121+
}
122+
123+
public Labels immutableCopy() {
124+
List<String> immutableValues = new ArrayList<>(values.size());
125+
for (int i = 0; i < keys.size(); i++) {
126+
immutableValues.add(values.get(i).toString());
127+
}
128+
final Labels labels = new Labels(keys, immutableValues, true);
129+
labels.transactionName = this.transactionName != null ? this.transactionName.toString() : null;
130+
labels.transactionType = this.transactionType;
131+
labels.spanType = this.spanType;
132+
labels.cachedHash = labels.hashCode();
133+
return labels;
134+
}
135+
136+
public List<String> getKeys() {
137+
return keys;
138+
}
139+
140+
public List<CharSequence> getValues() {
141+
return values;
142+
}
143+
144+
public boolean isEmpty() {
145+
return keys.isEmpty();
146+
}
147+
148+
public int size() {
149+
return keys.size();
150+
}
151+
152+
public String getKey(int i) {
153+
return keys.get(i);
154+
}
155+
156+
public CharSequence getValue(int i) {
157+
return values.get(i);
158+
}
159+
160+
@Override
161+
public boolean equals(Object o) {
162+
if (this == o) return true;
163+
if (o == null || getClass() != o.getClass()) return false;
164+
Labels labels = (Labels) o;
165+
return keys.equals(labels.keys) &&
166+
isEqual(values, labels.values) &&
167+
contentEquals(transactionName, labels.transactionName) &&
168+
Objects.equals(transactionType, labels.transactionType) &&
169+
Objects.equals(spanType, labels.spanType);
170+
}
171+
172+
@Override
173+
public int hashCode() {
174+
if (cachedHash != 0) {
175+
return cachedHash;
176+
}
177+
int h = 0;
178+
for (int i = 0; i < values.size(); i++) {
179+
h = 31 * h + hash(i);
180+
}
181+
h = 31 * h + hash(transactionName);
182+
h = 31 * h + Objects.hashCode(transactionType);
183+
h = 31 * h + Objects.hashCode(spanType);
184+
return h;
185+
}
186+
187+
@Override
188+
public String toString() {
189+
StringBuilder sb = new StringBuilder();
190+
for (int i = 0; i < keys.size(); i++) {
191+
if (i > 0) {
192+
sb.append(", ");
193+
}
194+
sb.append(keys.get(i)).append("=").append(values.get(i));
195+
196+
}
197+
return sb.toString();
198+
}
199+
200+
private int hash(int i) {
201+
return keys.get(i).hashCode() * 31 + hash(values.get(i));
202+
}
203+
204+
private static boolean isEqual(List<CharSequence> values, List<CharSequence> otherValues) {
205+
if (values.size() != otherValues.size()) {
206+
return false;
207+
}
208+
for (int i = 0; i < values.size(); i++) {
209+
if (!contentEquals(values.get(i), otherValues.get(i))) {
210+
return false;
211+
}
212+
}
213+
return true;
214+
}
215+
216+
private static boolean contentEquals(@Nullable CharSequence cs1, @Nullable CharSequence cs2) {
217+
if (cs1 == null || cs2 == null) {
218+
return cs1 == cs2;
219+
}
220+
if (cs1 instanceof String) {
221+
return ((String) cs1).contentEquals(cs2);
222+
} else if (cs2 instanceof String) {
223+
return ((String) cs2).contentEquals(cs1);
224+
} else {
225+
if (cs1.length() == cs2.length()) {
226+
for (int i = 0; i < cs1.length(); i++) {
227+
if (cs1.charAt(i) != cs2.charAt(i)) {
228+
return false;
229+
}
230+
}
231+
return true;
232+
}
233+
}
234+
return false;
235+
}
236+
237+
static int hash(@Nullable CharSequence cs) {
238+
if (cs == null) {
239+
return 0;
240+
}
241+
// this is safe as the hash code calculation is well defined
242+
// (see javadoc for String.hashCode())
243+
if (cs instanceof String) return cs.hashCode();
244+
int h = 0;
245+
for (int i = 0; i < cs.length(); i++) {
246+
h = 31 * h + cs.charAt(i);
247+
}
248+
return h;
249+
}
250+
251+
@Override
252+
public void resetState() {
253+
keys.clear();
254+
values.clear();
255+
transactionName = null;
256+
transactionType = null;
257+
spanType = null;
258+
}
259+
}

apm-agent-core/src/main/java/co/elastic/apm/agent/metrics/MetricRegistry.java

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,92 +36,98 @@
3636
public class MetricRegistry {
3737

3838
/**
39-
* Groups {@link MetricSet}s by their unique tags.
39+
* Groups {@link MetricSet}s by their unique labels.
4040
*/
41-
private final ConcurrentMap<Map<String, String>, MetricSet> metricSets = new ConcurrentHashMap<>();
41+
private final ConcurrentMap<Labels, MetricSet> metricSets = new ConcurrentHashMap<>();
4242
private final ReporterConfiguration config;
4343

4444
public MetricRegistry(ReporterConfiguration config) {
4545
this.config = config;
4646
}
4747

4848
/**
49-
* Same as {@link #add(String, Map, DoubleSupplier)} but only adds the metric
49+
* Same as {@link #add(String, Labels, DoubleSupplier)} but only adds the metric
5050
* if the {@link DoubleSupplier} does not return {@link Double#NaN}
5151
*
5252
* @param name the name of the metric
53-
* @param tags tags for the metric.
53+
* @param labels labels for the metric.
5454
* Tags can be used to create different graphs based for each value of a specific tag name, using a terms aggregation.
55-
* Note that there will be a {@link MetricSet} created for each distinct set of tags.
55+
* Note that there will be a {@link MetricSet} created for each distinct set of labels.
5656
* @param metric this supplier will be called for every reporting cycle
5757
* ({@link co.elastic.apm.agent.report.ReporterConfiguration#metricsInterval metrics_interval)})
58-
* @see #add(String, Map, DoubleSupplier)
58+
* @see #add(String, Labels, DoubleSupplier)
5959
*/
60-
public void addUnlessNan(String name, Map<String, String> tags, DoubleSupplier metric) {
60+
public void addUnlessNan(String name, Labels labels, DoubleSupplier metric) {
6161
if (isDisabled(name)) {
6262
return;
6363
}
6464
if (!Double.isNaN(metric.get())) {
65-
add(name, tags, metric);
65+
add(name, labels, metric);
6666
}
6767
}
6868

6969
/**
70-
* Same as {@link #add(String, Map, DoubleSupplier)} but only adds the metric
70+
* Same as {@link #add(String, Labels, DoubleSupplier)} but only adds the metric
7171
* if the {@link DoubleSupplier} returns a positive number or zero.
7272
*
7373
* @param name the name of the metric
74-
* @param tags tags for the metric.
74+
* @param labels labels for the metric.
7575
* Tags can be used to create different graphs based for each value of a specific tag name, using a terms aggregation.
76-
* Note that there will be a {@link MetricSet} created for each distinct set of tags.
76+
* Note that there will be a {@link MetricSet} created for each distinct set of labels.
7777
* @param metric this supplier will be called for every reporting cycle
7878
* ({@link co.elastic.apm.agent.report.ReporterConfiguration#metricsInterval metrics_interval)})
79-
* @see #add(String, Map, DoubleSupplier)
79+
* @see #add(String, Labels, DoubleSupplier)
8080
*/
81-
public void addUnlessNegative(String name, Map<String, String> tags, DoubleSupplier metric) {
81+
public void addUnlessNegative(String name, Labels labels, DoubleSupplier metric) {
8282
if (isDisabled(name)) {
8383
return;
8484
}
8585
if (metric.get() >= 0) {
86-
add(name, tags, metric);
86+
add(name, labels, metric);
8787
}
8888
}
8989

9090
/**
9191
* Adds a gauge to the metric registry.
9292
*
9393
* @param name the name of the metric
94-
* @param tags tags for the metric.
94+
* @param labels labels for the metric.
9595
* Tags can be used to create different graphs based for each value of a specific tag name, using a terms aggregation.
96-
* Note that there will be a {@link MetricSet} created for each distinct set of tags.
96+
* Note that there will be a {@link MetricSet} created for each distinct set of labels.
9797
* @param metric this supplier will be called for every reporting cycle
9898
* ({@link co.elastic.apm.agent.report.ReporterConfiguration#metricsInterval metrics_interval)})
9999
*/
100-
public void add(String name, Map<String, String> tags, DoubleSupplier metric) {
100+
public void add(String name, Labels labels, DoubleSupplier metric) {
101101
if (isDisabled(name)) {
102102
return;
103103
}
104-
MetricSet metricSet = metricSets.get(tags);
105-
if (metricSet == null) {
106-
metricSets.putIfAbsent(tags, new MetricSet(tags));
107-
metricSet = metricSets.get(tags);
108-
}
104+
MetricSet metricSet = getOrCreateMetricSet(labels);
109105
metricSet.add(name, metric);
110106
}
111107

112108
private boolean isDisabled(String name) {
113109
return WildcardMatcher.anyMatch(config.getDisableMetrics(), name) != null;
114110
}
115111

116-
public double get(String name, Map<String, String> tags) {
117-
final MetricSet metricSet = metricSets.get(tags);
112+
public double get(String name, Labels labels) {
113+
final MetricSet metricSet = metricSets.get(labels);
118114
if (metricSet != null) {
119115
return metricSet.get(name).get();
120116
}
121117
return Double.NaN;
122118
}
123119

124-
public Map<Map<String, String>, MetricSet> getMetricSets() {
120+
public Map<Labels, MetricSet> getMetricSets() {
125121
return metricSets;
126122
}
123+
124+
private MetricSet getOrCreateMetricSet(Labels labels) {
125+
MetricSet metricSet = metricSets.get(labels);
126+
if (metricSet == null) {
127+
final Labels copy = labels.immutableCopy();
128+
metricSets.putIfAbsent(copy, new MetricSet(copy));
129+
metricSet = metricSets.get(copy);
130+
}
131+
return metricSet;
132+
}
127133
}

0 commit comments

Comments
 (0)