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

Commit 68d3e2b

Browse files
authored
Metrics/Util: Add QueueMetricProducer. (#1804)
* Metrics/Util: Add QueueMetricProducer. Fixes #1802. * Use EvictingQueue instead of LinkedList. * Remove spans and fix styling issues.
1 parent d5ba9ce commit 68d3e2b

File tree

3 files changed

+255
-0
lines changed

3 files changed

+255
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- Refactor `Exemplar` and `AttachmentValue` to be under `metrics.data`. Note that this is a breaking change
1111
if you're using the `Exemplar` classes or APIs in the previous releases.
1212
- Add `TagMetadata` that defines the properties associated with a `Tag`.
13+
- Add `QueueMetricProducer` that supports pushing and buffering `Metric`s.
1314

1415
# 0.19.0 - 2019-01-28
1516
- Add an artifact `opencensus-contrib-http-jetty-client` for instrumenting jetty http client. Add extractor for Jetty Client.
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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.exporter.metrics.util;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.base.Preconditions.checkNotNull;
21+
22+
import com.google.auto.value.AutoValue;
23+
import com.google.common.collect.EvictingQueue;
24+
import io.opencensus.metrics.export.Metric;
25+
import io.opencensus.metrics.export.MetricProducer;
26+
import java.util.ArrayList;
27+
import java.util.Collection;
28+
import java.util.Collections;
29+
import java.util.List;
30+
import java.util.Queue;
31+
import javax.annotation.concurrent.GuardedBy;
32+
import javax.annotation.concurrent.Immutable;
33+
34+
/**
35+
* Wrapper of {@link MetricProducer} which allows metrics to be pushed and buffered.
36+
*
37+
* @since 0.20
38+
*/
39+
public final class QueueMetricProducer extends MetricProducer {
40+
41+
private static final Object monitor = new Object();
42+
43+
private static final int DEFAULT_BUFFER_SIZE = 32;
44+
45+
@GuardedBy("monitor")
46+
private final Queue<Metric> bufferedMetrics;
47+
48+
private QueueMetricProducer(int bufferSize) {
49+
synchronized (monitor) {
50+
bufferedMetrics = EvictingQueue.<Metric>create(bufferSize);
51+
}
52+
}
53+
54+
/**
55+
* Creates a new {@link QueueMetricProducer}.
56+
*
57+
* @param options the options for {@link QueueMetricProducer}.
58+
* @return a {@code QueueMetricProducer}.
59+
* @since 0.20
60+
*/
61+
public static QueueMetricProducer create(Options options) {
62+
checkNotNull(options, "options");
63+
checkArgument(options.getBufferSize() > 0, "buffer size should be positive.");
64+
return new QueueMetricProducer(options.getBufferSize());
65+
}
66+
67+
/**
68+
* Pushes {@link Metric}s to this {@link QueueMetricProducer}.
69+
*
70+
* <p>When buffer of this {@link QueueMetricProducer} is full, the oldest {@link Metric}s will be
71+
* dropped.
72+
*
73+
* @param metrics {@code Metrics} to be added to this {@code QueueMetricProducer}.
74+
* @since 0.20
75+
*/
76+
public void pushMetrics(Collection<Metric> metrics) {
77+
synchronized (monitor) {
78+
bufferedMetrics.addAll(metrics);
79+
}
80+
}
81+
82+
@Override
83+
public Collection<Metric> getMetrics() {
84+
List<Metric> metricsToExport;
85+
synchronized (monitor) {
86+
metricsToExport = new ArrayList<>(bufferedMetrics);
87+
bufferedMetrics.clear();
88+
}
89+
return Collections.unmodifiableList(metricsToExport);
90+
}
91+
92+
/**
93+
* Options for {@link QueueMetricProducer}.
94+
*
95+
* @since 0.20
96+
*/
97+
@AutoValue
98+
@Immutable
99+
public abstract static class Options {
100+
101+
Options() {}
102+
103+
/**
104+
* Returns the buffer size for the {@link QueueMetricProducer}.
105+
*
106+
* @return the buffer size for the {@code QueueMetricProducer}.
107+
* @since 0.20
108+
*/
109+
public abstract int getBufferSize();
110+
111+
/**
112+
* Returns a new {@link Builder}.
113+
*
114+
* @return a {@code Builder}.
115+
* @since 0.20
116+
*/
117+
public static Options.Builder builder() {
118+
return new AutoValue_QueueMetricProducer_Options.Builder().setBufferSize(DEFAULT_BUFFER_SIZE);
119+
}
120+
121+
/**
122+
* Builder for {@link Options}.
123+
*
124+
* @since 0.20
125+
*/
126+
@AutoValue.Builder
127+
public abstract static class Builder {
128+
129+
/**
130+
* Sets the buffer size to be used by the {@link QueueMetricProducer}.
131+
*
132+
* @param bufferSize the buffer size.
133+
* @return this.
134+
* @since 0.20
135+
*/
136+
public abstract Builder setBufferSize(int bufferSize);
137+
138+
/**
139+
* Builds a new {@link Options} with current settings.
140+
*
141+
* @return a {@code Options}.
142+
* @since 0.20
143+
*/
144+
public abstract Options build();
145+
}
146+
}
147+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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.exporter.metrics.util;
18+
19+
import static com.google.common.truth.Truth.assertThat;
20+
21+
import io.opencensus.common.Timestamp;
22+
import io.opencensus.exporter.metrics.util.QueueMetricProducer.Options;
23+
import io.opencensus.metrics.LabelKey;
24+
import io.opencensus.metrics.LabelValue;
25+
import io.opencensus.metrics.export.Metric;
26+
import io.opencensus.metrics.export.MetricDescriptor;
27+
import io.opencensus.metrics.export.Point;
28+
import io.opencensus.metrics.export.Value;
29+
import java.util.Collections;
30+
import java.util.List;
31+
import org.junit.Rule;
32+
import org.junit.Test;
33+
import org.junit.rules.ExpectedException;
34+
import org.junit.runner.RunWith;
35+
import org.junit.runners.JUnit4;
36+
37+
/** Unit tests for {@link QueueMetricProducer}. */
38+
@RunWith(JUnit4.class)
39+
public class QueueMetricProducerTest {
40+
41+
private static final String METRIC_NAME = "test_metric";
42+
private static final String METRIC_DESCRIPTION = "test_description";
43+
private static final String METRIC_UNIT = "us";
44+
private static final List<LabelKey> LABEL_KEY =
45+
Collections.singletonList(LabelKey.create("test_key", "test_description"));
46+
private static final List<LabelValue> LABEL_VALUE =
47+
Collections.singletonList(LabelValue.create("test_value"));
48+
private static final io.opencensus.metrics.export.MetricDescriptor METRIC_DESCRIPTOR =
49+
io.opencensus.metrics.export.MetricDescriptor.create(
50+
METRIC_NAME,
51+
METRIC_DESCRIPTION,
52+
METRIC_UNIT,
53+
MetricDescriptor.Type.CUMULATIVE_INT64,
54+
LABEL_KEY);
55+
56+
private static final Value VALUE_LONG = Value.longValue(12345678);
57+
private static final Value VALUE_LONG_2 = Value.longValue(23456789);
58+
private static final Timestamp TIMESTAMP = Timestamp.fromMillis(3000);
59+
private static final Timestamp TIMESTAMP_2 = Timestamp.fromMillis(4000);
60+
private static final Timestamp TIMESTAMP_3 = Timestamp.fromMillis(4000);
61+
private static final Point POINT = Point.create(VALUE_LONG, TIMESTAMP);
62+
private static final Point POINT_2 = Point.create(VALUE_LONG_2, TIMESTAMP);
63+
64+
private static final io.opencensus.metrics.export.TimeSeries CUMULATIVE_TIME_SERIES =
65+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(LABEL_VALUE, POINT, TIMESTAMP_2);
66+
private static final io.opencensus.metrics.export.TimeSeries CUMULATIVE_TIME_SERIES_2 =
67+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(LABEL_VALUE, POINT_2, TIMESTAMP_3);
68+
69+
private static final Metric METRIC_1 =
70+
Metric.createWithOneTimeSeries(METRIC_DESCRIPTOR, CUMULATIVE_TIME_SERIES);
71+
private static final Metric METRIC_2 =
72+
Metric.createWithOneTimeSeries(METRIC_DESCRIPTOR, CUMULATIVE_TIME_SERIES_2);
73+
74+
@Rule public final ExpectedException thrown = ExpectedException.none();
75+
76+
@Test
77+
public void createWithNegativeBufferSize() {
78+
Options options = Options.builder().setBufferSize(-1).build();
79+
thrown.expect(IllegalArgumentException.class);
80+
QueueMetricProducer.create(options);
81+
}
82+
83+
@Test
84+
public void createWithZeroBufferSize() {
85+
Options options = Options.builder().setBufferSize(0).build();
86+
thrown.expect(IllegalArgumentException.class);
87+
QueueMetricProducer.create(options);
88+
}
89+
90+
@Test
91+
public void pushMetrics() {
92+
Options options = Options.builder().setBufferSize(1).build();
93+
QueueMetricProducer producer = QueueMetricProducer.create(options);
94+
producer.pushMetrics(Collections.singleton(METRIC_1));
95+
assertThat(producer.getMetrics()).containsExactly(METRIC_1);
96+
assertThat(producer.getMetrics()).isEmpty(); // Flush after each getMetrics().
97+
}
98+
99+
@Test
100+
public void pushMetrics_ExceedBufferSize() {
101+
Options options = Options.builder().setBufferSize(1).build();
102+
QueueMetricProducer producer = QueueMetricProducer.create(options);
103+
producer.pushMetrics(Collections.singleton(METRIC_1));
104+
producer.pushMetrics(Collections.singleton(METRIC_2));
105+
assertThat(producer.getMetrics()).containsExactly(METRIC_2);
106+
}
107+
}

0 commit comments

Comments
 (0)