@oas-tools/oas-telemetry 0.7.1 → 0.8.0-alpha.1

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 (194) hide show
  1. package/.env.example +17 -3
  2. package/README.md +1 -2
  3. package/dist/cjs/config/bootConfig.cjs +16 -14
  4. package/dist/cjs/config/config.cjs +120 -125
  5. package/dist/cjs/config/config.types.cjs +1 -4
  6. package/dist/cjs/docs/openapi.yaml +158 -4
  7. package/dist/cjs/index.cjs +27 -30
  8. package/dist/cjs/routesManager.cjs +62 -70
  9. package/dist/cjs/telemetry/custom-implementations/exporters/DiskLogExporter.cjs +121 -0
  10. package/dist/cjs/telemetry/custom-implementations/exporters/DiskMetricExporter.cjs +101 -0
  11. package/dist/cjs/telemetry/custom-implementations/exporters/DiskTraceExporter.cjs +103 -0
  12. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.cjs +194 -190
  13. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.cjs +147 -99
  14. package/dist/cjs/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.cjs +143 -116
  15. package/dist/cjs/telemetry/custom-implementations/exporters/MultiMetricExporter.cjs +57 -0
  16. package/dist/cjs/telemetry/custom-implementations/instrumentations/logsInstrumentation.cjs +92 -0
  17. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/Chunk.cjs +159 -0
  18. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/Series.cjs +168 -0
  19. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.cjs +392 -0
  20. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/types.cjs +2 -0
  21. package/dist/cjs/telemetry/custom-implementations/metrics/tsdb/utils.cjs +77 -0
  22. package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.cjs +65 -63
  23. package/dist/cjs/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.cjs +63 -62
  24. package/dist/cjs/telemetry/custom-implementations/utils/circular.cjs +47 -47
  25. package/dist/cjs/telemetry/custom-implementations/wrappers.cjs +209 -138
  26. package/dist/cjs/telemetry/initializeTelemetry.cjs +35 -91
  27. package/dist/cjs/telemetry/persistence/DiskImporter.cjs +85 -0
  28. package/dist/cjs/telemetry/persistence/DiskUtils.cjs +61 -0
  29. package/dist/cjs/telemetry/persistence/DiskWriter.cjs +66 -0
  30. package/dist/cjs/telemetry/telemetryConfigurator.cjs +139 -72
  31. package/dist/cjs/telemetry/telemetryRegistry.cjs +45 -31
  32. package/dist/cjs/tlm-ai/agent.cjs +49 -64
  33. package/dist/cjs/tlm-ai/aiController.cjs +54 -76
  34. package/dist/cjs/tlm-ai/aiRoutes.cjs +17 -20
  35. package/dist/cjs/tlm-ai/aiService.cjs +91 -95
  36. package/dist/cjs/tlm-ai/tools.cjs +177 -174
  37. package/dist/cjs/tlm-auth/authController.cjs +80 -123
  38. package/dist/cjs/tlm-auth/authMiddleware.cjs +25 -30
  39. package/dist/cjs/tlm-auth/authRoutes.cjs +11 -14
  40. package/dist/cjs/tlm-log/logController.cjs +135 -116
  41. package/dist/cjs/tlm-log/logRoutes.cjs +19 -20
  42. package/dist/cjs/tlm-log/logService.cjs +29 -0
  43. package/dist/cjs/tlm-metric/metricsController.cjs +154 -122
  44. package/dist/cjs/tlm-metric/metricsRoutes.cjs +22 -20
  45. package/dist/cjs/tlm-metric/metricsService.cjs +26 -0
  46. package/dist/cjs/tlm-plugin/pluginController.cjs +128 -140
  47. package/dist/cjs/tlm-plugin/pluginProcess.cjs +89 -94
  48. package/dist/cjs/tlm-plugin/pluginRoutes.cjs +11 -14
  49. package/dist/cjs/tlm-plugin/pluginService.cjs +73 -74
  50. package/dist/cjs/tlm-trace/traceController.cjs +140 -123
  51. package/dist/cjs/tlm-trace/traceRoutes.cjs +19 -20
  52. package/dist/cjs/tlm-trace/traceService.cjs +29 -0
  53. package/dist/cjs/tlm-ui/uiRoutes.cjs +63 -32
  54. package/dist/cjs/tlm-util/utilController.cjs +68 -70
  55. package/dist/cjs/tlm-util/utilRoutes.cjs +51 -63
  56. package/dist/cjs/types/index.cjs +2 -5
  57. package/dist/cjs/utils/logger.cjs +38 -43
  58. package/dist/cjs/utils/regexUtils.cjs +22 -22
  59. package/dist/esm/config/bootConfig.js +5 -2
  60. package/dist/esm/config/config.js +9 -2
  61. package/dist/esm/docs/openapi.yaml +158 -4
  62. package/dist/esm/index.js +9 -8
  63. package/dist/esm/routesManager.js +6 -10
  64. package/dist/esm/telemetry/custom-implementations/exporters/DiskLogExporter.js +114 -0
  65. package/dist/esm/telemetry/custom-implementations/exporters/DiskMetricExporter.js +94 -0
  66. package/dist/esm/telemetry/custom-implementations/exporters/DiskTraceExporter.js +96 -0
  67. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.js +38 -7
  68. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.js +107 -48
  69. package/dist/esm/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.js +60 -29
  70. package/dist/esm/telemetry/custom-implementations/exporters/MultiMetricExporter.js +53 -0
  71. package/dist/esm/telemetry/custom-implementations/instrumentations/logsInstrumentation.js +85 -0
  72. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/Chunk.js +155 -0
  73. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/Series.js +164 -0
  74. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.js +385 -0
  75. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/types.js +1 -0
  76. package/dist/esm/telemetry/custom-implementations/metrics/tsdb/utils.js +74 -0
  77. package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.js +2 -1
  78. package/dist/esm/telemetry/custom-implementations/processors/dynamicMultiSpanProcessor.js +1 -1
  79. package/dist/esm/telemetry/custom-implementations/wrappers.js +77 -6
  80. package/dist/esm/telemetry/initializeTelemetry.js +27 -69
  81. package/dist/esm/telemetry/persistence/DiskImporter.js +78 -0
  82. package/dist/esm/telemetry/persistence/DiskUtils.js +51 -0
  83. package/dist/esm/telemetry/persistence/DiskWriter.js +59 -0
  84. package/dist/esm/telemetry/telemetryConfigurator.js +110 -39
  85. package/dist/esm/telemetry/telemetryRegistry.js +12 -1
  86. package/dist/esm/tlm-ai/agent.js +5 -3
  87. package/dist/esm/tlm-ai/aiController.js +3 -3
  88. package/dist/esm/tlm-ai/aiService.js +6 -2
  89. package/dist/esm/tlm-ai/tools.js +5 -9
  90. package/dist/esm/tlm-auth/authController.js +3 -2
  91. package/dist/esm/tlm-log/logController.js +62 -18
  92. package/dist/esm/tlm-log/logRoutes.js +3 -1
  93. package/dist/esm/tlm-log/logService.js +25 -0
  94. package/dist/esm/tlm-metric/metricsController.js +116 -50
  95. package/dist/esm/tlm-metric/metricsRoutes.js +8 -3
  96. package/dist/esm/tlm-metric/metricsService.js +22 -0
  97. package/dist/esm/tlm-plugin/pluginController.js +6 -11
  98. package/dist/esm/tlm-plugin/pluginService.js +2 -4
  99. package/dist/esm/tlm-trace/traceController.js +87 -36
  100. package/dist/esm/tlm-trace/traceRoutes.js +3 -1
  101. package/dist/esm/tlm-trace/traceService.js +25 -0
  102. package/dist/esm/tlm-ui/uiRoutes.js +5 -5
  103. package/dist/esm/tlm-util/utilController.js +3 -9
  104. package/dist/esm/tlm-util/utilRoutes.js +2 -2
  105. package/dist/types/config/bootConfig.d.ts +3 -0
  106. package/dist/types/config/config.d.ts +48 -7
  107. package/dist/types/config/config.types.d.ts +7 -0
  108. package/dist/types/index.d.ts +2 -3
  109. package/dist/types/telemetry/custom-implementations/exporters/DiskLogExporter.d.ts +24 -0
  110. package/dist/types/telemetry/custom-implementations/exporters/DiskMetricExporter.d.ts +23 -0
  111. package/dist/types/telemetry/custom-implementations/exporters/DiskTraceExporter.d.ts +23 -0
  112. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbLogExporter.d.ts +3 -1
  113. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbMetricExporter.d.ts +56 -15
  114. package/dist/types/telemetry/custom-implementations/exporters/InMemoryDbSpanExporter.d.ts +8 -4
  115. package/dist/types/telemetry/custom-implementations/exporters/MultiMetricExporter.d.ts +9 -0
  116. package/dist/types/telemetry/custom-implementations/instrumentations/logsInstrumentation.d.ts +23 -0
  117. package/dist/types/telemetry/custom-implementations/metrics/tsdb/Chunk.d.ts +49 -0
  118. package/dist/types/telemetry/custom-implementations/metrics/tsdb/Series.d.ts +67 -0
  119. package/dist/types/telemetry/custom-implementations/metrics/tsdb/SeriesRegistry.d.ts +69 -0
  120. package/dist/types/telemetry/custom-implementations/metrics/tsdb/types.d.ts +68 -0
  121. package/dist/types/telemetry/custom-implementations/metrics/tsdb/utils.d.ts +21 -0
  122. package/dist/types/telemetry/custom-implementations/processors/dynamicMultiLogProcessor.d.ts +2 -2
  123. package/dist/types/telemetry/custom-implementations/wrappers.d.ts +2 -1
  124. package/dist/types/telemetry/persistence/DiskImporter.d.ts +17 -0
  125. package/dist/types/telemetry/persistence/DiskUtils.d.ts +11 -0
  126. package/dist/types/telemetry/persistence/DiskWriter.d.ts +21 -0
  127. package/dist/types/telemetry/telemetryConfigurator.d.ts +1 -1
  128. package/dist/types/telemetry/telemetryRegistry.d.ts +8 -0
  129. package/dist/types/tlm-ai/agent.d.ts +1 -1
  130. package/dist/types/tlm-ai/aiService.d.ts +1 -1
  131. package/dist/types/tlm-log/logController.d.ts +2 -0
  132. package/dist/types/tlm-log/logService.d.ts +4 -0
  133. package/dist/types/tlm-metric/metricsController.d.ts +11 -2
  134. package/dist/types/tlm-metric/metricsService.d.ts +6 -0
  135. package/dist/types/tlm-trace/traceController.d.ts +9 -7
  136. package/dist/types/tlm-trace/traceService.d.ts +4 -0
  137. package/dist/types/types/index.d.ts +2 -2
  138. package/dist/ui/assets/{ApiDocsPage-C_VVPPHa.js → ApiDocsPage-DTCgVbW2.js} +2 -2
  139. package/dist/ui/assets/CollapsibleCard-lWgfsaAn.js +1 -0
  140. package/dist/ui/assets/DevToolsPage-DEhf8CBy.js +1 -0
  141. package/dist/ui/assets/LandingPage-CfEHCDxY.js +6 -0
  142. package/dist/ui/assets/LogsPage-DFDKRuGH.js +1 -0
  143. package/dist/ui/assets/{NotFoundPage-B3quk3P1.js → NotFoundPage-DCy0DcV7.js} +1 -1
  144. package/dist/ui/assets/PluginCreatePage-BawZ5_-h.js +50 -0
  145. package/dist/ui/assets/PluginPage-D3FmgU7d.js +27 -0
  146. package/dist/ui/assets/TraceSpansPage-D0_L45Rb.js +6 -0
  147. package/dist/ui/assets/VirtualizedListPanel-q605n9He.js +16 -0
  148. package/dist/ui/assets/alert-DBAFshSi.js +1133 -0
  149. package/dist/ui/assets/badge-DGNBtnxU.js +1 -0
  150. package/dist/ui/assets/{chevron-down-CPsvsmqj.js → chevron-down-CFEqYzGC.js} +1 -1
  151. package/dist/ui/assets/{chevron-up-Df9jMo1X.js → chevron-up-lDnFwAJq.js} +1 -1
  152. package/dist/ui/assets/{circle-alert-DOPQPvU8.js → circle-alert-BpYUuRs7.js} +1 -1
  153. package/dist/ui/assets/dialog-1dRyI6SC.js +15 -0
  154. package/dist/ui/assets/index-C7RfU6hR.js +1 -0
  155. package/dist/ui/assets/index-C9dDYIpd.js +305 -0
  156. package/dist/ui/assets/index-D6f1KjWV.css +1 -0
  157. package/dist/ui/assets/info-CuJQWoBU.js +6 -0
  158. package/dist/ui/assets/{input-Dzvg_ZEZ.js → input-BLXaar0X.js} +1 -1
  159. package/dist/ui/assets/label-DfAcltsl.js +1 -0
  160. package/dist/ui/assets/{loader-circle-CrvlRy5o.js → loader-circle-B7oLyPsi.js} +1 -1
  161. package/dist/ui/assets/{loginPage-qa4V-B70.js → loginPage-DswZvOJ-.js} +1 -1
  162. package/dist/ui/assets/metrics-page-BhtXrfUW.js +31 -0
  163. package/dist/ui/assets/metrics-page-D1GxaB_c.css +1 -0
  164. package/dist/ui/assets/popover-IDker85U.js +11 -0
  165. package/dist/ui/assets/select-B8y5IidE.js +6 -0
  166. package/dist/ui/assets/separator-B6EzrxYY.js +6 -0
  167. package/dist/ui/assets/severityOptions-DtCsaAZK.js +11 -0
  168. package/dist/ui/assets/square-pen-D_oecB1x.js +6 -0
  169. package/dist/ui/assets/switch-Dqo0XkRD.js +1 -0
  170. package/dist/ui/assets/trace-DJq1miYa.js +1 -0
  171. package/dist/ui/assets/upload-prIohEdY.js +11 -0
  172. package/dist/ui/assets/{utilService-DNyqzwj0.js → utilService-C8TJKLqs.js} +1 -1
  173. package/dist/ui/assets/wand-sparkles-OgXuzsSx.js +6 -0
  174. package/dist/ui/index.html +2 -2
  175. package/package.json +44 -49
  176. package/dist/ui/assets/CollapsibleCard-B3KR_8mL.js +0 -1
  177. package/dist/ui/assets/DevToolsPage-OyZcDcmw.js +0 -1
  178. package/dist/ui/assets/LandingPage-CppFBA6K.js +0 -6
  179. package/dist/ui/assets/LogsPage-9Fq8GArS.js +0 -26
  180. package/dist/ui/assets/PluginCreatePage-X_aCH4t4.js +0 -50
  181. package/dist/ui/assets/PluginPage-DMDSihrZ.js +0 -27
  182. package/dist/ui/assets/alert-jQ9HCPIf.js +0 -1133
  183. package/dist/ui/assets/badge-CNq0-mH5.js +0 -1
  184. package/dist/ui/assets/card-DFAwwhN3.js +0 -1
  185. package/dist/ui/assets/index-BkD6DijD.js +0 -15
  186. package/dist/ui/assets/index-CERGVYZK.js +0 -292
  187. package/dist/ui/assets/index-CSIPf9qw.css +0 -1
  188. package/dist/ui/assets/label-DuVnkZ4q.js +0 -1
  189. package/dist/ui/assets/select-DhS8YUtJ.js +0 -1
  190. package/dist/ui/assets/separator-isK4chBP.js +0 -6
  191. package/dist/ui/assets/severityOptions-O38dSOfk.js +0 -11
  192. package/dist/ui/assets/switch-Z3mImG9n.js +0 -1
  193. package/dist/ui/assets/tabs-_77MUUQe.js +0 -16
  194. package/dist/ui/assets/upload-C1LT4Gkb.js +0 -16
