Skip to content

refactor!: convert all SDK timestamps from HrTime to bigint #5522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
2c6b922
refactor!: convert all SDK timestamps from HrTime to bigint
dyladan Mar 4, 2025
7ef4dbd
Changelog
dyladan Mar 4, 2025
c4cb812
Lint
dyladan Mar 4, 2025
addb504
Fix conversion
dyladan Mar 5, 2025
19e1224
Fix zipkin tests
dyladan Mar 5, 2025
641f56f
Lint
dyladan Mar 5, 2025
989479a
Update packages/opentelemetry-core/src/common/time.ts
dyladan Mar 5, 2025
fb2c16c
Merge branch 'main' into hrtime-to-bigint
dyladan Mar 5, 2025
460d81d
Add logs to test
dyladan Mar 5, 2025
b5a32cd
Log start time too
dyladan Mar 5, 2025
d2c1506
Remove deep paths
dyladan Mar 6, 2025
9ed865f
log inconsistent times
dyladan Mar 6, 2025
6af9780
Gratuitous logging
dyladan Mar 6, 2025
7635a74
Fix time conversion
dyladan Mar 6, 2025
c2ca60e
Remove logging stuff
dyladan Mar 6, 2025
1d040bf
Use bigint in test case
dyladan Mar 6, 2025
ed25018
Fix browser tests
dyladan Mar 6, 2025
042f294
Add backwards compatible getters to metrics timestamps
dyladan Mar 6, 2025
0053b42
Add backwards compatible getters to logs timestamps
dyladan Mar 6, 2025
335367f
Revert messed up browser tests
dyladan Mar 6, 2025
ca0a17e
Lint
dyladan Mar 6, 2025
618dd6b
Merge branch 'main' into hrtime-to-bigint
dyladan Mar 6, 2025
eb5dadb
Remove param comment
dyladan Mar 7, 2025
8934944
Merge branch 'main' into hrtime-to-bigint
dyladan Mar 11, 2025
283c521
Undo API change
dyladan Mar 11, 2025
319ec97
Naming nit
dyladan Mar 11, 2025
7090018
Fix compile
dyladan Mar 11, 2025
a9b53e4
Remove obsolete test
dyladan Mar 11, 2025
3f01070
Lint
dyladan Mar 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2
* (user-facing): `DEFAULT_ATTRIBUTE_VALUE_COUNT_LIMIT` has been removed, please use `128` instead
* (user-facing): `DEFAULT_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT` has been removed, please use `128` instead
* (user-facing): `DEFAULT_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT` has been removed, please use `128` instead
* refactor!: convert all SDK timestamps from HrTime to bigint [#5522](https://github.com/open-telemetry/opentelemetry-js/pull/5522) @dyladan
* Times are now internally represented as `bigint` literals. This simplifies time math and makes the export pipeline more efficient.

### :rocket: (Enhancement)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ import {
DataPoint,
Histogram,
} from '@opentelemetry/sdk-metrics';
import { hrTimeToMilliseconds } from '@opentelemetry/core';
import { Resource } from '@opentelemetry/resources';
import { nanosecondsToMilliseconds } from '@opentelemetry/core';

type PrometheusDataTypeLiteral =
| 'counter'
Expand Down Expand Up @@ -282,7 +282,7 @@ export class PrometheusSerializer {

name = enforcePrometheusNamingConvention(name, data);
const { value, attributes } = dataPoint;
const timestamp = hrTimeToMilliseconds(dataPoint.endTime);
const timestamp = nanosecondsToMilliseconds(dataPoint.endTimeUnixNano);
results += stringify(
name,
attributes,
Expand All @@ -303,7 +303,7 @@ export class PrometheusSerializer {
name = enforcePrometheusNamingConvention(name, data);
const attributes = dataPoint.attributes;
const histogram = dataPoint.value;
const timestamp = hrTimeToMilliseconds(dataPoint.endTime);
const timestamp = nanosecondsToMilliseconds(dataPoint.endTimeUnixNano);
/** Histogram["bucket"] is not typed with `number` */
for (const key of ['count', 'sum'] as ('count' | 'sum')[]) {
const value = histogram[key];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ import { SpanKind, SpanStatusCode } from '@opentelemetry/api';
import * as assert from 'assert';
import type { status as GrpcStatus } from '@grpc/grpc-js';
import { ReadableSpan } from '@opentelemetry/sdk-trace-base';
import {
hrTimeToMilliseconds,
hrTimeToMicroseconds,
} from '@opentelemetry/core';
import {
SEMATTRS_NET_PEER_NAME,
SEMATTRS_NET_PEER_PORT,
Expand Down Expand Up @@ -52,13 +48,11 @@ export const assertSpan = (
assert.strictEqual(span.spanContext().spanId.length, 16);
assert.strictEqual(span.kind, kind);

assert.ok(span.endTime);
assert.ok(span.endTimeUnixNano);
assert.strictEqual(span.links.length, 0);

assert.ok(
hrTimeToMicroseconds(span.startTime) < hrTimeToMicroseconds(span.endTime)
);
assert.ok(hrTimeToMilliseconds(span.endTime) > 0);
assert.ok(span.startTimeUnixNano < span.endTimeUnixNano);
assert.ok(span.endTimeUnixNano > 0);

if (span.kind === SpanKind.SERVER) {
assert.ok(span.spanContext());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
SpanStatus,
Exception,
} from '@opentelemetry/api';
import { hrTimeToNanoseconds } from '@opentelemetry/core';
import { ReadableSpan } from '@opentelemetry/sdk-trace-base';
import {
SEMATTRS_HTTP_METHOD,
Expand Down Expand Up @@ -108,8 +107,11 @@ export const assertSpan = (
}
);

assert.ok(span.endTime, 'must be finished');
assert.ok(hrTimeToNanoseconds(span.duration), 'must have positive duration');
assert.ok(span.ended, 'must be finished');
assert.ok(
span.endTimeUnixNano > span.startTimeUnixNano,
'must have positive duration'
);

if (validations.reqHeaders) {
const userAgent = validations.reqHeaders['user-agent'];
Expand Down
18 changes: 6 additions & 12 deletions experimental/packages/otlp-transformer/src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import type { OtlpEncodingOptions, Fixed64, LongBits } from './internal-types';
import { HrTime } from '@opentelemetry/api';
import { hrTimeToNanoseconds } from '@opentelemetry/core';
import { hexToBinary } from './hex-to-binary';

export function hrTimeToNanos(hrTime: HrTime): bigint {
Expand All @@ -30,20 +29,15 @@ export function toLongBits(value: bigint): LongBits {
return { low, high };
}

export function encodeAsLongBits(hrTime: HrTime): LongBits {
const nanos = hrTimeToNanos(hrTime);
export function encodeAsLongBits(nanos: bigint): LongBits {
return toLongBits(nanos);
}

export function encodeAsString(hrTime: HrTime): string {
const nanos = hrTimeToNanos(hrTime);
export function encodeAsString(nanos: bigint): string {
return nanos.toString();
}

const encodeTimestamp =
typeof BigInt !== 'undefined' ? encodeAsString : hrTimeToNanoseconds;

export type HrTimeEncodeFunction = (hrTime: HrTime) => Fixed64;
export type UnixNanosEncodeFunction = (nanos: bigint) => Fixed64;
export type SpanContextEncodeFunction = (
spanContext: string
) => string | Uint8Array;
Expand All @@ -52,7 +46,7 @@ export type OptionalSpanContextEncodeFunction = (
) => string | Uint8Array | undefined;

export interface Encoder {
encodeHrTime: HrTimeEncodeFunction;
encodeBigIntNanos: UnixNanosEncodeFunction;
encodeSpanContext: SpanContextEncodeFunction;
encodeOptionalSpanContext: OptionalSpanContextEncodeFunction;
}
Expand All @@ -67,7 +61,7 @@ function optionalHexToBinary(str: string | undefined): Uint8Array | undefined {
}

const DEFAULT_ENCODER: Encoder = {
encodeHrTime: encodeAsLongBits,
encodeBigIntNanos: toLongBits,
encodeSpanContext: hexToBinary,
encodeOptionalSpanContext: optionalHexToBinary,
};
Expand All @@ -80,7 +74,7 @@ export function getOtlpEncoder(options?: OtlpEncodingOptions): Encoder {
const useLongBits = options.useLongBits ?? true;
const useHex = options.useHex ?? false;
return {
encodeHrTime: useLongBits ? encodeAsLongBits : encodeTimestamp,
encodeBigIntNanos: useLongBits ? toLongBits : encodeAsString,
encodeSpanContext: useHex ? identity : hexToBinary,
encodeOptionalSpanContext: useHex ? identity : optionalHexToBinary,
};
Expand Down
4 changes: 2 additions & 2 deletions experimental/packages/otlp-transformer/src/logs/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ function logRecordsToResourceLogs(

function toLogRecord(log: ReadableLogRecord, encoder: Encoder): ILogRecord {
return {
timeUnixNano: encoder.encodeHrTime(log.hrTime),
observedTimeUnixNano: encoder.encodeHrTime(log.hrTimeObserved),
timeUnixNano: encoder.encodeBigIntNanos(log.timeUnixNano),
observedTimeUnixNano: encoder.encodeBigIntNanos(log.timeUnixNanoObserved),
severityNumber: toSeverityNumber(log.severityNumber),
severityText: log.severityText,
body: toAnyValue(log.body),
Expand Down
12 changes: 6 additions & 6 deletions experimental/packages/otlp-transformer/src/metrics/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,8 @@ function toSingularDataPoint(
) {
const out: INumberDataPoint = {
attributes: toAttributes(dataPoint.attributes),
startTimeUnixNano: encoder.encodeHrTime(dataPoint.startTime),
timeUnixNano: encoder.encodeHrTime(dataPoint.endTime),
startTimeUnixNano: encoder.encodeBigIntNanos(dataPoint.startTimeUnixNano),
timeUnixNano: encoder.encodeBigIntNanos(dataPoint.endTimeUnixNano),
};

switch (valueType) {
Expand Down Expand Up @@ -161,8 +161,8 @@ function toHistogramDataPoints(
sum: histogram.sum,
min: histogram.min,
max: histogram.max,
startTimeUnixNano: encoder.encodeHrTime(dataPoint.startTime),
timeUnixNano: encoder.encodeHrTime(dataPoint.endTime),
startTimeUnixNano: encoder.encodeBigIntNanos(dataPoint.startTimeUnixNano),
timeUnixNano: encoder.encodeBigIntNanos(dataPoint.endTimeUnixNano),
};
});
}
Expand All @@ -189,8 +189,8 @@ function toExponentialHistogramDataPoints(
},
scale: histogram.scale,
zeroCount: histogram.zeroCount,
startTimeUnixNano: encoder.encodeHrTime(dataPoint.startTime),
timeUnixNano: encoder.encodeHrTime(dataPoint.endTime),
startTimeUnixNano: encoder.encodeBigIntNanos(dataPoint.startTimeUnixNano),
timeUnixNano: encoder.encodeBigIntNanos(dataPoint.endTimeUnixNano),
};
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ export function sdkSpanToOtlpSpan(span: ReadableSpan, encoder: Encoder): ISpan {
name: span.name,
// Span kind is offset by 1 because the API does not define a value for unset
kind: span.kind == null ? 0 : span.kind + 1,
startTimeUnixNano: encoder.encodeHrTime(span.startTime),
endTimeUnixNano: encoder.encodeHrTime(span.endTime),
startTimeUnixNano: encoder.encodeBigIntNanos(span.startTimeUnixNano),
endTimeUnixNano: encoder.encodeBigIntNanos(span.endTimeUnixNano),
attributes: toAttributes(span.attributes),
droppedAttributesCount: span.droppedAttributesCount,
events: span.events.map(event => toOtlpSpanEvent(event, encoder)),
Expand Down Expand Up @@ -83,7 +83,7 @@ export function toOtlpSpanEvent(
? toAttributes(timedEvent.attributes)
: [],
name: timedEvent.name,
timeUnixNano: encoder.encodeHrTime(timedEvent.time),
timeUnixNano: encoder.encodeBigIntNanos(timedEvent.timeUnixNano),
droppedAttributesCount: timedEvent.droppedAttributesCount || 0,
};
}
Expand Down
12 changes: 2 additions & 10 deletions experimental/packages/otlp-transformer/test/common.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ describe('common', () => {
describe('otlp encoder', () => {
it('defaults to long timestamps and binary encoding given no options', () => {
const encoder = getOtlpEncoder();
assert.deepStrictEqual(encoder.encodeHrTime([1697978649, 99870675]), {
assert.deepStrictEqual(encoder.encodeBigIntNanos(1697978649099870675n), {
low: 3352011219,
high: 395341461,
});
Expand All @@ -92,7 +92,7 @@ describe('common', () => {

it('defaults to long timestamps and base64 encoding given empty options', () => {
const encoder = getOtlpEncoder({});
assert.deepStrictEqual(encoder.encodeHrTime([1697978649, 99870675]), {
assert.deepStrictEqual(encoder.encodeBigIntNanos(1697978649099870675n), {
low: 3352011219,
high: 395341461,
});
Expand All @@ -110,14 +110,6 @@ describe('common', () => {
);
});

it('can encode HrTime as string', () => {
const encoder = getOtlpEncoder({ useLongBits: false });
assert.deepStrictEqual(
encoder.encodeHrTime([1697978649, 99870675]),
'1697978649099870675'
);
});

it('can encode span context as hex', () => {
const encoder = getOtlpEncoder({ useHex: true });
assert.deepStrictEqual(encoder.encodeSpanContext(traceId), traceId);
Expand Down
4 changes: 4 additions & 0 deletions experimental/packages/otlp-transformer/test/logs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,8 @@ describe('Logs', () => {
name: 'scope_name_2',
};
const log_fragment_1 = {
timeUnixNano: 1680253513123241635n,
timeUnixNanoObserved: 1683526948965142784n,
hrTime: [1680253513, 123241635] as HrTime,
hrTimeObserved: [1683526948, 965142784] as HrTime,
attributes: {
Expand All @@ -197,6 +199,8 @@ describe('Logs', () => {
},
};
const log_fragment_2 = {
timeUnixNano: 1680253797687038506n,
timeUnixNanoObserved: 1680253797687038506n,
hrTime: [1680253797, 687038506] as HrTime,
hrTimeObserved: [1680253797, 687038506] as HrTime,
attributes: {
Expand Down
Loading