@hemia/trace-manager 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,117 +1,370 @@
1
1
  # @hemia/trace-manager
2
2
 
3
- Sistema de gestión de trazabilidad para aplicaciones Node.js. Permite registrar eventos, errores y transacciones utilizando un repositorio NoSQL, con soporte para decoradores automáticos de traza (`@Traceable`) y segmentación por `traceId`, `checkpoint`, `functionCode` y `tags`.
3
+ Sistema de trazabilidad distribuida basado en **OpenTelemetry** para aplicaciones Node.js. Proporciona decoradores automáticos, logging contextual correlacionado con trazas y compatibilidad con ClickHouse para análisis de observabilidad.
4
4
 
5
5
  ---
6
6
 
7
- ## Instalación
7
+ ## 🚀 Características
8
+
9
+ - ✅ **Decorador `@Trace`** - Instrumentación automática de métodos con captura de inputs/outputs
10
+ - ✅ **AsyncLocalStorage** - Propagación automática de contexto sin pasar parámetros manualmente
11
+ - ✅ **Logger Contextual** - Logs automáticamente correlacionados con `traceId` y `spanId`
12
+ - ✅ **Formato OpenTelemetry** - Compatible con estándares de observabilidad
13
+ - ✅ **ClickHouse Ready** - Esquema optimizado para análisis en ClickHouse
14
+ - ✅ **Captura de Errores** - Excepciones registradas como eventos en spans
15
+ - ✅ **Jerarquía de Spans** - Parent-Child spans automáticos por nivel de invocación
16
+
17
+ ---
18
+
19
+ ## 📦 Instalación
8
20
 
9
21
  ```bash
10
- npm install @hemia/trace-manager
22
+ npm install @hemia/trace-manager @hemia/app-context
11
23
  ```
12
24
 
13
25
  ---
14
26
 
15
- ## Componentes principales
27
+ ## 🎯 Componentes principales
28
+
29
+ ### ✅ Decorador `@Trace()`
30
+
31
+ Instrumenta automáticamente métodos creando spans de OpenTelemetry con captura de inputs, outputs y errores.
32
+
33
+ #### **Características:**
34
+
35
+ - 🔹 Crea span automáticamente con `SpanId` único
36
+ - 🔹 Captura argumentos de entrada en `SpanAttributes.app.method.args`
37
+ - 🔹 Captura resultado de retorno en `SpanAttributes.app.method.result`
38
+ - 🔹 Registra excepciones como eventos (`EventsNested`)
39
+ - 🔹 Calcula duración en nanosegundos (`DurationUInt64`)
40
+ - 🔹 Propaga contexto automáticamente a métodos hijos
41
+
42
+ #### **Uso básico:**
16
43
 
17
- ### ✅ `TraceManager<T>`
44
+ ```ts
45
+ import { Trace } from '@hemia/trace-manager';
46
+
47
+ class UserService {
48
+ @Trace()
49
+ async createUser(userData: any) {
50
+ // El decorador captura automáticamente:
51
+ // - Input: userData
52
+ // - Output: usuario creado
53
+ // - Duración de ejecución
54
+ // - Excepciones si ocurren
55
+
56
+ const user = await this.repository.save(userData);
57
+ return user;
58
+ }
18
59
 
19
- Clase encargada de registrar y consultar trazas desde un repositorio MongoDB usando `NoSQLRepository`.
60
+ @Trace({ name: 'validate-user-email' })
61
+ private async validateEmail(email: string) {
62
+ // Span hijo automático (hereda ParentSpanId)
63
+ return await this.emailValidator.check(email);
64
+ }
65
+ }
66
+ ```
20
67
 
21
- #### Métodos disponibles:
68
+ #### **Opciones de configuración:**
22
69
 
23
- * `save(traceId, additionalData?)`: Guarda una traza
24
- * `find(filter, options?)`: Busca múltiples trazas
25
- * `findOne(filter)`: Busca una traza específica
26
- * `getById(id)`: Obtiene una traza por ID
27
- * `update(filter, data)`: Actualiza una traza
28
- * `delete(filter)`: Elimina una traza
29
- * `aggregate(pipeline)`: Ejecuta una agregación personalizada
70
+ ```ts
71
+ interface TraceOptions {
72
+ name?: string; // Nombre personalizado del span (default: ClassName.methodName)
73
+ kind?: 'SPAN_KIND_INTERNAL' // Tipo de span: INTERNAL, SERVER, CLIENT, PRODUCER, CONSUMER
74
+ | 'SPAN_KIND_SERVER'
75
+ | 'SPAN_KIND_CLIENT'
76
+ | 'SPAN_KIND_PRODUCER'
77
+ | 'SPAN_KIND_CONSUMER';
78
+ attributes?: Record<string, string>; // Atributos personalizados
79
+ }
80
+ ```
30
81
 
31
- #### Ejemplo:
82
+ #### **Ejemplo avanzado:**
32
83
 
33
84
  ```ts
34
- const manager = new TraceManager<TraceDocument>(traceRepo);
35
- await manager.save('trace-id-123', { message: 'Evento recibido' });
85
+ class PaymentService {
86
+ @Trace({
87
+ name: 'process-payment-stripe',
88
+ kind: 'SPAN_KIND_CLIENT',
89
+ attributes: { 'payment.provider': 'stripe' }
90
+ })
91
+ async processPayment(amount: number, currency: string) {
92
+ // Span con atributos personalizados
93
+ return await this.stripeClient.charge(amount, currency);
94
+ }
95
+ }
36
96
  ```
37
97
 
38
98
  ---
39
99
 
40
- ### ✅ Decorador `@Traceable()`
100
+ ### ✅ Logger Contextual
41
101
 
42
- Permite trazar automáticamente métodos con input/output y errores.
102
+ Logger que automáticamente correlaciona logs con el `traceId` y `spanId` activo, permitiendo rastrear logs específicos dentro de una traza distribuida.
43
103
 
44
- #### Uso:
104
+ #### **Características:**
45
105
 