@@ -1,37 +1,91 @@
1
- import { BatchSpanProcessor, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
2
- import { dynamicMultiLogProcessor, dynamicMultiSpanProcessor, inMemoryDbLogExporter, inMemoryDbMetricExporter, inMemoryDbSpanExporter, multiLogExporter, multiSpanExporter, oasTelemetryResource } from './telemetryRegistry.js';
1
+ import { BatchLogRecordProcessor as LogBatchLogRecordProcessor, SimpleLogRecordProcessor as LogSimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';
2
+ import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
3
+ import { BatchSpanProcessor as TraceBatchSpanProcessor, SimpleSpanProcessor as TraceSimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
3
4
  import logger from '../utils/logger.js';
4
- import { BatchLogRecordProcessor, SimpleLogRecordProcessor } from '@opentelemetry/sdk-logs';
5
- import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
6
- import { HostMetrics } from '@opentelemetry/host-metrics';
5
+ import { inMemoryDbLogExporter, inMemoryDbMetricExporter, inMemoryDbSpanExporter, instrumentations, multiLogExporter, multiSpanExporter, oasTelemetryResource } from './telemetryRegistry.js';
6
+ import { NodeSDK } from '@opentelemetry/sdk-node';
7
7
  import { bootEnvVariables } from '../config/bootConfig.js';
8
8
  import { pluginService } from '../tlm-plugin/pluginService.js';
9
- export const configureTelemetry = (oasTlmConfig) => {
9
+ import { DiskTraceExporter } from './custom-implementations/exporters/DiskTraceExporter.js';
10
+ import { DiskLogExporter } from './custom-implementations/exporters/DiskLogExporter.js';
11
+ import { DiskMetricExporter } from './custom-implementations/exporters/DiskMetricExporter.js';
12
+ import { MultiMetricExporter } from './custom-implementations/exporters/MultiMetricExporter.js';
13
+ import { DiskImporter } from './persistence/DiskImporter.js';
14
+ import { importTracesToMemory } from '../tlm-trace/traceService.js';
15
+ import { importLogsToMemory } from '../tlm-log/logService.js';
16
+ import { importMetricsToMemory } from '../tlm-metric/metricsService.js';
17
+ export function configureTelemetry(oasTlmConfig) {
18
+ logger.info("🚀 Configuring Telemetry...");
19
+ configureStorage(oasTlmConfig);
20
+ if (oasTlmConfig.instrumentations) {
21
+ instrumentations.push(...oasTlmConfig.instrumentations);
22
+ }
10
23
  configurePlugins(oasTlmConfig);
11
- configureTraces(oasTlmConfig);
12
- configureMetrics(oasTlmConfig);
13
- configureLogs(oasTlmConfig);
14
- logger.info("✅ Telemetry configured successfully. All exporters are ready");
15
- };
24
+ const mainTraceProcessor = configureTraces(oasTlmConfig);
25
+ const mainMetricReader = configureMetrics(oasTlmConfig);
26
+ const mainLogProcessor = configureLogs(oasTlmConfig);
27
+ const sdk = new NodeSDK({
28
+ instrumentations: instrumentations,
29
+ resource: oasTelemetryResource,
30
+ traceExporter: inMemoryDbSpanExporter,
31
+ spanProcessors: [mainTraceProcessor, ...oasTlmConfig.traces.extraProcessors || []],
32
+ metricReaders: [mainMetricReader, ...(oasTlmConfig.metrics.extraReaders || [])],
33
+ logRecordProcessors: [mainLogProcessor, ...oasTlmConfig.logs.extraProcessors || []],
34
+ });
35
+ sdk.start();
36
+ logger.info("✅ Node SDK started with telemetry configuration");
37
+ if (oasTlmConfig.storage.path && oasTlmConfig.storage.loadFromStart) {
38
+ scheduleStartupImports(oasTlmConfig.storage.path);
39
+ }
40
+ else if (oasTlmConfig.storage.path && !oasTlmConfig.storage.loadFromStart) {
41
+ logger.info(`[DiskImporter] Startup import skipped by storage.loadFromStart=false. Path: ${oasTlmConfig.storage.path}`);
42
+ }
43
+ return true;
44
+ }
45
+ function configureStorage(oasTlmConfig) {
46
+ const path = oasTlmConfig.storage.path;
47
+ oasTlmConfig.storage.path = typeof path === 'string' && path.trim().length > 0
48
+ ? path.trim()
49
+ : null;
50
+ }
16
51
  function configurePlugins(oasTlmConfig) {
17
52
  pluginService.enabled = oasTlmConfig.plugins.enabled;
18
53
  }
19
54
  function configureTraces(oasTlmConfig) {
20
55
  // TRACES CONFIGURATION
21
56
  // [OT]Provider -> [OT]SpanProcessor(multiSpan) -> n Processors(eg mainProcessor, extra) -> 1 SpanExporter
22
- inMemoryDbSpanExporter.baseUrl = oasTlmConfig.general.baseUrl; // TODO this will be done with filters
23
57
  inMemoryDbSpanExporter.retentionTimeInSeconds = oasTlmConfig.traces.memoryExporter.retentionTimeSeconds;
24
58
  inMemoryDbSpanExporter.setEnabledValue(oasTlmConfig.traces.memoryExporter.enabled);
25
59
  const mainExporter = multiSpanExporter;
26
- let mainProcessor = new BatchSpanProcessor(mainExporter);
60
+ mainExporter.clearExporters();
61
+ let mainProcessor = new TraceBatchSpanProcessor(mainExporter);
27
62
  if (bootEnvVariables.OASTLM_BOOT_ENV !== 'production') {
28
63
  logger.info('Not in production, using SimpleSpanProcessor for traces');
29
- mainProcessor = new SimpleSpanProcessor(mainExporter);
64
+ mainProcessor = new TraceSimpleSpanProcessor(mainExporter);
30
65
  }
31
66
  mainExporter.addExporters(inMemoryDbSpanExporter); // Main exporter have at least the in-memory exporter used by the traces controller
67
+ if (oasTlmConfig.storage.path) {
68
+ mainExporter.addExporters(new DiskTraceExporter({ directoryPath: oasTlmConfig.storage.path }));
69
+ logger.info(`[TraceDiskExporter] Enabled at: ${oasTlmConfig.storage.path}`);
70
+ }
32
71
  mainExporter.addExporters(oasTlmConfig.traces.extraExporters);
33
- dynamicMultiSpanProcessor.addProcessors(mainProcessor);
34
- dynamicMultiSpanProcessor.addProcessors(oasTlmConfig.traces.extraProcessors);
72
+ return mainProcessor;
73
+ }
74
+ function configureMetrics(oasTlmConfig) {
75
+ // METRICS CONFIGURATION
76
+ inMemoryDbMetricExporter.setEnabledValue(oasTlmConfig.metrics.memoryExporter.enabled);
77
+ inMemoryDbMetricExporter.retentionTimeInSeconds = oasTlmConfig.metrics.memoryExporter.retentionTimeSeconds;
78
+ const metricExporters = [inMemoryDbMetricExporter];
79
+ if (oasTlmConfig.storage.path) {
80
+ metricExporters.push(new DiskMetricExporter({ directoryPath: oasTlmConfig.storage.path }));
81
+ logger.info(`[MetricDiskExporter] Enabled at: ${oasTlmConfig.storage.path}`);
82
+ }
83
+ const mainReader = new PeriodicExportingMetricReader({
84
+ exporter: new MultiMetricExporter(metricExporters),
85
+ exportIntervalMillis: oasTlmConfig.metrics.mainMetricReaderOptions.exportIntervalMillis,
86
+ metricProducers: oasTlmConfig.metrics.mainMetricReaderOptions.metricProducers
87
+ });
88
+ return mainReader;
35
89
  }
36
90
  function configureLogs(oasTlmConfig) {
37
91
  // LOGS CONFIGURATION
@@ -39,33 +93,50 @@ function configureLogs(oasTlmConfig) {
39
93
  inMemoryDbLogExporter.setEnabledValue(oasTlmConfig.logs.memoryExporter.enabled);
40
94
  inMemoryDbLogExporter.retentionTimeInSeconds = oasTlmConfig.logs.memoryExporter.retentionTimeSeconds;
41
95
  const mainExporter = multiLogExporter;
42
- let mainProcessor = new BatchLogRecordProcessor(mainExporter);
96
+ mainExporter.clearExporters();
97
+ let mainProcessor = new LogBatchLogRecordProcessor(mainExporter);
43
98
  if (bootEnvVariables.OASTLM_BOOT_ENV !== 'production') {
44
99
  logger.info('Not in production, using SimpleLogRecordProcessor for logs');
45
- mainProcessor = new SimpleLogRecordProcessor(mainExporter);
100
+ mainProcessor = new LogSimpleLogRecordProcessor(mainExporter);
46
101
  }
47
102
  mainExporter.addExporters(inMemoryDbLogExporter); // Main exporter have at least the in-memory exporter used by the logs controller
103
+ if (oasTlmConfig.storage.path) {
104
+ mainExporter.addExporters(new DiskLogExporter({ directoryPath: oasTlmConfig.storage.path }));
105
+ logger.info(`[LogDiskExporter] Enabled at: ${oasTlmConfig.storage.path}`);
106
+ }
48
107
  mainExporter.addExporters(oasTlmConfig.logs.extraExporters);
49
- dynamicMultiLogProcessor.addProcessors(mainProcessor);
50
- dynamicMultiLogProcessor.addProcessors(oasTlmConfig.logs.extraProcessors);
108
+ return mainProcessor;
51
109
  }
52
- function configureMetrics(oasTlmConfig) {
53
- // METRICS CONFIGURATION
54
- // [CUSTOM]MeterProvider -> n [OTel]MetricReader -> (0-1) MetricExporter (only if push-based reader)
55
- inMemoryDbMetricExporter.setEnabledValue(oasTlmConfig.metrics.memoryExporter.enabled);
56
- inMemoryDbMetricExporter.retentionTimeInSeconds = oasTlmConfig.metrics.memoryExporter.retentionTimeSeconds;
57
- const mainReader = new PeriodicExportingMetricReader({
58
- exporter: inMemoryDbMetricExporter,
59
- exportIntervalMillis: oasTlmConfig.metrics.mainMetricReaderOptions.exportIntervalMillis,
60
- metricProducers: oasTlmConfig.metrics.mainMetricReaderOptions.metricProducers
61
- });
62
- const meterProvider = new MeterProvider({
63
- resource: oasTelemetryResource,
64
- readers: [mainReader, ...oasTlmConfig.metrics.extraReaders],
65
- views: oasTlmConfig.metrics.extraViews || []
66
- });
67
- // TODO maybe hostMetrics are too much, consider using only a subset of them.
68
- const hostMetrics = new HostMetrics({ meterProvider: meterProvider });
69
- // AFTER adding all readers, start the instrumentations
70
- hostMetrics.start();
110
+ function scheduleStartupImports(storagePath) {
111
+ setTimeout(async () => {
112
+ try {
113
+ await runTimedImport('TraceDiskImport', storagePath, new DiskImporter({ directoryPath: storagePath }), async (spans) => {
114
+ await importTracesToMemory(spans);
115
+ });
116
+ await runTimedImport('LogDiskImport', storagePath, new DiskImporter({ directoryPath: storagePath, segmentPrefix: 'logs' }), async (logs) => {
117
+ await importLogsToMemory(logs);
118
+ });
119
+ await runTimedImport('MetricDiskImport', storagePath, new DiskImporter({ directoryPath: storagePath, segmentPrefix: 'metrics' }), async (scopeMetrics) => {
120
+ importMetricsToMemory(scopeMetrics, { format: 'otel' });
121
+ });
122
+ }
123
+ catch (error) {
124
+ logger.error(`[DiskImporter] Startup import sequence failed: ${error?.message || error}`);
125
+ }
126
+ }, 0);
127
+ }
128
+ async function runTimedImport(label, storagePath, importer, onBatch) {
129
+ const startedAt = Date.now();
130
+ logger.info(`[${label}] Startup import started. Path: ${storagePath}`);
131
+ try {
132
+ const result = await importer.import(onBatch);
133
+ logger.info(`[${label}] Startup import completed. Files: ${result.segmentFilesRead}, imported records: ${result.importedRecords}, failed frames: ${result.failedFrames}`);
134
+ }
135
+ catch (error) {
136
+ logger.error(`[${label}] Startup import failed: ${error?.message || error}`);
137
+ }
138
+ finally {
139
+ const elapsedMs = Date.now() - startedAt;
140
+ logger.info(`[${label}] Startup import finished in ${elapsedMs}ms`);
141
+ }
71
142
  }
@@ -8,6 +8,15 @@ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
8
8
  import { resourceFromAttributes } from "@opentelemetry/resources";
9
9
  import { bootEnvVariables } from "../config/bootConfig.js";
10
10
  // GLOBAL REGISTRY of telemetry components, used by SDKs and controllers.
11
+ let _bootInitialized = false;
12
+ let _telemetryConfigured = false;
13
+ let _router = undefined;
14
+ export function isBootInitialized() { return _bootInitialized; }
15
+ export function setBootInitialized(v) { _bootInitialized = v; }
16
+ export function isTelemetryConfigured() { return _telemetryConfigured; }
17
+ export function setTelemetryConfigured(v) { _telemetryConfigured = v; }
18
+ export function setTelemetryRouter(router) { _router = router; }
19
+ export function getTelemetryRouter() { return _router; }
11
20
  export const oasTelemetryResource = resourceFromAttributes({
12
21
  [ATTR_SERVICE_NAME]: bootEnvVariables.OASTLM_BOOT_SERVICE_NAME
13
22
  });
@@ -22,7 +31,7 @@ export const dynamicMultiSpanProcessor = new DynamicMultiSpanProcessor();
22
31
  export const inMemoryDbLogExporter = new InMemoryDbLogExporter();
23
32
  export const multiLogExporter = new EnablerMultiLogExporter();
24
33
  export const dynamicMultiLogProcessor = new DynamicMultiLogRecordProcessor();
25
- // Override console methods to emit logs via OpenTelemetry, like an instrumentation
34
+ // For custom log instrumentation, and lib logging
26
35
  export const originalConsoleMethods = {
27
36
  log: console.log,
28
37
  warn: console.warn,
@@ -35,3 +44,5 @@ export const originalConsoleMethods = {
35
44
  export const inMemoryDbMetricExporter = new InMemoryDbMetricExporter();
36
45
  // Readers and their exporters cannot be grouped together in a MultiReader or similar construct
37
46
  // due to differences in aggregation temporality and aggregation selection.
47
+ // INSTRUMENTATIONS -----------------------------------------------------------------------
48
+ export const instrumentations = [];
@@ -1,6 +1,6 @@
1
1
  import { tools, availableTools } from './tools.js';
2
2
  import logger from '../utils/logger.js';
3
- export async function agent(openai, messages, model = "gpt-3.5-turbo", extraPrompts = []) {
3
+ export async function agent(openai, messages, model = "gpt-3.5-turbo") {
4
4
  for (let i = 0; i < 5; i++) {
5
5
  const modelResponse = await openai.chat.completions.create({
6
6
  model,
@@ -12,11 +12,13 @@ export async function agent(openai, messages, model = "gpt-3.5-turbo", extraProm
12
12
  logger.debug("Tool calls detected:", message.tool_calls);
13
13
  const results = [];
14
14
  for (const toolCall of message.tool_calls) {
15
+ // Type guard for tool_calls
16
+ if (toolCall.type !== 'function' || !toolCall.function)
17
+ continue;
15
18
  const functionName = toolCall.function.name;
16
19
  const functionToCall = availableTools[functionName];
17
20
  const functionArgs = JSON.parse(toolCall.function.arguments);
18
21
  const functionArgsArr = Object.values(functionArgs);
19
- // @ts-expect-error yes
20
22
  // eslint-disable-next-line prefer-spread
21
23
  const functionResponse = await functionToCall.apply(null, functionArgsArr);
22
24
  results.push({
@@ -26,7 +28,7 @@ export async function agent(openai, messages, model = "gpt-3.5-turbo", extraProm
26
28
  }
27
29
  const resultMessage = results.map(({ name, response }, idx) => {
28
30
  const toolCall = message.tool_calls?.[idx];
29
- const params = toolCall ? JSON.parse(toolCall.function.arguments) : {};
31
+ const params = toolCall && toolCall.type === 'function' && toolCall.function ? JSON.parse(toolCall.function.arguments) : {};
30
32
  return `Tool "${name}" called with parameters:\n${JSON.stringify(params, null, 2)}\nResult:\n${JSON.stringify(response, null, 2)}`;
31
33
  }).join("\n\n");
32
34
  messages.push({
@@ -20,7 +20,7 @@ export async function listConversations(req, res) {
20
20
  }
21
21
  export async function getConversationHistory(req, res) {
22
22
  try {
23
- const { conversationId } = req.params;
23
+ const conversationId = req.params.conversationId;
24
24
  const conversation = getAiService().getConversation(conversationId);
25
25
  if (!conversation) {
26
26
  res.status(404).json({ error: 'Not found' });
@@ -34,7 +34,7 @@ export async function getConversationHistory(req, res) {
34
34
  }
35
35
  export async function deleteConversation(req, res) {
36
36
  try {
37
- const { conversationId } = req.params;
37
+ const conversationId = req.params.conversationId;
38
38
  const deleted = getAiService().deleteConversation(conversationId);
39
39
  if (!deleted) {
40
40
  res.status(404).json({ error: 'Not found' });
@@ -48,7 +48,7 @@ export async function deleteConversation(req, res) {
48
48
  }
49
49
  export async function sendMessage(req, res) {
50
50
  try {
51
- const { conversationId } = req.params;
51
+ const conversationId = req.params.conversationId;
52
52
  const { content } = req.body;
53
53
  if (!content) {
54
54
  res.status(400).json({ error: 'Missing content' });
@@ -1,9 +1,13 @@
1
1
  import OpenAI from 'openai';
2
2
  import { agent } from './agent.js';
3
3
  class AIService {
4
+ config;
5
+ conversations = new Map();
6
+ openai;
7
+ model;
8
+ extraPrompts;
4
9
  constructor(config) {
5
10
  this.config = config;
6
- this.conversations = new Map();
7
11
  this.openai = new OpenAI({
8
12
  apiKey: this.config.apiKey,
9
13
  });
@@ -47,7 +51,7 @@ class AIService {
47
51
  // Use the agent function with tools
48
52
  // Internaly pushes new messages to conversation.messages
49
53
  const currentMessagesCount = conversation.messages.length;
50
- await agent(this.openai, conversation.messages, model || this.model, this.extraPrompts);
54
+ await agent(this.openai, conversation.messages, model || this.model);
51
55
  // Return the last assistant message (the one just added by agent)
52
56
  const generatedMessages = conversation.messages.slice(currentMessagesCount);
53
57
  return generatedMessages;
@@ -3,14 +3,10 @@ import { inMemoryDbLogExporter, inMemoryDbMetricExporter, inMemoryDbSpanExporter
3
3
  const getTraces = async (searchInput) => {
4
4
  logger.debug("getTraces called with searchInput:", searchInput);
5
5
  try {
6
- const search = searchInput || {};
7
- const traces = await new Promise((resolve, reject) => {
8
- inMemoryDbSpanExporter.find(search, (err, docs) => {
9
- if (err)
10
- reject(err);
11
- else
12
- resolve(docs);
13
- });
6
+ const search = searchInput ? JSON.parse(searchInput) : {};
7
+ const traces = await inMemoryDbSpanExporter.find({
8
+ query: search,
9
+ limit: 1000
14
10
  });
15
11
  const simplifiedTraces = getSimplifiedTraces(traces);
16
12
  logger.debug(`Searching for traces with searchInput: ${JSON.stringify(search)}`);
@@ -38,7 +34,7 @@ const getLogs = async (startDate, endDate) => {
38
34
  const logs = (await inMemoryDbLogExporter.find({
39
35
  query: nedbQuery,
40
36
  messageSearch: null,
41
- limit: 50 // or any appropriate limit
37
+ limit: 1000 // or any appropriate limit
42
38
  })) || [];
43
39
  logger.debug(`Found ${logs.length} logs in the specified range.`);
44
40
  const simplifiedLogs = getSimplifiedLogs(logs);
@@ -1,5 +1,6 @@
1
1
  import jwt from 'jsonwebtoken';
2
2
  import logger from '../utils/logger.js';
3
+ import { bootEnvVariables } from '../config/bootConfig.js';
3
4
  function generateAccessToken(secret, expiresIn) {
4
5
  return jwt.sign({ type: "access" }, secret, { expiresIn: Math.floor(expiresIn / 1000) });
5
6
  }
@@ -28,7 +29,7 @@ export const getLogin = (oasTlmConfig) => (req, res) => {
28
29
  httpOnly: true,
29
30
  secure: process.env.NODE_ENV === "production",
30
31
  sameSite: "lax",
31
- path: oasTlmConfig.general.baseUrl + "/auth/refresh"
32
+ path: bootEnvVariables.OASTLM_BOOT_BASE_URL + "/auth/refresh"
32
33
  });
33
34
  res.status(200).json({ valid: true, message: "Login successful" });
34
35
  return;
@@ -46,7 +47,7 @@ export const getLogout = (oasTlmConfig) => (req, res) => {
46
47
  return;
47
48
  }
48
49
  res.clearCookie('oas-tlm-access-token', { path: '/' });
49
- res.clearCookie('oas-tlm-refresh-token', { path: oasTlmConfig.general.baseUrl + '/auth/refresh' });
50
+ res.clearCookie('oas-tlm-refresh-token', { path: bootEnvVariables.OASTLM_BOOT_BASE_URL + '/auth/refresh' });
50
51
  res.status(200).json({ valid: true, message: "Logged out" });
51
52
  };
52
53
  export const getRefresh = (oasTlmConfig) => (req, res) => {
@@ -1,6 +1,8 @@
1
1
  import { inMemoryDbLogExporter } from '../telemetry/telemetryRegistry.js';
2
2
  import logger from '../utils/logger.js';
3
3
  import { convertRegexRecursively } from '../utils/regexUtils.js';
4
+ import { importLogsToMemory, sanitizeLogRecords } from './logService.js';
5
+ import { gzipSync } from 'zlib';
4
6
  export const findLogs = async (req, res) => {
5
7
  const body = req.body || {};
6
8
  const messageSearch = body.textSearch || null;
@@ -27,7 +29,8 @@ export const findLogs = async (req, res) => {
27
29
  };
28
30
  const docs = await inMemoryDbLogExporter.find(findConfig);
29
31
  res.send({
30
- items: docs,
32
+ logsCount: docs.length,
33
+ logs: docs,
31
34
  });
32
35
  }
33
36
  catch (err) {
@@ -40,32 +43,19 @@ export const resetLogs = (req, res) => {
40
43
  res.send('Logs reset');
41
44
  };
42
45
  export const insertLogsToDb = async (req, res) => {
43
- const jsonContent = req.body.logs;
46
+ const jsonContent = (req.body || {}).logs;
44
47
  const resetData = req.query.reset === 'true';
45
48
  if (!Array.isArray(jsonContent)) {
46
49
  res.status(400).send({ error: 'Invalid data format.' });
47
50
  return;
48
51
  }
49
- const cleanedLogs = jsonContent.map((log) => {
50
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
51
- const { _id, ...rest } = log; // Remove _id if it exists
52
- return rest; // Return the cleaned log object
53
- });
52
+ const cleanedLogs = sanitizeLogRecords(jsonContent);
54
53
  try {
55
54
  let message = '';
55
+ await importLogsToMemory(cleanedLogs, { reset: resetData });
56
56
  if (resetData) {
57
- inMemoryDbLogExporter.reset();
58
57
  message += 'Logs Database reset. ';
59
58
  }
60
- await new Promise((resolve, reject) => {
61
- inMemoryDbLogExporter.insert(cleanedLogs, (err, newDocs) => {
62
- if (err) {
63
- logger.error('Error inserting logs:', err);
64
- return reject(err);
65
- }
66
- resolve(newDocs);
67
- });
68
- });
69
59
  message += `Inserted ${cleanedLogs.length} logs.`;
70
60
  res.send({ message, InsertedLogsCount: cleanedLogs.length });
71
61
  }
@@ -74,6 +64,29 @@ export const insertLogsToDb = async (req, res) => {
74
64
  res.status(500).send({ error: 'Failed to reset and insert data', details: err.message });
75
65
  }
76
66
  };
67
+ export const importLogs = async (req, res) => {
68
+ const resetData = req.query.reset === 'true';
69
+ const body = req.body || {};
70
+ const importedLogs = Array.isArray(body) ? body : body.logs;
71
+ try {
72
+ if (!Array.isArray(importedLogs)) {
73
+ res.status(400).send({ error: 'Invalid data format. Expected an array in request body or body.logs.' });
74
+ return;
75
+ }
76
+ const cleanedLogs = sanitizeLogRecords(importedLogs);
77
+ let message = '';
78
+ await importLogsToMemory(cleanedLogs, { reset: resetData });
79
+ if (resetData) {
80
+ message += 'Logs Database reset. ';
81
+ }
82
+ message += `Imported ${cleanedLogs.length} logs.`;
83
+ res.send({ message, ImportedLogsCount: cleanedLogs.length });
84
+ }
85
+ catch (err) {
86
+ logger.error('Import failed:', err);
87
+ res.status(400).send({ error: 'Failed to import logs', details: err.message });
88
+ }
89
+ };
77
90
  export const startLogs = (req, res) => {
78
91
  inMemoryDbLogExporter.enable();
79
92
  res.send('Log collection started');
@@ -87,7 +100,7 @@ export const statusLogs = (req, res) => {
87
100
  res.send({ active: isRunning });
88
101
  };
89
102
  export const setLogRetentionTime = (req, res) => {
90
- const retentionTimeInSeconds = req.body.retentionTimeInSeconds;
103
+ const retentionTimeInSeconds = (req.body || {}).retentionTimeInSeconds;
91
104
  if (typeof retentionTimeInSeconds !== 'number' || retentionTimeInSeconds <= 0) {
92
105
  res.status(400).send({ error: 'Invalid retention time. Must be a positive number.' });
93
106
  return;
@@ -99,3 +112,34 @@ export const getLogRetentionTime = (req, res) => {
99
112
  const retentionTimeInSeconds = inMemoryDbLogExporter.retentionTimeInSeconds || 0;
100
113
  res.send({ retentionTimeInSeconds: retentionTimeInSeconds });
101
114
  };
115
+ export const exportLogs = async (req, res) => {
116
+ try {
117
+ const findConfig = {
118
+ query: {},
119
+ messageSearch: null,
120
+ sortOrder: { timestamp: -1 }
121
+ };
122
+ const docs = await inMemoryDbLogExporter.find(findConfig);
123
+ const responseBody = { logsCount: docs.length, logs: docs };
124
+ const payload = JSON.stringify(responseBody);
125
+ const payloadSize = Buffer.byteLength(payload, 'utf-8');
126
+ const acceptsGzip = String(req.headers['accept-encoding'] || '').includes('gzip');
127
+ const timestamp = new Date().toISOString().slice(0, 19).replace(/[-T:]/g, '');
128
+ res.setHeader('Content-Type', 'application/json');
129
+ res.setHeader('Content-Disposition', `attachment; filename="logs-${timestamp}.json"`);
130
+ res.setHeader('Vary', 'Accept-Encoding');
131
+ if (acceptsGzip && payloadSize > 64 * 1024) {
132
+ const compressed = gzipSync(payload);
133
+ res.setHeader('Content-Encoding', 'gzip');
134
+ res.setHeader('Content-Length', compressed.length.toString());
135
+ res.end(compressed);
136
+ return;
137
+ }
138
+ res.setHeader('Content-Length', payloadSize.toString());
139
+ res.end(payload);
140
+ }
141
+ catch (err) {
142
+ logger.error('Failed to export logs:', err);
143
+ res.status(500).send({ error: 'Failed to export logs', details: err.message });
144
+ }
145
+ };
@@ -1,5 +1,5 @@
1
1
  import { Router } from 'express';
2
- import { startLogs, stopLogs, statusLogs, resetLogs, findLogs, insertLogsToDb, setLogRetentionTime, getLogRetentionTime } from './logController.js';
2
+ import { startLogs, stopLogs, statusLogs, resetLogs, findLogs, insertLogsToDb, importLogs, setLogRetentionTime, getLogRetentionTime, exportLogs } from './logController.js';
3
3
  export const getLogRoutes = () => {
4
4
  const router = Router();
5
5
  // Logs Control
@@ -9,6 +9,8 @@ export const getLogRoutes = () => {
9
9
  router.post('/reset', resetLogs);
10
10
  router.post('/retention-time', setLogRetentionTime);
11
11
  router.get('/retention-time', getLogRetentionTime);
12
+ router.get('/export', exportLogs);
13
+ router.post('/import', importLogs);
12
14
  router.get('/', findLogs);
13
15
  router.post('/', insertLogsToDb);
14
16
  router.post('/find', findLogs);
@@ -0,0 +1,25 @@
1
+ import { inMemoryDbLogExporter } from '../telemetry/telemetryRegistry.js';
2
+ export function sanitizeLogRecords(records) {
3
+ return records.map((record) => {
4
+ const { _id, ...rest } = record;
5
+ return rest;
6
+ });
7
+ }
8
+ export async function importLogsToMemory(records, options) {
9
+ if (options?.reset) {
10
+ inMemoryDbLogExporter.reset();
11
+ }
12
+ if (!records.length) {
13
+ return 0;
14
+ }
15
+ await new Promise((resolve, reject) => {
16
+ inMemoryDbLogExporter.insert(records, (err) => {
17
+ if (err) {
18
+ reject(err);
19
+ return;
20
+ }
21
+ resolve();
22
+ });
23
+ });
24
+ return records.length;
25
+ }