Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0b0d1bc
feat: support OpenTelemetry JS SDK 2.x
bengigone Aug 24, 2025
42945f1
Resolving linting issues
bengigone Aug 24, 2025
cbf098d
feat: revert to custom protobuf encoding for 2.x compatibility
bengigone Sep 3, 2025
64a9843
feat: use custom proto encoding for otel 2.x support
bengigone Sep 3, 2025
37bac3d
feat: implement custom protobuf encoding with OpenTelemetry 2.x compa…
bengigone Sep 3, 2025
6b8211a
Remove package-lock.json
bengigone Sep 18, 2025
32cd780
fix: use official createExportTraceServiceRequest and improve protobu…
bengigone Sep 18, 2025
9ad64dd
update lockfile
kakadiadarpan Sep 19, 2025
02e46aa
Resolving comments
bengigone Sep 22, 2025
c24a59b
[build] increase build size temporarily
kakadiadarpan Sep 23, 2025
b1bda52
[opentelemetry] update OTel deps in all the packages/apps
kakadiadarpan Sep 23, 2025
cbc9c16
[otel] add missing context manager setup in sdk
kakadiadarpan Sep 23, 2025
8d43337
[collector] fix imports & types
kakadiadarpan Sep 23, 2025
0a0ea48
[otelzero] update tests
kakadiadarpan Sep 23, 2025
6d6869a
[otelzero] fix types & build size
kakadiadarpan Sep 23, 2025
5e436d1
add util to normalize id
kakadiadarpan Sep 24, 2025
1956819
[otelzero] add createGauge
kakadiadarpan Sep 24, 2025
5bab857
[dependencies] fix version conflicts to drop bundle size
kakadiadarpan Sep 24, 2025
2116caf
update build size limit
kakadiadarpan Sep 24, 2025
574fcac
[configuration] update logRecordProcessors type & setup
kakadiadarpan Sep 24, 2025
4543f03
fix env parsing & contextManager disabling
kakadiadarpan Sep 24, 2025
68d1b75
[env] load all env vars together
kakadiadarpan Sep 24, 2025
a0fbff9
fix: make SDK start() synchronous and add @internal annotationsRetry
bengigone Sep 24, 2025
d036965
remove usage of legacy `addLogRecordProcessor`
kakadiadarpan Sep 25, 2025
d62a92d
[env-parser] eslint and add comment
kakadiadarpan Sep 25, 2025
b79a447
drop void as start is not async anymore
kakadiadarpan Sep 25, 2025
1fb0065
[collector] normalize traceId before using them
kakadiadarpan Sep 25, 2025
3603246
add changeset & pre for beta release
kakadiadarpan Sep 25, 2025
6eabb51
update migration guide
kakadiadarpan Sep 25, 2025
3758bd5
[otel] update logs sdk version
kakadiadarpan Sep 25, 2025
963b1bb
update metricReaders types
kakadiadarpan Sep 25, 2025
8f86520
update @opentelemetry/api allowed versions
kakadiadarpan Sep 25, 2025
8e91d86
update configuration section
kakadiadarpan Sep 25, 2025
f5e904e
add node.js version requirement changes
kakadiadarpan Sep 25, 2025
15e3490
[packages] run prettier
kakadiadarpan Sep 25, 2025
a986198
[tests] run prettier
kakadiadarpan Sep 25, 2025
497e83d
[changeset] run prettier
kakadiadarpan Sep 25, 2025
99a9432
[changesets] update
kakadiadarpan Sep 26, 2025
2cf7cf1
remove pre.json to do a snapshot release
kakadiadarpan Sep 26, 2025
b62e0aa
reformat changesets
kakadiadarpan Sep 29, 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
121 changes: 121 additions & 0 deletions .changeset/khaki-toys-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
---
"otelzero": major
"@vercel/otel": major
---

Add support for OTel JS SDK 2.X

## Breaking Changes

### 1. OpenTelemetry SDK dependencies updated

**API package** (minimum version bumped):
- `@opentelemetry/api`: `>=1.9.0 <3.0.0` (was `>=1.7.0 <2.0.0`)