46
- ```ts
47
- import { Traceable } from '@hemia/trace-manager';
106
+ - 🔹 Correlación automática con `TraceId` y `SpanId`
107
+ - 🔹 Niveles de severidad: `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`
108
+ - 🔹 Atributos personalizados en cada log
109
+ - 🔹 Compatible con formato OpenTelemetry para ClickHouse
110
+
111
+ #### **Uso:**
48
112
 
49
- class MiServicio {
50
- @Traceable({ ck: 'MiServicio', fn: 'procesar' })
51
- async procesar(data: any) {
52
- // lógica
53
- return { ok: true };
113
+ ```ts
114
+ import { logger } from '@hemia/trace-manager';
115
+
116
+ class OrderService {
117
+ @Trace()
118
+ async placeOrder(order: Order) {
119
+ logger.info('Processing order', { orderId: order.id, amount: order.total });
120
+
121
+ try {
122
+ const result = await this.payment.charge(order.total);
123
+ logger.info('Payment successful', { transactionId: result.id });
124
+ return result;
125
+ } catch (error) {
126
+ logger.error('Payment failed', { error: error.message });
127
+ throw error;
128
+ }
54
129
  }
55
130
  }
56
131
  ```
57
132
 
58
- * Input se traza al inicio
59
- * Output se traza al finalizar
60
- * Error se traza si ocurre una excepción
133
+ #### **Métodos disponibles:**
134
+
135
+ ```ts
136
+ logger.debug(message: string, attributes?: Record<string, any>): void
137
+ logger.info(message: string, attributes?: Record<string, any>): void
138
+ logger.warn(message: string, attributes?: Record<string, any>): void
139
+ logger.error(message: string, attributes?: Record<string, any>): void
140
+ logger.fatal(message: string, attributes?: Record<string, any>): void
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 🏗️ Estructura de Datos
146
+
147
+ ### **TraceSpan** (Span de OpenTelemetry)
148
+
149
+ Mapea directamente con la tabla `otel_traces` de ClickHouse:
150
+
151
+ ```ts
152
+ interface TraceSpan {
153
+ Timestamp: string; // ISO 8601 - DateTime64(9)
154
+ TraceId: string; // ID único de la traza
155
+ SpanId: string; // ID único del span
156
+ ParentSpanId: string; // ID del span padre (jerarquía)
157
+ TraceState: string; // Estado de propagación W3C
158
+ ServiceName: string; // Nombre del servicio
159
+ SpanName: string; // Ej: "UserService.createUser"
160
+ SpanKind: 'SPAN_KIND_INTERNAL' | 'SPAN_KIND_SERVER' | 'SPAN_KIND_CLIENT' | ...;
161
+ DurationUInt64: bigint; // Duración en nanosegundos
162
+ StatusCode: 'STATUS_CODE_OK' | 'STATUS_CODE_ERROR' | 'STATUS_CODE_UNSET';
163
+ StatusMessage: string;
164
+ SpanAttributes: Record<string, string>; // app.method.args, app.method.result, etc.
165
+ ResourceAttributes: Record<string, string>; // host.name, service.name, etc.
166
+ EventsNested: SpanEvent[]; // Excepciones, logs puntuales
167
+ LinksNested: SpanLink[]; // Enlaces a otras trazas
168
+ }
169
+ ```
170
+
171
+ ### **TraceLog** (Log de OpenTelemetry)
172
+
173
+ Mapea directamente con la tabla `otel_logs` de ClickHouse:
174
+
175
+ ```ts
176
+ interface TraceLog {
177
+ Timestamp: string; // ISO 8601
178
+ TraceId: string; // Correlación automática con span activo
179
+ SpanId: string; // Span donde ocurrió el log
180
+ TraceFlags: number; // 1 = sampled
181
+ SeverityText: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'FATAL';
182
+ SeverityNumber: number; // 5, 9, 13, 17, 21
183
+ ServiceName: string;
184
+ Body: string; // Mensaje del log
185
+ LogAttributes: Record<string, string>; // Atributos custom
186
+ ResourceAttributes: Record<string, string>; // Metadata del servicio
187
+ }
188
+ ```
61
189
 
62
190
  ---
63
191
 
64
- ### `addTrace()` y `endTrace()`
192
+ ## 🔗 Integración con `@hemia/app-context`
193
+
194
+ Este paquete utiliza `AsyncLocalStorage` a través de `@hemia/app-context` para mantener el contexto de trazabilidad sin necesidad de pasarlo explícitamente como parámetro.
65
195
 
66
- Funciones internas para agregar nodos de traza al contexto.
196
+ ### **Requisitos:**
197
+
198
+ El contexto debe ser inicializado previamente usando `@hemia/app-context` (generalmente en un middleware HTTP):
67
199
 
68
200
  ```ts
69
- const spanId = addTrace('Login', 'authUser', 'input', { email }, 'Inicio de login');
70
- // lógica
71
- endTrace(spanId, true, { userId });
201
+ import { asyncContext } from '@hemia/app-context';
202
+
203
+ // En tu middleware o inicialización
204
+ const context = {
205
+ traceId: generateTraceId(),
206
+ spanId: generateSpanId(),
207
+ serviceName: 'user-service',
208
+ traceContext: {
209
+ resourceAttributes: {
210
+ 'service.name': 'user-service',
211
+ 'host.name': os.hostname(),
212
+ },
213
+ spans: [],
214
+ logs: []
215
+ }
216
+ };
217
+
218
+ await asyncContext.run(context, async () => {
219
+ // Todo el código dentro hereda este contexto automáticamente
220
+ await handleRequest(req, res);
221
+ });
222
+ ```
223
+
224
+ ---
225
+
226
+ ## 📊 Esquema ClickHouse
227
+
228
+ ### Tabla: `otel_traces`
229
+
230
+ ```sql
231
+ CREATE TABLE otel_traces (
232
+ Timestamp DateTime64(9) CODEC(Delta, ZSTD(1)),
233
+ TraceId String CODEC(ZSTD(1)),
234
+ SpanId String CODEC(ZSTD(1)),
235
+ ParentSpanId String CODEC(ZSTD(1)),
236
+ ServiceName LowCardinality(String) CODEC(ZSTD(1)),
237
+ SpanName LowCardinality(String) CODEC(ZSTD(1)),
238
+ SpanKind LowCardinality(String) CODEC(ZSTD(1)),
239
+ DurationUInt64 UInt64 CODEC(ZSTD(1)),
240
+ StatusCode LowCardinality(String) CODEC(ZSTD(1)),
241
+ SpanAttributes Map(LowCardinality(String), String) CODEC(ZSTD(1)),
242
+ ResourceAttributes Map(LowCardinality(String), String) CODEC(ZSTD(1)),
243
+ EventsNested Nested (
244
+ Timestamp DateTime64(9),
245
+ Name LowCardinality(String),
246
+ Attributes Map(LowCardinality(String), String)
247
+ ) CODEC(ZSTD(1))
248
+ ) ENGINE = MergeTree()
249
+ PARTITION BY toDate(Timestamp)
250
+ ORDER BY (ServiceName, SpanName, toUnixTimestamp(Timestamp), TraceId);
251
+ ```
252
+
253
+ ### Tabla: `otel_logs`
254
+
255
+ ```sql
256
+ CREATE TABLE otel_logs (
257
+ Timestamp DateTime64(9) CODEC(Delta, ZSTD(1)),
258
+ TraceId String CODEC(ZSTD(1)),
259
+ SpanId String CODEC(ZSTD(1)),
260
+ SeverityText LowCardinality(String) CODEC(ZSTD(1)),
261
+ SeverityNumber Int32 CODEC(ZSTD(1)),
262
+ ServiceName LowCardinality(String) CODEC(ZSTD(1)),
263
+ Body String CODEC(ZSTD(1)),
264
+ LogAttributes Map(LowCardinality(String), String) CODEC(ZSTD(1)),
265
+ ResourceAttributes Map(LowCardinality(String), String) CODEC(ZSTD(1))
266
+ ) ENGINE = MergeTree()
267
+ PARTITION BY toDate(Timestamp)
268
+ ORDER BY (ServiceName, SeverityText, toUnixTimestamp(Timestamp), TraceId);
72
269
  ```
