@axarai/axar
Version:
TypeScript-based agent framework for building agentic applications powered by LLMs
133 lines (132 loc) • 4.66 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Telemetry = void 0;
const api_1 = require("@opentelemetry/api");
const zod_to_json_schema_1 = require("zod-to-json-schema");
const zod_1 = require("zod");
/**
* Telemetry class that provides a wrapper around OpenTelemetry functionality
* for tracking and monitoring application behavior.
*
* @template T - The type of object being monitored. Must extend Object.
*/
class Telemetry {
/**
* Creates a new Telemetry instance
* @param object - The object to monitor. Used to generate span names based on the constructor name.
*/
constructor(object) {
this.object = object;
this.tracer = api_1.trace.getTracer(`${this.object.constructor.name}`);
}
/**
* Starts a new span with the given method name
* @param method - The method name to use for the span
* @throws Error if span creation fails
*/
startSpan(method) {
try {
this.span = this.tracer.startSpan(`${this.object.constructor.name}.${method}`);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
throw new Error(`Failed to start span: ${errorMessage}`);
}
}
/**
* Ends the current span if it exists and cleans up resources
*/
endSpan() {
this.span?.end();
this.span = undefined;
}
/**
* Adds an attribute to the current span if telemetry is enabled
* @param key - The attribute key to be added to the span
* @param value - The attribute value. Can be:
* - string: Added directly as an attribute
* - ZodSchema: Converted to JSON schema before adding
* - string[]: Stringified before adding
* - Object: Stringified with indentation before adding
* - undefined/null: Ignored
* @example
* ```typescript
* telemetry.addAttribute('userId', '12345');
* telemetry.addAttribute('requestBody', userSchema);
* telemetry.addAttribute('tags', ['important', 'urgent']);
* ```
*/
addAttribute(key, value) {
if (!this.span || value === undefined || value === null) {
return;
}
if (typeof value === 'string') {
this.span.setAttribute(key, value);
return;
}
if (Array.isArray(value)) {
this.span.setAttribute(key, JSON.stringify(value));
return;
}
if (value instanceof zod_1.ZodSchema) {
const jsonSchema = (0, zod_to_json_schema_1.zodToJsonSchema)(value, {
$refStrategy: 'none',
errorMessages: false,
});
this.span.setAttribute(key, JSON.stringify(jsonSchema));
return;
}
this.span.setAttribute(key, JSON.stringify(value));
}
/**
* Executes an operation within a new span, providing automatic error handling
* and context management.
*
* @param method - The method name for the span. Must not be empty.
* @param operation - The async operation to execute within the span
* @returns The result of the operation
* @throws Any error from the operation, after recording it in the span
*
* @example
* ```typescript
* const result = await telemetry.withSpan('processUser', async () => {
* const user = await processUserData();
* return user;
* });
* ```
*/
async withSpan(method, operation) {
this.startSpan(method);
try {
return await api_1.context.with(api_1.trace.setSpan(api_1.context.active(), this.span), operation);
}
catch (error) {
if (this.span) {
const errorObject = error instanceof Error ? error : new Error(String(error));
this.span.recordException(errorObject);
this.span.setStatus({
code: api_1.SpanStatusCode.ERROR,
message: errorObject.message,
});
}
throw error;
}
finally {
this.endSpan();
}
}
/**
* Gets the current context with the active span
* @returns The current context with the active span if one exists,
* otherwise returns the active context
*/
getContext() {
return this.span
? api_1.trace.setSpan(api_1.context.active(), this.span)
: api_1.context.active();
}
isRecording() {
return this.span !== undefined && this.span.isRecording();
}
}
exports.Telemetry = Telemetry;