Skip to content

Commit acc2b5d

Browse files
Series start time (#8180)
Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com> Co-authored-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
1 parent aa4400a commit acc2b5d

File tree

47 files changed

+751
-466
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+751
-466
lines changed

exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderTest.java

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -972,29 +972,23 @@ void createdTimestamp() throws IOException {
972972

973973
LongCounter counter = meter.counterBuilder("requests").build();
974974
testClock.advance(Duration.ofMillis(1));
975+
long bearStartNanos = testClock.now();
975976
counter.add(3, Attributes.builder().put("animal", "bear").build());
976977
testClock.advance(Duration.ofMillis(1));
978+
long mouseStartNanos = testClock.now();
977979
counter.add(2, Attributes.builder().put("animal", "mouse").build());
978980
testClock.advance(Duration.ofMillis(1));
979981

980-
// There is a curious difference between Prometheus and OpenTelemetry:
981-
// In Prometheus metrics the _created timestamp is per data point,
982-
// i.e. the _created timestamp says when this specific set of label values
983-
// was first observed.
984-
// In the OTel Java SDK the _created timestamp is the initialization time
985-
// of the SdkMeterProvider, i.e. all data points will have the same _created timestamp.
986-
// So we expect the _created timestamp to be the start time of the application,
987-
// not the timestamp when the counter or an individual data point was created.
988982
String expected =
989983
""
990984
+ "# TYPE requests counter\n"
991985
+ "requests_total{animal=\"bear\",otel_scope_name=\"test\"} 3.0\n"
992986
+ "requests_created{animal=\"bear\",otel_scope_name=\"test\"} "
993-
+ createdTimestamp
987+
+ convertTimestamp(bearStartNanos)
994988
+ "\n"
995989
+ "requests_total{animal=\"mouse\",otel_scope_name=\"test\"} 2.0\n"
996990
+ "requests_created{animal=\"mouse\",otel_scope_name=\"test\"} "
997-
+ createdTimestamp
991+
+ convertTimestamp(mouseStartNanos)
998992
+ "\n"
999993
+ "# TYPE target info\n"
1000994
+ "target_info{service_name=\"unknown_service:java\",telemetry_sdk_language=\"java\",telemetry_sdk_name=\"opentelemetry\",telemetry_sdk_version=\"1.x.x\"} 1\n"

sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramBenchmark.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import io.opentelemetry.api.common.Attributes;
99
import io.opentelemetry.context.Context;
10+
import io.opentelemetry.sdk.common.Clock;
1011
import java.util.concurrent.TimeUnit;
1112
import java.util.function.DoubleSupplier;
1213
import org.openjdk.jmh.annotations.Benchmark;
@@ -40,7 +41,7 @@ public static class ThreadState {
4041

4142
@Setup(Level.Trial)
4243
public final void setup() {
43-
aggregatorHandle = aggregation.getAggregator().createHandle();
44+
aggregatorHandle = aggregation.getAggregator().createHandle(Clock.getDefault().now());
4445
valueSupplier = valueGen.supplier();
4546
}
4647

sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramScaleBenchmark.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import io.opentelemetry.api.common.Attributes;
99
import io.opentelemetry.context.Context;
10+
import io.opentelemetry.sdk.common.Clock;
1011
import java.util.concurrent.TimeUnit;
1112
import java.util.function.DoubleSupplier;
1213
import org.openjdk.jmh.annotations.Benchmark;
@@ -46,7 +47,7 @@ public static class ThreadState {
4647

4748
@Setup(Level.Invocation)
4849
public final void setup() {
49-
aggregatorHandle = aggregation.getAggregator().createHandle();
50+
aggregatorHandle = aggregation.getAggregator().createHandle(Clock.getDefault().now());
5051
valueSupplier = valueGen.supplier();
5152
}
5253

sdk/metrics/src/jmhBasedTest/java/io/opentelemetry/sdk/metrics/internal/state/InstrumentGarbageCollectionBenchmarkTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ public class InstrumentGarbageCollectionBenchmarkTest {
3030

3131
/**
3232
* This test validates that in {@link MemoryMode#REUSABLE_DATA}, any {@link
33-
* MetricStorage#collect(Resource, InstrumentationScopeInfo, long, long)} barely allocates memory
34-
* which is then subsequently garbage collected. It is done so comparatively to {@link
33+
* MetricStorage#collect(Resource, InstrumentationScopeInfo, long)} barely allocates memory which
34+
* is then subsequently garbage collected. It is done so comparatively to {@link
3535
* MemoryMode#IMMUTABLE_DATA},
3636
*
3737
* <p>It runs the JMH test {@link InstrumentGarbageCollectionBenchmark} with GC profiler, and

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeter.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,7 @@ Collection<MetricData> collectAll(RegisteredReader registeredReader, long epochN
132132
// Only invoke callbacks if meter is enabled
133133
if (meterEnabled) {
134134
for (CallbackRegistration callbackRegistration : currentRegisteredCallbacks) {
135-
callbackRegistration.invokeCallback(
136-
registeredReader, meterProviderSharedState.getStartEpochNanos(), epochNanos);
135+
callbackRegistration.invokeCallback(registeredReader);
137136
}
138137
}
139138

@@ -145,10 +144,7 @@ Collection<MetricData> collectAll(RegisteredReader registeredReader, long epochN
145144
for (MetricStorage storage : storages) {
146145
MetricData current =
147146
storage.collect(
148-
meterProviderSharedState.getResource(),
149-
getInstrumentationScopeInfo(),
150-
meterProviderSharedState.getStartEpochNanos(),
151-
epochNanos);
147+
meterProviderSharedState.getResource(), getInstrumentationScopeInfo(), epochNanos);
152148
// Ignore if the metric data doesn't have any data points, for example when aggregation is
153149
// Aggregation#drop()
154150
if (!current.isEmpty()) {
@@ -288,6 +284,7 @@ WriteableMetricStorage registerSynchronousMetricStorage(InstrumentDescriptor ins
288284
SynchronousMetricStorage.create(
289285
reader,
290286
registeredView,
287+
meterProviderSharedState.getClock(),
291288
instrument,
292289
meterProviderSharedState.getExemplarFilter(),
293290
meterEnabled)));
@@ -317,7 +314,11 @@ SdkObservableMeasurement registerObservableMeasurement(
317314
registeredStorages.add(
318315
registry.register(
319316
AsynchronousMetricStorage.create(
320-
reader, registeredView, instrumentDescriptor, meterEnabled)));
317+
reader,
318+
registeredView,
319+
meterProviderSharedState.getClock(),
320+
instrumentDescriptor,
321+
meterEnabled)));
321322
}
322323
}
323324

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterProvider.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ public static SdkMeterProviderBuilder builder() {
7272
Resource resource,
7373
ExemplarFilterInternal exemplarFilter,
7474
ScopeConfigurator<MeterConfig> meterConfigurator) {
75-
long startEpochNanos = clock.now();
7675
this.registeredViews = registeredViews;
7776
this.registeredReaders =
7877
metricReaders.entrySet().stream()
@@ -83,8 +82,7 @@ public static SdkMeterProviderBuilder builder() {
8382
ViewRegistry.create(entry.getKey(), entry.getValue(), registeredViews)))
8483
.collect(toList());
8584
this.metricProducers = metricProducers;
86-
this.sharedState =
87-
MeterProviderSharedState.create(clock, resource, exemplarFilter, startEpochNanos);
85+
this.sharedState = MeterProviderSharedState.create(clock, resource, exemplarFilter);
8886
this.registry =
8987
new ComponentRegistry<>(
9088
instrumentationLibraryInfo ->
@@ -99,7 +97,6 @@ public static SdkMeterProviderBuilder builder() {
9997
readerMetricProducers.add(new LeasedMetricProducer(registry, sharedState, registeredReader));
10098
MetricReader reader = registeredReader.getReader();
10199
reader.register(new SdkCollectionRegistration(readerMetricProducers, sharedState));
102-
registeredReader.setLastCollectEpochNanos(startEpochNanos);
103100
if (reader instanceof PeriodicMetricReader) {
104101
setReaderMeterProvider((PeriodicMetricReader) reader, this);
105102
}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/Aggregator.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@ static Aggregator<?> drop() {
3030
}
3131

3232
/**
33-
* Returns a new {@link AggregatorHandle}. This MUST by used by the synchronous to aggregate
34-
* recorded measurements during the collection cycle.
33+
* Returns a new {@link AggregatorHandle}. Used by both synchronous and asynchronous metric
34+
* storage to aggregate recorded measurements during the collection cycle.
3535
*
36+
* @param creationEpochNanos the epoch timestamp (nanos) at which the handle is being created,
37+
* stored via {@link AggregatorHandle#getCreationEpochNanos()}. Whether this value is used as
38+
* the start timestamp of reported data points depends on the instrument and temporality — see
39+
* {@link AggregatorHandle#getCreationEpochNanos()} for details.
3640
* @return a new {@link AggregatorHandle}.
3741
*/
38-
AggregatorHandle<T> createHandle();
42+
AggregatorHandle<T> createHandle(long creationEpochNanos);
3943

4044
/**
4145
* Returns a new DELTA point by computing the difference between two cumulative points.

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/AggregatorHandle.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,16 @@ public abstract class AggregatorHandle<T extends PointData> {
3535
private static final String UNSUPPORTED_DOUBLE_MESSAGE =
3636
"This aggregator does not support double values.";
3737

38+
private final long creationEpochNanos;
3839
// A reservoir of sampled exemplars for this time period.
3940
@Nullable private final DoubleExemplarReservoir doubleReservoirFactory;
4041
@Nullable private final LongExemplarReservoir longReservoirFactory;
4142
private final boolean isDoubleType;
4243
private volatile boolean valuesRecorded = false;
4344

44-
protected AggregatorHandle(ExemplarReservoirFactory reservoirFactory, boolean isDoubleType) {
45+
protected AggregatorHandle(
46+
long creationEpochNanos, ExemplarReservoirFactory reservoirFactory, boolean isDoubleType) {
47+
this.creationEpochNanos = creationEpochNanos;
4548
this.isDoubleType = isDoubleType;
4649
if (isDoubleType) {
4750
this.doubleReservoirFactory = reservoirFactory.createDoubleExemplarReservoir();
@@ -145,4 +148,22 @@ private static <S> S throwUnsupportedIfNull(@Nullable S value, String message) {
145148
}
146149
return value;
147150
}
151+
152+
/**
153+
* Returns the epoch timestamp (nanos) at which this handle was created.
154+
*
155+
* <p>For cumulative synchronous instruments, this is the time of the first measurement for the
156+
* series and is used as {@link PointData#getStartEpochNanos()}.
157+
*
158+
* <p>For cumulative asynchronous instruments, this is either the instrument creation time (if the
159+
* series first appeared during the first collection cycle) or the preceding collection interval's
160+
* timestamp (if the series appeared in a later cycle), and is used as {@link
161+
* PointData#getStartEpochNanos()}.
162+
*
163+
* <p>Not used for delta instruments; their start epoch is computed directly from the reader's
164+
* last collection time or instrument creation time.
165+
*/
166+
public long getCreationEpochNanos() {
167+
return creationEpochNanos;
168+
}
148169
}

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregator.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ public DoubleBase2ExponentialHistogramAggregator(
6666
}
6767

6868
@Override
69-
public AggregatorHandle<ExponentialHistogramPointData> createHandle() {
70-
return new Handle(reservoirFactory, maxBuckets, maxScale, recordMinMax, memoryMode);
69+
public AggregatorHandle<ExponentialHistogramPointData> createHandle(long creationEpochNanos) {
70+
return new Handle(
71+
creationEpochNanos, reservoirFactory, maxBuckets, maxScale, recordMinMax, memoryMode);
7172
}
7273

7374
@Override
@@ -104,12 +105,13 @@ static final class Handle extends AggregatorHandle<ExponentialHistogramPointData
104105
@Nullable private final MutableExponentialHistogramPointData reusablePoint;
105106

106107
Handle(
108+
long creationEpochNanos,
107109
ExemplarReservoirFactory reservoirFactory,
108110
int maxBuckets,
109111
int maxScale,
110112
boolean recordMinMax,
111113
MemoryMode memoryMode) {
112-
super(reservoirFactory, /* isDoubleType= */ true);
114+
super(creationEpochNanos, reservoirFactory, /* isDoubleType= */ true);
113115
this.maxBuckets = maxBuckets;
114116
this.maxScale = maxScale;
115117
this.recordMinMax = recordMinMax;

sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregator.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,9 @@ public DoubleExplicitBucketHistogramAggregator(
7272
}
7373

7474
@Override
75-
public AggregatorHandle<HistogramPointData> createHandle() {
76-
return new Handle(boundaryList, boundaries, recordMinMax, reservoirFactory, memoryMode);
75+
public AggregatorHandle<HistogramPointData> createHandle(long creationEpochNanos) {
76+
return new Handle(
77+
creationEpochNanos, boundaryList, boundaries, recordMinMax, reservoirFactory, memoryMode);
7778
}
7879

7980
@Override
@@ -120,12 +121,13 @@ static final class Handle extends AggregatorHandle<HistogramPointData> {
120121
@Nullable private final MutableHistogramPointData reusablePoint;
121122

122123
Handle(
124+
long creationEpochNanos,
123125
List<Double> boundaryList,
124126
double[] boundaries,
125127
boolean recordMinMax,
126128
ExemplarReservoirFactory reservoirFactory,
127129
MemoryMode memoryMode) {
128-
super(reservoirFactory, /* isDoubleType= */ true);
130+
super(creationEpochNanos, reservoirFactory, /* isDoubleType= */ true);
129131
this.boundaryList = boundaryList;
130132
this.boundaries = boundaries;
131133
this.recordMinMax = recordMinMax;

0 commit comments

Comments
 (0)