73
270
 
74
271
  ---
75
272
 
76
- ## Tipos esperados
273
+ ## 🔍 Ejemplo completo
77
274
 
78
275
  ```ts
79
- interface TraceNode {
80
- traceId: string;
81
- spanId: string;
82
- parentSpanId?: string;
83
- projectId: string;
84
- function: string;
85
- checkpoint: string;
86
- description?: string;
87
- inputData?: any;
88
- outputData?: any;
89
- error?: any;
90
- type: 'input' | 'output';
91
- status: boolean;
92
- tags?: string[];
93
- startTime: number;
94
- endTime: number;
95
- duration?: number;
96
- source?: string;
276
+ import { Trace, logger } from '@hemia/trace-manager';
277
+ import { asyncContext } from '@hemia/app-context';
278
+
279
+ class OrderController {
280
+ @Trace({ name: 'http-create-order', kind: 'SPAN_KIND_SERVER' })
281
+ async createOrder(req: Request) {
282
+ logger.info('Order request received', { userId: req.userId });
283
+
284
+ const order = await this.orderService.create(req.body);
285
+
286
+ logger.info('Order created successfully', { orderId: order.id });
287
+ return order;
288
+ }
289
+ }
290
+
291
+ class OrderService {
292
+ @Trace()
293
+ async create(orderData: any) {
294
+ // Span hijo automático (ParentSpanId = span del controller)
295
+ logger.debug('Validating order data');
296
+
297
+ await this.validate(orderData);
298
+ const order = await this.repository.save(orderData);
299
+
300
+ logger.info('Order persisted', { orderId: order.id });
301
+ return order;
302
+ }
303
+
304
+ @Trace({ name: 'validate-order-rules' })
305
+ private async validate(data: any) {
306
+ // Span nieto (hijo del método create)
307
+ if (!data.items?.length) {
308
+ logger.error('Validation failed: no items');
309
+ throw new Error('Order must have items');
310
+ }
311
+ }
97
312
  }
98
313
  ```
99
314
 
315
+ **Resultado en ClickHouse:**
316
+
317
+ 3 spans jerárquicos:
318
+ 1. `http-create-order` (root, SPAN_KIND_SERVER)
319
+ 2. `OrderService.create` (child, SPAN_KIND_INTERNAL)
320
+ 3. `validate-order-rules` (grandchild, SPAN_KIND_INTERNAL)
321
+
322
+ Y múltiples logs correlacionados con el mismo `TraceId`.
323
+
324
+ ---
325
+
326
+ ## 📝 Buenas Prácticas
327
+
328
+ 1. **Usar `@Trace()` en capas de negocio críticas**: Controllers, Services, Repositories
329
+ 2. **Logger en puntos de decisión**: Validaciones, llamadas externas, errores
330
+ 3. **Atributos significativos**: Agregar IDs de entidades, estados, providers
331
+ 4. **Nombres descriptivos**: Usar `name` en `@Trace()` para operaciones complejas
332
+ 5. **Sanitización automática**: Los inputs/outputs se sanitizan para evitar datos sensibles
333
+
100
334
  ---
101
335
 
102
- ## Requisitos
336
+ ## 🛠️ Utilidades
103
337
 
104
- * `@hemia/db-manager` y `@hemia/app-context` para contexto y persistencia
105
- * MongoDB como backend de traza
338
+ ### `sanitizeArgs()` y `toSafeJSON()`
339
+
340
+ Utilidades internas que previenen:
341
+ - Serialización de objetos circulares
342
+ - Exposición de contraseñas/tokens en atributos
343
+ - Overflow de tamaño en ClickHouse
344
+
345
+ ---
346
+
347
+ ## 📚 Dependencias
348
+
349
+ - `@hemia/app-context` - Manejo de contexto con AsyncLocalStorage
350
+ - `uuid` - Generación de IDs únicos para spans
106
351
 
107
352
  ---
108
353
 
109
- ## Licencia
354
+ ## 📄 Licencia
110
355
 
111
356
  MIT
112
357
 
113
358
  ---
114
359
 
115
- ## Autor
360
+ ## 👨‍💻 Autor
361
+
362
+ **Hemia Technologies**
363
+
364
+ ---
365
+
366
+ ## 🔗 Recursos
116
367
 
