chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,181 @@
|
||||
import type { Attributes } from "@opentelemetry/api";
|
||||
import { RandomIdGenerator } from "@opentelemetry/sdk-trace-base";
|
||||
import { parseTraceparent } from "@trigger.dev/core/v3/isomorphic";
|
||||
import type {
|
||||
ExceptionEventProperties,
|
||||
SpanEvents,
|
||||
TaskRunError,
|
||||
} from "@trigger.dev/core/v3/schemas";
|
||||
import { unflattenAttributes } from "@trigger.dev/core/v3/utils/flattenAttributes";
|
||||
import { createHash } from "node:crypto";
|
||||
|
||||
export function extractContextFromCarrier(carrier: Record<string, unknown>) {
|
||||
const traceparent = carrier["traceparent"];
|
||||
const tracestate = carrier["tracestate"];
|
||||
|
||||
if (typeof traceparent !== "string") {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
...carrier,
|
||||
traceparent: parseTraceparent(traceparent),
|
||||
tracestate,
|
||||
};
|
||||
}
|
||||
|
||||
export function getNowInNanoseconds(): bigint {
|
||||
return BigInt(new Date().getTime() * 1_000_000);
|
||||
}
|
||||
|
||||
export function getDateFromNanoseconds(nanoseconds: bigint): Date {
|
||||
return new Date(Number(nanoseconds) / 1_000_000);
|
||||
}
|
||||
|
||||
export function calculateDurationFromStart(
|
||||
startTime: bigint,
|
||||
endTime: Date = new Date(),
|
||||
minimumDuration?: number
|
||||
) {
|
||||
const $endtime = typeof endTime === "string" ? new Date(endTime) : endTime;
|
||||
|
||||
const duration = Number(BigInt($endtime.getTime() * 1_000_000) - startTime);
|
||||
|
||||
if (minimumDuration && duration < minimumDuration) {
|
||||
return minimumDuration;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
export function calculateDurationFromStartJsDate(startTime: Date, endTime: Date = new Date()) {
|
||||
const $endtime = typeof endTime === "string" ? new Date(endTime) : endTime;
|
||||
|
||||
return ($endtime.getTime() - startTime.getTime()) * 1_000_000;
|
||||
}
|
||||
|
||||
export function convertDateToNanoseconds(date: Date): bigint {
|
||||
return BigInt(date.getTime()) * BigInt(1_000_000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a deterministically random 8-byte span ID formatted/encoded as a 16 lowercase hex
|
||||
* characters corresponding to 64 bits, based on the trace ID and seed.
|
||||
*/
|
||||
export function generateDeterministicSpanId(traceId: string, seed: string) {
|
||||
const hash = createHash("sha1");
|
||||
hash.update(traceId);
|
||||
hash.update(seed);
|
||||
const buffer = hash.digest();
|
||||
let hexString = "";
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const val = buffer.readUInt8(i);
|
||||
const str = val.toString(16).padStart(2, "0");
|
||||
hexString += str;
|
||||
}
|
||||
return hexString;
|
||||
}
|
||||
|
||||
const randomIdGenerator = new RandomIdGenerator();
|
||||
|
||||
export function generateTraceId() {
|
||||
return randomIdGenerator.generateTraceId();
|
||||
}
|
||||
|
||||
export function generateSpanId() {
|
||||
return randomIdGenerator.generateSpanId();
|
||||
}
|
||||
|
||||
export function stripAttributePrefix(attributes: Attributes, prefix: string) {
|
||||
const result: Attributes = {};
|
||||
|
||||
for (const [key, value] of Object.entries(attributes)) {
|
||||
if (key.startsWith(prefix)) {
|
||||
result[key.slice(prefix.length + 1)] = value;
|
||||
} else {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function parseEventsField(events: unknown): SpanEvents {
|
||||
if (!events) return [];
|
||||
if (!Array.isArray(events)) return [];
|
||||
|
||||
const unsafe = events
|
||||
? (events as any[]).map((e) => ({
|
||||
...e,
|
||||
properties: unflattenAttributes(e.properties as Attributes),
|
||||
}))
|
||||
: undefined;
|
||||
|
||||
return unsafe as SpanEvents;
|
||||
}
|
||||
|
||||
export function createExceptionPropertiesFromError(error: TaskRunError): ExceptionEventProperties {
|
||||
switch (error.type) {
|
||||
case "BUILT_IN_ERROR": {
|
||||
return {
|
||||
type: error.name,
|
||||
message: error.message,
|
||||
stacktrace: error.stackTrace,
|
||||
};
|
||||
}
|
||||
case "CUSTOM_ERROR": {
|
||||
return {
|
||||
type: "Error",
|
||||
message: error.raw,
|
||||
};
|
||||
}
|
||||
case "INTERNAL_ERROR": {
|
||||
return {
|
||||
type: "Internal error",
|
||||
message: [error.code, error.message].filter(Boolean).join(": "),
|
||||
stacktrace: error.stackTrace,
|
||||
};
|
||||
}
|
||||
case "STRING_ERROR": {
|
||||
return {
|
||||
type: "Error",
|
||||
message: error.raw,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Removes internal/private attribute keys from span properties.
|
||||
// Filters: "$" prefixed keys (private metadata) and "ctx." prefixed keys (Trigger.dev run context)
|
||||
export function removePrivateProperties(
|
||||
attributes: Attributes | undefined | null
|
||||
): Attributes | undefined {
|
||||
if (!attributes) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const result: Attributes = {};
|
||||
|
||||
for (const [key, value] of Object.entries(attributes)) {
|
||||
if (key.startsWith("$") || key.startsWith("ctx.")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
result[key] = value;
|
||||
}
|
||||
|
||||
if (Object.keys(result).length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function isEmptyObject(obj: object) {
|
||||
for (var prop in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, prop)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user