From 0a14b6eae34989b3ef84226f1fb7e26d00db4de5 Mon Sep 17 00:00:00 2001 From: Milan Gallas <44726162+GALLLASMILAN@users.noreply.github.com> Date: Thu, 9 Jan 2025 09:57:38 +0100 Subject: [PATCH] feat(trace): catch all traces from collector (#25) Signed-off-by: GALLLASMILAN --- .env.testing.docker | 2 +- .env.testing.local | 2 +- .github/workflows/test.yml | 6 +- .gitignore | 1 + compose.yml | 28 +- package.json | 11 +- .../install-collector.sh | 33 ++ .../otel-collector-config-docker.yaml | 33 ++ .../otel-collector-config-local.yaml | 36 ++ src/mlflow/queue/mlflow-trace-create.queue.ts | 9 +- src/span/span.document.ts | 2 +- src/span/span.service.ts | 23 + src/span/span.test.ts | 3 +- src/span/utilt.ts | 8 +- src/testing/telemetry.ts | 39 +- src/testing/utils.ts | 37 +- src/trace/trace.service.ts | 58 +-- src/trace/trace.test.ts | 103 +++- src/trace/utils/assembly-trace.ts | 15 +- src/utils/constants.ts | 3 + src/utils/error.ts | 7 +- yarn.lock | 439 ++++++++++-------- 22 files changed, 630 insertions(+), 268 deletions(-) create mode 100755 scripts/open_telemetry_collector/install-collector.sh create mode 100644 scripts/open_telemetry_collector/otel-collector-config-docker.yaml create mode 100644 scripts/open_telemetry_collector/otel-collector-config-local.yaml diff --git a/.env.testing.docker b/.env.testing.docker index 0ed567b..6c1c318 100644 --- a/.env.testing.docker +++ b/.env.testing.docker @@ -1,5 +1,5 @@ NODE_ENV=production -PORT=4318 +PORT=4319 AUTH_KEY=valid-api-key FASTIFY_BODY_LIMIT=10485760 diff --git a/.env.testing.local b/.env.testing.local index 2fd0023..35b103f 100644 --- a/.env.testing.local +++ b/.env.testing.local @@ -1,4 +1,4 @@ BEE_FRAMEWORK_INSTRUMENTATION_METRICS_ENABLED=false BEE_FRAMEWORK_INSTRUMENTATION_ENABLED=true -PORT=4318 +PORT=4319 AUTH_KEY=valid-api-key diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c792a6d..545bdd0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,9 +31,9 @@ jobs: run: yarn install - name: Copy .env file run: cp .env.testing.docker .env - - name: Build infra - run: docker compose build - - name: Build Observe API + - name: Pull images + run: docker compose pull + - name: Build images run: docker compose build - name: Run Observe API run: docker compose up -d diff --git a/.gitignore b/.gitignore index ef3aa40..3dceb2a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ node_modules #infra .env.docker +scripts/open_telemetry_collector/otelcol diff --git a/compose.yml b/compose.yml index 8d0197f..7ad7ec0 100644 --- a/compose.yml +++ b/compose.yml @@ -24,6 +24,7 @@ services: retries: 5 mlflow: image: bitnami/mlflow:2.17.2 + restart: always ports: - '${MLFLOW_EXPOSED_PORT:-8080}:8080' entrypoint: @@ -36,6 +37,27 @@ services: - 'label=disable' volumes: - ./entrypoint.sh:/entrypoint.sh:ro + wait_for_mlflow: + image: curlimages/curl:latest + depends_on: + mlflow: + condition: service_started + entrypoint: + [ + 'sh', + '-c', + "while ! curl --silent --fail http://mlflow:8080; do echo 'Waiting for API...'; sleep 5; done" + ] + otel-collector: + image: otel/opentelemetry-collector-contrib:0.114.0 + restart: always + ports: + - '4318:4318' # OTLP HTTP receiver + - '13133:13133' # Health Check + - '55679:55679' # Prometheus scraping port + command: ['--config=/etc/otel-collector-config.yaml'] + volumes: + - ./scripts/open_telemetry_collector/otel-collector-config-docker.yaml:/etc/otel-collector-config.yaml observe_api: build: context: . @@ -43,7 +65,7 @@ services: GIT_TAG: ${TAG:-testing} BUILD_DATETIME: ${BUILD_DATETIME:-} ports: - - '${OBSERVE_API_EXPOSED_PORT:-4318}:4318' + - '${OBSERVE_API_EXPOSED_PORT:-4319}:4319' env_file: - .env.testing.docker command: > @@ -53,7 +75,7 @@ services: node ./dist/index.js " healthcheck: - test: wget --no-verbose --tries=1 --spider http://0.0.0.0:4318/health || exit 1 + test: wget --no-verbose --tries=1 --spider http://0.0.0.0:4319/health || exit 1 interval: 10s timeout: 5s retries: 5 @@ -64,6 +86,8 @@ services: condition: service_healthy redis: condition: service_healthy + otel-collector: + condition: service_started wait_for_api: image: curlimages/curl:latest depends_on: diff --git a/package.json b/package.json index 86e21ff..c747a48 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "proto:generate": "./scripts/open_telemetry_generate_protos/generate_protos.sh", "start:infra": "docker compose up -d mongo redis mlflow", "start:dev": "tsx watch ./src/index.ts | pino-pretty --singleLine", + "start:collector:local": "./scripts/open_telemetry_collector/otelcol --config=./scripts/open_telemetry_collector/otel-collector-config-local.yaml", "stop:infra": "docker compose down", + "install:collector:local": "./scripts/open_telemetry_collector/install-collector.sh", "dev": "yarn start:dev", "start": "node ./dist/index.js", "test": "./scripts/test_local.sh", @@ -35,13 +37,15 @@ "@commitlint/config-conventional": "^19.5.0", "@commitlint/types": "^19.5.0", "@fastify/type-provider-json-schema-to-ts": "^4.0.1", - "@opentelemetry/exporter-trace-otlp-proto": "^0.55.0", - "@opentelemetry/instrumentation": "^0.55.0", - "@opentelemetry/sdk-node": "^0.55.0", + "@opentelemetry/exporter-trace-otlp-proto": "^0.57.0", + "@opentelemetry/instrumentation": "^0.57.0", + "@opentelemetry/sdk-node": "^0.57.0", + "@opentelemetry/sdk-trace-base": "^1.30.0", "@opentelemetry/semantic-conventions": "^1.28.0", "@release-it/conventional-changelog": "^8.0.1", "@types/dotenv-safe": "^8.1.6", "@types/node": "^20.16.5", + "@types/semver": "^7", "@typescript-eslint/eslint-plugin": "^7.13.0", "@typescript-eslint/parser": "^7.13.0", "@vitest/coverage-v8": "^1.6.0", @@ -88,6 +92,7 @@ "json-schema-to-ts": "^3.1.0", "pino": "^9.2.0", "protobufjs": "^7.4.0", + "semver": "^7.6.3", "typescript-json-schema": "^0.65.1" }, "resolutions": { diff --git a/scripts/open_telemetry_collector/install-collector.sh b/scripts/open_telemetry_collector/install-collector.sh new file mode 100755 index 0000000..0b2170a --- /dev/null +++ b/scripts/open_telemetry_collector/install-collector.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Copyright 2025 IBM Corp. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e + +cd "$(dirname "$0")" + +# Check if the 'otelcol' file already exists +if [ ! -f otelcol ]; then + echo "otelcol file not found. Downloading and extracting..." + + curl --proto '=https' --tlsv1.2 -fOL https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.115.1/otelcol_0.115.1_darwin_arm64.tar.gz + + tar -xvf otelcol_0.115.1_darwin_arm64.tar.gz + + ## clean unnecessary files + rm otelcol_0.115.1_darwin_arm64.tar.gz + rm README.MD +else + echo "otelcol file already exists. Skipping download and extraction." +fi diff --git a/scripts/open_telemetry_collector/otel-collector-config-docker.yaml b/scripts/open_telemetry_collector/otel-collector-config-docker.yaml new file mode 100644 index 0000000..30ba692 --- /dev/null +++ b/scripts/open_telemetry_collector/otel-collector-config-docker.yaml @@ -0,0 +1,33 @@ +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + +exporters: + debug: {} + otlphttp/observe: + endpoint: http://observe_api:4319 + compression: none + tls: + insecure: true + headers: + x-bee-authorization: 'valid-api-key' + +processors: + batch: {} + filter/observe: + traces: + span: + - instrumentation_scope.name != "bee-agent-framework" + memory_limiter: + check_interval: 5s + limit_percentage: 80 + spike_limit_percentage: 25 + +service: + pipelines: + traces/observe: + receivers: [otlp] + processors: [filter/observe, memory_limiter, batch] + exporters: [debug, otlphttp/observe] diff --git a/scripts/open_telemetry_collector/otel-collector-config-local.yaml b/scripts/open_telemetry_collector/otel-collector-config-local.yaml new file mode 100644 index 0000000..071f6e2 --- /dev/null +++ b/scripts/open_telemetry_collector/otel-collector-config-local.yaml @@ -0,0 +1,36 @@ +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + +exporters: + debug: {} + otlphttp/observe: + endpoint: http://127.0.0.1:4319 + tls: + insecure: true + headers: + x-bee-authorization: 'valid-api-key' + +extensions: + health_check: + endpoint: http://127.0.0.1:13133 + +processors: + batch: {} + filter/observe: + traces: + span: + - instrumentation_scope.name != "bee-agent-framework" + memory_limiter: + check_interval: 5s + limit_percentage: 80 + spike_limit_percentage: 25 + +service: + pipelines: + traces/observe: + receivers: [otlp] + processors: [filter/observe, memory_limiter, batch] + exporters: [debug, otlphttp/observe] diff --git a/src/mlflow/queue/mlflow-trace-create.queue.ts b/src/mlflow/queue/mlflow-trace-create.queue.ts index bcc4335..4b4dc28 100644 --- a/src/mlflow/queue/mlflow-trace-create.queue.ts +++ b/src/mlflow/queue/mlflow-trace-create.queue.ts @@ -113,6 +113,11 @@ const { queue } = createQueue({ } }); -export function addMlflowTraceToQueue(input: JobInput) { - queue.add(QueueName['mlflow-trace-create'], input); +export function addMlflowTracesToQueue(jobInputs: JobInput[]) { + queue.addBulk( + jobInputs.map((jobInput) => ({ + name: QueueName['mlflow-trace-create'], + data: jobInput + })) + ); } diff --git a/src/span/span.document.ts b/src/span/span.document.ts index 066811b..ebb67ca 100644 --- a/src/span/span.document.ts +++ b/src/span/span.document.ts @@ -54,7 +54,7 @@ export class Span extends BaseDocument { attributes!: SpanDto['attributes']; @ManyToOne() - trace!: Ref; + trace?: Ref; constructor(input: Span__Output) { super(new ObjectId()); diff --git a/src/span/span.service.ts b/src/span/span.service.ts index 03569fd..3c36531 100644 --- a/src/span/span.service.ts +++ b/src/span/span.service.ts @@ -15,10 +15,13 @@ */ import { QueryOrderNumeric } from '@mikro-orm/mongodb'; +import * as semver from 'semver'; import { isValidFrameworkId, ORM } from '../utils/db.js'; +import { constants } from '../utils/constants.js'; import { SpanDto, SpanGetOneParams, SpanGetOneQuery } from './span.dto.js'; +import { Span } from './span.document.js'; export async function getSpans(props: SpanGetOneParams & SpanGetOneQuery): Promise<{ totalCount: number; @@ -47,3 +50,23 @@ export async function getSpans(props: SpanGetOneParams & SpanGetOneQuery): Promi spans: spans.map((span) => span.toTelemetry(props)) }; } + +export async function loadAllNestedSpans(span: Span): Promise { + const { version } = span.attributes; + // Optimised query = use the traceId for loading all nested spans + if ( + version && + semver.valid(version) && + semver.gte(version, constants.FRAMEWORK_BRAKING_CHANGES.TRACE_ID_FOR_EACH_SPAN) + ) { + return ORM.span.find({ attributes: { traceId: span.attributes.traceId } }); + } + + // The old temporary way (will be removed soon) + const spans = await ORM.span.find({ parentId: span.context.spanId }); + if (spans.length === 0) return spans; + + const nestedSpans = await Promise.all(spans.map((span) => loadAllNestedSpans(span))); + + return [...spans, ...nestedSpans.flat()]; +} diff --git a/src/span/span.test.ts b/src/span/span.test.ts index b4033e3..0d756c3 100644 --- a/src/span/span.test.ts +++ b/src/span/span.test.ts @@ -18,7 +18,7 @@ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { Version } from 'bee-agent-framework'; import { sdk, spanTraceExporterProcessor } from '../testing/telemetry.js'; -import { generateTrace, makeRequest } from '../testing/utils.js'; +import { generateTrace, makeRequest, waitForTrace } from '../testing/utils.js'; let traceId: string | undefined = undefined; const prompt = 'hello'; @@ -28,6 +28,7 @@ describe('span module', () => { await sdk.start(); traceId = await generateTrace({ prompt }); await spanTraceExporterProcessor.forceFlush(); + if (traceId) await waitForTrace({ traceId, includeMlflow: true }); }); afterAll(async () => { diff --git a/src/span/utilt.ts b/src/span/utilt.ts index da1f8e1..91c7b31 100644 --- a/src/span/utilt.ts +++ b/src/span/utilt.ts @@ -18,6 +18,7 @@ import { Long } from '@grpc/proto-loader'; import { MainSpan } from '../types/internal/span.js'; import type { Span__Output } from '../types/open-telemetry/generated.js'; +import { constants } from '../utils/constants.js'; import { Span } from './span.document.js'; @@ -43,6 +44,9 @@ export function getAttributeValue({ return attributes.find((attr) => attr.key === key)?.value?.stringValue; } -export function findMainSpan(spans: Span[]): MainSpan | undefined { - return spans.find((span) => !span.parentId && span.attributes.traceId) as MainSpan | undefined; +export function filterMainSpans(spans: Span[]): MainSpan[] { + return spans.filter( + (span) => + span.attributes.traceId && span.name.includes(constants.OPENTELEMETRY.INSTRUMENTATION_SCOPE) + ) as MainSpan[]; } diff --git a/src/testing/telemetry.ts b/src/testing/telemetry.ts index 42e3c9a..a5084a3 100644 --- a/src/testing/telemetry.ts +++ b/src/testing/telemetry.ts @@ -20,22 +20,45 @@ import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; import { constants } from '../utils/constants.js'; - -import { buildUrl } from './utils.js'; +import { buildUrl } from '../mlflow/utils/api/utils/build-url.js'; const traceExporter = new OTLPTraceExporter({ - url: buildUrl('v1/traces'), headers: { [constants.BEE_AUTH_HEADER]: constants.AUTH_KEY }, timeoutMillis: 120_000 }); -export const spanTraceExporterProcessor = new node.BatchSpanProcessor(traceExporter); +const resource = new resources.Resource({ + [ATTR_SERVICE_NAME]: constants.OPENTELEMETRY.INSTRUMENTATION_SCOPE, + [ATTR_SERVICE_VERSION]: '0.0.1' +}); + +// main sdk with the default BatchSpanProcessor and the Collector backend +export const spanTraceExporterProcessor = new node.BatchSpanProcessor(traceExporter); export const sdk = new NodeSDK({ - resource: new resources.Resource({ - [ATTR_SERVICE_NAME]: 'bee-agent-framework', - [ATTR_SERVICE_VERSION]: '0.0.1' - }), + resource, spanProcessors: [spanTraceExporterProcessor] }); + +// sdk with the SimpleSpanProcessor and the Collector backend +export const sdkWithSimpleProcessor = new NodeSDK({ + resource, + spanProcessors: [new node.SimpleSpanProcessor(traceExporter)] +}); + +// sdk with the SimpleSpanProcessor and direct Observe backend +export const sdkWithSimpleProcessorAndObserveBackend = new NodeSDK({ + resource, + spanProcessors: [ + new node.SimpleSpanProcessor( + new OTLPTraceExporter({ + url: buildUrl('v1/traces'), + headers: { + [constants.BEE_AUTH_HEADER]: constants.AUTH_KEY + }, + timeoutMillis: 120_000 + }) + ) + ] +}); diff --git a/src/testing/utils.ts b/src/testing/utils.ts index 74b5591..0c2ae35 100644 --- a/src/testing/utils.ts +++ b/src/testing/utils.ts @@ -30,8 +30,7 @@ import { TraceDto } from '../trace/trace.dto.js'; import { constants } from '../utils/constants.js'; export const buildUrl = (route: string): string => { - const port = process.env.PORT || '4318'; - return new URL(route, `http://127.0.0.1:${port}`).toString(); + return new URL(route, `http://127.0.0.1:${process.env.PORT || '4318'}`).toString(); }; function parseRetryAfterHeader(retryAfter: string | null) { @@ -45,31 +44,37 @@ function parseRetryAfterHeader(retryAfter: string | null) { } const MLFLOW_MAX_RETREIVE_ATTEMPTS = 5; -export async function waitForMlflowTrace({ - attempt = 1, - traceId -}: { +interface WaitForTraceProps { attempt?: number; traceId: string; -}): Promise<{ + includeMlflow?: boolean; +} +interface WaitForTraceResponse { status: number; result: TraceDto; -}> { - const traceResponse = await makeRequest({ - route: `v1/traces/${traceId}?include_mlflow=true&include_tree=true&include_mlflow_tree=true` - }); +} + +export async function waitForTrace({ + attempt = 1, + traceId, + includeMlflow = false +}: WaitForTraceProps): Promise { + const route = includeMlflow + ? `v1/traces/${traceId}?include_mlflow=true&include_tree=true&include_mlflow_tree=true` + : `v1/traces/${traceId}`; + const traceResponse = await makeRequest({ route }); const { result } = await traceResponse.json(); - if (traceResponse.status === 404) { + if (traceResponse.status === 404 && attempt <= MLFLOW_MAX_RETREIVE_ATTEMPTS) { const retryAfter = parseRetryAfterHeader(traceResponse.headers.get('Retry-After')); await setTimeoutPromise(retryAfter * 1000); - return waitForMlflowTrace({ traceId, attempt: ++attempt }); + return waitForTrace({ traceId, attempt: ++attempt }); } if (result?.mlflow?.step !== 'CLOSE_TRACE' && attempt <= MLFLOW_MAX_RETREIVE_ATTEMPTS) { await setTimeoutPromise(2000); - return waitForMlflowTrace({ traceId, attempt: ++attempt }); + return waitForTrace({ traceId, attempt: ++attempt }); } return { @@ -151,8 +156,8 @@ export async function generateTrace({ prompt }: { prompt: string }) { } // mock - const tracer = api.trace.getTracer('bee-agent-framework', Version); - traceId = 'ea215b9f'; + const tracer = api.trace.getTracer(constants.OPENTELEMETRY.INSTRUMENTATION_SCOPE, Version); + traceId = Math.random().toString(16).slice(2, 10); // 1) main span tracer.startActiveSpan( diff --git a/src/trace/trace.service.ts b/src/trace/trace.service.ts index 0d9782b..73c3b78 100644 --- a/src/trace/trace.service.ts +++ b/src/trace/trace.service.ts @@ -21,9 +21,9 @@ import { FastifyRequest } from 'fastify'; import { ORM } from '../utils/db.js'; import { ErrorWithProps, ErrorWithPropsCodes } from '../utils/error.js'; -import { addMlflowTraceToQueue } from '../mlflow/queue/mlflow-trace-create.queue.js'; +import { addMlflowTracesToQueue } from '../mlflow/queue/mlflow-trace-create.queue.js'; import { Span } from '../span/span.document.js'; -import { findMainSpan } from '../span/utilt.js'; +import { filterMainSpans } from '../span/utilt.js'; import { getServiceLogger } from '../utils/logger-factories.js'; import { constants } from '../utils/constants.js'; import type { ExportTraceServiceRequest__Output } from '../types/open-telemetry/generated.js'; @@ -97,36 +97,36 @@ export async function createTrace(traceBody: ExportTraceServiceRequest__Output): return; } - const mainSpan = findMainSpan(spans); - if (!mainSpan) { - throw new ErrorWithProps( - `The root span does not exist`, - { code: ErrorWithPropsCodes.INVALID_ARGUMENT }, - StatusCodes.BAD_REQUEST + // save all spans from framework + await ORM.em.persistAndFlush(spans); + + // build traces + const mainSpans = filterMainSpans(spans); + if (mainSpans.length > 0) { + // create traces + const traces = await Promise.all( + mainSpans.map((mainSpan) => + assemblyTrace({ + mainSpan: mainSpan, + traceId: mainSpan.attributes.traceId, + request: { + message: mainSpan.attributes.prompt, + history: mainSpan.attributes.history, + framework: { version: mainSpan.attributes.version } + }, + response: mainSpan.attributes.response, + startTime: mainSpan.startTime, + endTime: mainSpan.endTime + }) + ) ); - } - const { traceId } = mainSpan.attributes; - - const trace = assemblyTrace({ - spans, - traceId, - request: { - message: mainSpan.attributes.prompt, - history: mainSpan.attributes.history, - framework: { version: mainSpan.attributes.version } - }, - response: mainSpan.attributes.response, - startTime: mainSpan.startTime, - endTime: mainSpan.endTime - }); - // save trace - await ORM.em.persistAndFlush(trace); + // save traces + await ORM.em.persistAndFlush(traces); - // mlflow processing - addMlflowTraceToQueue({ - traceId: trace.id - }); + // mlflow processing + addMlflowTracesToQueue(traces.map((trace) => ({ traceId: trace.id }))); + } } export function traceProtobufBufferParser( diff --git a/src/trace/trace.test.ts b/src/trace/trace.test.ts index 27dae91..3f9cf82 100644 --- a/src/trace/trace.test.ts +++ b/src/trace/trace.test.ts @@ -16,13 +16,13 @@ import { expect, it, describe, beforeAll, afterAll } from 'vitest'; -import { sdk, spanTraceExporterProcessor } from '../testing/telemetry.js'; import { - generateTrace, - makeRequest, - sendCustomProtobuf, - waitForMlflowTrace -} from '../testing/utils.js'; + sdk, + sdkWithSimpleProcessor, + sdkWithSimpleProcessorAndObserveBackend, + spanTraceExporterProcessor +} from '../testing/telemetry.js'; +import { generateTrace, makeRequest, sendCustomProtobuf, waitForTrace } from '../testing/utils.js'; let traceId: string | undefined = undefined; const prompt = 'hello'; @@ -32,7 +32,7 @@ describe('trace module', () => { await sdk.start(); traceId = await generateTrace({ prompt }); await spanTraceExporterProcessor.forceFlush(); - if (traceId) await waitForMlflowTrace({ traceId }); + if (traceId) await waitForTrace({ traceId, includeMlflow: true }); }); afterAll(async () => { @@ -47,7 +47,7 @@ describe('trace module', () => { it('should use "Retry-After" header and wait until the trace is ready', async () => { let retryAfterTraceId: string | undefined = undefined; retryAfterTraceId = await generateTrace({ prompt }); - if (retryAfterTraceId) await waitForMlflowTrace({ traceId: retryAfterTraceId }); + if (retryAfterTraceId) await waitForTrace({ traceId: retryAfterTraceId, includeMlflow: true }); const traceResponse = await makeRequest({ route: `v1/traces/${retryAfterTraceId}` }); @@ -125,4 +125,91 @@ describe('trace module', () => { expect(result.mlflow.step).toBe('CLOSE_TRACE'); }); }); + + describe('performance testing', () => { + const batchesCount = 3; + const requestsCount = 5; + it(`should process ${batchesCount} batches of ${requestsCount} parallel requests`, async () => { + let traces: (string | undefined)[] = []; + // generate requests + for (let i = 0; i < batchesCount; i++) { + const requests = Array.from({ length: requestsCount }, (_, number) => { + const prompt = `My favorite number is ${number}, What is yours?`; + return generateTrace({ prompt }); + }); + const resuls = await Promise.all(requests); + traces = [...traces, ...resuls]; + } + + // flush the exporter + await spanTraceExporterProcessor.forceFlush(); + + // should have all traces + const validTraces: string[] = traces.filter((trace) => trace !== undefined); + expect(validTraces.length).toBe(requestsCount * batchesCount); + + // all traces must be saved in the observe + const loadedTraces = await Promise.all( + validTraces.map((validTraceId) => waitForTrace({ traceId: validTraceId })) + ); + + const validLoadedTracesRespones = loadedTraces.filter( + (loadedTrace) => loadedTrace.status === 200 + ); + expect(validLoadedTracesRespones.length).toBe(requestsCount * batchesCount); + }); + }); + + describe('simple span processor', () => { + let simpleSpanProcessorTraceId: string | undefined = undefined; + beforeAll(async () => { + await sdkWithSimpleProcessor.start(); + simpleSpanProcessorTraceId = await generateTrace({ prompt }); + if (simpleSpanProcessorTraceId) + await waitForTrace({ traceId: simpleSpanProcessorTraceId, includeMlflow: true }); + }); + + afterAll(async () => { + await sdkWithSimpleProcessor.shutdown(); // Ensure spans are flushed after each test + }); + + it('should load the simple span trace', async () => { + const traceResponse = await makeRequest({ route: `v1/traces/${simpleSpanProcessorTraceId}` }); + + // assert it was successful response + expect(traceResponse.status).toBe(200); + }); + }); + + describe('simple span processor with Observe backend', () => { + beforeAll(async () => { + await sdkWithSimpleProcessorAndObserveBackend.start(); + }); + + afterAll(async () => { + await sdkWithSimpleProcessorAndObserveBackend.shutdown(); // Ensure spans are flushed after each test + }); + + it('should save the trace in the mlflow', async () => { + const traceId = await generateTrace({ prompt: 'Hello darling, how are you?' }); + if (traceId) await waitForTrace({ traceId, includeMlflow: true }); + + const traceResponse = await makeRequest({ + route: `v1/traces/${traceId}?include_mlflow=true` + }); + const spansResponse = await makeRequest({ route: `v1/traces/${traceId}/spans` }); + + // assert it were successful responses + expect(traceResponse.status).toBe(200); + expect(spansResponse.status).toBe(200); + + // test mlflow data + const { result } = await traceResponse.json(); + expect(result.mlflow.step).toBe('CLOSE_TRACE'); + + // test spans data + const { total_count } = await spansResponse.json(); + expect(total_count).toBeGreaterThanOrEqual(2); + }); + }); }); diff --git a/src/trace/utils/assembly-trace.ts b/src/trace/utils/assembly-trace.ts index 01b83db..d016a03 100644 --- a/src/trace/utils/assembly-trace.ts +++ b/src/trace/utils/assembly-trace.ts @@ -15,6 +15,8 @@ */ import { Span, SpanInput } from '../../span/span.document.js'; +import { loadAllNestedSpans } from '../../span/span.service.js'; +import { MainSpan } from '../../types/internal/span.js'; import { Trace } from '../trace.document.js'; import { TraceRequest } from '../trace.dto.js'; @@ -114,7 +116,7 @@ const groupBy = (spans: TraceSpanWithChildren[]): TreeItem[] => { }; interface AssemblyTraceProps { - spans: Span[]; + mainSpan: MainSpan; traceId: string; request: TraceRequest; response?: any; @@ -122,14 +124,17 @@ interface AssemblyTraceProps { endTime: Date; } -export function assemblyTrace({ - spans, +export async function assemblyTrace({ traceId, request, response, startTime, - endTime -}: AssemblyTraceProps): Trace { + endTime, + mainSpan +}: AssemblyTraceProps): Promise { + const nestedSpans = await loadAllNestedSpans(mainSpan); + const spans = [mainSpan, ...nestedSpans]; + const eventsTree = groupBy( spans .filter((span) => !span.parentId) diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 182e275..722e830 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -81,5 +81,8 @@ export const constants = Object.freeze({ }), OPENTELEMETRY: { INSTRUMENTATION_SCOPE: 'bee-agent-framework' + }, + FRAMEWORK_BRAKING_CHANGES: { + TRACE_ID_FOR_EACH_SPAN: '0.0.56' } }); diff --git a/src/utils/error.ts b/src/utils/error.ts index 63c2adf..b0bd4f9 100644 --- a/src/utils/error.ts +++ b/src/utils/error.ts @@ -111,32 +111,37 @@ export const errorPlugin: FastifyPluginAsync = fp.default(async (app) => { } if (error.config?.addRetryAfterHeader) { reply.header('Retry-After', constants.RETRY_AFTER_SECONDS); + } else { + this.log.error(error); } reply.status(error.statusCode).send(error.toDto()); } else if (error instanceof SyntaxError) { + this.log.error(error); // When request body cannot by parsed by ContentTypeParser reply.status(StatusCodes.BAD_REQUEST).send({ code: ErrorWithPropsCodes.INVALID_ARGUMENT, message: 'An unspecified syntax error occurred' }); } else if ('validation' in error && error.statusCode === StatusCodes.BAD_REQUEST) { + this.log.error(error); // This is ajv validation error reply.status(StatusCodes.BAD_REQUEST).send({ code: ErrorWithPropsCodes.INVALID_ARGUMENT, message: error.message }); } else if (error.statusCode) { + this.log.error(error); reply.status(error.statusCode).send({ code: error.code, message: error.message }); } else { + this.log.error(error); reply.status(StatusCodes.INTERNAL_SERVER_ERROR).send({ code: ErrorWithPropsCodes.INTERNAL_SERVER_ERROR, message: 'An unspecified error occurred' }); } - this.log.error(error); }); }); diff --git a/yarn.lock b/yarn.lock index 938d0de..6638e98 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1396,12 +1396,12 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api-logs@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/api-logs@npm:0.55.0" +"@opentelemetry/api-logs@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/api-logs@npm:0.57.0" dependencies: "@opentelemetry/api": "npm:^1.3.0" - checksum: 10c0/9de1601939df121828ed8c48fa2cd923df9d471ef53ca7345a809ff1a940d3541efb64d2adada123b9a7f7feafb1d2acc59fa70e30dc3c3261a17b351c1309c3 + checksum: 10c0/4aa333a1cd942d0905b4ceae369fabfd9785fc6879f38a9835ab09dd9c03f72eac06250d55c96f7ce83c088a8e8bcfc200f38b5e3313a0e9096add44b68bc92b languageName: node linkType: hard @@ -1412,138 +1412,200 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/context-async-hooks@npm:1.28.0": - version: 1.28.0 - resolution: "@opentelemetry/context-async-hooks@npm:1.28.0" +"@opentelemetry/context-async-hooks@npm:1.30.0": + version: 1.30.0 + resolution: "@opentelemetry/context-async-hooks@npm:1.30.0" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10c0/bfd3b76dce4495d538307fef597c9145949f2b67b9096b1c06c63e14003cdee73fc06fa36bb512eb403f9e625caa4282024ca39367307524e501fcece93611dd + checksum: 10c0/46fef8f3af37227c16cf4e3d9264bfc7cfbe7357cb4266fa10ef32aa3256da6782110bea997d7a6b6815afb540da0a937fb5ecbaaed248c0234f8872bf25e8df languageName: node linkType: hard -"@opentelemetry/core@npm:1.28.0": - version: 1.28.0 - resolution: "@opentelemetry/core@npm:1.28.0" +"@opentelemetry/core@npm:1.30.0": + version: 1.30.0 + resolution: "@opentelemetry/core@npm:1.30.0" dependencies: - "@opentelemetry/semantic-conventions": "npm:1.27.0" + "@opentelemetry/semantic-conventions": "npm:1.28.0" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10c0/4f87318ca59bc4c2f4302decfdbc3b3672604e4bbc7cb40d09f0ecbbe9e5a8b7db527a1c7ee17a93c3f9ca69c1dd88cc24cb07398b7828efc30ea9fcb8adbc4d + checksum: 10c0/52d17b5ddb06ab4241b977ff89b81f69f140edb5c2a78b2188d95fa7bdfdd1aa2dcafb1e2830ab77d557876682ab8f08727ba8f165ea3c39fbb6bf3b86ef33c8 languageName: node linkType: hard -"@opentelemetry/exporter-logs-otlp-grpc@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/exporter-logs-otlp-grpc@npm:0.55.0" +"@opentelemetry/exporter-logs-otlp-grpc@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-logs-otlp-grpc@npm:0.57.0" dependencies: "@grpc/grpc-js": "npm:^1.7.1" - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/otlp-grpc-exporter-base": "npm:0.55.0" - "@opentelemetry/otlp-transformer": "npm:0.55.0" - "@opentelemetry/sdk-logs": "npm:0.55.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/otlp-grpc-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" + "@opentelemetry/sdk-logs": "npm:0.57.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/9128ddf91a96fcec9bc916d027089eb09dff9061d7f8a85263182dcdfd139b412567871fe63ab9a84b6fb72eafd44d7fd05919b75f1d97baee4cb9505950f61f + checksum: 10c0/8ffd5e931afe5d370a0948e0523a812eadf65f577c26502890418eb2fd7ca28a1989fc8df01749c7f4a0bce6c760381c4e0ee17c23ddecfd38432101ce75ce13 languageName: node linkType: hard -"@opentelemetry/exporter-logs-otlp-http@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/exporter-logs-otlp-http@npm:0.55.0" +"@opentelemetry/exporter-logs-otlp-http@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-logs-otlp-http@npm:0.57.0" dependencies: - "@opentelemetry/api-logs": "npm:0.55.0" - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/otlp-exporter-base": "npm:0.55.0" - "@opentelemetry/otlp-transformer": "npm:0.55.0" - "@opentelemetry/sdk-logs": "npm:0.55.0" + "@opentelemetry/api-logs": "npm:0.57.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/otlp-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" + "@opentelemetry/sdk-logs": "npm:0.57.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/c451c59b4b502f8d0cb7a034fea16523b9a4f187e05dfc0eb5ea8be81679c3bb8084a2a2e530516db8cc8dbf1e08ae28a8c1fc5ab827a6b85f187b35c24b6c6d + checksum: 10c0/f2edde4efaa79043e4cc21dfe79e1d646325eaf86cc120becf94eea0d3df8cb3926d339ae6c46ae32878024a262b51f1172f5dbc6a4feebf0f2d3927d51b2b61 languageName: node linkType: hard -"@opentelemetry/exporter-logs-otlp-proto@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/exporter-logs-otlp-proto@npm:0.55.0" +"@opentelemetry/exporter-logs-otlp-proto@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-logs-otlp-proto@npm:0.57.0" dependencies: - "@opentelemetry/api-logs": "npm:0.55.0" - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/otlp-exporter-base": "npm:0.55.0" - "@opentelemetry/otlp-transformer": "npm:0.55.0" - "@opentelemetry/resources": "npm:1.28.0" - "@opentelemetry/sdk-logs": "npm:0.55.0" - "@opentelemetry/sdk-trace-base": "npm:1.28.0" + "@opentelemetry/api-logs": "npm:0.57.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/otlp-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-logs": "npm:0.57.0" + "@opentelemetry/sdk-trace-base": "npm:1.30.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/0f6ca147dbefba1b81a80d49b4f1bd7e9c46cac1201bb7e93265339756ae628118d8048ebc00521d8dd5a397eae052059e4181ee9f7a2867ea1780406ccf71d4 + checksum: 10c0/eae148057c9c79dc6317b926712cb8502ed813ff79a36be62ca53368876c913f9bf87449cde5eded25d4482e2e5427e3e63288fde82422f2fee019860f2f7adc languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-grpc@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.55.0" +"@opentelemetry/exporter-metrics-otlp-grpc@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-metrics-otlp-grpc@npm:0.57.0" dependencies: "@grpc/grpc-js": "npm:^1.7.1" - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/otlp-grpc-exporter-base": "npm:0.55.0" - "@opentelemetry/otlp-transformer": "npm:0.55.0" - "@opentelemetry/resources": "npm:1.28.0" - "@opentelemetry/sdk-trace-base": "npm:1.28.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/exporter-metrics-otlp-http": "npm:0.57.0" + "@opentelemetry/otlp-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-grpc-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-metrics": "npm:1.30.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/5b6f2821055179ba6b1d9a2fcf6e477890ec7cf9a778e6574ed2cec841df1e27bee5dee0059f91d25ea8200532c22accd063272f9f5f65d6613036480de1ab21 + checksum: 10c0/efd821580cb3dc3253cea4f1ec00ddefdff72270764eae4c9cfa15560b19e78bf06cf3994f2c871e2dde9416d38e36086e903f436bf62cca125319bd17bd224b languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-http@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.55.0" +"@opentelemetry/exporter-metrics-otlp-http@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-metrics-otlp-http@npm:0.57.0" dependencies: - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/otlp-exporter-base": "npm:0.55.0" - "@opentelemetry/otlp-transformer": "npm:0.55.0" - "@opentelemetry/resources": "npm:1.28.0" - "@opentelemetry/sdk-trace-base": "npm:1.28.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/otlp-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-metrics": "npm:1.30.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/3a64cdd70f5b3e0d21705114593b5e8528e6cda4fd8f68243dc02c059db823d2ce6e3c1d9d50a3028c9065a827caaf3aeb4e5eaecafd03875633e18c0a44b730 + checksum: 10c0/91d22cb1e60ae28d4f7121df1c29e31ac904d5d77d7d09818020f3a02dc1a3066d3e45a3b1705c03b32c360ac8b8724bce2205ac7d7e8c4c919fdd448c400de3 languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-proto@npm:0.55.0, @opentelemetry/exporter-trace-otlp-proto@npm:^0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/exporter-trace-otlp-proto@npm:0.55.0" +"@opentelemetry/exporter-metrics-otlp-proto@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-metrics-otlp-proto@npm:0.57.0" dependencies: - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/otlp-exporter-base": "npm:0.55.0" - "@opentelemetry/otlp-transformer": "npm:0.55.0" - "@opentelemetry/resources": "npm:1.28.0" - "@opentelemetry/sdk-trace-base": "npm:1.28.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/exporter-metrics-otlp-http": "npm:0.57.0" + "@opentelemetry/otlp-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-metrics": "npm:1.30.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/d66034bcab3782f056c35e5664fc915d55d776deb105e6eb8c9eab06036d19642608a43477818c5997cdcea245e65fcf9ee066827639a35a9a8d74e772cd9d6f + checksum: 10c0/c9f2b3eeea31ba55c9e642676db00faf47442f80c6cad7656cd14f3fb817052e47f9ca4415cc37a313d9b50bcf4e576520798a8535eb7b2f71a2d69e6fb5262e languageName: node linkType: hard -"@opentelemetry/exporter-zipkin@npm:1.28.0": - version: 1.28.0 - resolution: "@opentelemetry/exporter-zipkin@npm:1.28.0" +"@opentelemetry/exporter-prometheus@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-prometheus@npm:0.57.0" + dependencies: + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-metrics": "npm:1.30.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10c0/25c4d86993d77d0f829195734ac49629fd54fcde7926b12a4a0035760d96deca51d97f298f41a3c83be137d5461bd306de78e83f757c2bc770650a8314f64a7c + languageName: node + linkType: hard + +"@opentelemetry/exporter-trace-otlp-grpc@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.57.0" + dependencies: + "@grpc/grpc-js": "npm:^1.7.1" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/otlp-grpc-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-trace-base": "npm:1.30.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10c0/1529c9620bdcdebecb50d57edaac7c2741486c6cebeb16e8f293b1066396ceb370ceedb3e47bd4953876720e29dfd430bd1653bdd771448beaa54cbd7751ff3d + languageName: node + linkType: hard + +"@opentelemetry/exporter-trace-otlp-http@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.57.0" + dependencies: + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/otlp-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-trace-base": "npm:1.30.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10c0/426f21b8a82bbdfbbc7540d7cb88cfa46553d124d529e44a3612693a2abc6c42be33740f50ed5e1888783849e9f87d38c014a6ee1fe880d0dd80f40bf6058d35 + languageName: node + linkType: hard + +"@opentelemetry/exporter-trace-otlp-proto@npm:0.57.0, @opentelemetry/exporter-trace-otlp-proto@npm:^0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/exporter-trace-otlp-proto@npm:0.57.0" + dependencies: + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/otlp-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-trace-base": "npm:1.30.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10c0/679695d75f876becea1ea697e7eee89b8e5d42041b73ee60ec490bc072b5915ae33771e9b984a72a43b1f135a5abea33cc40a37575fd5753959c9858b80e6d82 + languageName: node + linkType: hard + +"@opentelemetry/exporter-zipkin@npm:1.30.0": + version: 1.30.0 + resolution: "@opentelemetry/exporter-zipkin@npm:1.30.0" dependencies: - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/resources": "npm:1.28.0" - "@opentelemetry/sdk-trace-base": "npm:1.28.0" - "@opentelemetry/semantic-conventions": "npm:1.27.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-trace-base": "npm:1.30.0" + "@opentelemetry/semantic-conventions": "npm:1.28.0" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 10c0/d4862dfa51a914e029c64a49694425358a907c5444eda8d7bece9e8071c0bba45fcc7f2b5a9fda31bc9bce0a4c6142e47939a859b99b53735148d3234f5396ae + checksum: 10c0/7c8e914c7f4a9c65f0ba9cbc8810ccd3b0f69e785270b04509f3f985f6f9a9abfd583970ce64fc6103fabb43c02917ac9c794ab666b7408f396da3cd741d3df7 languageName: node linkType: hard -"@opentelemetry/instrumentation@npm:0.55.0, @opentelemetry/instrumentation@npm:^0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/instrumentation@npm:0.55.0" +"@opentelemetry/instrumentation@npm:0.57.0, @opentelemetry/instrumentation@npm:^0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/instrumentation@npm:0.57.0" dependencies: - "@opentelemetry/api-logs": "npm:0.55.0" + "@opentelemetry/api-logs": "npm:0.57.0" "@types/shimmer": "npm:^1.2.0" import-in-the-middle: "npm:^1.8.1" require-in-the-middle: "npm:^7.1.1" @@ -1551,175 +1613,172 @@ __metadata: shimmer: "npm:^1.2.1" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/c1175c63ecd3940a14176f5db89c48bcb64e9a7a366db70d3560913ccf624f6629fc91eb4ff3b6d5208e36df416bb14b5d87dbce5975c23e174c0191f54d06ab + checksum: 10c0/8bceabc6c47376760d46275f0adba711efa4015f46c9d502dc9b2c6fea10bcf8dab196e38080ce41829685075bcfad71de713ef71ddbfd542a92bf46f1f78dcd languageName: node linkType: hard -"@opentelemetry/otlp-exporter-base@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/otlp-exporter-base@npm:0.55.0" +"@opentelemetry/otlp-exporter-base@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/otlp-exporter-base@npm:0.57.0" dependencies: - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/otlp-transformer": "npm:0.55.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/716a845b7e3f58e37a76adb3838f54da409352e7dd9c44fa5727dc952985ce7e4b9ee96bc52f6a762d2f38c6ca60d36a85afd82cabf55dcd2cc86cb252b4170b + checksum: 10c0/b61f5d51975b25bc8ca89942eba934fed00e8125b62955d9421cb90e20549f65386e5ac93ca88ffeacd5005be66a2f263fbbe9ae5288e8b250dd1e286983b2b3 languageName: node linkType: hard -"@opentelemetry/otlp-grpc-exporter-base@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/otlp-grpc-exporter-base@npm:0.55.0" +"@opentelemetry/otlp-grpc-exporter-base@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/otlp-grpc-exporter-base@npm:0.57.0" dependencies: "@grpc/grpc-js": "npm:^1.7.1" - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/otlp-exporter-base": "npm:0.55.0" - "@opentelemetry/otlp-transformer": "npm:0.55.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/otlp-exporter-base": "npm:0.57.0" + "@opentelemetry/otlp-transformer": "npm:0.57.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/cfcde887effe3d757caf1585fb9e6ea18ff87ff6431513e89ff43dcaed2b6497d2ab4ac8c316a123242d5315b30956e8c64ea049e7076607ee687ffe20d55ac6 + checksum: 10c0/a6f61647d26812034a07a1049b750c1ffe97006525f3e992c5dbbe25012ae5428e1152dea8f970ca054112e2b6f2c9f917f0a95e41aaf9a2b4b1407477176584 languageName: node linkType: hard -"@opentelemetry/otlp-transformer@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/otlp-transformer@npm:0.55.0" +"@opentelemetry/otlp-transformer@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/otlp-transformer@npm:0.57.0" dependencies: - "@opentelemetry/api-logs": "npm:0.55.0" - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/resources": "npm:1.28.0" - "@opentelemetry/sdk-logs": "npm:0.55.0" - "@opentelemetry/sdk-metrics": "npm:1.28.0" - "@opentelemetry/sdk-trace-base": "npm:1.28.0" + "@opentelemetry/api-logs": "npm:0.57.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-logs": "npm:0.57.0" + "@opentelemetry/sdk-metrics": "npm:1.30.0" + "@opentelemetry/sdk-trace-base": "npm:1.30.0" protobufjs: "npm:^7.3.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10c0/d0a21566f0b39f70475458d2532b3737b55585a91a699da04efab4869219d045e2e022d514ea0b26d0fdfe5c4fb432e8827a21f3128930c6a6936fc284908795 + checksum: 10c0/02e27b447f66774ea9fb98e643dde7ce5f1b1be1dc302690006af351cbfaf9e486f4e4ecc0521813c1d0334ac55ecdfdd499df06580a81033174802a75fa19e0 languageName: node linkType: hard -"@opentelemetry/propagator-b3@npm:1.28.0": - version: 1.28.0 - resolution: "@opentelemetry/propagator-b3@npm:1.28.0" +"@opentelemetry/propagator-b3@npm:1.30.0": + version: 1.30.0 + resolution: "@opentelemetry/propagator-b3@npm:1.30.0" dependencies: - "@opentelemetry/core": "npm:1.28.0" + "@opentelemetry/core": "npm:1.30.0" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10c0/dd0ca51d2b216d8443790a4ba9c922985a627aad00505a15b32e9db48cb2a7934173e1c9bbd85682dc28873f4c4f46803f717850e348e54f1fe01f7de26be8e1 + checksum: 10c0/2378d9527247982ad09c08f51b90364913640a72519df3b65fbd694a666f4e13ce035b3a42d3651f5d707e85b3f48b7837e4aa50fbbfe3fcb8f6af47e0af5c34 languageName: node linkType: hard -"@opentelemetry/propagator-jaeger@npm:1.28.0": - version: 1.28.0 - resolution: "@opentelemetry/propagator-jaeger@npm:1.28.0" +"@opentelemetry/propagator-jaeger@npm:1.30.0": + version: 1.30.0 + resolution: "@opentelemetry/propagator-jaeger@npm:1.30.0" dependencies: - "@opentelemetry/core": "npm:1.28.0" + "@opentelemetry/core": "npm:1.30.0" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10c0/e6e3d26593041abf86fff9e667720b4cc15fa823359d9b8ae9d9f8e127c43a3298229090321628556fad25e83b39bb799b3bd10e05ed12b4e0119134fd6d90e4 + checksum: 10c0/a2cd68d3ca08ba84b62427d363f7054a8d51922805376987d67bbf7d61513cde9665a4f5df262f46ed2affae0557d3bc13b0ec3aa68f84088f092f007849f781 languageName: node linkType: hard -"@opentelemetry/resources@npm:1.28.0": - version: 1.28.0 - resolution: "@opentelemetry/resources@npm:1.28.0" +"@opentelemetry/resources@npm:1.30.0": + version: 1.30.0 + resolution: "@opentelemetry/resources@npm:1.30.0" dependencies: - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/semantic-conventions": "npm:1.27.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/semantic-conventions": "npm:1.28.0" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10c0/84fae85d3e26d338b8e31c765a42da637d4e626e564b8d86108f87963a5a07f58793eab8041a602e74b8144bbdea851825b22e3fd4222977bf59bcd42ae1bab4 + checksum: 10c0/2b298193de85f8d7d05f9d71e5ea63189668f99248486246a4cfdc8667a5face205d650ef1ee6204a9f9c16d0b0e7704bb89a5d47537279c8e3378231ed35d1d languageName: node linkType: hard -"@opentelemetry/sdk-logs@npm:0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/sdk-logs@npm:0.55.0" +"@opentelemetry/sdk-logs@npm:0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/sdk-logs@npm:0.57.0" dependencies: - "@opentelemetry/api-logs": "npm:0.55.0" - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/resources": "npm:1.28.0" + "@opentelemetry/api-logs": "npm:0.57.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/resources": "npm:1.30.0" peerDependencies: "@opentelemetry/api": ">=1.4.0 <1.10.0" - checksum: 10c0/aa272b3f939911550def2d8f225e6e020b864b8fb4474a468f9324ed63ca0adb5afca0ece32264eaff7bd5cb891cd9dae1030671ae3d9d9313d29801d2b6efa7 + checksum: 10c0/8e7004492693d89a8cb50c885b2993218827a099b13f83f7c03cc49bb9766ff3040b576126b14c8053b6327464946f87d302184e7eef361783c62426d2420ce2 languageName: node linkType: hard -"@opentelemetry/sdk-metrics@npm:1.28.0": - version: 1.28.0 - resolution: "@opentelemetry/sdk-metrics@npm:1.28.0" +"@opentelemetry/sdk-metrics@npm:1.30.0": + version: 1.30.0 + resolution: "@opentelemetry/sdk-metrics@npm:1.30.0" dependencies: - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/resources": "npm:1.28.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/resources": "npm:1.30.0" peerDependencies: "@opentelemetry/api": ">=1.3.0 <1.10.0" - checksum: 10c0/deafed540ea5ece764729216eb4019bb5fa3d2759c25c3a0217228bb07324590047520d3f3936a21d1fdfcc10c266f98bdcc22e8620db8d2e443eaa4d5588c11 - languageName: node - linkType: hard - -"@opentelemetry/sdk-node@npm:^0.55.0": - version: 0.55.0 - resolution: "@opentelemetry/sdk-node@npm:0.55.0" - dependencies: - "@opentelemetry/api-logs": "npm:0.55.0" - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/exporter-logs-otlp-grpc": "npm:0.55.0" - "@opentelemetry/exporter-logs-otlp-http": "npm:0.55.0" - "@opentelemetry/exporter-logs-otlp-proto": "npm:0.55.0" - "@opentelemetry/exporter-trace-otlp-grpc": "npm:0.55.0" - "@opentelemetry/exporter-trace-otlp-http": "npm:0.55.0" - "@opentelemetry/exporter-trace-otlp-proto": "npm:0.55.0" - "@opentelemetry/exporter-zipkin": "npm:1.28.0" - "@opentelemetry/instrumentation": "npm:0.55.0" - "@opentelemetry/resources": "npm:1.28.0" - "@opentelemetry/sdk-logs": "npm:0.55.0" - "@opentelemetry/sdk-metrics": "npm:1.28.0" - "@opentelemetry/sdk-trace-base": "npm:1.28.0" - "@opentelemetry/sdk-trace-node": "npm:1.28.0" - "@opentelemetry/semantic-conventions": "npm:1.27.0" + checksum: 10c0/447a0d11d1b702533ffdfdf643903559ec2e1162d82009a83d6ff0ff36e990c23a5b3a1ab27e38720a56b818e83c61e1dd0257357decf896253407aa8124e1a7 + languageName: node + linkType: hard + +"@opentelemetry/sdk-node@npm:^0.57.0": + version: 0.57.0 + resolution: "@opentelemetry/sdk-node@npm:0.57.0" + dependencies: + "@opentelemetry/api-logs": "npm:0.57.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/exporter-logs-otlp-grpc": "npm:0.57.0" + "@opentelemetry/exporter-logs-otlp-http": "npm:0.57.0" + "@opentelemetry/exporter-logs-otlp-proto": "npm:0.57.0" + "@opentelemetry/exporter-metrics-otlp-grpc": "npm:0.57.0" + "@opentelemetry/exporter-metrics-otlp-http": "npm:0.57.0" + "@opentelemetry/exporter-metrics-otlp-proto": "npm:0.57.0" + "@opentelemetry/exporter-prometheus": "npm:0.57.0" + "@opentelemetry/exporter-trace-otlp-grpc": "npm:0.57.0" + "@opentelemetry/exporter-trace-otlp-http": "npm:0.57.0" + "@opentelemetry/exporter-trace-otlp-proto": "npm:0.57.0" + "@opentelemetry/exporter-zipkin": "npm:1.30.0" + "@opentelemetry/instrumentation": "npm:0.57.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/sdk-logs": "npm:0.57.0" + "@opentelemetry/sdk-metrics": "npm:1.30.0" + "@opentelemetry/sdk-trace-base": "npm:1.30.0" + "@opentelemetry/sdk-trace-node": "npm:1.30.0" + "@opentelemetry/semantic-conventions": "npm:1.28.0" peerDependencies: "@opentelemetry/api": ">=1.3.0 <1.10.0" - checksum: 10c0/84e25018e35516d3743844c50eeaa4b1aed68aa32da4bd676ebae86b2c50e8516527daba640d07d55c4c763b9f73be7f2a4c0ce53671005cd313e6cff28c303c + checksum: 10c0/d0211e88e3a1cea29c22a951b6329f6b1d00f3d86de91ddfdd6d1b5e07e5c02cf5a35c5f56a26c5a39f9bf359e8f2d734ce708b55c372c9d9c8b4265ca7063a2 languageName: node linkType: hard -"@opentelemetry/sdk-trace-base@npm:1.28.0": - version: 1.28.0 - resolution: "@opentelemetry/sdk-trace-base@npm:1.28.0" +"@opentelemetry/sdk-trace-base@npm:1.30.0, @opentelemetry/sdk-trace-base@npm:^1.30.0": + version: 1.30.0 + resolution: "@opentelemetry/sdk-trace-base@npm:1.30.0" dependencies: - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/resources": "npm:1.28.0" - "@opentelemetry/semantic-conventions": "npm:1.27.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/resources": "npm:1.30.0" + "@opentelemetry/semantic-conventions": "npm:1.28.0" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10c0/0a9ac13dc375ba320323f72dd853133e169473a6bb971d82c8e6403ffb4d25124c6ad2b9ad31e0a2320b7971b83590094030f9716a57d5a3a2ede8c11ae67c5d + checksum: 10c0/3d8dcb0ec4e70405593421ea4df8b9a5e7faceea16cb900f30747eaeaa1f96059d40312ff2171208bb627deab6a6f32024681128cfba45af2671c6cfba528af1 languageName: node linkType: hard -"@opentelemetry/sdk-trace-node@npm:1.28.0": - version: 1.28.0 - resolution: "@opentelemetry/sdk-trace-node@npm:1.28.0" +"@opentelemetry/sdk-trace-node@npm:1.30.0": + version: 1.30.0 + resolution: "@opentelemetry/sdk-trace-node@npm:1.30.0" dependencies: - "@opentelemetry/context-async-hooks": "npm:1.28.0" - "@opentelemetry/core": "npm:1.28.0" - "@opentelemetry/propagator-b3": "npm:1.28.0" - "@opentelemetry/propagator-jaeger": "npm:1.28.0" - "@opentelemetry/sdk-trace-base": "npm:1.28.0" + "@opentelemetry/context-async-hooks": "npm:1.30.0" + "@opentelemetry/core": "npm:1.30.0" + "@opentelemetry/propagator-b3": "npm:1.30.0" + "@opentelemetry/propagator-jaeger": "npm:1.30.0" + "@opentelemetry/sdk-trace-base": "npm:1.30.0" semver: "npm:^7.5.2" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10c0/ea05da3279cae12526440c2966e2988108ea8cc5102acfc263d2080f711c0cf7e9bb22949e32bb918785dda0521a6096af63480280c2dbbe0556316e641ae066 - languageName: node - linkType: hard - -"@opentelemetry/semantic-conventions@npm:1.27.0": - version: 1.27.0 - resolution: "@opentelemetry/semantic-conventions@npm:1.27.0" - checksum: 10c0/b859773ba06b7e53dd9c6b45a171bf3000e405733adbf462ae91004ed011bc80edb5beecb817fb344a085adfd06045ab5b729c9bd0f1479650ad377134fb798c + checksum: 10c0/284b314c8c5b6da6e7e2b6c6814d6ef7cdfeeb3bce211bc1c38dc1e4b092f811727040265a75b5f6b67c287429cbd23661210b253429370918cb111bef1b57ac languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:^1.28.0": +"@opentelemetry/semantic-conventions@npm:1.28.0, @opentelemetry/semantic-conventions@npm:^1.28.0": version: 1.28.0 resolution: "@opentelemetry/semantic-conventions@npm:1.28.0" checksum: 10c0/deb8a0f744198071e70fea27143cf7c9f7ecb7e4d7b619488c917834ea09b31543c1c2bcea4ec5f3cf68797f0ef3549609c14e859013d9376400ac1499c2b9cb @@ -2338,6 +2397,13 @@ __metadata: languageName: node linkType: hard +"@types/semver@npm:^7": + version: 7.5.8 + resolution: "@types/semver@npm:7.5.8" + checksum: 10c0/8663ff927234d1c5fcc04b33062cb2b9fcfbe0f5f351ed26c4d1e1581657deebd506b41ff7fdf89e787e3d33ce05854bc01686379b89e9c49b564c4cfa988efa + languageName: node + linkType: hard + "@types/shimmer@npm:^1.2.0": version: 1.2.0 resolution: "@types/shimmer@npm:1.2.0" @@ -3162,13 +3228,15 @@ __metadata: "@mikro-orm/migrations-mongodb": "npm:6.2.9" "@mikro-orm/mongodb": "npm:6.2.9" "@mikro-orm/reflection": "npm:6.2.9" - "@opentelemetry/exporter-trace-otlp-proto": "npm:^0.55.0" - "@opentelemetry/instrumentation": "npm:^0.55.0" - "@opentelemetry/sdk-node": "npm:^0.55.0" + "@opentelemetry/exporter-trace-otlp-proto": "npm:^0.57.0" + "@opentelemetry/instrumentation": "npm:^0.57.0" + "@opentelemetry/sdk-node": "npm:^0.57.0" + "@opentelemetry/sdk-trace-base": "npm:^1.30.0" "@opentelemetry/semantic-conventions": "npm:^1.28.0" "@release-it/conventional-changelog": "npm:^8.0.1" "@types/dotenv-safe": "npm:^8.1.6" "@types/node": "npm:^20.16.5" + "@types/semver": "npm:^7" "@typescript-eslint/eslint-plugin": "npm:^7.13.0" "@typescript-eslint/parser": "npm:^7.13.0" "@vitest/coverage-v8": "npm:^1.6.0" @@ -3194,6 +3262,7 @@ __metadata: prettier: "npm:^3.3.2" protobufjs: "npm:^7.4.0" release-it: "npm:^17.6.0" + semver: "npm:^7.6.3" ts-node: "npm:^10.9.2" tsup: "npm:^8.3.5" tsx: "npm:^4.11.0" @@ -9621,7 +9690,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2": +"semver@npm:^7.3.5, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.2, semver@npm:^7.6.3": version: 7.6.3 resolution: "semver@npm:7.6.3" bin: