@orgn/otel-helpers
v0.1.1
Published
Tiny TypeScript helpers for native OpenTelemetry spans and GenAI semantic conventions. Orgn Observe distribution of superloglabs/otel-helpers.
Maintainers
Readme
@orgn/otel-helpers
Tiny TypeScript helpers for native OpenTelemetry, used by Orgn Observe.
- Add spans with minimal diff impact
- Record LLM costs
This is the Orgn Observe distribution of the open-source
@superlog/otel-helpers(MIT). Same API, published under the@orgnscope so Orgn Observe onboarding skills canimport { withSpan } from "@orgn/otel-helpers".
npm i @orgn/otel-helpers @opentelemetry/apiwithSpan
withSpan allows you to create a span around a function in a way that is easy to review.
Here is the same handleCheckout instrumented two ways.
Original:
async function handleCheckout(orderId: string) {
const order = await loadOrder(orderId);
await chargeCard(order);
return order;
}Vanilla OpenTelemetry SDK:
--- original.ts
+++ manual.ts
@@ -1,5 +1,20 @@
+import { trace, SpanStatusCode } from "@opentelemetry/api";
+
+const tracer = trace.getTracer("checkout");
+
async function handleCheckout(orderId: string) {
- const order = await loadOrder(orderId);
- await chargeCard(order);
- return order;
+ return tracer.startActiveSpan("api.checkout", async (span) => {
+ try {
+ span.setAttribute("order.id", orderId);
+ const order = await loadOrder(orderId);
+ await chargeCard(order);
+ return order;
+ } catch (err) {
+ span.recordException(err as Error);
+ span.setStatus({ code: SpanStatusCode.ERROR });
+ throw err;
+ } finally {
+ span.end();
+ }
+ });
}Notice that:
- The entire function is indented, creating a long diff hunk.
- The reviewer needs to visually compare versions to analyze changes.
- The try/catch block creates code changes around the key logic of the function.
Here's the diff produced by withSpan:
--- original.ts
+++ helper.ts
@@ -1,5 +1,9 @@
+import { withSpan } from "@orgn/otel-helpers";
+
async function handleCheckout(orderId: string) {
- const order = await loadOrder(orderId);
- await chargeCard(order);
- return order;
+ return withSpan("api.checkout", async (span) => {
+ span.setAttribute("order.id", orderId);
+ const order = await loadOrder(orderId);
+ await chargeCard(order);
+ return order;
+ });
}- The diff is now purely additive
- It is easy to identify the scope of the changes and their potential impact.
GenAI spans
- Creates spans and metrics for LLM calls
- Respects OTel Semantic conventions
import { withGenAiSpan, recordGenAiUsage, anthropicUsage } from "@orgn/otel-helpers";
const response = await withGenAiSpan(
{
operation: "chat",
provider: "anthropic",
requestModel: MODEL,
useCase: "stylist",
callSite: "initial",
},
async (span) => {
const res = await client.messages.create({
model: MODEL,
max_tokens: 2048,
system: SYSTEM_PROMPT,
tools,
messages,
});
recordGenAiUsage(span, anthropicUsage(res));
return res;
},
);The package emits current OpenTelemetry GenAI semantic convention attributes:
gen_ai.operation.namegen_ai.provider.namegen_ai.request.modelgen_ai.response.modelgen_ai.usage.input_tokensgen_ai.usage.output_tokensgen_ai.response.finish_reasons
The GenAI semantic conventions are currently marked Development by OpenTelemetry. This package follows the current names, and keeps product-specific dimensions under app.gen_ai.*.
License
MIT — see LICENSE. Derived from
@superlog/otel-helpers.
