diff --git a/packages/@eventual/aws-cdk/src/service-api.ts b/packages/@eventual/aws-cdk/src/service-api.ts index 66129c75d..166816d7d 100644 --- a/packages/@eventual/aws-cdk/src/service-api.ts +++ b/packages/@eventual/aws-cdk/src/service-api.ts @@ -4,7 +4,7 @@ import { HttpLambdaIntegration } from "@aws-cdk/aws-apigatewayv2-integrations-al import { ServiceType } from "@eventual/core"; import { HttpMethod } from "aws-cdk-lib/aws-events"; import { Effect, IGrantable, PolicyStatement } from "aws-cdk-lib/aws-iam"; -import { Code, Function } from "aws-cdk-lib/aws-lambda"; +import { Code, Function, Tracing } from "aws-cdk-lib/aws-lambda"; import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs"; import { Arn, Stack } from "aws-cdk-lib"; import { Construct } from "constructs"; @@ -149,6 +149,7 @@ export class Api extends Construct { "../../esm/handlers/api", entry ), + tracing: Tracing.ACTIVE, ...baseNodeFnProps, }); } @@ -157,6 +158,7 @@ export class Api extends Construct { return new Function(this, id, { code: Code.fromAsset(outDir(this, entry)), ...baseNodeFnProps, + tracing: Tracing.ACTIVE, handler: "index.handler", }); } diff --git a/packages/@eventual/aws-cdk/src/service-function.ts b/packages/@eventual/aws-cdk/src/service-function.ts index e0ba86516..fcfa251dc 100644 --- a/packages/@eventual/aws-cdk/src/service-function.ts +++ b/packages/@eventual/aws-cdk/src/service-function.ts @@ -1,4 +1,5 @@ import { ServiceType, SERVICE_TYPE_FLAG } from "@eventual/core"; +import { Duration } from "aws-cdk-lib"; import { Architecture, Code, @@ -22,6 +23,7 @@ export class ServiceFunction extends Function { runtime: Runtime.NODEJS_16_X, architecture: Architecture.ARM_64, memorySize: 512, + timeout: Duration.seconds(10), ...props, code: Code.fromAsset(outDir(scope, props.serviceType)), handler: props.handler ?? "index.default", diff --git a/packages/@eventual/aws-cdk/src/service.ts b/packages/@eventual/aws-cdk/src/service.ts index 884a49209..a8b5c5f70 100644 --- a/packages/@eventual/aws-cdk/src/service.ts +++ b/packages/@eventual/aws-cdk/src/service.ts @@ -21,6 +21,7 @@ import { Api } from "./service-api"; import { outDir } from "./utils"; import { IWorkflows, Workflows } from "./workflows"; import { Events } from "./events"; +import { Telemetry } from "./telemetry"; export interface ServiceProps { entry: string; @@ -28,6 +29,7 @@ export interface ServiceProps { environment?: { [key: string]: string; }; + telemetryCollectorConfigPath?: string; } export class Service extends Construct implements IGrantable { @@ -72,6 +74,8 @@ export class Service extends Construct implements IGrantable { readonly grantPrincipal: IPrincipal; + readonly telemetry: Telemetry; + constructor(scope: Construct, id: string, props: ServiceProps) { super(scope, id); @@ -140,6 +144,16 @@ export class Service extends Construct implements IGrantable { events: this.events, }); + this.telemetry = new Telemetry(this, "Telemetry", { + serviceName: this.serviceName, + collectorConfigPath: props.telemetryCollectorConfigPath, + }); + this.telemetry.configureFunction(this.activities.worker, "worker"); + this.telemetry.configureFunction( + this.workflows.orchestrator, + "orchestrator" + ); + this.grantPrincipal = new CompositePrincipal( // when granting permissions to the service, // propagate them to the following principals diff --git a/packages/@eventual/aws-cdk/src/telemetry.ts b/packages/@eventual/aws-cdk/src/telemetry.ts new file mode 100644 index 000000000..ef0356a07 --- /dev/null +++ b/packages/@eventual/aws-cdk/src/telemetry.ts @@ -0,0 +1,116 @@ +import { ENV_NAMES } from "@eventual/aws-runtime"; +import { DockerImage, Duration } from "aws-cdk-lib"; +import { + Architecture, + Code, + FunctionUrl, + IFunction, + Runtime, + Function, + FunctionUrlAuthType, + LayerVersion, + Tracing, +} from "aws-cdk-lib/aws-lambda"; +import { Construct } from "constructs"; +import path from "path"; +import fs from "fs"; +import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam"; + +export interface TelemetryProps { + serviceName: string; + collectorConfigPath?: string; +} + +export interface ITelemetry { + collectorFn: IFunction; + collectorFnUrl: FunctionUrl; +} + +export class Telemetry extends Construct { + collectorFn: IFunction; + collectorFnUrl: FunctionUrl; + collectorConfigPath?: string; + + constructor(scope: Construct, id: string, props: TelemetryProps) { + super(scope, id); + + let collectorConfigPath = + props?.collectorConfigPath ?? + require.resolve( + "@eventual/aws-runtime/otlp-proxy-lambda/otel-config.yaml" + ); + + this.collectorFn = new Function(this, "collector", { + runtime: Runtime.PROVIDED_AL2, + architecture: Architecture.ARM_64, + memorySize: 512, + timeout: Duration.seconds(10), + handler: "bootstrap", + code: Code.fromAsset( + require.resolve("@eventual/aws-runtime/otlp-proxy-lambda/bootstrap.zip") + ), + layers: [ + new LayerVersion(this, "otel-collector-extension", { + code: Code.fromAsset( + require.resolve( + "@eventual/aws-runtime/otlp-proxy-lambda/collector-extension.zip" + ) + ), + }), + new LayerVersion(this, "otel-config-file", { + code: Code.fromAsset(path.dirname(collectorConfigPath), { + bundling: { + image: new DockerImage(""), + local: { + tryBundle(outputDir, _options) { + const config = fs + .readFileSync(collectorConfigPath, "utf-8") + .replace("{SERVICE_NAME}", props.serviceName); + fs.writeFileSync( + path.join(outputDir, path.basename(collectorConfigPath)), + config + ); + return true; + }, + }, + }, + }), + }), + ], + environment: { + OPENTELEMETRY_COLLECTOR_CONFIG_FILE: `/opt/${path.basename( + collectorConfigPath + )}`, + }, + initialPolicy: [ + new PolicyStatement({ + actions: [ + "xray:PutTraceSegments", + "xray:PutTelemetryRecords", + "xray:GetSamplingRules", + "xray:GetSamplingTargets", + "xray:GetSamplingStatisticSummaries", + "cloudwatch:PutMetricData", + "cloudwatch:PutMetricStream", + "cloudwatch:StartMetricStreams", + ], + resources: ["*"], + effect: Effect.ALLOW, + }), + ], + tracing: Tracing.ACTIVE, + }); + this.collectorFn; + this.collectorFnUrl = this.collectorFn.addFunctionUrl({ + authType: FunctionUrlAuthType.NONE, + }); + } + + configureFunction(fn: Function, componentName: string) { + fn.addEnvironment( + ENV_NAMES.OTEL_EXPORTER_OTLP_ENDPOINT, + this.collectorFnUrl.url + ); + fn.addEnvironment(ENV_NAMES.TELEMETRY_COMPONENT_NAME, componentName); + } +} diff --git a/packages/@eventual/aws-runtime/otlp-proxy-lambda/bootstrap.zip b/packages/@eventual/aws-runtime/otlp-proxy-lambda/bootstrap.zip new file mode 100644 index 000000000..279e74fe4 Binary files /dev/null and b/packages/@eventual/aws-runtime/otlp-proxy-lambda/bootstrap.zip differ diff --git a/packages/@eventual/aws-runtime/otlp-proxy-lambda/collector-extension.zip b/packages/@eventual/aws-runtime/otlp-proxy-lambda/collector-extension.zip new file mode 100644 index 000000000..277134de3 Binary files /dev/null and b/packages/@eventual/aws-runtime/otlp-proxy-lambda/collector-extension.zip differ diff --git a/packages/@eventual/aws-runtime/otlp-proxy-lambda/otel-config.yaml b/packages/@eventual/aws-runtime/otlp-proxy-lambda/otel-config.yaml new file mode 100644 index 000000000..ddd5fec9e --- /dev/null +++ b/packages/@eventual/aws-runtime/otlp-proxy-lambda/otel-config.yaml @@ -0,0 +1,29 @@ +receivers: + otlp: + protocols: + grpc: + +processors: + batch: + timeout: 25ms + +exporters: + logging: + loglevel: debug + awsxray: + index_all_attributes: true + awsemf: + log_group_name: /metrics/eventual + log_stream_name: "{SERVICE_NAME}" + +#enables output for traces to xray +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [logging, awsxray] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [logging, awsemf] diff --git a/packages/@eventual/aws-runtime/package.json b/packages/@eventual/aws-runtime/package.json index 2e2374fd5..ee2e3c48c 100644 --- a/packages/@eventual/aws-runtime/package.json +++ b/packages/@eventual/aws-runtime/package.json @@ -4,7 +4,8 @@ ".": { "import": "./lib/esm/index.js", "require": "./lib/cjs/index.js" - } + }, + "./otlp-proxy-lambda/*": "./otlp-proxy-lambda/*" }, "main": "./lib/cjs/index.js", "module": "./lib/esm/index.js", @@ -14,9 +15,22 @@ }, "dependencies": { "@aws-lambda-powertools/logger": "^1.4.1", + "@aws-sdk/client-cloudwatch-logs": "^3.226.0", "@eventual/core": "workspace:^", "@middy/core": "^3.6.2", "@middy/error-logger": "^3.6.2", + "@opentelemetry/api": "^1.3.0", + "@opentelemetry/context-async-hooks": "^1.8.0", + "@opentelemetry/core": "^1.8.0", + "@opentelemetry/exporter-metrics-otlp-proto": "^0.34.0", + "@opentelemetry/exporter-trace-otlp-proto": "^0.34.0", + "@opentelemetry/id-generator-aws-xray": "^1.1.1", + "@opentelemetry/propagator-aws-xray": "^1.1.1", + "@opentelemetry/propagator-b3": "^1.8.0", + "@opentelemetry/resources": "^1.8.0", + "@opentelemetry/sdk-metrics": "^1.8.0", + "@opentelemetry/sdk-trace-base": "^1.8.0", + "@opentelemetry/semantic-conventions": "^1.8.0", "aws-embedded-metrics": "^4.0.0", "aws-lambda": "^1.0.7", "node-fetch": "^2.6.7", diff --git a/packages/@eventual/aws-runtime/src/clients/workflow-client.ts b/packages/@eventual/aws-runtime/src/clients/workflow-client.ts index 2fb4b598f..5d7c24c84 100644 --- a/packages/@eventual/aws-runtime/src/clients/workflow-client.ts +++ b/packages/@eventual/aws-runtime/src/clients/workflow-client.ts @@ -21,6 +21,7 @@ import { } from "@eventual/core"; import { ulid } from "ulidx"; import { AWSActivityRuntimeClient } from "./activity-runtime-client.js"; +import { SpanKind, trace } from "@opentelemetry/api"; export interface AWSWorkflowClientProps { readonly dynamo: DynamoDBClient; @@ -50,46 +51,57 @@ export class AWSWorkflowClient extends WorkflowClient { timeoutSeconds, }: StartWorkflowRequest) { const executionId = formatExecutionId(workflowName, executionName); - console.log("execution input:", input); - - await this.props.dynamo.send( - new PutItemCommand({ - TableName: this.props.tableName, - Item: { - pk: { S: ExecutionRecord.PRIMARY_KEY }, - sk: { S: ExecutionRecord.sortKey(executionId) }, - id: { S: executionId }, - name: { S: executionName }, - workflowName: { S: workflowName }, - status: { S: ExecutionStatus.IN_PROGRESS }, - startTime: { S: new Date().toISOString() }, - ...(parentExecutionId - ? { - parentExecutionId: { S: parentExecutionId }, - seq: { N: seq!.toString(10) }, - } - : {}), - }, - }) - ); - - const workflowStartedEvent = createEvent({ - type: WorkflowEventType.WorkflowStarted, - input, - workflowName, - // generate the time for the workflow to timeout based on when it was started. - // the timer will be started by the orchestrator so the client does not need to have access to the timer client. - timeoutTime: timeoutSeconds - ? new Date(new Date().getTime() + timeoutSeconds * 1000).toISOString() - : undefined, - context: { - name: executionName, - parentId: parentExecutionId, + const tracer = trace.getTracer(executionId, "0.0.0"); + await tracer.startActiveSpan( + "startWorkflow", + { + attributes: { workflowName, input }, + kind: SpanKind.PRODUCER, }, - }); - - await this.submitWorkflowTask(executionId, workflowStartedEvent); - + async () => { + console.log("execution input:", input); + + await this.props.dynamo.send( + new PutItemCommand({ + TableName: this.props.tableName, + Item: { + pk: { S: ExecutionRecord.PRIMARY_KEY }, + sk: { S: ExecutionRecord.sortKey(executionId) }, + id: { S: executionId }, + name: { S: executionName }, + workflowName: { S: workflowName }, + status: { S: ExecutionStatus.IN_PROGRESS }, + startTime: { S: new Date().toISOString() }, + ...(parentExecutionId + ? { + parentExecutionId: { S: parentExecutionId }, + seq: { N: seq!.toString(10) }, + } + : {}), + }, + }) + ); + + const workflowStartedEvent = createEvent({ + type: WorkflowEventType.WorkflowStarted, + input, + workflowName, + // generate the time for the workflow to timeout based on when it was started. + // the timer will be started by the orchestrator so the client does not need to have access to the timer client. + timeoutTime: timeoutSeconds + ? new Date( + new Date().getTime() + timeoutSeconds * 1000 + ).toISOString() + : undefined, + context: { + name: executionName, + parentId: parentExecutionId, + }, + }); + + await this.submitWorkflowTask(executionId, workflowStartedEvent); + } + ); return executionId; } @@ -97,21 +109,24 @@ export class AWSWorkflowClient extends WorkflowClient { executionId: string, ...events: HistoryStateEvent[] ) { - // send workflow task to workflow queue - const workflowTask: SQSWorkflowTaskMessage = { - task: { - executionId, - events, - }, - }; - - await this.props.sqs.send( - new SendMessageCommand({ - MessageBody: JSON.stringify(workflowTask), - QueueUrl: this.props.workflowQueueUrl, - MessageGroupId: executionId, - }) - ); + const tracer = trace.getTracer(executionId, "0.0.0"); + await tracer.startActiveSpan("submitWorkflowTask", async () => { + // send workflow task to workflow queue + const workflowTask: SQSWorkflowTaskMessage = { + task: { + executionId, + events, + }, + }; + + await this.props.sqs.send( + new SendMessageCommand({ + MessageBody: JSON.stringify(workflowTask), + QueueUrl: this.props.workflowQueueUrl, + MessageGroupId: executionId, + }) + ); + }); } async getExecutions(): Promise { diff --git a/packages/@eventual/aws-runtime/src/env.ts b/packages/@eventual/aws-runtime/src/env.ts index 857b9d47c..fef0a9c5e 100644 --- a/packages/@eventual/aws-runtime/src/env.ts +++ b/packages/@eventual/aws-runtime/src/env.ts @@ -17,6 +17,9 @@ export namespace ENV_NAMES { export const TIMER_QUEUE_URL = "EVENTUAL_TIMER_QUEUE_URL"; export const TIMER_QUEUE_ARN = "EVENTUAL_TIMER_QUEUE_ARN"; export const SCHEDULE_FORWARDER_ARN = "EVENTUAL_SCHEDULE_FORWARDER_ARN"; + export const TELEMETRY_COLLECTOR_URL = "EVENTUAL_TELEMETRY_COLLECTOR_URL"; + export const OTEL_EXPORTER_OTLP_ENDPOINT = "OTEL_EXPORTER_OTLP_ENDPOINT"; + export const TELEMETRY_COMPONENT_NAME = "EVENTUAL_TELEMETRY_COMPONENT_NAME"; } export function tryGetEnv(name: string) { @@ -46,3 +49,5 @@ export const timerQueueArn = () => tryGetEnv(ENV_NAMES.TIMER_QUEUE_ARN); export const timerQueueUrl = () => tryGetEnv(ENV_NAMES.TIMER_QUEUE_URL); export const schedulerForwarderArn = () => tryGetEnv(ENV_NAMES.SCHEDULE_FORWARDER_ARN); +export const telemetryComponentName = () => + tryGetEnv(ENV_NAMES.TELEMETRY_COMPONENT_NAME); diff --git a/packages/@eventual/aws-runtime/src/handlers/fetch-polyfill.ts b/packages/@eventual/aws-runtime/src/fetch-polyfill.ts similarity index 100% rename from packages/@eventual/aws-runtime/src/handlers/fetch-polyfill.ts rename to packages/@eventual/aws-runtime/src/fetch-polyfill.ts diff --git a/packages/@eventual/aws-runtime/src/handlers/activity-worker.ts b/packages/@eventual/aws-runtime/src/handlers/activity-worker.ts index f4136679e..729b04ece 100644 --- a/packages/@eventual/aws-runtime/src/handlers/activity-worker.ts +++ b/packages/@eventual/aws-runtime/src/handlers/activity-worker.ts @@ -11,6 +11,12 @@ import { } from "../clients/create.js"; import { AWSMetricsClient } from "../clients/metrics-client.js"; import { logger, loggerMiddlewares } from "../logger.js"; +import { registerTelemetryApi } from "../telemetry.js"; +import { trace } from "@opentelemetry/api"; +import { serviceName } from "../env.js"; + +registerTelemetryApi(); +const tracer = trace.getTracer(serviceName()); export default middy( createActivityWorker({ @@ -20,5 +26,6 @@ export default middy( timerClient: createTimerClient(), metricsClient: AWSMetricsClient, logger, + tracer, }) ).use(loggerMiddlewares); diff --git a/packages/@eventual/aws-runtime/src/handlers/api-handler.ts b/packages/@eventual/aws-runtime/src/handlers/api-handler.ts index cc520d247..357716fc6 100644 --- a/packages/@eventual/aws-runtime/src/handlers/api-handler.ts +++ b/packages/@eventual/aws-runtime/src/handlers/api-handler.ts @@ -6,7 +6,7 @@ import itty from "itty-router"; import { createEventClient, createWorkflowClient } from "../clients/create.js"; // TODO: remove once we can upgrade to Node 18 in AWS Lambda -import "./fetch-polyfill.js"; +import "../fetch-polyfill.js"; const processRequest = createApiHandler({ workflowClient: createWorkflowClient(), diff --git a/packages/@eventual/aws-runtime/src/handlers/orchestrator.ts b/packages/@eventual/aws-runtime/src/handlers/orchestrator.ts index 13f3ee519..1321586d1 100644 --- a/packages/@eventual/aws-runtime/src/handlers/orchestrator.ts +++ b/packages/@eventual/aws-runtime/src/handlers/orchestrator.ts @@ -2,9 +2,8 @@ import "@eventual/entry/injected"; import { createOrchestrator } from "@eventual/core"; import middy from "@middy/core"; +import { SpanKind, trace } from "@opentelemetry/api"; import type { SQSEvent, SQSRecord } from "aws-lambda"; -import { logger, loggerMiddlewares } from "../logger.js"; -import { AWSMetricsClient } from "../clients/metrics-client.js"; import { createEventClient, createExecutionHistoryClient, @@ -13,11 +12,16 @@ import { createWorkflowRuntimeClient, SQSWorkflowTaskMessage, } from "../clients/index.js"; +import { AWSMetricsClient } from "../clients/metrics-client.js"; +import { logger, loggerMiddlewares } from "../logger.js"; +import { registerTelemetryApi } from "../telemetry.js"; /** * Creates an entrypoint function for orchestrating a workflow * from within an AWS Lambda Function attached to a SQS FIFO queue. */ +const telemetry = registerTelemetryApi(); + const orchestrate = createOrchestrator({ executionHistoryClient: createExecutionHistoryClient(), timerClient: createTimerClient(), @@ -29,6 +33,10 @@ const orchestrate = createOrchestrator({ }); export default middy(async (event: SQSEvent) => { + const tracer = trace.getTracer("orchestrator"); + const orchestratorSpan = tracer.startSpan("orchestrator", { + kind: SpanKind.PRODUCER, + }); if (event.Records.some((r) => !r.attributes.MessageGroupId)) { throw new Error("Expected SQS Records to contain fifo message id"); } @@ -53,12 +61,16 @@ export default middy(async (event: SQSEvent) => { recordsByExecutionId[executionId]?.map((record) => record.messageId) ?? [] ); + orchestratorSpan.end(); + return { batchItemFailures: failedMessageIds.map((r) => ({ itemIdentifier: r, })), }; -}).use(loggerMiddlewares); +}) + .use(loggerMiddlewares) + .after(telemetry.flush); function sqsRecordsToEvents(sqsRecords: SQSRecord[]) { return sqsRecords.flatMap(sqsRecordToEvents); diff --git a/packages/@eventual/aws-runtime/src/telemetry.ts b/packages/@eventual/aws-runtime/src/telemetry.ts new file mode 100644 index 000000000..ccea4a784 --- /dev/null +++ b/packages/@eventual/aws-runtime/src/telemetry.ts @@ -0,0 +1,105 @@ +import { + CompositePropagator, + W3CTraceContextPropagator, +} from "@opentelemetry/core"; +import { + BasicTracerProvider, + BatchSpanProcessor, +} from "@opentelemetry/sdk-trace-base"; +import { B3InjectEncoding, B3Propagator } from "@opentelemetry/propagator-b3"; +import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions"; +import { AsyncHooksContextManager } from "@opentelemetry/context-async-hooks"; +import { Resource } from "@opentelemetry/resources"; +import { + trace, + metrics, + diag, + DiagConsoleLogger, + TracerProvider, +} from "@opentelemetry/api"; +import { serviceName, telemetryComponentName } from "./env.js"; +import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto"; +import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-proto"; +import { + MeterProvider, + PeriodicExportingMetricReader, +} from "@opentelemetry/sdk-metrics"; +import { AWSXRayIdGenerator } from "@opentelemetry/id-generator-aws-xray"; +import { AWSXRayPropagator } from "@opentelemetry/propagator-aws-xray"; + +/** + * Register the openetelemetry provider with the api. + * The trace exporter exports over otlp/grpc to an extension running as a layer + * This function will fail if run more than once, and we won't try to save it + * Ensure that its run during the init phase of the lambda (ie global scope) + */ + +export interface Telemetry { + tracerProvider: TracerProvider; + meterProvider: MeterProvider; + flush: () => Promise; +} + +//Set up tracing and metrics provider, and supply a hook to flush, ensuring pending requests are sent +export function registerTelemetryApi(): Telemetry { + const tracerProvider = registerTracerProvider(); + const meterProvider = registerMeterProvider(); + diag.setLogger(new DiagConsoleLogger()); + return { + tracerProvider, + meterProvider, + flush: async () => { + await tracerProvider.forceFlush(); + await meterProvider.forceFlush(); + }, + }; +} + +function registerTracerProvider() { + const provider = new BasicTracerProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAMESPACE]: serviceName(), + [SemanticResourceAttributes.SERVICE_NAME]: telemetryComponentName(), + }), + //Important for traces to show up on xray + idGenerator: new AWSXRayIdGenerator(), + }); + provider.addSpanProcessor( + new BatchSpanProcessor(new OTLPTraceExporter(), { + scheduledDelayMillis: 25, + }) + ); + const contextManager = new AsyncHooksContextManager(); + provider.register({ + contextManager, + propagator: new CompositePropagator({ + propagators: [ + new AWSXRayPropagator(), + new W3CTraceContextPropagator(), + new B3Propagator(), + new B3Propagator({ injectEncoding: B3InjectEncoding.MULTI_HEADER }), + ], + }), + }); + contextManager.enable(); + trace.setGlobalTracerProvider(provider); + return provider; +} + +function registerMeterProvider() { + const metricExporter = new OTLPMetricExporter(); + const meterProvider = new MeterProvider({ + resource: new Resource({ + [SemanticResourceAttributes.SERVICE_NAMESPACE]: serviceName(), + [SemanticResourceAttributes.SERVICE_NAME]: telemetryComponentName(), + }), + }); + meterProvider.addMetricReader( + new PeriodicExportingMetricReader({ + exporter: metricExporter, + exportIntervalMillis: 250, + }) + ); + metrics.setGlobalMeterProvider(meterProvider); + return meterProvider; +} diff --git a/packages/@eventual/aws-runtime/tsconfig.json b/packages/@eventual/aws-runtime/tsconfig.json index 6c919ca6b..24c47d3e5 100644 --- a/packages/@eventual/aws-runtime/tsconfig.json +++ b/packages/@eventual/aws-runtime/tsconfig.json @@ -12,7 +12,10 @@ "@eventual/injected/*": ["./src/injected/*"] } }, - "include": ["src", "src/package.json"], + "include": [ + "src", + "src/package.json" + ], "exclude": ["lib", "node_modules"], "references": [{ "path": "../core" }] } diff --git a/packages/@eventual/cli/src/commands/start.ts b/packages/@eventual/cli/src/commands/start.ts index 21fa2df14..522897617 100644 --- a/packages/@eventual/cli/src/commands/start.ts +++ b/packages/@eventual/cli/src/commands/start.ts @@ -87,7 +87,7 @@ export const start = (yargs: Argv) => spinner.fail("Workflow failed"); styledConsole.error((failedEvent as WorkflowFailed).message); } else { - setTimeout(pollEvents, 1000); + setTimeout(pollEvents, 200); } } await pollEvents(); @@ -108,12 +108,6 @@ async function getNewEvents( const updatedEvents = await ky( `executions/${encodeExecutionId(executionId)}/history` ).json(); - if (updatedEvents.length == 0) { - //Unfortunately if the execution id is wrong, our dynamo query is just going to return an empty record set - //Not super helpful - //So we use this heuristic to give up, since we should at least have a start event. - throw new Error("No events at all. Check your execution id"); - } // The sort is important to ensure we don't chop off new events, // as we cannot rely on the event log to be sorted. // ie a later event may be be output into the history before events we have previously seen. diff --git a/packages/@eventual/compiler/src/eventual-bundle.ts b/packages/@eventual/compiler/src/eventual-bundle.ts index b0bd834ee..7f1001cea 100755 --- a/packages/@eventual/compiler/src/eventual-bundle.ts +++ b/packages/@eventual/compiler/src/eventual-bundle.ts @@ -6,6 +6,8 @@ import { eventualESPlugin } from "./esbuild-plugin.js"; import { prepareOutDir } from "./build.js"; import { createRequire } from "module"; import { ServiceType } from "@eventual/core"; +import child_process from "child_process"; +import util from "node:util"; // @ts-ignore - ts is complaining about not having module:esnext even thought it is in tsconfig.json const require = createRequire(import.meta.url); @@ -78,6 +80,8 @@ export function runtimeEntrypoint(name: string) { ); } +const nonBundledDeps = { "@opentelemetry/exporter-trace-otlp-grpc": "^0.34.0" }; + async function build({ outDir, injectedEntry, @@ -93,7 +97,7 @@ async function build({ plugins?: esbuild.Plugin[]; sourcemap?: boolean | "inline"; }) { - const outfile = path.join(outDir, `${name}/index.mjs`); + const outfile = path.join(outDir, name, "index.mjs"); const bundle = await esbuild.build({ mainFields: ["module", "main"], sourcemap: sourcemap ?? true, @@ -121,25 +125,27 @@ async function build({ bundle: true, entryPoints: [path.resolve(entry)], banner: esmPolyfillRequireBanner(), + external: Object.keys(nonBundledDeps), outfile, }); - await writeEsBuildMetafile( - bundle, - path.resolve(outDir!, `${name}/meta.json`) - ); + await writeEsBuildMetafile(bundle, path.resolve(outDir, name, "meta.json")); + + await installNonbundledDeps(path.resolve(outDir, name)); return outfile; } /** * Allows ESM module bundles to support dynamo requires when necessary. + * __dirname polyfill is necessary for @opentelemetry/otlp-grpc-exporter-base */ function esmPolyfillRequireBanner() { return { js: [ `import { createRequire as topLevelCreateRequire } from 'module'`, `const require = topLevelCreateRequire(import.meta.url)`, + // `const __dirname = url.fileURLToPath(new URL('.', import.meta.url));`, ].join("\n"), }; } @@ -150,3 +156,15 @@ function writeEsBuildMetafile( ) { return fs.writeFile(path, JSON.stringify(esbuildResult.metafile)); } + +async function installNonbundledDeps(outPath: string) { + await fs.writeFile( + path.resolve(outPath, "package.json"), + JSON.stringify({ + dependencies: nonBundledDeps, + }) + ); + const exec = util.promisify(child_process.exec); + const res = await exec("npm install", { cwd: outPath }); + console.log(res.stdout); +} diff --git a/packages/@eventual/core/package.json b/packages/@eventual/core/package.json index 1ecb60078..9c556ba78 100644 --- a/packages/@eventual/core/package.json +++ b/packages/@eventual/core/package.json @@ -16,6 +16,7 @@ "test": "jest" }, "dependencies": { + "@opentelemetry/api": "^1.3.0", "ulidx": "^0.3.0" }, "peerDependencies": { @@ -26,10 +27,10 @@ "@types/node": "^16", "itty-router": "2.6.6", "jest": "^29", - "ulidx": "^0.3.0", "ts-jest": "^29", "ts-node": "^10.9.1", "typescript": "^4.9.3", + "ulidx": "^0.3.0", "zx": "^7.1.1" }, "jest": { diff --git a/packages/@eventual/core/src/runtime/handlers/activity-worker.ts b/packages/@eventual/core/src/runtime/handlers/activity-worker.ts index c43c60022..d8106b455 100644 --- a/packages/@eventual/core/src/runtime/handlers/activity-worker.ts +++ b/packages/@eventual/core/src/runtime/handlers/activity-worker.ts @@ -26,6 +26,7 @@ import { ActivityMetrics, MetricsCommon } from "../metrics/constants.js"; import { Unit } from "../metrics/unit.js"; import { timed } from "../metrics/utils.js"; import type { EventClient } from "../index.js"; +import { SpanStatusCode, Tracer } from "@opentelemetry/api"; export interface CreateActivityWorkerProps { activityRuntimeClient: ActivityRuntimeClient; @@ -34,6 +35,7 @@ export interface CreateActivityWorkerProps { metricsClient: MetricsClient; logger: Logger; eventClient: EventClient; + tracer: Tracer; } export interface ActivityWorkerRequest { @@ -57,6 +59,7 @@ export function createActivityWorker({ metricsClient, logger, eventClient, + tracer, }: CreateActivityWorkerProps): ( request: ActivityWorkerRequest ) => Promise { @@ -67,6 +70,13 @@ export function createActivityWorker({ return metricsClient.metricScope( (metrics) => async (request: ActivityWorkerRequest) => { + const span = tracer.startSpan("activity", { + attributes: { + command: request.command.name, + executionId: request.executionId, + workflowName: request.workflowName, + }, + }); logger.addPersistentLogAttributes({ workflowName: request.workflowName, executionId: request.executionId, @@ -165,6 +175,7 @@ export function createActivityWorker({ // TODO: do not write event here, write it in the orchestrator. const endTime = new Date(); + span.addEvent("ActivityCompleted"); const event = createEvent( { type: WorkflowEventType.ActivityCompleted, @@ -178,7 +189,13 @@ export function createActivityWorker({ event, recordAge + (endTime.getTime() - start.getTime()) ); + span.setStatus({ code: SpanStatusCode.OK }); + span.end(); } catch (err) { + span.setStatus({ + code: SpanStatusCode.ERROR, + message: (err as any).toString(), + }); const [error, message] = err instanceof Error ? [err.name, err.message] @@ -205,6 +222,7 @@ export function createActivityWorker({ recordAge + (endTime.getTime() - start.getTime()) ); + span.end(); throw err; } diff --git a/packages/@eventual/core/src/runtime/handlers/orchestrator.ts b/packages/@eventual/core/src/runtime/handlers/orchestrator.ts index 9acde41e3..fb30679ab 100644 --- a/packages/@eventual/core/src/runtime/handlers/orchestrator.ts +++ b/packages/@eventual/core/src/runtime/handlers/orchestrator.ts @@ -43,6 +43,7 @@ import { MetricsLogger } from "../metrics/metrics-logger.js"; import { Unit } from "../metrics/unit.js"; import { timed, timedSync } from "../metrics/utils.js"; import { promiseAllSettledPartitioned } from "../utils.js"; +import { metrics, trace } from "@opentelemetry/api"; /** * The Orchestrator's client dependencies. @@ -89,6 +90,11 @@ export function createOrchestrator({ }); return async (eventsByExecutionId) => { + const tracer = trace.getTracer("orchestrator"); + const metric = metrics.getMeter("orchestrator"); + const invokesCounter = metric.createCounter("invocations"); + invokesCounter.add(1); + logger.debug("Handle workflowQueue records"); logger.info( @@ -111,7 +117,14 @@ export function createOrchestrator({ throw new Error(`no such workflow with name '${workflowName}'`); } // TODO: get workflow from execution id - return orchestrateExecution(workflow, executionId, records); + const orchestrateSpan = tracer.startSpan("orchestrateExecution", { + attributes: { + executionId, + }, + }); + const ret = await orchestrateExecution(workflow, executionId, records); + orchestrateSpan.end(); + return ret; } ); diff --git a/packages/@eventual/core/src/workflow-events.ts b/packages/@eventual/core/src/workflow-events.ts index 208ce6bed..4e705fcff 100644 --- a/packages/@eventual/core/src/workflow-events.ts +++ b/packages/@eventual/core/src/workflow-events.ts @@ -1,3 +1,4 @@ +import { trace } from "@opentelemetry/api"; import { ulid } from "ulidx"; import { ExecutionContext } from "./context.js"; import { EventEnvelope } from "./event.js"; @@ -455,6 +456,9 @@ export function createEvent( time: Date = new Date(), id: string = ulid() ): T { + trace + .getActiveSpan() + ?.addEvent(event.type, { event: JSON.stringify(event) }, time); const timestamp = time.toISOString(); // history events do not have IDs, use getEventId diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e01ea25d2..5562ea0d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,7 +20,7 @@ importers: lerna: 5.6.2 lint-staged: 13.0.3 prettier: 2.8.0 - ts-jest: 29.0.3_6crhf7ajeizammv76u753sn6i4 + ts-jest: 29.0.3_gruvivcimsmdfhnjphbo3uejz4 turbo: 1.6.3 typescript: 4.9.3 @@ -86,7 +86,7 @@ importers: '@types/jest': 29.2.2 '@types/node': 16.18.3 jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe - ts-jest: 29.0.3_6crhf7ajeizammv76u753sn6i4 + ts-jest: 29.0.3_gruvivcimsmdfhnjphbo3uejz4 ts-node: 10.9.1_fvpuwgkpfe3dm3hnpcpbcxmb3y typescript: 4.9.3 @@ -127,7 +127,7 @@ importers: '@types/node': 16.18.3 esbuild: 0.15.16 jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe - ts-jest: 29.0.3_6pop3d2qgwazuipggnnpxsi7y4 + ts-jest: 29.0.3_r7ajvdpa2y34tsjdd7rsppjonq ts-node: 10.9.1_fvpuwgkpfe3dm3hnpcpbcxmb3y typescript: 4.9.3 @@ -158,7 +158,7 @@ importers: esbuild: 0.15.14 jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe tests-runtime: link:../aws-runtime - ts-jest: 29.0.3_dayqsg3kqnc62at3s4tm6zqa2y + ts-jest: 29.0.3_nfuojx57y7wkpkn7glrnvhxo2q ts-node: 10.9.1_fvpuwgkpfe3dm3hnpcpbcxmb3y typescript: 4.9.3 @@ -199,13 +199,14 @@ importers: constructs: 10.1.154 esbuild: 0.15.14 jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe - ts-jest: 29.0.3_dayqsg3kqnc62at3s4tm6zqa2y + ts-jest: 29.0.3_nfuojx57y7wkpkn7glrnvhxo2q ts-node: 10.9.1_fvpuwgkpfe3dm3hnpcpbcxmb3y typescript: 4.9.3 packages/@eventual/aws-runtime: specifiers: '@aws-lambda-powertools/logger': ^1.4.1 + '@aws-sdk/client-cloudwatch-logs': ^3.226.0 '@aws-sdk/client-dynamodb': 3.226.0 '@aws-sdk/client-eventbridge': 3.226.0 '@aws-sdk/client-lambda': 3.226.0 @@ -215,6 +216,18 @@ importers: '@eventual/core': workspace:^ '@middy/core': ^3.6.2 '@middy/error-logger': ^3.6.2 + '@opentelemetry/api': ^1.3.0 + '@opentelemetry/context-async-hooks': ^1.8.0 + '@opentelemetry/core': ^1.8.0 + '@opentelemetry/exporter-metrics-otlp-proto': ^0.34.0 + '@opentelemetry/exporter-trace-otlp-proto': ^0.34.0 + '@opentelemetry/id-generator-aws-xray': ^1.1.1 + '@opentelemetry/propagator-aws-xray': ^1.1.1 + '@opentelemetry/propagator-b3': ^1.8.0 + '@opentelemetry/resources': ^1.8.0 + '@opentelemetry/sdk-metrics': ^1.8.0 + '@opentelemetry/sdk-trace-base': ^1.8.0 + '@opentelemetry/semantic-conventions': ^1.8.0 '@types/aws-lambda': 8.10.108 '@types/jest': ^29 '@types/node': ^16 @@ -230,9 +243,22 @@ importers: ulidx: ^0.3.0 dependencies: '@aws-lambda-powertools/logger': 1.4.1 + '@aws-sdk/client-cloudwatch-logs': 3.226.0 '@eventual/core': link:../core '@middy/core': 3.6.2 '@middy/error-logger': 3.6.2 + '@opentelemetry/api': 1.3.0 + '@opentelemetry/context-async-hooks': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/exporter-metrics-otlp-proto': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/exporter-trace-otlp-proto': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/id-generator-aws-xray': 1.1.1_@opentelemetry+api@1.3.0 + '@opentelemetry/propagator-aws-xray': 1.1.1_@opentelemetry+api@1.3.0 + '@opentelemetry/propagator-b3': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/resources': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/sdk-metrics': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/sdk-trace-base': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/semantic-conventions': 1.8.0 aws-embedded-metrics: 4.0.0 aws-lambda: 1.0.7 node-fetch: 2.6.7 @@ -333,7 +359,7 @@ importers: '@types/serve-static': 1.15.0 '@types/yargs': 17.0.13 jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe - ts-jest: 29.0.3_6crhf7ajeizammv76u753sn6i4 + ts-jest: 29.0.3_gruvivcimsmdfhnjphbo3uejz4 ts-node: 10.9.1_72os6jwxu2zrt2v7mxnztv2e74 typescript: 4.9.3 @@ -362,12 +388,13 @@ importers: '@types/minimatch': 5.1.2 '@types/node': 16.18.3 jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe - ts-jest: 29.0.3_dayqsg3kqnc62at3s4tm6zqa2y + ts-jest: 29.0.3_nfuojx57y7wkpkn7glrnvhxo2q ts-node: 10.9.1_72os6jwxu2zrt2v7mxnztv2e74 typescript: 4.9.3 packages/@eventual/core: specifiers: + '@opentelemetry/api': ^1.3.0 '@types/jest': ^29 '@types/node': ^16 itty-router: 2.6.6 @@ -378,13 +405,14 @@ importers: ulidx: ^0.3.0 zx: ^7.1.1 dependencies: + '@opentelemetry/api': 1.3.0 ulidx: 0.3.0 devDependencies: '@types/jest': 29.2.2 '@types/node': 16.18.3 itty-router: 2.6.6 jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe - ts-jest: 29.0.3_6crhf7ajeizammv76u753sn6i4 + ts-jest: 29.0.3_gruvivcimsmdfhnjphbo3uejz4 ts-node: 10.9.1_fvpuwgkpfe3dm3hnpcpbcxmb3y typescript: 4.9.3 zx: 7.1.1 @@ -2984,11 +3012,6 @@ packages: engines: {node: '>=6.9.0'} dev: true - /@babel/compat-data/7.20.5: - resolution: {integrity: sha512-KZXo2t10+/jxmkhNXc7pZTqRvSOIvVv/+lJwHS+B2rErwOyjuVRh60yVpb7liQ1U5t7lLJ1bz+t8tSypUZdm0g==} - engines: {node: '>=6.9.0'} - dev: true - /@babel/core/7.20.2: resolution: {integrity: sha512-w7DbG8DtMrJcFOi4VrLm+8QM4az8Mo+PuLBKLp2zrYRCow8W/f9xiXm5sN53C8HksCyDQwCKha9JiDoIyPjT2g==} engines: {node: '>=6.9.0'} @@ -3072,7 +3095,7 @@ packages: peerDependencies: '@babel/core': ^7.0.0 dependencies: - '@babel/compat-data': 7.20.5 + '@babel/compat-data': 7.20.1 '@babel/core': 7.20.5 '@babel/helper-validator-option': 7.18.6 browserslist: 4.21.4 @@ -5057,6 +5080,180 @@ packages: '@octokit/openapi-types': 14.0.0 dev: true + /@opentelemetry/api/1.3.0: + resolution: {integrity: sha512-YveTnGNsFFixTKJz09Oi4zYkiLT5af3WpZDu4aIUM7xX+2bHAkOJayFTVQd6zB8kkWPpbua4Ha6Ql00grdLlJQ==} + engines: {node: '>=8.0.0'} + dev: false + + /@opentelemetry/context-async-hooks/1.8.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-ueLmocbWDi1aoU4IPdOQyt4qz/Dx+NYyU4qoa3d683usbnkDLUXYXJFfKIMPFV2BbrI5qtnpTtzErCKewoM8aw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.4.0' + dependencies: + '@opentelemetry/api': 1.3.0 + dev: false + + /@opentelemetry/core/1.8.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-6SDjwBML4Am0AQmy7z1j6HGrWDgeK8awBRUvl1PGw6HayViMk4QpnUXvv4HTHisecgVBy43NE/cstWprm8tIfw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.4.0' + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/semantic-conventions': 1.8.0 + dev: false + + /@opentelemetry/exporter-metrics-otlp-http/0.34.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-ToRJA4frErHGiKKnPCI3+cvdyK8rksRI+mV6xZ6Yt7HiIrArY9eDX7QaCEZcTLbQIib09LTlCX87TKEL3TToWQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/otlp-exporter-base': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/otlp-transformer': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/resources': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/sdk-metrics': 1.8.0_@opentelemetry+api@1.3.0 + dev: false + + /@opentelemetry/exporter-metrics-otlp-proto/0.34.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-l9748EtVH+wl1MWFptiRdieS9OkZgGkG4jQRDj+BGIxKJifIiVu6E2o6y+31fkxVzpLAtcxjAG/far0HHpPeZg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/exporter-metrics-otlp-http': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/otlp-exporter-base': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/otlp-proto-exporter-base': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/otlp-transformer': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/resources': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/sdk-metrics': 1.8.0_@opentelemetry+api@1.3.0 + dev: false + + /@opentelemetry/exporter-trace-otlp-proto/0.34.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-Ump/OyKxq1b4I01aBWSHJw8PCquZAHZh6ykplcmFBs9BZ8DIM7Jl3+zqrS8Vb7YcZ7DZTYORl8Xv/JQoQ+cFlw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/otlp-exporter-base': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/otlp-proto-exporter-base': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/otlp-transformer': 0.34.0_@opentelemetry+api@1.3.0 + '@opentelemetry/resources': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/sdk-trace-base': 1.8.0_@opentelemetry+api@1.3.0 + dev: false + + /@opentelemetry/id-generator-aws-xray/1.1.1_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-G+zEv0M5N9f+vpD0BIlGFdBwAAYjHL/M9ByfF+6L53JUBZMSPUxb3n0e/OW5WRnlPml5KV3NULSMfMS/JdSJEQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + dev: false + + /@opentelemetry/otlp-exporter-base/0.34.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-xVNvQm7oXeQogeI21iTZRnBrBYS0OVekPutEJgb7jQtHg7x2GWuCBQK9sDo84FRWNXBpNOgSYqsf8/+PxIJ2vA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + dev: false + + /@opentelemetry/otlp-proto-exporter-base/0.34.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-qHnwcAafW8OKeM2a1YQNoL9/sgWVE+JxvMgxf2CtYBqsccIakGPoQ43hLCFLAL3I2Af4BNb5t4KnW8lrtnyUjg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/otlp-exporter-base': 0.34.0_@opentelemetry+api@1.3.0 + protobufjs: 7.1.1 + dev: false + + /@opentelemetry/otlp-transformer/0.34.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-NghPJvn3pVoWBuhWyBe1n/nWIrj1D1EFUH/bIkWEp0CMVWFLux6R+BkRPZQo5klTcj8xFhCZZIZsL/ubkYPryg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.4.0' + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/resources': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/sdk-metrics': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/sdk-trace-base': 1.8.0_@opentelemetry+api@1.3.0 + dev: false + + /@opentelemetry/propagator-aws-xray/1.1.1_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-RExCA3v2/xZcGN//JaGIs/WXm2bs2z1YhvC07NG6SBF7Vyt5IGrDoHIQXb5raSP7RjYGbaJ7Qg7ND8qkWTXP6A==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.0.0 + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + dev: false + + /@opentelemetry/propagator-b3/1.8.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-ffP6AVHyISqK1kiUY1MoVKt43Wp3FJXI8NOePqxBrAU7bRDJ13276VbSl4ugCZbZLTPrPhhSmvQh1WqlfUgcAg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.4.0' + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + dev: false + + /@opentelemetry/resources/1.8.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-KSyMH6Jvss/PFDy16z5qkCK0ERlpyqixb1xwb73wLMvVq+j7i89lobDjw3JkpCcd1Ws0J6jAI4fw28Zufj2ssg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.4.0' + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/semantic-conventions': 1.8.0 + dev: false + + /@opentelemetry/sdk-metrics/1.8.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-+KYb+uj0vHhl8xzJO+oChS4oP1e+/2Wl3SXoHoIdcEjd1TQfDV+lxOm4oqxWq6wykXvI35/JHyejxSoT+qxGmg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.4.0' + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/resources': 1.8.0_@opentelemetry+api@1.3.0 + lodash.merge: 4.6.2 + dev: false + + /@opentelemetry/sdk-trace-base/1.8.0_@opentelemetry+api@1.3.0: + resolution: {integrity: sha512-iH41m0UTddnCKJzZx3M85vlhKzRcmT48pUeBbnzsGrq4nIay1oWVHKM5nhB5r8qRDGvd/n7f/YLCXClxwM0tvA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.4.0' + dependencies: + '@opentelemetry/api': 1.3.0 + '@opentelemetry/core': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/resources': 1.8.0_@opentelemetry+api@1.3.0 + '@opentelemetry/semantic-conventions': 1.8.0 + dev: false + + /@opentelemetry/semantic-conventions/1.8.0: + resolution: {integrity: sha512-TYh1MRcm4JnvpqtqOwT9WYaBYY4KERHdToxs/suDTLviGRsQkIjS5yYROTYTSJQUnYLOn/TuOh5GoMwfLSU+Ew==} + engines: {node: '>=14'} + dev: false + /@parcel/watcher/2.0.4: resolution: {integrity: sha512-cTDi+FUDBIUOBKEtj+nhiJ71AZVlkAsQFuGQTun5tV9mwQBQgZvhCzG+URPQc8myeN32yRVZEfVAPCs1RW+Jvg==} engines: {node: '>= 10.0.0'} @@ -5075,6 +5272,49 @@ packages: typescript: 4.9.3 dev: true + /@protobufjs/aspromise/1.1.2: + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + dev: false + + /@protobufjs/base64/1.1.2: + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + dev: false + + /@protobufjs/codegen/2.0.4: + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + dev: false + + /@protobufjs/eventemitter/1.1.0: + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + dev: false + + /@protobufjs/fetch/1.1.0: + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/inquire': 1.1.0 + dev: false + + /@protobufjs/float/1.0.2: + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + dev: false + + /@protobufjs/inquire/1.1.0: + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + dev: false + + /@protobufjs/path/1.1.2: + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + dev: false + + /@protobufjs/pool/1.1.0: + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + dev: false + + /@protobufjs/utf8/1.1.0: + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + dev: false + /@sinclair/typebox/0.24.51: resolution: {integrity: sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==} dev: true @@ -8339,7 +8579,7 @@ packages: pretty-format: 29.3.1 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1_72os6jwxu2zrt2v7mxnztv2e74 + ts-node: 10.9.1_fvpuwgkpfe3dm3hnpcpbcxmb3y transitivePeerDependencies: - supports-color dev: true @@ -8379,7 +8619,7 @@ packages: pretty-format: 29.3.1 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1_72os6jwxu2zrt2v7mxnztv2e74 + ts-node: 10.9.1_fvpuwgkpfe3dm3hnpcpbcxmb3y transitivePeerDependencies: - supports-color dev: true @@ -9035,6 +9275,10 @@ packages: wrap-ansi: 6.2.0 dev: true + /long/5.2.1: + resolution: {integrity: sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==} + dev: false + /loose-envify/1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -10056,6 +10300,25 @@ packages: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} dev: true + /protobufjs/7.1.1: + resolution: {integrity: sha512-d0nMQqS/aT3lfV8bKi9Gbg73vPd2LcDdTDOu6RE/M+h9DY8g1EmDzk3ADPccthEWfTBjkR2oxNdx9Gs8YubT+g==} + engines: {node: '>=12.0.0'} + requiresBuild: true + dependencies: + '@protobufjs/aspromise': 1.1.2 + '@protobufjs/base64': 1.1.2 + '@protobufjs/codegen': 2.0.4 + '@protobufjs/eventemitter': 1.1.0 + '@protobufjs/fetch': 1.1.0 + '@protobufjs/float': 1.0.2 + '@protobufjs/inquire': 1.1.0 + '@protobufjs/path': 1.1.2 + '@protobufjs/pool': 1.1.0 + '@protobufjs/utf8': 1.1.0 + '@types/node': 18.11.9 + long: 5.2.1 + dev: false + /protocols/2.0.1: resolution: {integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==} dev: true @@ -10889,7 +11152,7 @@ packages: yargs-parser: 21.1.1 dev: true - /ts-jest/29.0.3_6pop3d2qgwazuipggnnpxsi7y4: + /ts-jest/29.0.3_dayqsg3kqnc62at3s4tm6zqa2y: resolution: {integrity: sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -10912,7 +11175,7 @@ packages: dependencies: '@babel/core': 7.20.2 bs-logger: 0.2.6 - esbuild: 0.15.16 + esbuild: 0.15.14 fast-json-stable-stringify: 2.1.0 jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe jest-util: 29.3.1 @@ -10924,7 +11187,7 @@ packages: yargs-parser: 21.1.1 dev: true - /ts-jest/29.0.3_dayqsg3kqnc62at3s4tm6zqa2y: + /ts-jest/29.0.3_gruvivcimsmdfhnjphbo3uejz4: resolution: {integrity: sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} hasBin: true @@ -10945,7 +11208,41 @@ packages: esbuild: optional: true dependencies: - '@babel/core': 7.20.2 + '@babel/core': 7.20.5 + bs-logger: 0.2.6 + fast-json-stable-stringify: 2.1.0 + jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe + jest-util: 29.3.1 + json5: 2.2.1 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.8 + typescript: 4.9.3 + yargs-parser: 21.1.1 + dev: true + + /ts-jest/29.0.3_nfuojx57y7wkpkn7glrnvhxo2q: + resolution: {integrity: sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.20.5 bs-logger: 0.2.6 esbuild: 0.15.14 fast-json-stable-stringify: 2.1.0 @@ -10959,6 +11256,41 @@ packages: yargs-parser: 21.1.1 dev: true + /ts-jest/29.0.3_r7ajvdpa2y34tsjdd7rsppjonq: + resolution: {integrity: sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/types': ^29.0.0 + babel-jest: ^29.0.0 + esbuild: '*' + jest: ^29.0.0 + typescript: '>=4.3' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + dependencies: + '@babel/core': 7.20.5 + bs-logger: 0.2.6 + esbuild: 0.15.16 + fast-json-stable-stringify: 2.1.0 + jest: 29.3.1_dnlfjp7n5lpfgnj4digwzn5fhe + jest-util: 29.3.1 + json5: 2.2.1 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.3.8 + typescript: 4.9.3 + yargs-parser: 21.1.1 + dev: true + /ts-node/10.9.1_72os6jwxu2zrt2v7mxnztv2e74: resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} hasBin: true