**Stable packages** (updated to v2.x):
- `@opentelemetry/resources`: `>=2.0.0 <3.0.0` (was `>=1.19.0 <2.0.0`)
- `@opentelemetry/sdk-metrics`: `>=2.0.0 <3.0.0` (was `>=1.19.0 <2.0.0`)
- `@opentelemetry/sdk-trace-base`: `>=2.0.0 <3.0.0` (was `>=1.19.0 <2.0.0`)

**Experimental packages** (updated to v0.2XX):

- `@opentelemetry/api-logs`: `>=0.200.0 <0.300.0` (was `>=0.46.0 <0.200.0`)
- `@opentelemetry/instrumentation`: `>=0.200.0 <0.300.0` (was `>=0.46.0 <0.200.0`)
- `@opentelemetry/sdk-logs`: `>=0.200.0 <0.300.0` (was `>=0.46.0 <0.200.0`)

### 2. Minimum Node.js version requirement

The minimum supported Node.js has been raised to `^18.19.0 || >=20.6.0` to align with OpenTelemetry JS SDK 2.x requirements. This means that support for Node.js 14 and 16 has been dropped.

### 3. Configuration changes

**Log Record Processors:**

```typescript
// Before (v1.x)
registerOTel({
serviceName: 'your-service-name',
logRecordProcessor: myProcessor // Single processor
});

// After (v2.x)
registerOTel({
serviceName: 'your-service-name',
logRecordProcessors: [myProcessor] // Array of processors
});
```

**Metric Readers:**

```typescript
// Before (v1.x)
registerOTel({
serviceName: 'your-service-name',
metricReader: myReader // Single reader
});

// After (v2.x)
registerOTel({
serviceName: 'your-service-name',
metricReaders: [myReader] // Array of readers
});
```

## Migration Guide

1. **Update OpenTelemetry dependencies**: Update the OpenTelemetry packages you are using in your project to the compatible versions:

**API package** (minimum version bumped):
```bash
npm install @opentelemetry/api@^1.9.0
```

**Stable packages** (upgrade to v2.x):
```bash
npm install @opentelemetry/resources@^2.1.0 @opentelemetry/sdk-trace-base@^2.1.0 @opentelemetry/sdk-metrics@^2.1.0
```

**Experimental packages** (upgrade to v0.2XX):
```bash
npm install @opentelemetry/sdk-logs@^0.205.0 @opentelemetry/instrumentation@^0.205.0 @opentelemetry/api-logs@^0.205.0
```

**Note**: Only install the packages you are actually using in your project.

2. **Update configuration**:

**Log Record Processors** - Change `logRecordProcessor` to `logRecordProcessors`:

```typescript
// Before
registerOTel({
serviceName: 'your-service-name',
logRecordProcessor: myProcessor // Single processor
});

// After
registerOTel({
serviceName: 'your-service-name',
logRecordProcessors: [myProcessor];
});
```

**Metric Readers** - Change `metricReader` to `metricReaders`:

```typescript
// Before
registerOTel({
serviceName: 'your-service-name',
metricReader: myReader
});

// After
registerOtel({
serviceName: 'your-service-name',
metricReaders: [myReader];
});
```

3. **No code changes needed** for basic usage - the SDK interface remains the same for most common use cases.

