import { v4 as uuidv4 } from 'uuid'; import { SemanticAttributes } from '@opentelemetry/semantic-conventions'; import { Tracer, Span } from '@opentelemetry/sdk-trace-base'; import { OpentelemetryAgendaProvider, OpentelemetryChatProvider, OpentelemetryInterceptorProvider, OpentelemetryLogging, OpentelemetryNotificationProvider } from './opentelemetry'; import { Device, DeviceInfo } from '@capacitor/device'; import { SessionStore } from 'src/app/store/session.service'; import { environment } from 'src/environments/environment'; import { UseCaseCounter } from './matrix'; import { openTelemetryLogging } from './logging'; import { SpanStatus, SpanStatusCode } from '@opentelemetry/api'; const tracerInstance = OpentelemetryAgendaProvider.getTracer('example-tracer-hole', '111', {}) const tracerNotificationInstance = OpentelemetryNotificationProvider.getTracer('example-tracer-hole', '111', {}) const tracerChat = OpentelemetryChatProvider.getTracer('OpentelemetryChatProvider','some' ,{}) let device: DeviceInfo; Device.getInfo().then(e => { device = e }); function convertAttributesToString(obj) { const result = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { if (typeof obj[key] === 'object' && obj[key] !== null) { // Convert only the object attribute to string result[key] = JSON.stringify(obj[key], null, 2); } else { // Convert primitive values to string result[key] = obj[key]; } } } return result; } const createTracingInstance = ({bugPrint, name, module, autoFinish}): TracingType => { const requestId = uuidv4() let _tracerInstance:Tracer if(module == 'notification') { _tracerInstance = tracerNotificationInstance } else if (module == 'chat') { _tracerInstance = tracerChat } else { _tracerInstance = tracerInstance } const span = _tracerInstance.startSpan(name); let finish = false const data = { event: {}, tags: {}, status: {} as any, logs:[], errors: [] } const returnObject = { name, span: span as any, tracer: tracerInstance, tracerId: requestId, attributes: SemanticAttributes, setStatus: (status: SpanStatus) => { span.setStatus(status); }, addEvent: (context: string, message: string = "") => { data.event[context] = message; span.addEvent(context, message as any); }, LocalLogEvent:(context: string, message: any, obj: any) => { data.tags[context] = message; }, setAttribute: (key: string, value: string) => { data.tags[key] = value; span.setAttribute(key, value); if(key =='outcome' && value == 'failed') { if(data.errors.length == 0) { returnObject.hasError('error') } if(!autoFinish) { returnObject.finish() } } else if (key =='outcome' && value == 'success') { span.setStatus({code: SpanStatusCode.OK, message:name}) if(!autoFinish) { returnObject.finish() } } }, log(message: string, dataObject: Object) { const spanId = span.spanContext().spanId; const _tracer = OpentelemetryLogging.getTracer('logging') const spanContext = _tracer.startSpan(name) dataObject = convertAttributesToString(dataObject) if(environment.apiURL != 'https://gdqas-api.oapr.gov.ao/api/') { openTelemetryLogging.send({ type: 'graylog', spanContext, payload: { message: message, object: { ...dataObject, spanId, name, user: SessionStore?.user?.FullName, device_name: device?.name || device?.model, commit_date: environment.version.lastCommitTime, } } }) } data.logs.push(dataObject) }, getAttribute: (key: string) => { return data.tags[key] }, finish: () => { if(finish) return if(environment.apiURL != 'https://gdqas-api.oapr.gov.ao/api/') { span.setAttribute('error.list', data.errors.join(',')) span.end(); UseCaseCounter.add(1, {user: SessionStore?.user?.FullName, outcome:data.tags['outcome'] || data.status?.code , usecase: name}) } if(bugPrint && (data.tags['outcome'] == 'failed' || data.status?.code == SpanStatusCode.ERROR)) { console.error(name, data) } finish = true }, hasError:(message: string) => { data.errors.push(message) data.status = {code: SpanStatusCode.ERROR, message} span.setStatus({code: SpanStatusCode.ERROR, message}) }, createSpan: (name, parent?: any) => { return tracerInstance.startSpan(name, { root: false }, parent) as Span; } } return returnObject } export function XTracerAsync({ name, bugPrint, module = null, autoFinish = true, daley =0 }) { return ( target: unknown, propertyKey: string, descriptor: PropertyDescriptor, ) => { const originalMethod = descriptor.value; descriptor.value = async function (...args: unknown[]) { const tracing = createTracingInstance({bugPrint, name, module, autoFinish}) tracing.setAttribute('User', SessionStore?.user?.FullName); tracing.setAttribute('current.page', window.location.pathname); tracing.setAttribute('device.name', device?.name || device?.model) tracing.setAttribute('commit.date', environment.version.lastCommitTime) tracing.setAttribute('commit.branch', environment.version.branch) args.push(tracing) try { const result = await originalMethod.apply(this, args); if(autoFinish ) { setTimeout(tracing.finish , daley) } return result } catch (e) { tracing.setAttribute('catch', 'true') tracing.log("cath", { error: e }) if(autoFinish) { setTimeout(tracing.finish , daley) } console.error(e); return false } }; }; } export function XTracer({ name, bugPrint, module, autoFinish = true, daley =0 }) { return ( target: unknown, propertyKey: string, descriptor: PropertyDescriptor, ) => { const originalMethod = descriptor.value; descriptor.value = function (...args: unknown[]) { const tracing = createTracingInstance({bugPrint, name, module, autoFinish}) tracing.setAttribute('User', SessionStore?.user?.FullName); tracing.setAttribute('current.page', window.location.pathname); tracing.setAttribute('device.name', device?.name || device?.model) tracing.setAttribute('commit.date', environment.version.lastCommitTime) tracing.setAttribute('commit.branch', environment.version.branch) args.push(tracing) try { const result = originalMethod.apply(this, args); if(autoFinish) { setTimeout(tracing.finish , daley) } return result } catch (e) { tracing.setAttribute('catch', 'true') tracing.log("cath", { error: e }) if(autoFinish) { setTimeout(tracing.finish , daley) } console.error(e); return false } }; }; } export type TracingType = { name: string, span: Span; tracer: Tracer; tracerId: string; attributes: typeof SemanticAttributes; // axios: (config?: AxiosRequestConfig) => AxiosInstance; setStatus: (status: any) => void; log: (message: string, data: Object) => void; addEvent: (context: string, message?: any, obj?: any) => void; setAttribute: (key: string, value: string) => void; getAttribute: (key: string) => string; LocalLogEvent: (name: string, attributesOrStartTime: any, obj?:any) => void; finish: () => void; hasError:(message: string) => void; createSpan:(name, parent?: any) => Span; }; export interface UserInteraction { readonly params: any; readonly tracing: TracingType; readonly user: Pick; readonly headers: Headers & { authorization: string }; } export type InteractionTrancingInput = Pick; export const getPathWithoutUUID = (path: string) => path.replace( /[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/, 'uuid', );