apitally 0.24.0 → 0.25.0

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.
Files changed (152) hide show
  1. package/dist/adonisjs/index.cjs +3 -0
  2. package/dist/adonisjs/index.cjs.map +1 -1
  3. package/dist/adonisjs/index.d.cts +2 -0
  4. package/dist/adonisjs/index.d.ts +2 -0
  5. package/dist/adonisjs/index.js +2 -0
  6. package/dist/adonisjs/index.js.map +1 -1
  7. package/dist/adonisjs/middleware.cjs +6 -2
  8. package/dist/adonisjs/middleware.cjs.map +1 -1
  9. package/dist/adonisjs/middleware.d.cts +2 -0
  10. package/dist/adonisjs/middleware.d.ts +2 -0
  11. package/dist/adonisjs/middleware.js +6 -2
  12. package/dist/adonisjs/middleware.js.map +1 -1
  13. package/dist/adonisjs/provider.d.cts +2 -0
  14. package/dist/adonisjs/provider.d.ts +2 -0
  15. package/dist/adonisjs/stubs/config/apitally.stub +1 -0
  16. package/dist/common/client.cjs +4 -0
  17. package/dist/common/client.cjs.map +1 -1
  18. package/dist/common/client.d.cts +3 -0
  19. package/dist/common/client.d.ts +3 -0
  20. package/dist/common/client.js +4 -0
  21. package/dist/common/client.js.map +1 -1
  22. package/dist/common/consumerRegistry.d.cts +2 -0
  23. package/dist/common/consumerRegistry.d.ts +2 -0
  24. package/dist/common/requestCounter.d.cts +2 -0
  25. package/dist/common/requestCounter.d.ts +2 -0
  26. package/dist/common/requestLogger.cjs +11 -2
  27. package/dist/common/requestLogger.cjs.map +1 -1
  28. package/dist/common/requestLogger.d.cts +6 -1
  29. package/dist/common/requestLogger.d.ts +6 -1
  30. package/dist/common/requestLogger.js +11 -2
  31. package/dist/common/requestLogger.js.map +1 -1
  32. package/dist/common/serverErrorCounter.d.cts +2 -0
  33. package/dist/common/serverErrorCounter.d.ts +2 -0
  34. package/dist/common/spanCollector.cjs +217 -0
  35. package/dist/common/spanCollector.cjs.map +1 -0
  36. package/dist/common/spanCollector.d.cts +50 -0
  37. package/dist/common/spanCollector.d.ts +50 -0
  38. package/dist/common/spanCollector.js +194 -0
  39. package/dist/common/spanCollector.js.map +1 -0
  40. package/dist/common/types.d.cts +2 -0
  41. package/dist/common/types.d.ts +2 -0
  42. package/dist/common/validationErrorCounter.d.cts +2 -0
  43. package/dist/common/validationErrorCounter.d.ts +2 -0
  44. package/dist/elysia/index.cjs +3 -0
  45. package/dist/elysia/index.cjs.map +1 -1
  46. package/dist/elysia/index.d.cts +2 -0
  47. package/dist/elysia/index.d.ts +2 -0
  48. package/dist/elysia/index.js +2 -0
  49. package/dist/elysia/index.js.map +1 -1
  50. package/dist/elysia/plugin.cjs +9 -1
  51. package/dist/elysia/plugin.cjs.map +1 -1
  52. package/dist/elysia/plugin.d.cts +4 -0
  53. package/dist/elysia/plugin.d.ts +4 -0
  54. package/dist/elysia/plugin.js +9 -1
  55. package/dist/elysia/plugin.js.map +1 -1
  56. package/dist/elysia/utils.d.cts +2 -0
  57. package/dist/elysia/utils.d.ts +2 -0
  58. package/dist/express/index.cjs +3 -0
  59. package/dist/express/index.cjs.map +1 -1
  60. package/dist/express/index.d.cts +2 -0
  61. package/dist/express/index.d.ts +2 -0
  62. package/dist/express/index.js +2 -0
  63. package/dist/express/index.js.map +1 -1
  64. package/dist/express/middleware.cjs +85 -77
  65. package/dist/express/middleware.cjs.map +1 -1
  66. package/dist/express/middleware.d.cts +2 -0
  67. package/dist/express/middleware.d.ts +2 -0
  68. package/dist/express/middleware.js +85 -77
  69. package/dist/express/middleware.js.map +1 -1
  70. package/dist/fastify/index.cjs +3 -0
  71. package/dist/fastify/index.cjs.map +1 -1
  72. package/dist/fastify/index.d.cts +2 -0
  73. package/dist/fastify/index.d.ts +2 -0
  74. package/dist/fastify/index.js +2 -0
  75. package/dist/fastify/index.js.map +1 -1
  76. package/dist/fastify/plugin.cjs +14 -5
  77. package/dist/fastify/plugin.cjs.map +1 -1
  78. package/dist/fastify/plugin.d.cts +4 -0
  79. package/dist/fastify/plugin.d.ts +4 -0
  80. package/dist/fastify/plugin.js +14 -5
  81. package/dist/fastify/plugin.js.map +1 -1
  82. package/dist/h3/index.cjs +3 -0
  83. package/dist/h3/index.cjs.map +1 -1
  84. package/dist/h3/index.d.cts +2 -0
  85. package/dist/h3/index.d.ts +2 -0
  86. package/dist/h3/index.js +2 -0
  87. package/dist/h3/index.js.map +1 -1
  88. package/dist/h3/plugin.cjs +13 -5
  89. package/dist/h3/plugin.cjs.map +1 -1
  90. package/dist/h3/plugin.d.cts +4 -0
  91. package/dist/h3/plugin.d.ts +4 -0
  92. package/dist/h3/plugin.js +13 -5
  93. package/dist/h3/plugin.js.map +1 -1
  94. package/dist/hapi/index.cjs +3 -0
  95. package/dist/hapi/index.cjs.map +1 -1
  96. package/dist/hapi/index.d.cts +2 -0
  97. package/dist/hapi/index.d.ts +2 -0
  98. package/dist/hapi/index.js +2 -0
  99. package/dist/hapi/index.js.map +1 -1
  100. package/dist/hapi/plugin.cjs +10 -3
  101. package/dist/hapi/plugin.cjs.map +1 -1
  102. package/dist/hapi/plugin.d.cts +4 -0
  103. package/dist/hapi/plugin.d.ts +4 -0
  104. package/dist/hapi/plugin.js +10 -3
  105. package/dist/hapi/plugin.js.map +1 -1
  106. package/dist/hapi/utils.d.cts +2 -0
  107. package/dist/hapi/utils.d.ts +2 -0
  108. package/dist/hono/index.cjs +3 -0
  109. package/dist/hono/index.cjs.map +1 -1
  110. package/dist/hono/index.d.cts +2 -0
  111. package/dist/hono/index.d.ts +2 -0
  112. package/dist/hono/index.js +2 -0
  113. package/dist/hono/index.js.map +1 -1
  114. package/dist/hono/middleware.cjs +6 -2
  115. package/dist/hono/middleware.cjs.map +1 -1
  116. package/dist/hono/middleware.d.cts +2 -0
  117. package/dist/hono/middleware.d.ts +2 -0
  118. package/dist/hono/middleware.js +6 -2
  119. package/dist/hono/middleware.js.map +1 -1
  120. package/dist/hono/utils.d.cts +2 -0
  121. package/dist/hono/utils.d.ts +2 -0
  122. package/dist/koa/index.cjs +3 -0
  123. package/dist/koa/index.cjs.map +1 -1
  124. package/dist/koa/index.d.cts +2 -0
  125. package/dist/koa/index.d.ts +2 -0
  126. package/dist/koa/index.js +2 -0
  127. package/dist/koa/index.js.map +1 -1
  128. package/dist/koa/middleware.cjs +8 -4
  129. package/dist/koa/middleware.cjs.map +1 -1
  130. package/dist/koa/middleware.d.cts +2 -0
  131. package/dist/koa/middleware.d.ts +2 -0
  132. package/dist/koa/middleware.js +8 -4
  133. package/dist/koa/middleware.js.map +1 -1
  134. package/dist/loggers/console.d.cts +4 -0
  135. package/dist/loggers/console.d.ts +4 -0
  136. package/dist/loggers/hapi.d.cts +4 -0
  137. package/dist/loggers/hapi.d.ts +4 -0
  138. package/dist/loggers/index.d.cts +4 -0
  139. package/dist/loggers/index.d.ts +4 -0
  140. package/dist/loggers/nestjs.d.cts +4 -0
  141. package/dist/loggers/nestjs.d.ts +4 -0
  142. package/dist/loggers/pino.d.cts +4 -0
  143. package/dist/loggers/pino.d.ts +4 -0
  144. package/dist/loggers/winston.d.cts +4 -0
  145. package/dist/loggers/winston.d.ts +4 -0
  146. package/dist/nestjs/index.cjs +3 -0
  147. package/dist/nestjs/index.cjs.map +1 -1
  148. package/dist/nestjs/index.d.cts +2 -0
  149. package/dist/nestjs/index.d.ts +2 -0
  150. package/dist/nestjs/index.js +2 -0
  151. package/dist/nestjs/index.js.map +1 -1
  152. package/package.json +5 -2