For complete details on migrating from OpenTelemetry JS SDK 1.x to 2.x, see the [official OpenTelemetry migration guide](https://github.com/open-telemetry/opentelemetry-js/blob/v2.0.0/doc/upgrade-to-2.x.md).
4 changes: 2 additions & 2 deletions apps/multiple-exporters/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"lint": "next lint"
},
"dependencies": {
"@opentelemetry/api": "^1.7.0",
"@opentelemetry/sdk-trace-base": "^1.19.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/sdk-trace-base": "^2.1.0",
"@vercel/otel": "workspace:^",
"bridge-emulator": "workspace:^",
"next": "14.2.1-canary.7",
Expand Down
4 changes: 2 additions & 2 deletions apps/sample/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"lint": "next lint"
},
"dependencies": {
"@opentelemetry/api": "^1.7.0",
"@opentelemetry/sdk-trace-base": "^1.19.0",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/sdk-trace-base": "^2.1.0",
"@vercel/otel": "workspace:^",
"bridge-emulator": "workspace:^",
"next": "14.2.1-canary.7",
Expand Down
2 changes: 1 addition & 1 deletion packages/bridge-emulator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"next": "14.2.1-canary.7"
},
"peerDependencies": {
"@opentelemetry/api": "^1.7.0",
"@opentelemetry/api": "^1.9.0",
"@vercel/otel": "workspace:^"
},
"devDependencies": {
Expand Down
8 changes: 4 additions & 4 deletions packages/bridge-emulator/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class BridgeEmulatorServer implements Bridge {
async connect(): Promise<void> {
const handler = async (
req: IncomingMessage,
res: ServerResponse
res: ServerResponse,
): Promise<void> => {
let json: BridgeEmulatorRequest;
if ((req.headers["content-type"] ?? "").includes("multipart/form-data")) {
Expand All @@ -77,7 +77,7 @@ class BridgeEmulatorServer implements Bridge {
.map(([key, value]) => [
key.slice(5),
Array.isArray(value) ? value[0] : value,
])
]),
),
} as unknown as BridgeEmulatorRequest);
});
Expand All @@ -94,7 +94,7 @@ class BridgeEmulatorServer implements Bridge {
req.on("error", reject);
});
json = JSON.parse(
body.toString("utf-8") || "{}"
body.toString("utf-8") || "{}",
) as BridgeEmulatorRequest;
}