117
- Hemia Technologies
368
+ - [OpenTelemetry Specification](https://opentelemetry.io/docs/specs/otel/)
369
+ - [ClickHouse Documentation](https://clickhouse.com/docs)
370
+ - [AsyncLocalStorage Node.js](https://nodejs.org/api/async_context.html)
@@ -1 +1 @@
1
- import{asyncContext as t}from"@hemia/app-context";import{Schema as e}from"@hemia/db-manager";var r;function n(t){try{const e=JSON.stringify(t,function(){const t=new WeakSet;return(e,r)=>{if(null===r)return null;if("object"==typeof r){if(t.has(r))return"[Circular]";t.add(r)}return"function"==typeof r?r.toString():r}}());return JSON.parse(e)}catch(e){return String(t)}}!function(t){t.TRACE="x-trace-id",t.CORRELATION="x-correlation-id"}(r||(r={}));const o=["content-type","host","x-no-cookies","x-api-key","Authorization","origin"];function i(t){return t.map(t=>{if(function(t){return null!=t&&"object"==typeof t&&"string"==typeof t.method&&"string"==typeof t.url&&"object"==typeof t.headers}(t)){const e=o.reduce((e,r)=>{const n=r.toLowerCase();return t.headers[n]&&(e[n]=t.headers[n]),e},{});return{method:t.method,url:t.url,params:t.params,query:t.query,body:t.body,headers:e}}return function(t){return null!=t&&"object"==typeof t&&"number"==typeof t.statusCode&&"function"==typeof t.setHeader&&"function"==typeof t.end}(t)?"[ExpressResponseObject]":"function"==typeof t?"[Function next]":t})}for(var a,p=[],c=0;c<256;++c)p.push((c+256).toString(16).slice(1));var s=new Uint8Array(16);function u(){if(!a&&!(a="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return a(s)}var y={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function d(t,e,r){if(y.randomUUID&&!t)return y.randomUUID();var n=(t=t||{}).random||(t.rng||u)();return n[6]=15&n[6]|64,n[8]=63&n[8]|128,function(t,e=0){return(p[t[e+0]]+p[t[e+1]]+p[t[e+2]]+p[t[e+3]]+"-"+p[t[e+4]]+p[t[e+5]]+"-"+p[t[e+6]]+p[t[e+7]]+"-"+p[t[e+8]]+p[t[e+9]]+"-"+p[t[e+10]]+p[t[e+11]]+p[t[e+12]]+p[t[e+13]]+p[t[e+14]]+p[t[e+15]]).toLowerCase()}(n)}function f(t){return function(e,r,o){const a="function"==typeof e;if(o){const p=o.value;if("function"!=typeof p)throw new Error("@Traceable can only be applied to methods, but was applied to "+typeof p);o.value=async function(...o){const c=a?e.name:this?.constructor?.name||e.constructor.name||"DEFAULT_CHECKPOINT",s=r;let u="";try{const e=n(i(o));u=g(t?.ck||c,t?.fn||s,"input",e,t?.description,t?.tags,t?.source)}catch(t){console.error("Error in Traceable decorator (addTrace):",t)}try{const t=await p.apply(this,o),e=Array.isArray(t)?t:[t],r=n(i(e));return m(u,!0,n(r)),t}catch(t){throw m(u,!1,n({error:t}),!0),t}}}else{const o=e[r];if("function"!=typeof o)throw new Error(`@Traceable can only be applied to methods, but was applied to ${typeof o}, without descriptor`);e[r]=async function(...p){const c=a?e.name:this?.constructor?.name||e.constructor.name||"DEFAULT_CHECKPOINT",s=r;let u="";try{const e=n(i(p));u=g(t?.ck||c,t?.fn||s,"input",e,t?.description,t?.tags,t?.source)}catch(t){console.error("Error in Traceable decorator (addTrace):",t)}try{const t=await o.apply(this,p),e=Array.isArray(t)?t:[t],r=n(i(e));return m(u,!0,n(r)),t}catch(t){throw m(u,!1,n({error:t}),!0),t}}}}}function g(e,r,n,o,i,a,p){const c=t.getStore();if(!c)return"";const s=c.projectId||"unknown",u=c.traceId,y=d(),f={description:i||"new trace input",tags:a,endTime:0,traceId:u,projectId:s,spanId:y,parentSpanId:c.parentSpanId,checkpoint:e,function:r,status:!0,type:n,startTime:Date.now(),inputData:o,source:p||"api"};return c.parentSpanId=y,c.trace.trace.push(f),y}function m(e,r,n,o){const i=t.getStore();if(!i)return;const a=i.trace.trace.find(t=>t.spanId===e);a&&(a.status=r,a.type="output",a.duration=Date.now()-a.startTime,a.endTime=Date.now(),a.outputData=n,o&&(a.error=n))}class l{constructor(t){this.repository=t}async save(t,e={}){if(!this.repository)throw new Error("Manager no inicializado. Llama a TraceManager primero.");const r={timestamp:(new Date).toISOString(),traceId:t,...e};return await this.repository.create(r)}async find(t,e){return await this.repository.find(t,e)}async findOne(t){return await this.repository.findOne(t)}async getById(t){return await this.repository.findById(t)}async update(t,e,r){return await this.repository.update(t,e,r)}async delete(t,e){await this.repository.delete(t,e)}async aggregate(t,e){return await this.repository.aggregate(t,e)}}const S=new e({traceId:{type:String,required:!0},projectId:{type:String,required:!0},meta:{userAgent:{type:String},environment:{type:String},platform:{type:String},ip:{type:String},event:{type:String},url:{type:String},path:{type:String},route:{type:String},tags:[{type:String}],user:{id:{type:String},email:{type:String}},browser:{name:{type:String},version:{type:String}},os:{name:{type:String},version:{type:String}},device:{model:{type:String},type:{type:String},vendor:{type:String}}},trace:{type:[new e({description:{type:String},traceId:{type:String,required:!0},spanId:{type:String,required:!0},parentSpanId:{type:String},projectId:{type:String,required:!0},parentId:{type:Number},checkpoint:{type:String,required:!0},function:{type:String,required:!0},status:{type:Boolean,required:!0},type:{type:String,enum:["input","output"],required:!0},startTime:{type:Number,default:()=>Date.now()},endTime:{type:Number,default:()=>Date.now()},duration:{type:Number},inputData:{type:e.Types.Mixed},outputData:{type:e.Types.Mixed},additionalData:{type:e.Types.Mixed},tags:[{type:String}],source:{type:String},error:{message:{type:String},code:{type:String},stack:{type:String}}},{_id:!1})],required:!0}});export{r as TraceHeader,l as TraceManager,S as TraceSchema,f as Traceable};
1
+ import{asyncContext as t}from"@hemia/app-context";var e;!function(t){t.TRACE="x-trace-id",t.CORRELATION="x-correlation-id"}(e||(e={}));const n={DEBUG:5,INFO:9,WARN:13,ERROR:17,FATAL:21};const r=new class{constructor(t){this.defaultServiceName=t}log(e,r,o){const s=t.getStore();if(!s)return console.warn("[Logger] No active context. Log not traced."),void console.log(`[${e}] ${r}`,o);const a={Timestamp:(new Date).toISOString(),TraceId:s.traceId,SpanId:s.spanId,TraceFlags:1,SeverityText:e,SeverityNumber:n[e],ServiceName:s.serviceName,Body:r,LogAttributes:this.stringifyAttributes(o||{}),ResourceAttributes:s.traceContext.resourceAttributes};s.traceContext.logs.push(a),"production"!==process.env.NODE_ENV&&console.log(`[${e}] [TraceId:${s.traceId.substring(0,8)}...] ${r}`,o||"")}stringifyAttributes(t){const e={};for(const[n,r]of Object.entries(t))if(null==r)e[n]="";else if("string"==typeof r)e[n]=r;else try{e[n]=JSON.stringify(r)}catch{e[n]=String(r)}return e}debug(t,e){this.log("DEBUG",t,e)}info(t,e){this.log("INFO",t,e)}warn(t,e){this.log("WARN",t,e)}error(t,e){this.log("ERROR",t,e)}fatal(t,e){this.log("FATAL",t,e)}};function o(t){try{const e=JSON.stringify(t,function(){const t=new WeakSet;return(e,n)=>{if(null===n)return null;if("object"==typeof n){if(t.has(n))return"[Circular]";t.add(n)}return"function"==typeof n?n.toString():n}}());return JSON.parse(e)}catch(e){return String(t)}}const s=["content-type","host","x-no-cookies","x-api-key","Authorization","origin"];function a(t){return t.map(t=>{if(function(t){return null!=t&&"object"==typeof t&&"string"==typeof t.method&&"string"==typeof t.url&&"object"==typeof t.headers}(t)){const e=s.reduce((e,n)=>{const r=n.toLowerCase();return t.headers[r]&&(e[r]=t.headers[r]),e},{});return{method:t.method,url:t.url,params:t.params,query:t.query,body:t.body,headers:e}}return function(t){return null!=t&&"object"==typeof t&&"number"==typeof t.statusCode&&"function"==typeof t.setHeader&&"function"==typeof t.end}(t)?"[ExpressResponseObject]":"function"==typeof t?"[Function next]":t})}for(var i,c=[],u=0;u<256;++u)c.push((u+256).toString(16).slice(1));var p=new Uint8Array(16);function d(){if(!i&&!(i="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return i(p)}var f={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function g(t,e,n){if(f.randomUUID&&!t)return f.randomUUID();var r=(t=t||{}).random||(t.rng||d)();return r[6]=15&r[6]|64,r[8]=63&r[8]|128,function(t,e=0){return(c[t[e+0]]+c[t[e+1]]+c[t[e+2]]+c[t[e+3]]+"-"+c[t[e+4]]+c[t[e+5]]+"-"+c[t[e+6]]+c[t[e+7]]+"-"+c[t[e+8]]+c[t[e+9]]+"-"+c[t[e+10]]+c[t[e+11]]+c[t[e+12]]+c[t[e+13]]+c[t[e+14]]+c[t[e+15]]).toLowerCase()}(r)}function l(e){return function(n,r,s){const i=s?.value;if("function"!=typeof i)throw new Error("@Trace can only be applied to methods");s.value=async function(...s){const c=t.getStore();if(!c)return console.warn("[Trace] No active context. Skipping trace."),await i.apply(this,s);const u=g(),p=this?.constructor?.name||n.name||"UnknownClass",d=e?.name||`${p}.${r}`,f=process.hrtime.bigint(),l=(new Date).toISOString(),m=c.spanId,y={Timestamp:l,TraceId:c.traceId,SpanId:u,ParentSpanId:m,TraceState:"",ServiceName:c.serviceName,SpanName:d,SpanKind:e?.kind||"SPAN_KIND_INTERNAL",DurationUInt64:BigInt(0),StatusCode:"STATUS_CODE_UNSET",StatusMessage:"",SpanAttributes:{"code.function":r,"code.namespace":p,...e?.attributes},ResourceAttributes:c.traceContext.resourceAttributes,EventsNested:[],LinksNested:[]},S=c.spanId;c.spanId=u;try{const t=a(s);y.SpanAttributes["app.method.args"]=o(t),y.SpanAttributes["app.method.args.count"]=s.length.toString();const e=await i.apply(this,s),n=o(a(Array.isArray(e)?e:[e]));return y.SpanAttributes["app.method.result"]=n.substring(0,2e3),y.StatusCode="STATUS_CODE_OK",e}catch(t){const e={Timestamp:(new Date).toISOString(),Name:"exception",Attributes:{"exception.type":t.constructor?.name||"Error","exception.message":t.message||"Unknown error","exception.stacktrace":(t.stack||"").substring(0,5e3)}};throw y.EventsNested.push(e),y.StatusCode="STATUS_CODE_ERROR",y.StatusMessage=t.message||"Unhandled exception",t}finally{const t=process.hrtime.bigint();y.DurationUInt64=t-f,c.traceContext.spans.push(y),c.spanId=S}}}}export{l as Trace,e as TraceHeader,r as logger};
@@ -1 +1 @@
1
- "use strict";var t,e=require("@hemia/app-context"),r=require("@hemia/db-manager");function n(t){try{const e=JSON.stringify(t,function(){const t=new WeakSet;return(e,r)=>{if(null===r)return null;if("object"==typeof r){if(t.has(r))return"[Circular]";t.add(r)}return"function"==typeof r?r.toString():r}}());return JSON.parse(e)}catch(e){return String(t)}}exports.TraceHeader=void 0,(t=exports.TraceHeader||(exports.TraceHeader={})).TRACE="x-trace-id",t.CORRELATION="x-correlation-id";const o=["content-type","host","x-no-cookies","x-api-key","Authorization","origin"];function a(t){return t.map(t=>{if(function(t){return null!=t&&"object"==typeof t&&"string"==typeof t.method&&"string"==typeof t.url&&"object"==typeof t.headers}(t)){const e=o.reduce((e,r)=>{const n=r.toLowerCase();return t.headers[n]&&(e[n]=t.headers[n]),e},{});return{method:t.method,url:t.url,params:t.params,query:t.query,body:t.body,headers:e}}return function(t){return null!=t&&"object"==typeof t&&"number"==typeof t.statusCode&&"function"==typeof t.setHeader&&"function"==typeof t.end}(t)?"[ExpressResponseObject]":"function"==typeof t?"[Function next]":t})}for(var i,p=[],c=0;c<256;++c)p.push((c+256).toString(16).slice(1));var s=new Uint8Array(16);function u(){if(!i&&!(i="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return i(s)}var y={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function d(t,e,r){if(y.randomUUID&&!t)return y.randomUUID();var n=(t=t||{}).random||(t.rng||u)();return n[6]=15&n[6]|64,n[8]=63&n[8]|128,function(t,e=0){return(p[t[e+0]]+p[t[e+1]]+p[t[e+2]]+p[t[e+3]]+"-"+p[t[e+4]]+p[t[e+5]]+"-"+p[t[e+6]]+p[t[e+7]]+"-"+p[t[e+8]]+p[t[e+9]]+"-"+p[t[e+10]]+p[t[e+11]]+p[t[e+12]]+p[t[e+13]]+p[t[e+14]]+p[t[e+15]]).toLowerCase()}(n)}function f(t,r,n,o,a,i,p){const c=e.asyncContext.getStore();if(!c)return"";const s=c.projectId||"unknown",u=c.traceId,y=d(),f={description:a||"new trace input",tags:i,endTime:0,traceId:u,projectId:s,spanId:y,parentSpanId:c.parentSpanId,checkpoint:t,function:r,status:!0,type:n,startTime:Date.now(),inputData:o,source:p||"api"};return c.parentSpanId=y,c.trace.trace.push(f),y}function g(t,r,n,o){const a=e.asyncContext.getStore();if(!a)return;const i=a.trace.trace.find(e=>e.spanId===t);i&&(i.status=r,i.type="output",i.duration=Date.now()-i.startTime,i.endTime=Date.now(),i.outputData=n,o&&(i.error=n))}const m=new r.Schema({traceId:{type:String,required:!0},projectId:{type:String,required:!0},meta:{userAgent:{type:String},environment:{type:String},platform:{type:String},ip:{type:String},event:{type:String},url:{type:String},path:{type:String},route:{type:String},tags:[{type:String}],user:{id:{type:String},email:{type:String}},browser:{name:{type:String},version:{type:String}},os:{name:{type:String},version:{type:String}},device:{model:{type:String},type:{type:String},vendor:{type:String}}},trace:{type:[new r.Schema({description:{type:String},traceId:{type:String,required:!0},spanId:{type:String,required:!0},parentSpanId:{type:String},projectId:{type:String,required:!0},parentId:{type:Number},checkpoint:{type:String,required:!0},function:{type:String,required:!0},status:{type:Boolean,required:!0},type:{type:String,enum:["input","output"],required:!0},startTime:{type:Number,default:()=>Date.now()},endTime:{type:Number,default:()=>Date.now()},duration:{type:Number},inputData:{type:r.Schema.Types.Mixed},outputData:{type:r.Schema.Types.Mixed},additionalData:{type:r.Schema.Types.Mixed},tags:[{type:String}],source:{type:String},error:{message:{type:String},code:{type:String},stack:{type:String}}},{_id:!1})],required:!0}});exports.TraceManager=class{constructor(t){this.repository=t}async save(t,e={}){if(!this.repository)throw new Error("Manager no inicializado. Llama a TraceManager primero.");const r={timestamp:(new Date).toISOString(),traceId:t,...e};return await this.repository.create(r)}async find(t,e){return await this.repository.find(t,e)}async findOne(t){return await this.repository.findOne(t)}async getById(t){return await this.repository.findById(t)}async update(t,e,r){return await this.repository.update(t,e,r)}async delete(t,e){await this.repository.delete(t,e)}async aggregate(t,e){return await this.repository.aggregate(t,e)}},exports.TraceSchema=m,exports.Traceable=function(t){return function(e,r,o){const i="function"==typeof e;if(o){const p=o.value;if("function"!=typeof p)throw new Error("@Traceable can only be applied to methods, but was applied to "+typeof p);o.value=async function(...o){const c=i?e.name:this?.constructor?.name||e.constructor.name||"DEFAULT_CHECKPOINT",s=r;let u="";try{const e=n(a(o));u=f(t?.ck||c,t?.fn||s,"input",e,t?.description,t?.tags,t?.source)}catch(t){console.error("Error in Traceable decorator (addTrace):",t)}try{const t=await p.apply(this,o),e=Array.isArray(t)?t:[t],r=n(a(e));return g(u,!0,n(r)),t}catch(t){throw g(u,!1,n({error:t}),!0),t}}}else{const o=e[r];if("function"!=typeof o)throw new Error(`@Traceable can only be applied to methods, but was applied to ${typeof o}, without descriptor`);e[r]=async function(...p){const c=i?e.name:this?.constructor?.name||e.constructor.name||"DEFAULT_CHECKPOINT",s=r;let u="";try{const e=n(a(p));u=f(t?.ck||c,t?.fn||s,"input",e,t?.description,t?.tags,t?.source)}catch(t){console.error("Error in Traceable decorator (addTrace):",t)}try{const t=await o.apply(this,p),e=Array.isArray(t)?t:[t],r=n(a(e));return g(u,!0,n(r)),t}catch(t){throw g(u,!1,n({error:t}),!0),t}}}}};
1
+ "use strict";var t,e=require("@hemia/app-context");exports.TraceHeader=void 0,(t=exports.TraceHeader||(exports.TraceHeader={})).TRACE="x-trace-id",t.CORRELATION="x-correlation-id";const r={DEBUG:5,INFO:9,WARN:13,ERROR:17,FATAL:21};const n=new class{constructor(t){this.defaultServiceName=t}log(t,n,o){const s=e.asyncContext.getStore();if(!s)return console.warn("[Logger] No active context. Log not traced."),void console.log(`[${t}] ${n}`,o);const a={Timestamp:(new Date).toISOString(),TraceId:s.traceId,SpanId:s.spanId,TraceFlags:1,SeverityText:t,SeverityNumber:r[t],ServiceName:s.serviceName,Body:n,LogAttributes:this.stringifyAttributes(o||{}),ResourceAttributes:s.traceContext.resourceAttributes};s.traceContext.logs.push(a),"production"!==process.env.NODE_ENV&&console.log(`[${t}] [TraceId:${s.traceId.substring(0,8)}...] ${n}`,o||"")}stringifyAttributes(t){const e={};for(const[r,n]of Object.entries(t))if(null==n)e[r]="";else if("string"==typeof n)e[r]=n;else try{e[r]=JSON.stringify(n)}catch{e[r]=String(n)}return e}debug(t,e){this.log("DEBUG",t,e)}info(t,e){this.log("INFO",t,e)}warn(t,e){this.log("WARN",t,e)}error(t,e){this.log("ERROR",t,e)}fatal(t,e){this.log("FATAL",t,e)}};function o(t){try{const e=JSON.stringify(t,function(){const t=new WeakSet;return(e,r)=>{if(null===r)return null;if("object"==typeof r){if(t.has(r))return"[Circular]";t.add(r)}return"function"==typeof r?r.toString():r}}());return JSON.parse(e)}catch(e){return String(t)}}const s=["content-type","host","x-no-cookies","x-api-key","Authorization","origin"];function a(t){return t.map(t=>{if(function(t){return null!=t&&"object"==typeof t&&"string"==typeof t.method&&"string"==typeof t.url&&"object"==typeof t.headers}(t)){const e=s.reduce((e,r)=>{const n=r.toLowerCase();return t.headers[n]&&(e[n]=t.headers[n]),e},{});return{method:t.method,url:t.url,params:t.params,query:t.query,body:t.body,headers:e}}return function(t){return null!=t&&"object"==typeof t&&"number"==typeof t.statusCode&&"function"==typeof t.setHeader&&"function"==typeof t.end}(t)?"[ExpressResponseObject]":"function"==typeof t?"[Function next]":t})}for(var i,c=[],u=0;u<256;++u)c.push((u+256).toString(16).slice(1));var p=new Uint8Array(16);function d(){if(!i&&!(i="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return i(p)}var f={randomUUID:"undefined"!=typeof crypto&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function g(t,e,r){if(f.randomUUID&&!t)return f.randomUUID();var n=(t=t||{}).random||(t.rng||d)();return n[6]=15&n[6]|64,n[8]=63&n[8]|128,function(t,e=0){return(c[t[e+0]]+c[t[e+1]]+c[t[e+2]]+c[t[e+3]]+"-"+c[t[e+4]]+c[t[e+5]]+"-"+c[t[e+6]]+c[t[e+7]]+"-"+c[t[e+8]]+c[t[e+9]]+"-"+c[t[e+10]]+c[t[e+11]]+c[t[e+12]]+c[t[e+13]]+c[t[e+14]]+c[t[e+15]]).toLowerCase()}(n)}exports.Trace=function(t){return function(r,n,s){const i=s?.value;if("function"!=typeof i)throw new Error("@Trace can only be applied to methods");s.value=async function(...s){const c=e.asyncContext.getStore();if(!c)return console.warn("[Trace] No active context. Skipping trace."),await i.apply(this,s);const u=g(),p=this?.constructor?.name||r.name||"UnknownClass",d=t?.name||`${p}.${n}`,f=process.hrtime.bigint(),l=(new Date).toISOString(),y=c.spanId,m={Timestamp:l,TraceId:c.traceId,SpanId:u,ParentSpanId:y,TraceState:"",ServiceName:c.serviceName,SpanName:d,SpanKind:t?.kind||"SPAN_KIND_INTERNAL",DurationUInt64:BigInt(0),StatusCode:"STATUS_CODE_UNSET",StatusMessage:"",SpanAttributes:{"code.function":n,"code.namespace":p,...t?.attributes},ResourceAttributes:c.traceContext.resourceAttributes,EventsNested:[],LinksNested:[]},S=c.spanId;c.spanId=u;try{const t=a(s);m.SpanAttributes["app.method.args"]=o(t),m.SpanAttributes["app.method.args.count"]=s.length.toString();const e=await i.apply(this,s),r=o(a(Array.isArray(e)?e:[e]));return m.SpanAttributes["app.method.result"]=r.substring(0,2e3),m.StatusCode="STATUS_CODE_OK",e}catch(t){const e={Timestamp:(new Date).toISOString(),Name:"exception",Attributes:{"exception.type":t.constructor?.name||"Error","exception.message":t.message||"Unknown error","exception.stacktrace":(t.stack||"").substring(0,5e3)}};throw m.EventsNested.push(e),m.StatusCode="STATUS_CODE_ERROR",m.StatusMessage=t.message||"Unhandled exception",t}finally{const t=process.hrtime.bigint();m.DurationUInt64=t-f,c.traceContext.spans.push(m),c.spanId=S}}}},exports.logger=n;
@@ -1,4 +1,6 @@
1
- import { ITraceOptions } from "../types/traceOptions";
2
- export declare function Traceable(options?: ITraceOptions): <T extends (...args: any[]) => any>(target: any, key: string, descriptor?: PropertyDescriptor) => void;
3
- export declare function addTrace(checkPoint: string, functionCode: string, type: "input" | "output", data?: any, description?: string, tags?: string[], source?: string): string;
4
- export declare function endTrace(spanId: string, status: boolean, data?: any, isError?: boolean): void;
1
+ import { TraceOptions } from "../types/traceOptions";
2
+ /**
3
+ * Decorador que captura método como Span en formato OpenTelemetry/ClickHouse
4
+ * Usa AsyncLocalStorage para propagar contexto automáticamente
5
+ */
6
+ export declare function Trace(options?: TraceOptions): <T extends (...args: any[]) => any>(target: any, key: string, descriptor: PropertyDescriptor) => void;
@@ -1,7 +1,5 @@
1
1
  export { TraceHeader } from "./types/traceHeadersNames";
2
2
  export { TraceContext } from "./types/traceContext";
3
- export { ITraceOptions } from "./types/traceOptions";
4
- export { Traceable } from "./decorator/Traceable";
5
- export { TraceManager } from "./TraceManager";
6
- export { Trace, ITrace } from "./types/traceModel";
7
- export { TraceSchema } from "./types/traceSchema";
3
+ export { TraceOptions } from "./types/traceOptions";
4
+ export { logger } from "./log/Logger";
5
+ export { Trace } from "./decorator/Traceable";
@@ -0,0 +1,15 @@
1
+ export declare class Logger {
2
+ private defaultServiceName?;
3
+ constructor(defaultServiceName?: string | undefined);
4
+ /**
5
+ * Logs que se correlacionan automáticamente con el TraceId activo
6
+ */
7
+ private log;
8
+ private stringifyAttributes;
9
+ debug(message: string, attributes?: Record<string, any>): void;
10
+ info(message: string, attributes?: Record<string, any>): void;
11
+ warn(message: string, attributes?: Record<string, any>): void;
12
+ error(message: string, attributes?: Record<string, any>): void;
13
+ fatal(message: string, attributes?: Record<string, any>): void;
14
+ }
15
+ export declare const logger: Logger;
@@ -1,19 +1,6 @@
1
- /**
2
- * Opciones para el trazado de la ejecución de una función o proceso.
3
- *
4
- * @property {string} ck - Clave o identificador único para el trazo.
5
- * @property {string} fn - Nombre de la función o proceso que se está trazando.
6
- * @property {string} st - Estado inicial o mensaje que indica el inicio del trazo.
7
- * @property {string} [errSt] - Estado o mensaje que describe el error (opcional).
8
- * @property {string} [errCode] - Código que identifica el tipo de error (opcional).
9
- */
10
- export interface ITraceOptions {
11
- ck?: string;
12
- fn?: string;
13
- st?: string;
14
- errSt?: string;
15
- errCode?: string;
16
- description?: string;
17
- tags?: string[];
18
- source?: 'api' | 'web' | 'mobile' | 'cron' | 'system' | string;
1
+ import { TraceSpan } from "@hemia/app-context";
2
+ export interface TraceOptions {
3
+ name?: string;
4
+ kind?: TraceSpan['SpanKind'];
5
+ attributes?: Record<string, string>;
19
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hemia/trace-manager",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Gestor de trazas para registrar logs, errores y evento",
5
5
  "main": "dist/hemia-trace-manager.js",
6
6
  "module": "dist/hemia-trace-manager.esm.js",
@@ -13,8 +13,7 @@
13
13
  "test": "jest --passWithNoTests --detectOpenHandles"
14
14
  },
15
15
  "devDependencies": {
16
- "@hemia/app-context": "^0.0.1",
17
- "@hemia/db-manager": "^0.0.1",
16
+ "@hemia/app-context": "^0.0.3",
18
17
  "@rollup/plugin-commonjs": "^26.0.1",
19
18
  "@rollup/plugin-json": "^6.1.0",
20
19
  "@rollup/plugin-node-resolve": "^15.2.3",
@@ -1,50 +0,0 @@
1
- import { AggregateOptions, ClientSession, NoSQLOptions, NoSQLRepository, PipelineStage, FilterQuery, Document } from "@hemia/db-manager";
2
- export declare class TraceManager<T extends Document> {
3
- private repository;
4
- constructor(service: NoSQLRepository<T>);
5
- /**
6
- * Servicio que permite guardar LOGS
7
- * @param level - Nivel del Log
8
- * @param message - Mensaje
9
- * @param traceId - Trace ID
10
- * @param trace - Traza
11
- * @param additionalData - Datos adicionales
12
- * @returns Documento guardado
13
- */
14
- save(traceId: string, additionalData?: Partial<T>): Promise<T | null>;
15
- /**
16
- * Servicio que permite buscar documentos en la colección
17
- * @param filter Filtros que se aplican en la colección
18
- * @returns Lista de documentos de la colección
19
- */
20
- find(filter: FilterQuery<T>, options?: NoSQLOptions): Promise<T[]>;
21
- /**
22
- * Servicio que permite buscar un documento en la colleción
23
- * @param filter - Filtros de la colección
24
- * @returns Documentos de la colección
25
- */
26
- findOne(filter: FilterQuery<T>): Promise<T | null>;
27
- /**
28
- * Servicio que permite obtener un documento por ID
29
- * @param id - Identificador del documento
30
- * @returns Documento
31
- */
32
- getById(id: string): Promise<T | null>;
33
- /**
34
- * Serivicio para actualizar documentos en la base de datos
35
- * @param filter - criterio de actualización
36
- * @param updateData - Datos del documento a actualizar
37
- * @returns Documento actualizado
38
- */
39
- update(filter: FilterQuery<T>, updateData: Partial<T>, session?: ClientSession): Promise<T | null>;
40
- /**
41
- * Servicio para eliminar un documento de la base de datos
42
- * @param id - Identificador del documento
43
- * @returns Documento Eliminado
44
- */
45
- delete(filter: FilterQuery<T>, session?: ClientSession): Promise<void>;
46
- /**
47
- * Ejecuta una consulta de agregación con un pipeline personalizado.
48
- */
49
- aggregate(pipeline: PipelineStage[], options?: AggregateOptions): Promise<any[]>;
50
- }
@@ -1,63 +0,0 @@
1
- import { Document } from "mongoose";
2
- export interface ITrace extends Document {
3
- traceId: string;
4
- projectId: string;
5
- meta?: TraceMetadata;
6
- trace: Trace[];
7
- }
8
- /**
9
- * Node de las trazas
10
- */
11
- export interface Trace {
12
- description: string;
13
- traceId: string;
14
- spanId: string;
15
- parentSpanId: string;
16
- projectId: string;
17
- parentId?: number;
18
- checkpoint: string;
19
- function: string;
20
- status: boolean;
21
- type: "input" | "output";
22
- startTime: number;
23
- endTime?: number;
24
- duration?: number;
25
- inputData?: any;
26
- outputData?: any;
27
- additionalData?: any;
28
- tags?: string[];
29
- error?: {
30
- message: string;
31
- code?: string;
32
- stack?: string;
33
- };
34
- source?: 'api' | 'web' | 'mobile' | 'cron' | 'system' | string;
35
- }
36
- export interface TraceMetadata {
37
- userAgent: string | null;
38
- environment: string | null;
39
- platform: string | null;
40
- ip: string | null;
41
- user: {
42
- id: string;
43
- email: string;
44
- } | null;
45
- event: string;
46
- url: string;
47
- path: string;
48
- route: string | null;
49
- tags: string[];
50
- browser: {
51
- name: string | null;
52
- version: string | null;
53
- };
54
- os: {
55
- name: string | null;
56
- version: string | null;
57
- };
58
- device: {
59
- model: string | null;
60
- type: string | null;
61
- vendor: string | null;
62
- };
63
- }
@@ -1,11 +0,0 @@
1
- import { Schema } from "@hemia/db-manager";
2
- import { ITrace } from "./traceModel";
3
- export declare const TraceSchema: Schema<ITrace, import("mongoose").Model<ITrace, any, any, any, import("mongoose").Document<unknown, any, ITrace, any, {}> & ITrace & Required<{
4
- _id: unknown;
5
- }> & {
6
- __v: number;
7
- }, any>, {}, {}, {}, {}, import("mongoose").DefaultSchemaOptions, ITrace, import("mongoose").Document<unknown, {}, import("mongoose").FlatRecord<ITrace>, {}, import("mongoose").ResolveSchemaOptions<import("mongoose").DefaultSchemaOptions>> & import("mongoose").FlatRecord<ITrace> & Required<{
8
- _id: unknown;
9
- }> & {
10
- __v: number;
11
- }>;