@@ -19,12 +19,14 @@ var __copyProps = (to, from, except, desc) => {
19
19
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
20
  var adonisjs_exports = {};
21
21
  __export(adonisjs_exports, {
22
+ ApitallySpanProcessor: () => import_spanCollector.ApitallySpanProcessor,
22
23
  captureError: () => captureError,
23
24
  configure: () => import_configure.configure,
24
25
  defineConfig: () => defineConfig,
25
26
  setConsumer: () => setConsumer
26
27
  });
27
28
  module.exports = __toCommonJS(adonisjs_exports);
29
+ var import_spanCollector = require("../common/spanCollector.js");
28
30
  var import_configure = require("./configure.js");
29
31
  function defineConfig(config) {
30
32
  return config;
@@ -42,6 +44,7 @@ function setConsumer(ctx, consumer) {
42
44
  __name(setConsumer, "setConsumer");
43
45
  // Annotate the CommonJS export names for ESM import in node:
44
46
  0 && (module.exports = {
47
+ ApitallySpanProcessor,
45
48
  captureError,
46
49
  configure,
47
50
  defineConfig,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adonisjs/index.ts"],"sourcesContent":["import type { HttpContext } from \"@adonisjs/core/http\";\n\nimport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { configure } from \"./configure.js\";\n\nexport function defineConfig(config: ApitallyConfig) {\n return config;\n}\n\nexport function captureError(error: unknown, ctx: HttpContext) {\n if (error instanceof Error) {\n ctx.apitallyError = error;\n }\n}\n\nexport function setConsumer(\n ctx: HttpContext,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n ctx.apitallyConsumer = consumer || undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAIA;;;;;;;;AAAA,uBAA0B;AAEnB,SAASA,aAAaC,QAAsB;AACjD,SAAOA;AACT;AAFgBD;AAIT,SAASE,aAAaC,OAAgBC,KAAgB;AAC3D,MAAID,iBAAiBE,OAAO;AAC1BD,QAAIE,gBAAgBH;EACtB;AACF;AAJgBD;AAMT,SAASK,YACdH,KACAI,UAAsD;AAEtDJ,MAAIK,mBAAmBD,YAAYE;AACrC;AALgBH;","names":["defineConfig","config","captureError","error","ctx","Error","apitallyError","setConsumer","consumer","apitallyConsumer","undefined"]}
1
+ {"version":3,"sources":["../../src/adonisjs/index.ts"],"sourcesContent":["import type { HttpContext } from \"@adonisjs/core/http\";\n\nimport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { ApitallySpanProcessor } from \"../common/spanCollector.js\";\nexport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { configure } from \"./configure.js\";\n\nexport function defineConfig(config: ApitallyConfig) {\n return config;\n}\n\nexport function captureError(error: unknown, ctx: HttpContext) {\n if (error instanceof Error) {\n ctx.apitallyError = error;\n }\n}\n\nexport function setConsumer(\n ctx: HttpContext,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n ctx.apitallyConsumer = consumer || undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAGA;;;;;;;;;AAAA,2BAAsC;AAEtC,uBAA0B;AAEnB,SAASA,aAAaC,QAAsB;AACjD,SAAOA;AACT;AAFgBD;AAIT,SAASE,aAAaC,OAAgBC,KAAgB;AAC3D,MAAID,iBAAiBE,OAAO;AAC1BD,QAAIE,gBAAgBH;EACtB;AACF;AAJgBD;AAMT,SAASK,YACdH,KACAI,UAAsD;AAEtDJ,MAAIK,mBAAmBD,YAAYE;AACrC;AALgBH;","names":["defineConfig","config","captureError","error","ctx","Error","apitallyError","setConsumer","consumer","apitallyConsumer","undefined"]}
@@ -1,5 +1,6 @@
1
1
  import { HttpContext } from '@adonisjs/core/http';
2
2
  import { ApitallyConfig, ApitallyConsumer } from '../common/types.cjs';
3
+ export { ApitallySpanProcessor } from '../common/spanCollector.cjs';
3
4
  export { configure } from './configure.cjs';
4
5
  import '../common/logging.cjs';
5
6
  import 'winston';
@@ -7,6 +8,7 @@ import '../common/requestLogger.cjs';
7
8
  import 'node:buffer';
8
9
  import 'node:http';
9
10
  import '../common/tempGzipFile.cjs';
11
+ import '@opentelemetry/sdk-trace-base';
10
12
  import '@adonisjs/core/commands/configure';
11
13
 
12
14
  declare function defineConfig(config: ApitallyConfig): ApitallyConfig;
@@ -1,5 +1,6 @@
1
1
  import { HttpContext } from '@adonisjs/core/http';
2
2
  import { ApitallyConfig, ApitallyConsumer } from '../common/types.js';
3
+ export { ApitallySpanProcessor } from '../common/spanCollector.js';
3
4
  export { configure } from './configure.js';
4
5
  import '../common/logging.js';
5
6
  import 'winston';
@@ -7,6 +8,7 @@ import '../common/requestLogger.js';
7
8
  import 'node:buffer';
8
9
  import 'node:http';
9
10
  import '../common/tempGzipFile.js';
11
+ import '@opentelemetry/sdk-trace-base';
10
12
  import '@adonisjs/core/commands/configure';
11
13
 
12
14
  declare function defineConfig(config: ApitallyConfig): ApitallyConfig;
@@ -1,5 +1,6 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ import { ApitallySpanProcessor } from "../common/spanCollector.js";
3
4
  import { configure } from "./configure.js";
4
5
  function defineConfig(config) {
5
6
  return config;
@@ -16,6 +17,7 @@ function setConsumer(ctx, consumer) {
16
17
  }
17
18
  __name(setConsumer, "setConsumer");
18
19
  export {
20
+ ApitallySpanProcessor,
19
21
  captureError,
20
22
  configure,
21
23
  defineConfig,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adonisjs/index.ts"],"sourcesContent":["import type { HttpContext } from \"@adonisjs/core/http\";\n\nimport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { configure } from \"./configure.js\";\n\nexport function defineConfig(config: ApitallyConfig) {\n return config;\n}\n\nexport function captureError(error: unknown, ctx: HttpContext) {\n if (error instanceof Error) {\n ctx.apitallyError = error;\n }\n}\n\nexport function setConsumer(\n ctx: HttpContext,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n ctx.apitallyConsumer = consumer || undefined;\n}\n"],"mappings":";;AAIA,SAASA,iBAAiB;AAEnB,SAASC,aAAaC,QAAsB;AACjD,SAAOA;AACT;AAFgBD;AAIT,SAASE,aAAaC,OAAgBC,KAAgB;AAC3D,MAAID,iBAAiBE,OAAO;AAC1BD,QAAIE,gBAAgBH;EACtB;AACF;AAJgBD;AAMT,SAASK,YACdH,KACAI,UAAsD;AAEtDJ,MAAIK,mBAAmBD,YAAYE;AACrC;AALgBH;","names":["configure","defineConfig","config","captureError","error","ctx","Error","apitallyError","setConsumer","consumer","apitallyConsumer","undefined"]}
1
+ {"version":3,"sources":["../../src/adonisjs/index.ts"],"sourcesContent":["import type { HttpContext } from \"@adonisjs/core/http\";\n\nimport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { ApitallySpanProcessor } from \"../common/spanCollector.js\";\nexport type { ApitallyConfig, ApitallyConsumer } from \"../common/types.js\";\nexport { configure } from \"./configure.js\";\n\nexport function defineConfig(config: ApitallyConfig) {\n return config;\n}\n\nexport function captureError(error: unknown, ctx: HttpContext) {\n if (error instanceof Error) {\n ctx.apitallyError = error;\n }\n}\n\nexport function setConsumer(\n ctx: HttpContext,\n consumer: ApitallyConsumer | string | null | undefined,\n) {\n ctx.apitallyConsumer = consumer || undefined;\n}\n"],"mappings":";;AAGA,SAASA,6BAA6B;AAEtC,SAASC,iBAAiB;AAEnB,SAASC,aAAaC,QAAsB;AACjD,SAAOA;AACT;AAFgBD;AAIT,SAASE,aAAaC,OAAgBC,KAAgB;AAC3D,MAAID,iBAAiBE,OAAO;AAC1BD,QAAIE,gBAAgBH;EACtB;AACF;AAJgBD;AAMT,SAASK,YACdH,KACAI,UAAsD;AAEtDJ,MAAIK,mBAAmBD,YAAYE;AACrC;AALgBH;","names":["ApitallySpanProcessor","configure","defineConfig","config","captureError","error","ctx","Error","apitallyError","setConsumer","consumer","apitallyConsumer","undefined"]}
@@ -39,8 +39,12 @@ const _ApitallyMiddleware = class _ApitallyMiddleware {
39
39
  const path = (_a = ctx.route) == null ? void 0 : _a.pattern;
40
40
  const timestamp = Date.now() / 1e3;
41
41
  const startTime = import_node_perf_hooks.performance.now();
42
- await next();
42
+ const spanHandle = client.spanCollector.startSpan();
43
+ await spanHandle.runInContext(next);
43
44
  const responseTime = import_node_perf_hooks.performance.now() - startTime;
45
+ spanHandle.setName(`${ctx.request.method()} ${path}`);
46
+ const spans = spanHandle.end();
47
+ const traceId = spanHandle.traceId;
44
48
  const requestSize = (0, import_headers.parseContentLength)(ctx.request.header("content-length"));
45
49
  const requestContentType = (_b = ctx.request.header("content-type")) == null ? void 0 : _b.toString();
46
50
  let responseStatus = ctx.response.getStatus();
@@ -115,7 +119,7 @@ const _ApitallyMiddleware = class _ApitallyMiddleware {
115
119
  headers: (0, import_requestLogger.convertHeaders)(responseHeaders),
116
120
  size: responseSize,
117
121
  body: responseBody ? Buffer.from(responseBody) : void 0
118
- }, ctx.apitallyError, logs);
122
+ }, ctx.apitallyError, logs, spans, traceId);
119
123
  }, "onEnd");
120
124
  const originalEnd = ctx.response.response.end;
121
125
  ctx.response.response.end = (...args) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adonisjs/middleware.ts"],"sourcesContent":["import { HttpContext } from \"@adonisjs/core/http\";\nimport { NextFn } from \"@adonisjs/core/types/http\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { OutgoingHttpHeaders } from \"node:http\";\nimport { performance } from \"node:perf_hooks\";\n\nimport type { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport type { ApitallyConsumer } from \"../common/types.js\";\n\ndeclare module \"@adonisjs/core/http\" {\n interface HttpContext {\n apitallyConsumer?: ApitallyConsumer | string;\n apitallyError?: Error;\n }\n}\n\nexport default class ApitallyMiddleware {\n async handle(ctx: HttpContext, next: NextFn) {\n const client: ApitallyClient =\n await ctx.containerResolver.make(\"apitallyClient\");\n const logsContext: AsyncLocalStorage<LogRecord[]> =\n await ctx.containerResolver.make(\"apitallyLogsContext\");\n\n if (\n !client.isEnabled() ||\n ctx.request.method().toUpperCase() === \"OPTIONS\"\n ) {\n await next();\n return;\n }\n\n return logsContext.run([], async () => {\n const path = ctx.route?.pattern;\n const timestamp = Date.now() / 1000;\n const startTime = performance.now();\n\n await next();\n\n const responseTime = performance.now() - startTime;\n const requestSize = parseContentLength(\n ctx.request.header(\"content-length\"),\n );\n const requestContentType = ctx.request.header(\"content-type\")?.toString();\n let responseStatus = ctx.response.getStatus();\n let responseHeaders = ctx.response.getHeaders();\n let responseSize: number | undefined;\n let responseContentType: string | undefined;\n\n const consumer = ctx.apitallyConsumer\n ? consumerFromStringOrObject(ctx.apitallyConsumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n const onWriteHead = (\n statusCode: number,\n headers: OutgoingHttpHeaders,\n ) => {\n responseStatus = statusCode;\n responseHeaders = headers;\n responseSize = parseContentLength(headers[\"content-length\"]);\n responseContentType = headers[\"content-type\"]?.toString();\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n statusCode: responseStatus,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n responseStatus === 422 &&\n ctx.apitallyError &&\n \"code\" in ctx.apitallyError &&\n \"messages\" in ctx.apitallyError &&\n ctx.apitallyError.code === \"E_VALIDATION_ERROR\" &&\n Array.isArray(ctx.apitallyError.messages)\n ) {\n ctx.apitallyError.messages.forEach((message) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n loc: message.field,\n msg: message.message,\n type: message.rule,\n });\n });\n }\n\n if (responseStatus === 500 && ctx.apitallyError) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n type: ctx.apitallyError.name,\n msg: ctx.apitallyError.message,\n traceback: ctx.apitallyError.stack || \"\",\n });\n }\n }\n };\n\n // Capture the final status code and response headers just before they are sent\n const originalWriteHead = ctx.response.response.writeHead;\n ctx.response.response.writeHead = (...args: any) => {\n originalWriteHead.apply(ctx.response.response, args);\n onWriteHead(args[0], typeof args[1] === \"string\" ? args[2] : args[1]);\n return ctx.response.response;\n };\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n\n const onEnd = (chunk: any) => {\n const requestBody =\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType)\n ? ctx.request.raw()\n : undefined;\n const responseBody =\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(responseContentType)\n ? chunk\n : undefined;\n client.requestLogger.logRequest(\n {\n timestamp,\n method: ctx.request.method(),\n path,\n url: ctx.request.completeUrl(true),\n headers: convertHeaders(ctx.request.headers()),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody ? Buffer.from(requestBody) : undefined,\n },\n {\n statusCode: responseStatus,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: responseBody ? Buffer.from(responseBody) : undefined,\n },\n ctx.apitallyError,\n logs,\n );\n };\n\n // Capture the final response body just before it is sent\n const originalEnd = ctx.response.response.end;\n ctx.response.response.end = (...args: any) => {\n originalEnd.apply(ctx.response.response, args);\n onEnd(typeof args[0] !== \"function\" ? args[0] : undefined);\n return ctx.response.response;\n };\n }\n });\n }\n}\n"],"mappings":"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","names":["ApitallyMiddleware","handle","ctx","next","client","containerResolver","make","logsContext","isEnabled","request","method","toUpperCase","run","path","route","pattern","timestamp","Date","now","startTime","performance","responseTime","requestSize","parseContentLength","header","requestContentType","toString","responseStatus","response","getStatus","responseHeaders","getHeaders","responseSize","responseContentType","consumer","apitallyConsumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","onWriteHead","statusCode","headers","_a","requestCounter","addRequest","identifier","apitallyError","code","Array","isArray","messages","forEach","message","validationErrorCounter","addValidationError","loc","field","msg","type","rule","serverErrorCounter","addServerError","name","traceback","stack","originalWriteHead","writeHead","args","apply","requestLogger","enabled","logs","getStore","onEnd","chunk","requestBody","config","logRequestBody","isSupportedContentType","raw","undefined","responseBody","logResponseBody","logRequest","url","completeUrl","convertHeaders","size","body","Buffer","from","originalEnd","end"]}
