Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ambar-core.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ jobs:
version: 10

- name: Install
run: pnpm install
# pnpm 10 ignores env-var auth in the committed .npmrc; write the token to ~/.npmrc instead.
run: |
echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> ~/.npmrc
pnpm install
env:
GITHUB_TOKEN: ${{ secrets.READ_ACCESS_TO_REPOS }}

Expand Down
10 changes: 8 additions & 2 deletions .github/workflows/ambar-task-explorer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ jobs:
version: 10

- name: Install
run: pnpm install
# pnpm 10 ignores env-var auth in the committed .npmrc; write the token to ~/.npmrc instead.
run: |
echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> ~/.npmrc
pnpm install
env:
GITHUB_TOKEN: ${{ secrets.READ_ACCESS_TO_REPOS }}

Expand Down Expand Up @@ -74,7 +77,10 @@ jobs:
version: 10

- name: Install
run: pnpm install
# pnpm 10 ignores env-var auth in the committed .npmrc; write the token to ~/.npmrc instead.
run: |
echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> ~/.npmrc
pnpm install
env:
GITHUB_TOKEN: ${{ secrets.READ_ACCESS_TO_REPOS }}

Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/ambar-tasks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ jobs:
version: 10

- name: Install
run: pnpm install
# pnpm 10 ignores env-var auth in the committed .npmrc; write the token to ~/.npmrc instead.
run: |
echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" >> ~/.npmrc
pnpm install
env:
GITHUB_TOKEN: ${{ secrets.READ_ACCESS_TO_REPOS }}

Expand Down
8 changes: 7 additions & 1 deletion core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@ambarltd/core",
"version": "0.1.16",
"version": "0.1.17",
"type": "module",
"repository": "https://github.com/ambarltd/typescript-libs.git",
"publishConfig": {
Expand All @@ -23,6 +23,12 @@
"prepack": "pnpm build"
},
"dependencies": {
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/context-async-hooks": "^2.7.1",
"@opentelemetry/resources": "^2.7.1",
"@opentelemetry/sdk-trace-base": "^2.7.1",
"@opentelemetry/sdk-trace-node": "^2.7.1",
"@opentelemetry/semantic-conventions": "^1.41.1",
"@optique/core": "^0.6.2",
"@optique/run": "^0.6.2",
"fluture": "^14.0.0",
Expand Down
97 changes: 97 additions & 0 deletions core/src/tracing/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Tracing facade.
//
// The application imports `trace`, `traceP`, `traceF` and `traceRequests` from
// here. The first three delegate to the currently-selected `Tracer`, so the
// whole program's tracing destination is switched at runtime with `setTracer`.
//
// There is NO tracer by default: Use `setTracer` to set one.
//
export { type Tracer, type Attributes, type Name, setTracer, trace, traceP, traceF, event };

import type { Future } from "../future";

// A flat bag of primitive values attached to a trace or event.
type Attributes = Record<string, string | number | boolean>;

// The first argument to a trace function: either a plain name, or an attributes
// object that carries the name under its `name` field. The `name` becomes the
// trace's name; the remaining keys become its attributes.
type Name = string | (Attributes & { name: string });

// The common shape of a tracer. The facade normalises the public `Name` argument
// into a name + attributes before delegating, so implementations receive both
// explicitly (no polyvariadic signatures, no trailing attributes).
interface Tracer {
/**
* Trace a synchronous call.
*/
trace<A>(name: string, attributes: Attributes, f: () => A): A;

/**
* Trace an asynchronous call.
*/
traceP<A>(name: string, attributes: Attributes, f: () => Promise<A>): Promise<A>;

/**
* Trace a Future
*/
traceF<E, A>(name: string, attributes: Attributes, f: Future<E, A>): Future<E, A>;

/**
* Record a point-in-time event on the currently-active trace.
*/
event(name: string, attributes?: Attributes): void;
}

// The active tracer. None by default — traces are no-ops until one is set.
let current: Tracer | undefined = undefined;

// Choose where traces go. Affects every subsequent trace call across the program.
function setTracer(tracer: Tracer): void {
current = tracer;
}

// The delegating API. These read `current` at call time, so a `setTracer` switch
// takes effect immediately for all callers. With no tracer set, they run the
// work untraced. The name argument may be a plain string or an attributes object
// carrying the name (see `Name`).
function trace<A>(name: Name, f: () => A): A {
if (!current) {
return f();
}
const t = named(name);
return current.trace(t.name, t.attributes, f);
}

function traceP<A>(name: Name, f: () => Promise<A>): Promise<A> {
if (!current) {
return f();
}
const t = named(name);
return current.traceP(t.name, t.attributes, f);
}

function traceF<E, A>(name: Name, f: Future<E, A>): Future<E, A> {
if (!current) {
return f;
}
const t = named(name);
return current.traceF(t.name, t.attributes, f);
}

// Record a named, timestamped marker on whatever trace is currently active — for
// annotating "something happened here" (a cache miss, a retry, a validation
// failure) at a point in time, without opening a child trace. A no-op when no
// trace is active (or no tracer is set).
function event(name: string, attributes?: Attributes): void {
current?.event(name, attributes);
}

// Split a `Name` into the trace name and its attributes.
function named(name: Name): { name: string; attributes: Attributes } {
if (typeof name === "string") {
return { name, attributes: {} };
}
const { name: n, ...attributes } = name;
return { name: n, attributes };
}
Loading
Loading