Expand Down Expand Up @@ -130,7 +130,7 @@ class BridgeEmulatorServer implements Bridge {
}
const fetchReq = new Request(
`http://${req.headers.host || ""}${req.url || ""}`,
{ headers: fetchHeaders }
{ headers: fetchHeaders },
);
this.fetches.push(fetchReq);
res.writeHead(200, "OK", { "X-Server": "bridge" });
Expand Down
4 changes: 2 additions & 2 deletions packages/bridge-emulator/src/server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {

export interface VercelRequestContext {
waitUntil: (
promiseOrFunc: Promise<unknown> | (() => Promise<unknown>)
promiseOrFunc: Promise<unknown> | (() => Promise<unknown>),
) => void;
headers: Record<string, string | undefined>;
url: string;
Expand Down Expand Up @@ -63,7 +63,7 @@ export class BridgeEmulatorContextReader implements TextMapPropagator {
return [key, value[0]];
}
return [key, value];
})
}),
);
const {
"x-otel-test-id": testId,
Expand Down
4 changes: 2 additions & 2 deletions packages/bridge-emulator/src/vercel-request-context/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

export interface VercelRequestContext {
waitUntil: (
promiseOrFunc: Promise<unknown> | (() => Promise<unknown>)
promiseOrFunc: Promise<unknown> | (() => Promise<unknown>),
) => void;
headers: Record<string, string | undefined>;
url: string;
Expand All @@ -31,7 +31,7 @@ let testContext: VercelRequestContext | undefined;

export function withRequestContext<T>(
context: VercelRequestContext,
fn: () => T
fn: () => T,
): T {
let reader = (globalThis as GlobalWithReader)[symbol];
if (!reader) {
Expand Down
8 changes: 4 additions & 4 deletions packages/collector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
"author": "",
"license": "ISC",
"dependencies": {
"@opentelemetry/otlp-transformer": "^0.46.0",
"@opentelemetry/semantic-conventions": "^1.19.0"
"@opentelemetry/otlp-transformer": "^0.205.0"
},
"devDependencies": {
"@types/node": "^20",
"eslint-config": "workspace:*",
"typescript-config": "workspace:*",
"typescript": "^5"
"typescript": "^5",
"typescript-config": "workspace:*"
}
}
2 changes: 1 addition & 1 deletion packages/collector/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export interface OtelCollector {
}

export async function start(
opts?: OtelCollectorOptions
opts?: OtelCollectorOptions,
): Promise<OtelCollector> {
const server = new Server();
server.addHandlers(createCollectorHandlers());
Expand Down
2 changes: 1 addition & 1 deletion packages/collector/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {

export type IHandler = (
req: IncomingMessage,
res: ServerResponse
res: ServerResponse,
) => void | Promise<void>;

export class Server {
Expand Down
37 changes: 23 additions & 14 deletions packages/collector/src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
* See https://opentelemetry.io/docs/.
*/

import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
import type {
Fixed64,
IResource,
Resource,
} from "@opentelemetry/otlp-transformer/build/src/common/internal-types";
import type {
IResourceSpans,
ISpan,
} from "@opentelemetry/otlp-transformer";
} from "@opentelemetry/otlp-transformer/build/src/trace/internal-types";
import type { IHandler } from "./server";

type ISpanWithService = ISpan & { serviceName: string };
Expand All @@ -25,7 +26,7 @@ export interface ITrace {
}

interface Store {
services: Record<string, IResource>;
services: Record<string, Resource>;
traces: ITrace[];
}

Expand Down Expand Up @@ -58,13 +59,13 @@ export function processResourceSpans(resourceSpans: IResourceSpans[]): void {
continue;
}
const serviceNameRaw = resource.attributes.find(
({ key }) => key === SemanticResourceAttributes.SERVICE_NAME
({ key }) => key === "service.name",
)?.value.stringValue;
if (!serviceNameRaw) {
continue;
}
const runtimeName = resource.attributes.find(
({ key }) => key === "runtime.name" || key === "vercel.runtime"
({ key }) => key === "runtime.name" || key === "vercel.runtime",
)?.value.stringValue;
const serviceName = `${serviceNameRaw}${
runtimeName ? `:${runtimeName}` : ""
Expand All @@ -78,7 +79,8 @@ export function processResourceSpans(resourceSpans: IResourceSpans[]): void {
if (!isValidSpan(span)) {
continue;
}
const { traceId, parentSpanId, name, attributes } = span;
const { traceId: traceIdRaw, parentSpanId, name, attributes } = span;
const traceId = normalizeId(traceIdRaw);
let trace = store.traces.find((t) => t.traceId === traceId);
if (!trace) {
trace = {
Expand All @@ -88,7 +90,7 @@ export function processResourceSpans(resourceSpans: IResourceSpans[]): void {
name: "",
spans: [],
timestamp: unixNanoToMillis(span.startTimeUnixNano),
};
} satisfies ITrace;
store.traces.push(trace);
}
trace.spans.push({
Expand Down Expand Up @@ -133,7 +135,7 @@ export function processResourceSpans(resourceSpans: IResourceSpans[]): void {
span.spanId,
parentSpanId,
span.spanId === rootSpan?.spanId,
rootSpan?.name
rootSpan?.name,
);
}
if (span.spanId === rootSpan?.spanId) {
Expand All @@ -153,8 +155,8 @@ export function getServiceNames(): string[] {
return Object.keys(store.services);
}

export function getServiceMap(): Record<string, IResource> {
return { ...store.services } as Record<string, IResource>;
export function getServiceMap(): Record<string, Resource> {
return { ...store.services } as Record<string, Resource>;
}

export function getTraceCount(): number {
Expand Down Expand Up @@ -211,11 +213,11 @@ function createCollectorTraceHandler(): IHandler {
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader(
"Access-Control-Allow-Methods",
"POST, GET, OPTIONS, DELETE"
"POST, GET, OPTIONS, DELETE",
);
res.setHeader(
"Access-Control-Allow-Headers",
"content-type, accept, otel-encoding"
"content-type, accept, otel-encoding",
);
res.setHeader("Access-Control-Max-Age", "86400");

Expand Down Expand Up @@ -285,7 +287,7 @@ function createCollectorGetTraceHandler(): IHandler {
function isValidSpan(span: ISpan): boolean {
const { attributes } = span;
const hasBubble = attributes.some(
({ key, value }) => key === "next.bubble" && value.boolValue === true
({ key, value }) => key === "next.bubble" && value.boolValue === true,
);
if (hasBubble) {
return false;
Expand Down Expand Up @@ -321,3 +323,10 @@ function unixNanoToMillis(unixNano: Fixed64): number {
}
return unixNano / 1e6;
}

function normalizeId(id: string | Uint8Array): string {
if (typeof id === "string") {
return id;
}
return Buffer.from(id).toString("hex");
}
Loading