1
+ {"version":3,"sources":["../../src/adonisjs/middleware.ts"],"sourcesContent":["import { HttpContext } from \"@adonisjs/core/http\";\nimport { NextFn } from \"@adonisjs/core/types/http\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { OutgoingHttpHeaders } from \"node:http\";\nimport { performance } from \"node:perf_hooks\";\n\nimport type { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport type { ApitallyConsumer } from \"../common/types.js\";\n\ndeclare module \"@adonisjs/core/http\" {\n interface HttpContext {\n apitallyConsumer?: ApitallyConsumer | string;\n apitallyError?: Error;\n }\n}\n\nexport default class ApitallyMiddleware {\n async handle(ctx: HttpContext, next: NextFn) {\n const client: ApitallyClient =\n await ctx.containerResolver.make(\"apitallyClient\");\n const logsContext: AsyncLocalStorage<LogRecord[]> =\n await ctx.containerResolver.make(\"apitallyLogsContext\");\n\n if (\n !client.isEnabled() ||\n ctx.request.method().toUpperCase() === \"OPTIONS\"\n ) {\n await next();\n return;\n }\n\n return logsContext.run([], async () => {\n const path = ctx.route?.pattern;\n const timestamp = Date.now() / 1000;\n const startTime = performance.now();\n\n const spanHandle = client.spanCollector.startSpan();\n await spanHandle.runInContext(next);\n\n const responseTime = performance.now() - startTime;\n spanHandle.setName(`${ctx.request.method()} ${path}`);\n const spans = spanHandle.end();\n const traceId = spanHandle.traceId;\n\n const requestSize = parseContentLength(\n ctx.request.header(\"content-length\"),\n );\n const requestContentType = ctx.request.header(\"content-type\")?.toString();\n let responseStatus = ctx.response.getStatus();\n let responseHeaders = ctx.response.getHeaders();\n let responseSize: number | undefined;\n let responseContentType: string | undefined;\n\n const consumer = ctx.apitallyConsumer\n ? consumerFromStringOrObject(ctx.apitallyConsumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n const onWriteHead = (\n statusCode: number,\n headers: OutgoingHttpHeaders,\n ) => {\n responseStatus = statusCode;\n responseHeaders = headers;\n responseSize = parseContentLength(headers[\"content-length\"]);\n responseContentType = headers[\"content-type\"]?.toString();\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n statusCode: responseStatus,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n responseStatus === 422 &&\n ctx.apitallyError &&\n \"code\" in ctx.apitallyError &&\n \"messages\" in ctx.apitallyError &&\n ctx.apitallyError.code === \"E_VALIDATION_ERROR\" &&\n Array.isArray(ctx.apitallyError.messages)\n ) {\n ctx.apitallyError.messages.forEach((message) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n loc: message.field,\n msg: message.message,\n type: message.rule,\n });\n });\n }\n\n if (responseStatus === 500 && ctx.apitallyError) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n type: ctx.apitallyError.name,\n msg: ctx.apitallyError.message,\n traceback: ctx.apitallyError.stack || \"\",\n });\n }\n }\n };\n\n // Capture the final status code and response headers just before they are sent\n const originalWriteHead = ctx.response.response.writeHead;\n ctx.response.response.writeHead = (...args: any) => {\n originalWriteHead.apply(ctx.response.response, args);\n onWriteHead(args[0], typeof args[1] === \"string\" ? args[2] : args[1]);\n return ctx.response.response;\n };\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n\n const onEnd = (chunk: any) => {\n const requestBody =\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType)\n ? ctx.request.raw()\n : undefined;\n const responseBody =\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(responseContentType)\n ? chunk\n : undefined;\n\n client.requestLogger.logRequest(\n {\n timestamp,\n method: ctx.request.method(),\n path,\n url: ctx.request.completeUrl(true),\n headers: convertHeaders(ctx.request.headers()),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody ? Buffer.from(requestBody) : undefined,\n },\n {\n statusCode: responseStatus,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: responseBody ? Buffer.from(responseBody) : undefined,\n },\n ctx.apitallyError,\n logs,\n spans,\n traceId,\n );\n };\n\n // Capture the final response body just before it is sent\n const originalEnd = ctx.response.response.end;\n ctx.response.response.end = (...args: any) => {\n originalEnd.apply(ctx.response.response, args);\n onEnd(typeof args[0] !== \"function\" ? args[0] : undefined);\n return ctx.response.response;\n };\n }\n });\n }\n}\n"],"mappings":"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","names":["ApitallyMiddleware","handle","ctx","next","client","containerResolver","make","logsContext","isEnabled","request","method","toUpperCase","run","path","route","pattern","timestamp","Date","now","startTime","performance","spanHandle","spanCollector","startSpan","runInContext","responseTime","setName","spans","end","traceId","requestSize","parseContentLength","header","requestContentType","toString","responseStatus","response","getStatus","responseHeaders","getHeaders","responseSize","responseContentType","consumer","apitallyConsumer","consumerFromStringOrObject","consumerRegistry","addOrUpdateConsumer","onWriteHead","statusCode","headers","_a","requestCounter","addRequest","identifier","apitallyError","code","Array","isArray","messages","forEach","message","validationErrorCounter","addValidationError","loc","field","msg","type","rule","serverErrorCounter","addServerError","name","traceback","stack","originalWriteHead","writeHead","args","apply","requestLogger","enabled","logs","getStore","onEnd","chunk","requestBody","config","logRequestBody","isSupportedContentType","raw","undefined","responseBody","logResponseBody","logRequest","url","completeUrl","convertHeaders","size","body","Buffer","from","originalEnd"]}
@@ -6,6 +6,8 @@ import 'winston';
6
6
  import '../common/requestLogger.cjs';
7
7
  import 'node:buffer';
8
8
  import 'node:http';
9
+ import '../common/spanCollector.cjs';
10
+ import '@opentelemetry/sdk-trace-base';
9
11
  import '../common/tempGzipFile.cjs';
10
12
 
11
13
  declare module "@adonisjs/core/http" {
@@ -6,6 +6,8 @@ import 'winston';
6
6
  import '../common/requestLogger.js';
7
7
  import 'node:buffer';
8
8
  import 'node:http';
9
+ import '../common/spanCollector.js';
10
+ import '@opentelemetry/sdk-trace-base';
9
11
  import '../common/tempGzipFile.js';
10
12
 
11
13
  declare module "@adonisjs/core/http" {
@@ -17,8 +17,12 @@ const _ApitallyMiddleware = class _ApitallyMiddleware {
17
17
  const path = (_a = ctx.route) == null ? void 0 : _a.pattern;
18
18
  const timestamp = Date.now() / 1e3;
19
19
  const startTime = performance.now();
20
- await next();
20
+ const spanHandle = client.spanCollector.startSpan();
21
+ await spanHandle.runInContext(next);
21
22
  const responseTime = performance.now() - startTime;
23
+ spanHandle.setName(`${ctx.request.method()} ${path}`);
24
+ const spans = spanHandle.end();
25
+ const traceId = spanHandle.traceId;
22
26
  const requestSize = parseContentLength(ctx.request.header("content-length"));
23
27
  const requestContentType = (_b = ctx.request.header("content-type")) == null ? void 0 : _b.toString();
24
28
  let responseStatus = ctx.response.getStatus();
@@ -93,7 +97,7 @@ const _ApitallyMiddleware = class _ApitallyMiddleware {
93
97
  headers: convertHeaders(responseHeaders),
94
98
  size: responseSize,
95
99
  body: responseBody ? Buffer.from(responseBody) : void 0
96
- }, ctx.apitallyError, logs);
100
+ }, ctx.apitallyError, logs, spans, traceId);
97
101
  }, "onEnd");
