Skip to content
This repository was archived by the owner on Dec 23, 2023. It is now read-only.

Commit 1a82946

Browse files
authored
Metrics: Implementation of Cumulative APIs. (#1856)
1 parent 68a8d7d commit 1a82946

File tree

9 files changed

+2133
-4
lines changed

9 files changed

+2133
-4
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Copyright 2019, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opencensus.implcore.metrics;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.base.Preconditions.checkNotNull;
21+
22+
import io.opencensus.common.Clock;
23+
import io.opencensus.common.Timestamp;
24+
import io.opencensus.common.ToDoubleFunction;
25+
import io.opencensus.implcore.internal.Utils;
26+
import io.opencensus.metrics.DerivedDoubleCumulative;
27+
import io.opencensus.metrics.LabelKey;
28+
import io.opencensus.metrics.LabelValue;
29+
import io.opencensus.metrics.export.Metric;
30+
import io.opencensus.metrics.export.MetricDescriptor;
31+
import io.opencensus.metrics.export.MetricDescriptor.Type;
32+
import io.opencensus.metrics.export.Point;
33+
import io.opencensus.metrics.export.TimeSeries;
34+
import io.opencensus.metrics.export.Value;
35+
import java.lang.ref.WeakReference;
36+
import java.util.ArrayList;
37+
import java.util.Collections;
38+
import java.util.LinkedHashMap;
39+
import java.util.List;
40+
import java.util.Map;
41+
import java.util.Map.Entry;
42+
import javax.annotation.concurrent.GuardedBy;
43+
44+
/*>>>
45+
import org.checkerframework.checker.nullness.qual.Nullable;
46+
*/
47+
48+
/** Implementation of {@link DerivedDoubleCumulative}. */
49+
public final class DerivedDoubleCumulativeImpl extends DerivedDoubleCumulative implements Meter {
50+
private final MetricDescriptor metricDescriptor;
51+
private final int labelKeysSize;
52+
private final List<LabelValue> constantLabelValues;
53+
private final Timestamp startTime;
54+
55+
private volatile Map<List<LabelValue>, PointWithFunction<?>> registeredPoints =
56+
Collections.<List<LabelValue>, PointWithFunction<?>>emptyMap();
57+
58+
DerivedDoubleCumulativeImpl(
59+
String name,
60+
String description,
61+
String unit,
62+
List<LabelKey> labelKeys,
63+
Map<LabelKey, LabelValue> constantLabels,
64+
Timestamp startTime) {
65+
List<LabelValue> constantLabelValues = new ArrayList<LabelValue>();
66+
List<LabelKey> allKeys = new ArrayList<>(labelKeys);
67+
for (Entry<LabelKey, LabelValue> label : constantLabels.entrySet()) {
68+
// Ensure constant label keys and values are in the same order.
69+
allKeys.add(label.getKey());
70+
constantLabelValues.add(label.getValue());
71+
}
72+
labelKeysSize = allKeys.size();
73+
this.metricDescriptor =
74+
MetricDescriptor.create(name, description, unit, Type.CUMULATIVE_DOUBLE, allKeys);
75+
this.constantLabelValues = Collections.unmodifiableList(constantLabelValues);
76+
this.startTime = startTime;
77+
}
78+
79+
@Override
80+
public synchronized <T> void createTimeSeries(
81+
List<LabelValue> labelValues,
82+
@javax.annotation.Nullable T obj,
83+
ToDoubleFunction</*@Nullable*/ T> function) {
84+
Utils.checkListElementNotNull(checkNotNull(labelValues, "labelValues"), "labelValue");
85+
List<LabelValue> labelValuesCopy = new ArrayList<LabelValue>(labelValues);
86+
labelValuesCopy.addAll(constantLabelValues);
87+
88+
checkArgument(
89+
labelKeysSize == labelValuesCopy.size(),
90+
"Label Keys and Label Values don't have same size.");
91+
checkNotNull(function, "function");
92+
93+
PointWithFunction<?> existingPoint =
94+
registeredPoints.get(Collections.unmodifiableList(labelValuesCopy));
95+
if (existingPoint != null) {
96+
throw new IllegalArgumentException(
97+
"A different time series with the same labels already exists.");
98+
}
99+
100+
PointWithFunction<T> newPoint =
101+
new PointWithFunction<T>(labelValuesCopy, obj, function, startTime);
102+
// Updating the map of time series happens under a lock to avoid multiple add operations
103+
// to happen in the same time.
104+
Map<List<LabelValue>, PointWithFunction<?>> registeredPointsCopy =
105+
new LinkedHashMap<List<LabelValue>, PointWithFunction<?>>(registeredPoints);
106+
registeredPointsCopy.put(labelValuesCopy, newPoint);
107+
registeredPoints = Collections.unmodifiableMap(registeredPointsCopy);
108+
}
109+
110+
@Override
111+
public synchronized void removeTimeSeries(List<LabelValue> labelValues) {
112+
List<LabelValue> labelValuesCopy =
113+
new ArrayList<LabelValue>(checkNotNull(labelValues, "labelValues"));
114+
labelValuesCopy.addAll(constantLabelValues);
115+
116+
Map<List<LabelValue>, PointWithFunction<?>> registeredPointsCopy =
117+
new LinkedHashMap<List<LabelValue>, PointWithFunction<?>>(registeredPoints);
118+
if (registeredPointsCopy.remove(labelValuesCopy) == null) {
119+
// The element not present, no need to update the current map of time series.
120+
return;
121+
}
122+
registeredPoints = Collections.unmodifiableMap(registeredPointsCopy);
123+
}
124+
125+
@Override
126+
public synchronized void clear() {
127+
registeredPoints = Collections.<List<LabelValue>, PointWithFunction<?>>emptyMap();
128+
}
129+
130+
@javax.annotation.Nullable
131+
@Override
132+
public Metric getMetric(Clock clock) {
133+
Map<List<LabelValue>, PointWithFunction<?>> currentRegisteredPoints = registeredPoints;
134+
if (currentRegisteredPoints.isEmpty()) {
135+
return null;
136+
}
137+
138+
if (currentRegisteredPoints.size() == 1) {
139+
PointWithFunction<?> point = currentRegisteredPoints.values().iterator().next();
140+
return Metric.createWithOneTimeSeries(metricDescriptor, point.getTimeSeries(clock));
141+
}
142+
143+
List<TimeSeries> timeSeriesList = new ArrayList<TimeSeries>(currentRegisteredPoints.size());
144+
for (Map.Entry<List<LabelValue>, PointWithFunction<?>> entry :
145+
currentRegisteredPoints.entrySet()) {
146+
timeSeriesList.add(entry.getValue().getTimeSeries(clock));
147+
}
148+
return Metric.create(metricDescriptor, timeSeriesList);
149+
}
150+
151+
/** Implementation of {@link PointWithFunction} with an object and a callback function. */
152+
public static final class PointWithFunction<T> {
153+
private final List<LabelValue> labelValues;
154+
private final Timestamp startTime;
155+
@javax.annotation.Nullable private final WeakReference<T> ref;
156+
private final ToDoubleFunction</*@Nullable*/ T> function;
157+
158+
@GuardedBy("this")
159+
private double value = 0.0;
160+
161+
PointWithFunction(
162+
List<LabelValue> labelValues,
163+
@javax.annotation.Nullable T obj,
164+
ToDoubleFunction</*@Nullable*/ T> function,
165+
Timestamp startTime) {
166+
this.labelValues = labelValues;
167+
this.startTime = startTime;
168+
ref = obj != null ? new WeakReference<T>(obj) : null;
169+
this.function = function;
170+
}
171+
172+
private TimeSeries getTimeSeries(Clock clock) {
173+
final T obj = ref != null ? ref.get() : null;
174+
double newValue = function.applyAsDouble(obj);
175+
Point point;
176+
synchronized (this) {
177+
value = newValue > value ? newValue : value;
178+
point = Point.create(Value.doubleValue(value), clock.now());
179+
}
180+
return TimeSeries.createWithOnePoint(labelValues, point, startTime);
181+
}
182+
}
183+
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
/*
2+
* Copyright 2019, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.opencensus.implcore.metrics;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.base.Preconditions.checkNotNull;
21+
22+
import io.opencensus.common.Clock;
23+
import io.opencensus.common.Timestamp;
24+
import io.opencensus.common.ToLongFunction;
25+
import io.opencensus.implcore.internal.Utils;
26+
import io.opencensus.metrics.DerivedLongCumulative;
27+
import io.opencensus.metrics.LabelKey;
28+
import io.opencensus.metrics.LabelValue;
29+
import io.opencensus.metrics.export.Metric;
30+
import io.opencensus.metrics.export.MetricDescriptor;
31+
import io.opencensus.metrics.export.MetricDescriptor.Type;
32+
import io.opencensus.metrics.export.Point;
33+
import io.opencensus.metrics.export.TimeSeries;
34+
import io.opencensus.metrics.export.Value;
35+
import java.lang.ref.WeakReference;
36+
import java.util.ArrayList;
37+
import java.util.Collections;
38+
import java.util.LinkedHashMap;
39+
import java.util.List;
40+
import java.util.Map;
41+
import java.util.Map.Entry;
42+
import javax.annotation.concurrent.GuardedBy;
43+
44+
/*>>>
45+
import org.checkerframework.checker.nullness.qual.Nullable;
46+
*/
47+
48+
/** Implementation of {@link DerivedLongCumulative}. */
49+
public final class DerivedLongCumulativeImpl extends DerivedLongCumulative implements Meter {
50+
private final MetricDescriptor metricDescriptor;
51+
private final int labelKeysSize;
52+
private final List<LabelValue> constantLabelValues;
53+
private final Timestamp startTime;
54+
55+
private volatile Map<List<LabelValue>, PointWithFunction<?>> registeredPoints =
56+
Collections.<List<LabelValue>, PointWithFunction<?>>emptyMap();
57+
58+
DerivedLongCumulativeImpl(
59+
String name,
60+
String description,
61+
String unit,
62+
List<LabelKey> labelKeys,
63+
Map<LabelKey, LabelValue> constantLabels,
64+
Timestamp startTime) {
65+
List<LabelValue> constantLabelValues = new ArrayList<LabelValue>();
66+
List<LabelKey> allKeys = new ArrayList<>(labelKeys);
67+
for (Entry<LabelKey, LabelValue> label : constantLabels.entrySet()) {
68+
// Ensure constant label keys and values are in the same order.
69+
allKeys.add(label.getKey());
70+
constantLabelValues.add(label.getValue());
71+
}
72+
labelKeysSize = allKeys.size();
73+
this.metricDescriptor =
74+
MetricDescriptor.create(name, description, unit, Type.CUMULATIVE_INT64, allKeys);
75+
this.constantLabelValues = Collections.unmodifiableList(constantLabelValues);
76+
this.startTime = startTime;
77+
}
78+
79+
@Override
80+
public synchronized <T> void createTimeSeries(
81+
List<LabelValue> labelValues,
82+
@javax.annotation.Nullable T obj,
83+
ToLongFunction</*@Nullable*/ T> function) {
84+
checkNotNull(function, "function");
85+
Utils.checkListElementNotNull(checkNotNull(labelValues, "labelValues"), "labelValue");
86+
List<LabelValue> labelValuesCopy = new ArrayList<LabelValue>(labelValues);
87+
labelValuesCopy.addAll(constantLabelValues);
88+
89+
checkArgument(
90+
labelKeysSize == labelValuesCopy.size(),
91+
"Label Keys and Label Values don't have same size.");
92+
93+
PointWithFunction<?> existingPoint =
94+
registeredPoints.get(Collections.unmodifiableList(labelValuesCopy));
95+
if (existingPoint != null) {
96+
throw new IllegalArgumentException(
97+
"A different time series with the same labels already exists.");
98+
}
99+
100+
PointWithFunction<T> newPoint =
101+
new PointWithFunction<T>(labelValuesCopy, obj, function, startTime);
102+
// Updating the map of time series happens under a lock to avoid multiple add operations
103+
// to happen in the same time.
104+
Map<List<LabelValue>, PointWithFunction<?>> registeredPointsCopy =
105+
new LinkedHashMap<List<LabelValue>, PointWithFunction<?>>(registeredPoints);
106+
registeredPointsCopy.put(labelValuesCopy, newPoint);
107+
registeredPoints = Collections.unmodifiableMap(registeredPointsCopy);
108+
}
109+
110+
@Override
111+
public synchronized void removeTimeSeries(List<LabelValue> labelValues) {
112+
List<LabelValue> labelValuesCopy =
113+
new ArrayList<LabelValue>(checkNotNull(labelValues, "labelValues"));
114+
labelValuesCopy.addAll(constantLabelValues);
115+
116+
Map<List<LabelValue>, PointWithFunction<?>> registeredPointsCopy =
117+
new LinkedHashMap<List<LabelValue>, PointWithFunction<?>>(registeredPoints);
118+
if (registeredPointsCopy.remove(labelValuesCopy) == null) {
119+
// The element not present, no need to update the current map of time series.
120+
return;
121+
}
122+
registeredPoints = Collections.unmodifiableMap(registeredPointsCopy);
123+
}
124+
125+
@Override
126+
public synchronized void clear() {
127+
registeredPoints = Collections.<List<LabelValue>, PointWithFunction<?>>emptyMap();
128+
}
129+
130+
@javax.annotation.Nullable
131+
@Override
132+
public Metric getMetric(Clock clock) {
133+
Map<List<LabelValue>, PointWithFunction<?>> currentRegisteredPoints = registeredPoints;
134+
if (currentRegisteredPoints.isEmpty()) {
135+
return null;
136+
}
137+
138+
if (currentRegisteredPoints.size() == 1) {
139+
PointWithFunction<?> point = currentRegisteredPoints.values().iterator().next();
140+
return Metric.createWithOneTimeSeries(metricDescriptor, point.getTimeSeries(clock));
141+
}
142+
143+
List<TimeSeries> timeSeriesList = new ArrayList<TimeSeries>(currentRegisteredPoints.size());
144+
for (Map.Entry<List<LabelValue>, PointWithFunction<?>> entry :
145+
currentRegisteredPoints.entrySet()) {
146+
timeSeriesList.add(entry.getValue().getTimeSeries(clock));
147+
}
148+
return Metric.create(metricDescriptor, timeSeriesList);
149+
}
150+
151+
/** Implementation of {@link PointWithFunction} with an object and a callback function. */
152+
public static final class PointWithFunction<T> {
153+
private final List<LabelValue> labelValues;
154+
private final Timestamp startTime;
155+
@javax.annotation.Nullable private final WeakReference<T> ref;
156+
private final ToLongFunction</*@Nullable*/ T> function;
157+
158+
@GuardedBy("this")
159+
private long value = 0;
160+
161+
PointWithFunction(
162+
List<LabelValue> labelValues,
163+
@javax.annotation.Nullable T obj,
164+
ToLongFunction</*@Nullable*/ T> function,
165+
Timestamp startTime) {
166+
this.labelValues = labelValues;
167+
this.startTime = startTime;
168+
ref = obj != null ? new WeakReference<T>(obj) : null;
169+
this.function = function;
170+
}
171+
172+
private TimeSeries getTimeSeries(Clock clock) {
173+
final T obj = ref != null ? ref.get() : null;
174+
long newValue = function.applyAsLong(obj);
175+
Point point;
176+
synchronized (this) {
177+
value = newValue > value ? newValue : value;
178+
point = Point.create(Value.longValue(value), clock.now());
179+
}
180+
return TimeSeries.createWithOnePoint(labelValues, point, startTime);
181+
}
182+
}
183+
}

0 commit comments

Comments
 (0)