import type { AttributeValue, Span } from '@opentelemetry/api'; import { isPrimitive, isRecord, normalizer, Size } from '@rsdk/common'; import { catchError, mergeMap, type Observable, of, throwError } from 'rxjs'; // reference from import { SpanStatusCode } from '@opentelemetry/api'; // in order not to depend on the package @opentelemetry/api enum SpanStatusCode { /** * The default status. */ UNSET = 0, /** * The operation has been validated by an Application developer or * Operator to have completed successfully. */ OK = 1, /** * The operation contains an error. */ ERROR = 2, } /** * Maximum span attribute size */ export const MAX_ATTRIBUTE_SIZE = new Size(100, 'kb'); /** * Makes correct opentelemetry span attribute from any value * @param data unknown value of any type * @returns AttributeValue */ export const toAttribute = (data: unknown): AttributeValue => { if (isPrimitive(data)) { return data; } if (isRecord(data) || Array.isArray(data)) { const serialized = JSON.stringify(normalizer()(data)); return serialized.length <= MAX_ATTRIBUTE_SIZE.bytes() ? serialized : `data exceeds limit of ${MAX_ATTRIBUTE_SIZE.bytes()} bytes`; } return 'data is undefined'; }; /** * Adds data to span with key "result" and ends it. * @param span Span * @param data Any piece of data */ export const endWithResult = (span: Span, data: unknown): void => { span.setAttribute('result', toAttribute(data)); span.setStatus({ code: SpanStatusCode.OK }); span.end(); }; /** * Adds error object to span and ends it * @param span Span * @param error Error */ export const endWithError = (span: Span, error: Error): void => { span.recordException(error); span.setStatus({ code: SpanStatusCode.ERROR, message: error.message, }); span.end(); }; export const processSyncResult = (result: T, span: Span): T => { endWithResult(span, result); return result; }; export const processObservableResult = ( result: Observable, span: Span, ): Observable => result.pipe( mergeMap((result) => { endWithResult(span, result); return of(result); }), catchError((error) => { endWithError(span, error); return throwError(() => error); }), ); export const processPromiseResult = ( result: Promise, span: Span, ): Promise => result .then((result) => { endWithResult(span, result); return result; }) .catch((error) => { endWithError(span, error); throw error; });