98
102
  const originalEnd = ctx.response.response.end;
99
103
  ctx.response.response.end = (...args) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/adonisjs/middleware.ts"],"sourcesContent":["import { HttpContext } from \"@adonisjs/core/http\";\nimport { NextFn } from \"@adonisjs/core/types/http\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { OutgoingHttpHeaders } from \"node:http\";\nimport { performance } from \"node:perf_hooks\";\n\nimport type { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport type { ApitallyConsumer } from \"../common/types.js\";\n\ndeclare module \"@adonisjs/core/http\" {\n interface HttpContext {\n apitallyConsumer?: ApitallyConsumer | string;\n apitallyError?: Error;\n }\n}\n\nexport default class ApitallyMiddleware {\n async handle(ctx: HttpContext, next: NextFn) {\n const client: ApitallyClient =\n await ctx.containerResolver.make(\"apitallyClient\");\n const logsContext: AsyncLocalStorage<LogRecord[]> =\n await ctx.containerResolver.make(\"apitallyLogsContext\");\n\n if (\n !client.isEnabled() ||\n ctx.request.method().toUpperCase() === \"OPTIONS\"\n ) {\n await next();\n return;\n }\n\n return logsContext.run([], async () => {\n const path = ctx.route?.pattern;\n const timestamp = Date.now() / 1000;\n const startTime = performance.now();\n\n await next();\n\n const responseTime = performance.now() - startTime;\n const requestSize = parseContentLength(\n ctx.request.header(\"content-length\"),\n );\n const requestContentType = ctx.request.header(\"content-type\")?.toString();\n let responseStatus = ctx.response.getStatus();\n let responseHeaders = ctx.response.getHeaders();\n let responseSize: number | undefined;\n let responseContentType: string | undefined;\n\n const consumer = ctx.apitallyConsumer\n ? consumerFromStringOrObject(ctx.apitallyConsumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n const onWriteHead = (\n statusCode: number,\n headers: OutgoingHttpHeaders,\n ) => {\n responseStatus = statusCode;\n responseHeaders = headers;\n responseSize = parseContentLength(headers[\"content-length\"]);\n responseContentType = headers[\"content-type\"]?.toString();\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n statusCode: responseStatus,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n responseStatus === 422 &&\n ctx.apitallyError &&\n \"code\" in ctx.apitallyError &&\n \"messages\" in ctx.apitallyError &&\n ctx.apitallyError.code === \"E_VALIDATION_ERROR\" &&\n Array.isArray(ctx.apitallyError.messages)\n ) {\n ctx.apitallyError.messages.forEach((message) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n loc: message.field,\n msg: message.message,\n type: message.rule,\n });\n });\n }\n\n if (responseStatus === 500 && ctx.apitallyError) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n type: ctx.apitallyError.name,\n msg: ctx.apitallyError.message,\n traceback: ctx.apitallyError.stack || \"\",\n });\n }\n }\n };\n\n // Capture the final status code and response headers just before they are sent\n const originalWriteHead = ctx.response.response.writeHead;\n ctx.response.response.writeHead = (...args: any) => {\n originalWriteHead.apply(ctx.response.response, args);\n onWriteHead(args[0], typeof args[1] === \"string\" ? args[2] : args[1]);\n return ctx.response.response;\n };\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n\n const onEnd = (chunk: any) => {\n const requestBody =\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType)\n ? ctx.request.raw()\n : undefined;\n const responseBody =\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(responseContentType)\n ? chunk\n : undefined;\n client.requestLogger.logRequest(\n {\n timestamp,\n method: ctx.request.method(),\n path,\n url: ctx.request.completeUrl(true),\n headers: convertHeaders(ctx.request.headers()),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody ? Buffer.from(requestBody) : undefined,\n },\n {\n statusCode: responseStatus,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: responseBody ? Buffer.from(responseBody) : undefined,\n },\n ctx.apitallyError,\n logs,\n );\n };\n\n // Capture the final response body just before it is sent\n const originalEnd = ctx.response.response.end;\n ctx.response.response.end = (...args: any) => {\n originalEnd.apply(ctx.response.response, args);\n onEnd(typeof args[0] !== \"function\" ? args[0] : undefined);\n return ctx.response.response;\n };\n }\n });\n }\n}\n"],"mappings":"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","names":["performance","consumerFromStringOrObject","parseContentLength","convertHeaders","ApitallyMiddleware","handle","ctx","next","client","containerResolver","make","logsContext","isEnabled","request","method","toUpperCase","run","path","route","pattern","timestamp","Date","now","startTime","responseTime","requestSize","header","requestContentType","toString","responseStatus","response","getStatus","responseHeaders","getHeaders","responseSize","responseContentType","consumer","apitallyConsumer","consumerRegistry","addOrUpdateConsumer","onWriteHead","statusCode","headers","_a","requestCounter","addRequest","identifier","apitallyError","code","Array","isArray","messages","forEach","message","validationErrorCounter","addValidationError","loc","field","msg","type","rule","serverErrorCounter","addServerError","name","traceback","stack","originalWriteHead","writeHead","args","apply","requestLogger","enabled","logs","getStore","onEnd","chunk","requestBody","config","logRequestBody","isSupportedContentType","raw","undefined","responseBody","logResponseBody","logRequest","url","completeUrl","size","body","Buffer","from","originalEnd","end"]}
1
+ {"version":3,"sources":["../../src/adonisjs/middleware.ts"],"sourcesContent":["import { HttpContext } from \"@adonisjs/core/http\";\nimport { NextFn } from \"@adonisjs/core/types/http\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\nimport type { OutgoingHttpHeaders } from \"node:http\";\nimport { performance } from \"node:perf_hooks\";\n\nimport type { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { parseContentLength } from \"../common/headers.js\";\nimport type { LogRecord } from \"../common/requestLogger.js\";\nimport { convertHeaders } from \"../common/requestLogger.js\";\nimport type { ApitallyConsumer } from \"../common/types.js\";\n\ndeclare module \"@adonisjs/core/http\" {\n interface HttpContext {\n apitallyConsumer?: ApitallyConsumer | string;\n apitallyError?: Error;\n }\n}\n\nexport default class ApitallyMiddleware {\n async handle(ctx: HttpContext, next: NextFn) {\n const client: ApitallyClient =\n await ctx.containerResolver.make(\"apitallyClient\");\n const logsContext: AsyncLocalStorage<LogRecord[]> =\n await ctx.containerResolver.make(\"apitallyLogsContext\");\n\n if (\n !client.isEnabled() ||\n ctx.request.method().toUpperCase() === \"OPTIONS\"\n ) {\n await next();\n return;\n }\n\n return logsContext.run([], async () => {\n const path = ctx.route?.pattern;\n const timestamp = Date.now() / 1000;\n const startTime = performance.now();\n\n const spanHandle = client.spanCollector.startSpan();\n await spanHandle.runInContext(next);\n\n const responseTime = performance.now() - startTime;\n spanHandle.setName(`${ctx.request.method()} ${path}`);\n const spans = spanHandle.end();\n const traceId = spanHandle.traceId;\n\n const requestSize = parseContentLength(\n ctx.request.header(\"content-length\"),\n );\n const requestContentType = ctx.request.header(\"content-type\")?.toString();\n let responseStatus = ctx.response.getStatus();\n let responseHeaders = ctx.response.getHeaders();\n let responseSize: number | undefined;\n let responseContentType: string | undefined;\n\n const consumer = ctx.apitallyConsumer\n ? consumerFromStringOrObject(ctx.apitallyConsumer)\n : null;\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n\n const onWriteHead = (\n statusCode: number,\n headers: OutgoingHttpHeaders,\n ) => {\n responseStatus = statusCode;\n responseHeaders = headers;\n responseSize = parseContentLength(headers[\"content-length\"]);\n responseContentType = headers[\"content-type\"]?.toString();\n\n if (path) {\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n statusCode: responseStatus,\n responseTime,\n requestSize,\n responseSize,\n });\n\n if (\n responseStatus === 422 &&\n ctx.apitallyError &&\n \"code\" in ctx.apitallyError &&\n \"messages\" in ctx.apitallyError &&\n ctx.apitallyError.code === \"E_VALIDATION_ERROR\" &&\n Array.isArray(ctx.apitallyError.messages)\n ) {\n ctx.apitallyError.messages.forEach((message) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n loc: message.field,\n msg: message.message,\n type: message.rule,\n });\n });\n }\n\n if (responseStatus === 500 && ctx.apitallyError) {\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: ctx.request.method(),\n path,\n type: ctx.apitallyError.name,\n msg: ctx.apitallyError.message,\n traceback: ctx.apitallyError.stack || \"\",\n });\n }\n }\n };\n\n // Capture the final status code and response headers just before they are sent\n const originalWriteHead = ctx.response.response.writeHead;\n ctx.response.response.writeHead = (...args: any) => {\n originalWriteHead.apply(ctx.response.response, args);\n onWriteHead(args[0], typeof args[1] === \"string\" ? args[2] : args[1]);\n return ctx.response.response;\n };\n\n if (client.requestLogger.enabled) {\n const logs = logsContext.getStore();\n\n const onEnd = (chunk: any) => {\n const requestBody =\n client.requestLogger.config.logRequestBody &&\n client.requestLogger.isSupportedContentType(requestContentType)\n ? ctx.request.raw()\n : undefined;\n const responseBody =\n client.requestLogger.config.logResponseBody &&\n client.requestLogger.isSupportedContentType(responseContentType)\n ? chunk\n : undefined;\n\n client.requestLogger.logRequest(\n {\n timestamp,\n method: ctx.request.method(),\n path,\n url: ctx.request.completeUrl(true),\n headers: convertHeaders(ctx.request.headers()),\n size: requestSize,\n consumer: consumer?.identifier,\n body: requestBody ? Buffer.from(requestBody) : undefined,\n },\n {\n statusCode: responseStatus,\n responseTime: responseTime / 1000,\n headers: convertHeaders(responseHeaders),\n size: responseSize,\n body: responseBody ? Buffer.from(responseBody) : undefined,\n },\n ctx.apitallyError,\n logs,\n spans,\n traceId,\n );\n };\n\n // Capture the final response body just before it is sent\n const originalEnd = ctx.response.response.end;\n ctx.response.response.end = (...args: any) => {\n originalEnd.apply(ctx.response.response, args);\n onEnd(typeof args[0] !== \"function\" ? args[0] : undefined);\n return ctx.response.response;\n };\n }\n });\n }\n}\n"],"mappings":"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","names":["performance","consumerFromStringOrObject","parseContentLength","convertHeaders","ApitallyMiddleware","handle","ctx","next","client","containerResolver","make","logsContext","isEnabled","request","method","toUpperCase","run","path","route","pattern","timestamp","Date","now","startTime","spanHandle","spanCollector","startSpan","runInContext","responseTime","setName","spans","end","traceId","requestSize","header","requestContentType","toString","responseStatus","response","getStatus","responseHeaders","getHeaders","responseSize","responseContentType","consumer","apitallyConsumer","consumerRegistry","addOrUpdateConsumer","onWriteHead","statusCode","headers","_a","requestCounter","addRequest","identifier","apitallyError","code","Array","isArray","messages","forEach","message","validationErrorCounter","addValidationError","loc","field","msg","type","rule","serverErrorCounter","addServerError","name","traceback","stack","originalWriteHead","writeHead","args","apply","requestLogger","enabled","logs","getStore","onEnd","chunk","requestBody","config","logRequestBody","isSupportedContentType","raw","undefined","responseBody","logResponseBody","logRequest","url","completeUrl","size","body","Buffer","from","originalEnd"]}
@@ -8,6 +8,8 @@ import '../common/logging.cjs';
8
8
  import 'winston';
9
9
  import '../common/requestCounter.cjs';
10
10
  import '../common/serverErrorCounter.cjs';
11
+ import '../common/spanCollector.cjs';
12
+ import '@opentelemetry/sdk-trace-base';
11
13
  import '../common/validationErrorCounter.cjs';
12
14
  import 'node:buffer';
13
15
  import 'node:http';
@@ -8,6 +8,8 @@ import '../common/logging.js';
8
8
  import 'winston';
9
9
  import '../common/requestCounter.js';
10
10
  import '../common/serverErrorCounter.js';
11
+ import '../common/spanCollector.js';
12
+ import '@opentelemetry/sdk-trace-base';
11
13
  import '../common/validationErrorCounter.js';
12
14
  import 'node:buffer';
13
15
  import 'node:http';
@@ -17,6 +17,7 @@ const apitallyConfig = defineConfig({
17
17
  logRequestBody: true,
18
18
  logResponseBody: true,
19
19
  captureLogs: true,
20
+ captureTraces: true,
20
21
  },
21
22
  {{/if}}
22
23
  })
@@ -44,6 +44,7 @@ var import_requestCounter = __toESM(require("./requestCounter.js"), 1);
44
44
  var import_requestLogger = __toESM(require("./requestLogger.js"), 1);
45
45
  var import_resources = require("./resources.js");
46
46
  var import_serverErrorCounter = __toESM(require("./serverErrorCounter.js"), 1);
47
+ var import_spanCollector = __toESM(require("./spanCollector.js"), 1);
47
48
  var import_validationErrorCounter = __toESM(require("./validationErrorCounter.js"), 1);
48
49
  var _a;
49
50
  const SYNC_INTERVAL = 6e4;
@@ -69,6 +70,7 @@ const _ApitallyClient = class _ApitallyClient {
69
70
  enabled = true;
70
71
  requestCounter;
71
72
  requestLogger;
73
+ spanCollector;
72
74
  validationErrorCounter;
73
75
  serverErrorCounter;
74
76
  consumerRegistry;
@@ -96,6 +98,7 @@ const _ApitallyClient = class _ApitallyClient {
96
98
  this.syncDataQueue = [];
97
99
  this.requestCounter = new import_requestCounter.default();
98
100
  this.requestLogger = new import_requestLogger.default(requestLogging ?? requestLoggingConfig);
101
+ this.spanCollector = new import_spanCollector.default(this.enabled && this.requestLogger.enabled && this.requestLogger.config.captureTraces, this.logger);
99
102
  this.validationErrorCounter = new import_validationErrorCounter.default();
100
103
  this.serverErrorCounter = new import_serverErrorCounter.default();
101
104
  this.consumerRegistry = new import_consumerRegistry.default();
@@ -120,6 +123,7 @@ const _ApitallyClient = class _ApitallyClient {
120
123
  this.stopSync();
121
124
  await this.sendSyncData();
122
125
  await this.sendLogData();
126
+ await this.spanCollector.shutdown();
123
127
  await this.requestLogger.close();
124
128
  _ApitallyClient.instance = void 0;
125
129
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { getOrCreateInstanceUuid } from \"./instance.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport { getCpuMemoryUsage } from \"./resources.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = getOrCreateInstanceUuid(clientId, env);\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n resources: getCpuMemoryUsage(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":"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","names":["SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","getLogger","isValidClientId","error","isValidEnv","console","warn","getOrCreateInstanceUuid","RequestCounter","RequestLogger","ValidationErrorCounter","ServerErrorCounter","ConsumerRegistry","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetchRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","randomUUID","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","resources","getCpuMemoryUsage","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
1
+ {"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { getOrCreateInstanceUuid } from \"./instance.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport { getCpuMemoryUsage } from \"./resources.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport SpanCollector from \"./spanCollector.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public spanCollector: SpanCollector;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = getOrCreateInstanceUuid(clientId, env);\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.spanCollector = new SpanCollector(\n this.enabled &&\n this.requestLogger.enabled &&\n this.requestLogger.config.captureTraces,\n this.logger,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.spanCollector.shutdown();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n resources: getCpuMemoryUsage(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":"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","names":["SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","spanCollector","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","getLogger","isValidClientId","error","isValidEnv","console","warn","getOrCreateInstanceUuid","RequestCounter","RequestLogger","SpanCollector","config","captureTraces","ValidationErrorCounter","ServerErrorCounter","ConsumerRegistry","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetchRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","randomUUID","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","resources","getCpuMemoryUsage","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
@@ -3,12 +3,14 @@ import { Logger } from './logging.cjs';
3
3
  import RequestCounter from './requestCounter.cjs';
4
4
  import RequestLogger from './requestLogger.cjs';
5
5
  import ServerErrorCounter from './serverErrorCounter.cjs';
6
+ import SpanCollector from './spanCollector.cjs';
6
7
  import { StartupData, ApitallyConfig } from './types.cjs';
7
8
  import ValidationErrorCounter from './validationErrorCounter.cjs';
8
9
  import 'winston';
9
10
  import 'node:buffer';
10
11
  import 'node:http';
11
12
  import './tempGzipFile.cjs';
13
+ import '@opentelemetry/sdk-trace-base';
12
14
 
13
15
  declare class ApitallyClient {
14
16
  private clientId;
@@ -22,6 +24,7 @@ declare class ApitallyClient {
22
24
  private enabled;
23
25
  requestCounter: RequestCounter;
24
26
  requestLogger: RequestLogger;
27
+ spanCollector: SpanCollector;
25
28
  validationErrorCounter: ValidationErrorCounter;
26
29
  serverErrorCounter: ServerErrorCounter;
27
30
  consumerRegistry: ConsumerRegistry;
@@ -3,12 +3,14 @@ import { Logger } from './logging.js';
3
3
  import RequestCounter from './requestCounter.js';
4
4
  import RequestLogger from './requestLogger.js';
5
5
  import ServerErrorCounter from './serverErrorCounter.js';
6
+ import SpanCollector from './spanCollector.js';
6
7
  import { StartupData, ApitallyConfig } from './types.js';
7
8
  import ValidationErrorCounter from './validationErrorCounter.js';
8
9
  import 'winston';
9
10
  import 'node:buffer';
10
11
  import 'node:http';
11
12
  import './tempGzipFile.js';
13
+ import '@opentelemetry/sdk-trace-base';
12
14
 
13
15
  declare class ApitallyClient {
14
16
  private clientId;
@@ -22,6 +24,7 @@ declare class ApitallyClient {
22
24
  private enabled;
23
25
  requestCounter: RequestCounter;
24
26
  requestLogger: RequestLogger;
27
+ spanCollector: SpanCollector;
25
28
  validationErrorCounter: ValidationErrorCounter;
26
29
  serverErrorCounter: ServerErrorCounter;
27
30
  consumerRegistry: ConsumerRegistry;
@@ -13,6 +13,7 @@ import RequestCounter from "./requestCounter.js";
13
13
  import RequestLogger from "./requestLogger.js";
14
14
  import { getCpuMemoryUsage } from "./resources.js";
15
15
  import ServerErrorCounter from "./serverErrorCounter.js";
16
+ import SpanCollector from "./spanCollector.js";
16
17
  import ValidationErrorCounter from "./validationErrorCounter.js";
17
18
  const SYNC_INTERVAL = 6e4;
18
19
  const INITIAL_SYNC_INTERVAL = 1e4;
@@ -37,6 +38,7 @@ const _ApitallyClient = class _ApitallyClient {
37
38
  enabled = true;
38
39
  requestCounter;
39
40
  requestLogger;
41
+ spanCollector;
40
42
  validationErrorCounter;
41
43
  serverErrorCounter;
42
44
  consumerRegistry;
@@ -64,6 +66,7 @@ const _ApitallyClient = class _ApitallyClient {
64
66
  this.syncDataQueue = [];
65
67
  this.requestCounter = new RequestCounter();
66
68
  this.requestLogger = new RequestLogger(requestLogging ?? requestLoggingConfig);
69
+ this.spanCollector = new SpanCollector(this.enabled && this.requestLogger.enabled && this.requestLogger.config.captureTraces, this.logger);
67
70
  this.validationErrorCounter = new ValidationErrorCounter();
68
71
  this.serverErrorCounter = new ServerErrorCounter();
69
72
  this.consumerRegistry = new ConsumerRegistry();
@@ -88,6 +91,7 @@ const _ApitallyClient = class _ApitallyClient {
88
91
  this.stopSync();
89
92
  await this.sendSyncData();
90
93
  await this.sendLogData();
94
+ await this.spanCollector.shutdown();
91
95
  await this.requestLogger.close();
92
96
  _ApitallyClient.instance = void 0;
93
97
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { getOrCreateInstanceUuid } from \"./instance.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport { getCpuMemoryUsage } from \"./resources.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = getOrCreateInstanceUuid(clientId, env);\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n resources: getCpuMemoryUsage(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":";;;;AAAA;OAAOA,gBAAgB;AACvB,SAASC,kBAAkB;AAE3B,OAAOC,sBAAsB;AAC7B,SAASC,+BAA+B;AACxC,SAAiBC,iBAAiB;AAClC,SAASC,iBAAiBC,kBAAkB;AAC5C,OAAOC,oBAAoB;AAC3B,OAAOC,mBAAmB;AAC1B,SAASC,yBAAyB;AAClC,OAAOC,wBAAwB;AAO/B,OAAOC,4BAA4B;AAEnC,MAAMC,gBAAgB;AACtB,MAAMC,wBAAwB;AAC9B,MAAMC,iCAAiC;AACvC,MAAMC,iBAAiB;AAEvB,IAAMC,aAAN,mBAAwBC,MAAAA;EACfC;EAEP,YAAYA,UAAoB;AAC9B,UAAMC,SAASD,SAASE,SACpB,eAAeF,SAASE,MAAM,KAC9B;AACJ,UAAM,uBAAuBD,MAAAA,EAAQ;AACrC,SAAKD,WAAWA;EAClB;AACF,GAVwBD,yBAAxB;AAYO,MAAMI,kBAAN,MAAMA,gBAAAA;EACHC;EACAC;EAGAC;EACAC;EACAC;EACDC;EACCC,kBAA2B;EAC3BC,UAAmB;EAEpBC;EACAC;EACAC;EACAC;EACAC;EACAC;EAEP,YAAY,EACVb,UACAC,MAAM,OACNa,gBACAC,sBACAF,OAAM,GACW;AACjB,QAAId,gBAAeiB,UAAU;AAC3B,YAAM,IAAIrB,MAAM,wCAAA;IAClB;AAEA,SAAKkB,SAASA,UAAU/B,UAAAA;AAExB,QAAI,CAACC,gBAAgBiB,QAAAA,GAAW;AAC9B,WAAKa,OAAOI,MACV,+BAA+BjB,QAAAA,uCAA+C;AAEhF,WAAKO,UAAU;IACjB;AACA,QAAI,CAACvB,WAAWiB,GAAAA,GAAM;AACpB,WAAKY,OAAOI,MACV,yBAAyBhB,GAAAA,6DAAgE;AAE3F,WAAKM,UAAU;IACjB;AACA,QAAIQ,wBAAwB,CAACD,gBAAgB;AAC3CI,cAAQC,KACN,iEAAA;IAEJ;AAEApB,oBAAeiB,WAAW;AAC1B,SAAKhB,WAAWA;AAChB,SAAKC,MAAMA;AACX,SAAKC,eAAerB,wBAAwBmB,UAAUC,GAAAA;AACtD,SAAKE,gBAAgB,CAAA;AACrB,SAAKK,iBAAiB,IAAIvB,eAAAA;AAC1B,SAAKwB,gBAAgB,IAAIvB,cACvB4B,kBAAkBC,oBAAAA;AAEpB,SAAKL,yBAAyB,IAAIrB,uBAAAA;AAClC,SAAKsB,qBAAqB,IAAIvB,mBAAAA;AAC9B,SAAKwB,mBAAmB,IAAIhC,iBAAAA;AAC5B,SAAKwC,iBAAiB,KAAKA,eAAeC,KAAK,IAAI;EACrD;EAEA,OAAcC,cAAc;AAC1B,QAAI,CAACvB,gBAAeiB,UAAU;AAC5B,YAAM,IAAIrB,MAAM,oCAAA;IAClB;AACA,WAAOI,gBAAeiB;EACxB;EAEOO,YAAY;AACjB,WAAO,KAAKhB;EACd;EAEA,aAAoBiB,WAAW;AAC7B,QAAIzB,gBAAeiB,UAAU;AAC3B,YAAMjB,gBAAeiB,SAASI,eAAc;IAC9C;EACF;EAEA,MAAaA,iBAAiB;AAC5B,SAAKb,UAAU;AACf,SAAKkB,SAAQ;AACb,UAAM,KAAKC,aAAY;AACvB,UAAM,KAAKC,YAAW;AACtB,UAAM,KAAKlB,cAAcmB,MAAK;AAC9B7B,oBAAeiB,WAAWa;EAC5B;EAEQC,kBAAkB;AACxB,UAAMC,UACJC,QAAQ/B,IAAIgC,yBAAyB;AACvC,UAAMC,UAAU;AAChB,WAAO,GAAGH,OAAAA,IAAWG,OAAAA,IAAW,KAAKlC,QAAQ,IAAI,KAAKC,GAAG;EAC3D;EAEA,MAAckC,SAASC,KAAaC,SAAc;AAChD,UAAMC,iBAAiB5D,WAAW6D,OAAO;MACvCC,SAAS;MACTC,YAAY;MACZC,SAAS;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;;IACrC,CAAA;AACA,UAAM9C,WAAW,MAAM0C,eAAe,KAAKR,gBAAe,IAAKM,KAAK;MAClEO,QAAQ;MACRC,MAAMC,KAAKC,UAAUT,OAAAA;MACrBU,SAAS;QAAE,gBAAgB;MAAmB;IAChD,CAAA;AACA,QAAI,CAACnD,SAASoD,IAAI;AAChB,YAAM,IAAItD,UAAUE,QAAAA;IACtB;EACF;EAEOqD,YAAY;AACjB,QAAI,CAAC,KAAK1C,SAAS;AACjB;IACF;AACA,SAAK2C,KAAI;AACT,SAAK9C,iBAAiB+C,YAAY,MAAA;AAChC,WAAKD,KAAI;IACX,GAAG3D,qBAAAA;AACH6D,eAAW,MAAA;AACT,UAAI,KAAKhD,gBAAgB;AACvBiD,sBAAc,KAAKjD,cAAc;AACjC,aAAKA,iBAAiB+C,YAAY,MAAA;AAChC,eAAKD,KAAI;QACX,GAAG5D,aAAAA;MACL;IACF,GAAGE,8BAAAA;EACL;EAEA,MAAc0D,OAAO;AACnB,QAAI;AACF,YAAMI,WAAW;QAAC,KAAK5B,aAAY;QAAI,KAAKC,YAAW;;AACvD,UAAI,CAAC,KAAKrB,iBAAiB;AACzBgD,iBAASC,KAAK,KAAKC,gBAAe,CAAA;MACpC;AACA,YAAMC,QAAQC,IAAIJ,QAAAA;IACpB,SAASrC,OAAO;AACd,WAAKJ,OAAOI,MAAM,yCAAyC;QACzDA;MACF,CAAA;IACF;EACF;EAEQQ,WAAW;AACjB,QAAI,KAAKrB,gBAAgB;AACvBiD,oBAAc,KAAKjD,cAAc;AACjC,WAAKA,iBAAiByB;IACxB;EACF;EAEO8B,eAAeC,MAAmB;AACvC,SAAKvD,cAAcuD;AACnB,SAAKtD,kBAAkB;EACzB;EAEA,MAAckD,kBAAkB;AAC9B,QAAI,KAAKnD,aAAa;AACpB,WAAKQ,OAAOgD,MAAM,sCAAA;AAClB,YAAMxB,UAA0B;QAC9ByB,eAAe,KAAK5D;QACpB6D,cAAcpF,WAAAA;QACd,GAAG,KAAK0B;MACV;AACA,UAAI;AACF,cAAM,KAAK8B,SAAS,WAAWE,OAAAA;AAC/B,aAAK/B,kBAAkB;MACzB,SAASW,OAAO;AACd,cAAM+C,UAAU,KAAKC,eAAehD,KAAAA;AACpC,YAAI,CAAC+C,SAAS;AACZ,eAAKnD,OAAOI,MAAOA,MAAgBiD,OAAO;AAC1C,eAAKrD,OAAOgD,MACV,iEACA;YAAE5C;UAAM,CAAA;QAEZ;MACF;IACF;EACF;EAEA,MAAcS,eAAe;AAC3B,SAAKb,OAAOgD,MAAM,sCAAA;AAClB,UAAMM,aAA0B;MAC9BC,WAAWC,KAAKC,IAAG,IAAK;MACxBR,eAAe,KAAK5D;MACpB6D,cAAcpF,WAAAA;MACd4F,UAAU,KAAK/D,eAAegE,oBAAmB;MACjDC,mBACE,KAAK/D,uBAAuBgE,4BAA2B;MACzDC,eAAe,KAAKhE,mBAAmBiE,wBAAuB;MAC9DC,WAAW,KAAKjE,iBAAiBkE,4BAA2B;MAC5DC,WAAW5F,kBAAAA;IACb;AACA,SAAKgB,cAAcoD,KAAKY,UAAAA;AAExB,QAAIa,IAAI;AACR,WAAO,KAAK7E,cAAc8E,SAAS,GAAG;AACpC,YAAM5C,UAAU,KAAKlC,cAAc+E,MAAK;AACxC,UAAI7C,SAAS;AACX,YAAI;AACF,cAAIgC,KAAKC,IAAG,IAAKjC,QAAQ+B,YAAY,OAAQ3E,gBAAgB;AAC3D,gBAAIuF,IAAI,GAAG;AACT,oBAAM,KAAKG,YAAW;YACxB;AACA,kBAAM,KAAKhD,SAAS,QAAQE,OAAAA;AAC5B2C,iBAAK;UACP;QACF,SAAS/D,OAAO;AACd,gBAAM+C,UAAU,KAAKC,eAAehD,KAAAA;AACpC,cAAI,CAAC+C,SAAS;AACZ,iBAAKnD,OAAOgD,MACV,iEACA;cAAE5C;YAAM,CAAA;AAEV,iBAAKd,cAAcoD,KAAKlB,OAAAA;AACxB;UACF;QACF;MACF;IACF;EACF;EAEA,MAAcV,cAAc;AAC1B,SAAKd,OAAOgD,MAAM,0CAAA;AAClB,UAAM,KAAKpD,cAAc2E,WAAU;AAEnC,UAAM9C,iBAAiB5D,WAAW6D,OAAO;MACvCC,SAAS;MACTC,YAAY;MACZC,SAAS;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;;IACrC,CAAA;AAEA,QAAIsC,IAAI;AACR,QAAIK;AACJ,WAAQA,UAAU,KAAK5E,cAAc6E,QAAO,GAAK;AAC/C,UAAIN,IAAI,GAAG;AACT,cAAM,KAAKG,YAAW;MACxB;AAEA,UAAI;AACF,cAAMvF,WAAW,MAAM0C,eACrB,GAAG,KAAKR,gBAAe,CAAA,YAAcuD,QAAQE,IAAI,IACjD;UACE5C,QAAQ;UACRC,MAAO,MAAMyC,QAAQG,WAAU;QACjC,CAAA;AAGF,YAAI5F,SAASE,WAAW,OAAOF,SAASmD,QAAQ0C,IAAI,aAAA,GAAgB;AAClE,gBAAMC,aAAaC,SACjB/F,SAASmD,QAAQ6C,IAAI,aAAA,KAAkB,GAAA;AAEzC,cAAIF,aAAa,GAAG;AAClB,iBAAKjF,cAAcoF,eAAexB,KAAKC,IAAG,IAAKoB,aAAa;AAC5D,iBAAKjF,cAAcqF,MAAK;AACxB;UACF;QACF;AAEA,YAAI,CAAClG,SAASoD,IAAI;AAChB,gBAAM,IAAItD,UAAUE,QAAAA;QACtB;AAEAyF,gBAAQU,OAAM;MAChB,SAAS9E,OAAO;AACd,aAAKR,cAAcuF,eAAeX,OAAAA;AAClC;MACF;AAEAL;AACA,UAAIA,KAAK,GAAI;IACf;EACF;EAEQf,eAAehD,OAAgB;AACrC,QAAIA,iBAAiBvB,WAAW;AAC9B,UAAIuB,MAAMrB,SAASE,WAAW,KAAK;AACjC,aAAKe,OAAOI,MAAM,gCAAgC,KAAKjB,QAAQ,GAAG;AAClE,aAAKO,UAAU;AACf,aAAKkB,SAAQ;AACb,eAAO;MACT;AACA,UAAIR,MAAMrB,SAASE,WAAW,KAAK;AACjC,aAAKe,OAAOI,MAAM,6CAAA;AAClB,eAAO;MACT;IACF;AACA,WAAO;EACT;EAEA,MAAckE,cAAc;AAC1B,UAAMc,QAAQ,MAAMC,KAAKC,OAAM,IAAK;AACpC,UAAM,IAAI1C,QAAQ,CAAC2C,YAAYhD,WAAWgD,SAASH,KAAAA,CAAAA;EACrD;AACF;AAxSalG;AAIX,cAJWA,iBAIIiB;AAJV,IAAMjB,iBAAN;","names":["fetchRetry","randomUUID","ConsumerRegistry","getOrCreateInstanceUuid","getLogger","isValidClientId","isValidEnv","RequestCounter","RequestLogger","getCpuMemoryUsage","ServerErrorCounter","ValidationErrorCounter","SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","error","console","warn","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","resources","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
1
+ {"version":3,"sources":["../../src/common/client.ts"],"sourcesContent":["import fetchRetry from \"fetch-retry\";\nimport { randomUUID } from \"node:crypto\";\n\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { getOrCreateInstanceUuid } from \"./instance.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport RequestLogger from \"./requestLogger.js\";\nimport { getCpuMemoryUsage } from \"./resources.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport SpanCollector from \"./spanCollector.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: SyncPayload[];\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n private enabled: boolean = true;\n\n public requestCounter: RequestCounter;\n public requestLogger: RequestLogger;\n public spanCollector: SpanCollector;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({\n clientId,\n env = \"dev\",\n requestLogging,\n requestLoggingConfig,\n logger,\n }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n\n this.logger = logger ?? getLogger();\n\n if (!isValidClientId(clientId)) {\n this.logger.error(\n `Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`,\n );\n this.enabled = false;\n }\n if (!isValidEnv(env)) {\n this.logger.error(\n `Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`,\n );\n this.enabled = false;\n }\n if (requestLoggingConfig && !requestLogging) {\n console.warn(\n \"requestLoggingConfig is deprecated, use requestLogging instead.\",\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = getOrCreateInstanceUuid(clientId, env);\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.requestLogger = new RequestLogger(\n requestLogging ?? requestLoggingConfig,\n );\n this.spanCollector = new SpanCollector(\n this.enabled &&\n this.requestLogger.enabled &&\n this.requestLogger.config.captureTraces,\n this.logger,\n );\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public isEnabled() {\n return this.enabled;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.enabled = false;\n this.stopSync();\n await this.sendSyncData();\n await this.sendLogData();\n await this.spanCollector.shutdown();\n await this.requestLogger.close();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n public startSync() {\n if (!this.enabled) {\n return;\n }\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData(), this.sendLogData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n timestamp: Date.now() / 1000,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n resources: getCpuMemoryUsage(),\n };\n this.syncDataQueue.push(newPayload);\n\n let i = 0;\n while (this.syncDataQueue.length > 0) {\n const payload = this.syncDataQueue.shift();\n if (payload) {\n try {\n if (Date.now() - payload.timestamp * 1000 <= MAX_QUEUE_TIME) {\n if (i > 0) {\n await this.randomDelay();\n }\n await this.sendData(\"sync\", payload);\n i += 1;\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n this.syncDataQueue.push(payload);\n break;\n }\n }\n }\n }\n }\n\n private async sendLogData() {\n this.logger.debug(\"Sending request log data to Apitally Hub\");\n await this.requestLogger.rotateFile();\n\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n\n let i = 0;\n let logFile;\n while ((logFile = this.requestLogger.getFile())) {\n if (i > 0) {\n await this.randomDelay();\n }\n\n try {\n const response = await fetchWithRetry(\n `${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`,\n {\n method: \"POST\",\n body: (await logFile.getContent()) as any,\n },\n );\n\n if (response.status === 402 && response.headers.has(\"Retry-After\")) {\n const retryAfter = parseInt(\n response.headers.get(\"Retry-After\") ?? \"0\",\n );\n if (retryAfter > 0) {\n this.requestLogger.suspendUntil = Date.now() + retryAfter * 1000;\n this.requestLogger.clear();\n return;\n }\n }\n\n if (!response.ok) {\n throw new HTTPError(response);\n }\n\n logFile.delete();\n } catch (error) {\n this.requestLogger.retryFileLater(logFile);\n break;\n }\n\n i++;\n if (i >= 10) break;\n }\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.enabled = false;\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n\n private async randomDelay() {\n const delay = 100 + Math.random() * 400;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n}\n"],"mappings":";;;;AAAA;OAAOA,gBAAgB;AACvB,SAASC,kBAAkB;AAE3B,OAAOC,sBAAsB;AAC7B,SAASC,+BAA+B;AACxC,SAAiBC,iBAAiB;AAClC,SAASC,iBAAiBC,kBAAkB;AAC5C,OAAOC,oBAAoB;AAC3B,OAAOC,mBAAmB;AAC1B,SAASC,yBAAyB;AAClC,OAAOC,wBAAwB;AAC/B,OAAOC,mBAAmB;AAO1B,OAAOC,4BAA4B;AAEnC,MAAMC,gBAAgB;AACtB,MAAMC,wBAAwB;AAC9B,MAAMC,iCAAiC;AACvC,MAAMC,iBAAiB;AAEvB,IAAMC,aAAN,mBAAwBC,MAAAA;EACfC;EAEP,YAAYA,UAAoB;AAC9B,UAAMC,SAASD,SAASE,SACpB,eAAeF,SAASE,MAAM,KAC9B;AACJ,UAAM,uBAAuBD,MAAAA,EAAQ;AACrC,SAAKD,WAAWA;EAClB;AACF,GAVwBD,yBAAxB;AAYO,MAAMI,kBAAN,MAAMA,gBAAAA;EACHC;EACAC;EAGAC;EACAC;EACAC;EACDC;EACCC,kBAA2B;EAC3BC,UAAmB;EAEpBC;EACAC;EACAC;EACAC;EACAC;EACAC;EACAC;EAEP,YAAY,EACVd,UACAC,MAAM,OACNc,gBACAC,sBACAF,OAAM,GACW;AACjB,QAAIf,gBAAekB,UAAU;AAC3B,YAAM,IAAItB,MAAM,wCAAA;IAClB;AAEA,SAAKmB,SAASA,UAAUjC,UAAAA;AAExB,QAAI,CAACC,gBAAgBkB,QAAAA,GAAW;AAC9B,WAAKc,OAAOI,MACV,+BAA+BlB,QAAAA,uCAA+C;AAEhF,WAAKO,UAAU;IACjB;AACA,QAAI,CAACxB,WAAWkB,GAAAA,GAAM;AACpB,WAAKa,OAAOI,MACV,yBAAyBjB,GAAAA,6DAAgE;AAE3F,WAAKM,UAAU;IACjB;AACA,QAAIS,wBAAwB,CAACD,gBAAgB;AAC3CI,cAAQC,KACN,iEAAA;IAEJ;AAEArB,oBAAekB,WAAW;AAC1B,SAAKjB,WAAWA;AAChB,SAAKC,MAAMA;AACX,SAAKC,eAAetB,wBAAwBoB,UAAUC,GAAAA;AACtD,SAAKE,gBAAgB,CAAA;AACrB,SAAKK,iBAAiB,IAAIxB,eAAAA;AAC1B,SAAKyB,gBAAgB,IAAIxB,cACvB8B,kBAAkBC,oBAAAA;AAEpB,SAAKN,gBAAgB,IAAItB,cACvB,KAAKmB,WACH,KAAKE,cAAcF,WACnB,KAAKE,cAAcY,OAAOC,eAC5B,KAAKR,MAAM;AAEb,SAAKH,yBAAyB,IAAItB,uBAAAA;AAClC,SAAKuB,qBAAqB,IAAIzB,mBAAAA;AAC9B,SAAK0B,mBAAmB,IAAIlC,iBAAAA;AAC5B,SAAK4C,iBAAiB,KAAKA,eAAeC,KAAK,IAAI;EACrD;EAEA,OAAcC,cAAc;AAC1B,QAAI,CAAC1B,gBAAekB,UAAU;AAC5B,YAAM,IAAItB,MAAM,oCAAA;IAClB;AACA,WAAOI,gBAAekB;EACxB;EAEOS,YAAY;AACjB,WAAO,KAAKnB;EACd;EAEA,aAAoBoB,WAAW;AAC7B,QAAI5B,gBAAekB,UAAU;AAC3B,YAAMlB,gBAAekB,SAASM,eAAc;IAC9C;EACF;EAEA,MAAaA,iBAAiB;AAC5B,SAAKhB,UAAU;AACf,SAAKqB,SAAQ;AACb,UAAM,KAAKC,aAAY;AACvB,UAAM,KAAKC,YAAW;AACtB,UAAM,KAAKpB,cAAciB,SAAQ;AACjC,UAAM,KAAKlB,cAAcsB,MAAK;AAC9BhC,oBAAekB,WAAWe;EAC5B;EAEQC,kBAAkB;AACxB,UAAMC,UACJC,QAAQlC,IAAImC,yBAAyB;AACvC,UAAMC,UAAU;AAChB,WAAO,GAAGH,OAAAA,IAAWG,OAAAA,IAAW,KAAKrC,QAAQ,IAAI,KAAKC,GAAG;EAC3D;EAEA,MAAcqC,SAASC,KAAaC,SAAc;AAChD,UAAMC,iBAAiBhE,WAAWiE,OAAO;MACvCC,SAAS;MACTC,YAAY;MACZC,SAAS;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;;IACrC,CAAA;AACA,UAAMjD,WAAW,MAAM6C,eAAe,KAAKR,gBAAe,IAAKM,KAAK;MAClEO,QAAQ;MACRC,MAAMC,KAAKC,UAAUT,OAAAA;MACrBU,SAAS;QAAE,gBAAgB;MAAmB;IAChD,CAAA;AACA,QAAI,CAACtD,SAASuD,IAAI;AAChB,YAAM,IAAIzD,UAAUE,QAAAA;IACtB;EACF;EAEOwD,YAAY;AACjB,QAAI,CAAC,KAAK7C,SAAS;AACjB;IACF;AACA,SAAK8C,KAAI;AACT,SAAKjD,iBAAiBkD,YAAY,MAAA;AAChC,WAAKD,KAAI;IACX,GAAG9D,qBAAAA;AACHgE,eAAW,MAAA;AACT,UAAI,KAAKnD,gBAAgB;AACvBoD,sBAAc,KAAKpD,cAAc;AACjC,aAAKA,iBAAiBkD,YAAY,MAAA;AAChC,eAAKD,KAAI;QACX,GAAG/D,aAAAA;MACL;IACF,GAAGE,8BAAAA;EACL;EAEA,MAAc6D,OAAO;AACnB,QAAI;AACF,YAAMI,WAAW;QAAC,KAAK5B,aAAY;QAAI,KAAKC,YAAW;;AACvD,UAAI,CAAC,KAAKxB,iBAAiB;AACzBmD,iBAASC,KAAK,KAAKC,gBAAe,CAAA;MACpC;AACA,YAAMC,QAAQC,IAAIJ,QAAAA;IACpB,SAASvC,OAAO;AACd,WAAKJ,OAAOI,MAAM,yCAAyC;QACzDA;MACF,CAAA;IACF;EACF;EAEQU,WAAW;AACjB,QAAI,KAAKxB,gBAAgB;AACvBoD,oBAAc,KAAKpD,cAAc;AACjC,WAAKA,iBAAiB4B;IACxB;EACF;EAEO8B,eAAeC,MAAmB;AACvC,SAAK1D,cAAc0D;AACnB,SAAKzD,kBAAkB;EACzB;EAEA,MAAcqD,kBAAkB;AAC9B,QAAI,KAAKtD,aAAa;AACpB,WAAKS,OAAOkD,MAAM,sCAAA;AAClB,YAAMxB,UAA0B;QAC9ByB,eAAe,KAAK/D;QACpBgE,cAAcxF,WAAAA;QACd,GAAG,KAAK2B;MACV;AACA,UAAI;AACF,cAAM,KAAKiC,SAAS,WAAWE,OAAAA;AAC/B,aAAKlC,kBAAkB;MACzB,SAASY,OAAO;AACd,cAAMiD,UAAU,KAAKC,eAAelD,KAAAA;AACpC,YAAI,CAACiD,SAAS;AACZ,eAAKrD,OAAOI,MAAOA,MAAgBmD,OAAO;AAC1C,eAAKvD,OAAOkD,MACV,iEACA;YAAE9C;UAAM,CAAA;QAEZ;MACF;IACF;EACF;EAEA,MAAcW,eAAe;AAC3B,SAAKf,OAAOkD,MAAM,sCAAA;AAClB,UAAMM,aAA0B;MAC9BC,WAAWC,KAAKC,IAAG,IAAK;MACxBR,eAAe,KAAK/D;MACpBgE,cAAcxF,WAAAA;MACdgG,UAAU,KAAKlE,eAAemE,oBAAmB;MACjDC,mBACE,KAAKjE,uBAAuBkE,4BAA2B;MACzDC,eAAe,KAAKlE,mBAAmBmE,wBAAuB;MAC9DC,WAAW,KAAKnE,iBAAiBoE,4BAA2B;MAC5DC,WAAWhG,kBAAAA;IACb;AACA,SAAKiB,cAAcuD,KAAKY,UAAAA;AAExB,QAAIa,IAAI;AACR,WAAO,KAAKhF,cAAciF,SAAS,GAAG;AACpC,YAAM5C,UAAU,KAAKrC,cAAckF,MAAK;AACxC,UAAI7C,SAAS;AACX,YAAI;AACF,cAAIgC,KAAKC,IAAG,IAAKjC,QAAQ+B,YAAY,OAAQ9E,gBAAgB;AAC3D,gBAAI0F,IAAI,GAAG;AACT,oBAAM,KAAKG,YAAW;YACxB;AACA,kBAAM,KAAKhD,SAAS,QAAQE,OAAAA;AAC5B2C,iBAAK;UACP;QACF,SAASjE,OAAO;AACd,gBAAMiD,UAAU,KAAKC,eAAelD,KAAAA;AACpC,cAAI,CAACiD,SAAS;AACZ,iBAAKrD,OAAOkD,MACV,iEACA;cAAE9C;YAAM,CAAA;AAEV,iBAAKf,cAAcuD,KAAKlB,OAAAA;AACxB;UACF;QACF;MACF;IACF;EACF;EAEA,MAAcV,cAAc;AAC1B,SAAKhB,OAAOkD,MAAM,0CAAA;AAClB,UAAM,KAAKvD,cAAc8E,WAAU;AAEnC,UAAM9C,iBAAiBhE,WAAWiE,OAAO;MACvCC,SAAS;MACTC,YAAY;MACZC,SAAS;QAAC;QAAK;QAAK;QAAK;QAAK;QAAK;;IACrC,CAAA;AAEA,QAAIsC,IAAI;AACR,QAAIK;AACJ,WAAQA,UAAU,KAAK/E,cAAcgF,QAAO,GAAK;AAC/C,UAAIN,IAAI,GAAG;AACT,cAAM,KAAKG,YAAW;MACxB;AAEA,UAAI;AACF,cAAM1F,WAAW,MAAM6C,eACrB,GAAG,KAAKR,gBAAe,CAAA,YAAcuD,QAAQE,IAAI,IACjD;UACE5C,QAAQ;UACRC,MAAO,MAAMyC,QAAQG,WAAU;QACjC,CAAA;AAGF,YAAI/F,SAASE,WAAW,OAAOF,SAASsD,QAAQ0C,IAAI,aAAA,GAAgB;AAClE,gBAAMC,aAAaC,SACjBlG,SAASsD,QAAQ6C,IAAI,aAAA,KAAkB,GAAA;AAEzC,cAAIF,aAAa,GAAG;AAClB,iBAAKpF,cAAcuF,eAAexB,KAAKC,IAAG,IAAKoB,aAAa;AAC5D,iBAAKpF,cAAcwF,MAAK;AACxB;UACF;QACF;AAEA,YAAI,CAACrG,SAASuD,IAAI;AAChB,gBAAM,IAAIzD,UAAUE,QAAAA;QACtB;AAEA4F,gBAAQU,OAAM;MAChB,SAAShF,OAAO;AACd,aAAKT,cAAc0F,eAAeX,OAAAA;AAClC;MACF;AAEAL;AACA,UAAIA,KAAK,GAAI;IACf;EACF;EAEQf,eAAelD,OAAgB;AACrC,QAAIA,iBAAiBxB,WAAW;AAC9B,UAAIwB,MAAMtB,SAASE,WAAW,KAAK;AACjC,aAAKgB,OAAOI,MAAM,gCAAgC,KAAKlB,QAAQ,GAAG;AAClE,aAAKO,UAAU;AACf,aAAKqB,SAAQ;AACb,eAAO;MACT;AACA,UAAIV,MAAMtB,SAASE,WAAW,KAAK;AACjC,aAAKgB,OAAOI,MAAM,6CAAA;AAClB,eAAO;MACT;IACF;AACA,WAAO;EACT;EAEA,MAAcoE,cAAc;AAC1B,UAAMc,QAAQ,MAAMC,KAAKC,OAAM,IAAK;AACpC,UAAM,IAAI1C,QAAQ,CAAC2C,YAAYhD,WAAWgD,SAASH,KAAAA,CAAAA;EACrD;AACF;AAhTarG;AAIX,cAJWA,iBAIIkB;AAJV,IAAMlB,iBAAN;","names":["fetchRetry","randomUUID","ConsumerRegistry","getOrCreateInstanceUuid","getLogger","isValidClientId","isValidEnv","RequestCounter","RequestLogger","getCpuMemoryUsage","ServerErrorCounter","SpanCollector","ValidationErrorCounter","SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","enabled","requestCounter","requestLogger","spanCollector","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","requestLogging","requestLoggingConfig","instance","error","console","warn","config","captureTraces","handleShutdown","bind","getInstance","isEnabled","shutdown","stopSync","sendSyncData","sendLogData","close","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","startSync","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","setStartupData","data","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","timestamp","Date","now","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","resources","i","length","shift","randomDelay","rotateFile","logFile","getFile","uuid","getContent","has","retryAfter","parseInt","get","suspendUntil","clear","delete","retryFileLater","delay","Math","random","resolve"]}
@@ -4,6 +4,8 @@ import 'winston';
4
4
  import './requestLogger.cjs';
5
5
  import 'node:buffer';
6
6
  import 'node:http';
7
+ import './spanCollector.cjs';
8
+ import '@opentelemetry/sdk-trace-base';
7
9
  import './tempGzipFile.cjs';
8
10
 
9
11
  declare const consumerFromStringOrObject: (consumer: ApitallyConsumer | string) => ApitallyConsumer | null;