@newrelic/preflight 0.0.1-pre.1 → 1.0.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.
- package/LICENSE +183 -0
- package/README.md +498 -0
- package/dist/alerts/alert-log.d.ts +24 -0
- package/dist/alerts/alert-log.d.ts.map +1 -0
- package/dist/alerts/alert-log.js +159 -0
- package/dist/alerts/alert-log.js.map +1 -0
- package/dist/alerts/alert-snapshot-collector.d.ts +168 -0
- package/dist/alerts/alert-snapshot-collector.d.ts.map +1 -0
- package/dist/alerts/alert-snapshot-collector.js +243 -0
- package/dist/alerts/alert-snapshot-collector.js.map +1 -0
- package/dist/alerts/local-alert-engine.d.ts +86 -0
- package/dist/alerts/local-alert-engine.d.ts.map +1 -0
- package/dist/alerts/local-alert-engine.js +466 -0
- package/dist/alerts/local-alert-engine.js.map +1 -0
- package/dist/alerts/local-alert-rule.d.ts +439 -0
- package/dist/alerts/local-alert-rule.d.ts.map +1 -0
- package/dist/alerts/local-alert-rule.js +139 -0
- package/dist/alerts/local-alert-rule.js.map +1 -0
- package/dist/alerts/os-notifier.d.ts +39 -0
- package/dist/alerts/os-notifier.d.ts.map +1 -0
- package/dist/alerts/os-notifier.js +170 -0
- package/dist/alerts/os-notifier.js.map +1 -0
- package/dist/alerts/types.d.ts +35 -0
- package/dist/alerts/types.d.ts.map +1 -0
- package/dist/alerts/types.js +8 -0
- package/dist/alerts/types.js.map +1 -0
- package/dist/config.d.ts +169 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +868 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard/dashboard-server.d.ts +38 -0
- package/dist/dashboard/dashboard-server.d.ts.map +1 -0
- package/dist/dashboard/dashboard-server.js +207 -0
- package/dist/dashboard/dashboard-server.js.map +1 -0
- package/dist/dashboard/index.d.ts +3 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +2 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/dashboard/live-event-bus.d.ts +99 -0
- package/dist/dashboard/live-event-bus.d.ts.map +1 -0
- package/dist/dashboard/live-event-bus.js +56 -0
- package/dist/dashboard/live-event-bus.js.map +1 -0
- package/dist/dashboard/routes/api-handler.d.ts +122 -0
- package/dist/dashboard/routes/api-handler.d.ts.map +1 -0
- package/dist/dashboard/routes/api-handler.js +1414 -0
- package/dist/dashboard/routes/api-handler.js.map +1 -0
- package/dist/dashboard/routes/replay-analyzer.d.ts +15 -0
- package/dist/dashboard/routes/replay-analyzer.d.ts.map +1 -0
- package/dist/dashboard/routes/replay-analyzer.js +227 -0
- package/dist/dashboard/routes/replay-analyzer.js.map +1 -0
- package/dist/dashboard/routes/sse-handler.d.ts +4 -0
- package/dist/dashboard/routes/sse-handler.d.ts.map +1 -0
- package/dist/dashboard/routes/sse-handler.js +122 -0
- package/dist/dashboard/routes/sse-handler.js.map +1 -0
- package/dist/dashboard/routes/static-handler.d.ts +3 -0
- package/dist/dashboard/routes/static-handler.d.ts.map +1 -0
- package/dist/dashboard/routes/static-handler.js +123 -0
- package/dist/dashboard/routes/static-handler.js.map +1 -0
- package/dist/data/alerts/conditions/01-daily-cost-spike.json +16 -0
- package/dist/data/alerts/conditions/02-low-efficiency-score.json +16 -0
- package/dist/data/alerts/conditions/03-stuck-loop-rate.json +16 -0
- package/dist/data/alerts/conditions/04-anti-pattern-rate.json +16 -0
- package/dist/data/alerts/conditions/05-session-cost-budget.json +16 -0
- package/dist/data/alerts/conditions-personal/01-personal-daily-cost.json +16 -0
- package/dist/data/alerts/conditions-personal/02-personal-session-cost.json +16 -0
- package/dist/data/alerts/conditions-personal/03-personal-low-efficiency.json +16 -0
- package/dist/data/alerts/conditions-personal/04-personal-anti-pattern-rate.json +16 -0
- package/dist/data/alerts/conditions-personal/05-personal-stuck-loop.json +16 -0
- package/dist/data/alerts/policy.json +4 -0
- package/dist/data/dashboards/ai-coding-assistant-manager-view.json +103 -0
- package/dist/data/dashboards/ai-coding-assistant-overview.json +239 -0
- package/dist/data/dashboards/ai-coding-assistant-personal.json +442 -0
- package/dist/data/dashboards/ai-coding-assistant-platform-comparison.json +320 -0
- package/dist/data/dashboards/ai-coding-assistant-security.json +275 -0
- package/dist/data/dashboards/ai-coding-assistant-session-detail.json +296 -0
- package/dist/data/dashboards/ai-coding-assistant-team-view.json +345 -0
- package/dist/deploy/data-paths.d.ts +22 -0
- package/dist/deploy/data-paths.d.ts.map +1 -0
- package/dist/deploy/data-paths.js +69 -0
- package/dist/deploy/data-paths.js.map +1 -0
- package/dist/deploy/deploy-alerts.d.ts +58 -0
- package/dist/deploy/deploy-alerts.d.ts.map +1 -0
- package/dist/deploy/deploy-alerts.js +371 -0
- package/dist/deploy/deploy-alerts.js.map +1 -0
- package/dist/deploy/deploy-dashboards.d.ts +92 -0
- package/dist/deploy/deploy-dashboards.d.ts.map +1 -0
- package/dist/deploy/deploy-dashboards.js +282 -0
- package/dist/deploy/deploy-dashboards.js.map +1 -0
- package/dist/digest/digest-formatter.d.ts +3 -0
- package/dist/digest/digest-formatter.d.ts.map +1 -0
- package/dist/digest/digest-formatter.js +37 -0
- package/dist/digest/digest-formatter.js.map +1 -0
- package/dist/digest/digest-sender.d.ts +2 -0
- package/dist/digest/digest-sender.d.ts.map +1 -0
- package/dist/digest/digest-sender.js +29 -0
- package/dist/digest/digest-sender.js.map +1 -0
- package/dist/hooks/bash-classifier.d.ts +26 -0
- package/dist/hooks/bash-classifier.d.ts.map +1 -0
- package/dist/hooks/bash-classifier.js +409 -0
- package/dist/hooks/bash-classifier.js.map +1 -0
- package/dist/hooks/collector-script.d.ts +47 -0
- package/dist/hooks/collector-script.d.ts.map +1 -0
- package/dist/hooks/collector-script.js +662 -0
- package/dist/hooks/collector-script.js.map +1 -0
- package/dist/hooks/event-processor.d.ts +65 -0
- package/dist/hooks/event-processor.d.ts.map +1 -0
- package/dist/hooks/event-processor.js +342 -0
- package/dist/hooks/event-processor.js.map +1 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +5 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/session-resolver.d.ts +66 -0
- package/dist/hooks/session-resolver.d.ts.map +1 -0
- package/dist/hooks/session-resolver.js +196 -0
- package/dist/hooks/session-resolver.js.map +1 -0
- package/dist/hooks/tool-parsers.d.ts +19 -0
- package/dist/hooks/tool-parsers.d.ts.map +1 -0
- package/dist/hooks/tool-parsers.js +260 -0
- package/dist/hooks/tool-parsers.js.map +1 -0
- package/dist/index.d.ts +107 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1505 -0
- package/dist/index.js.map +1 -0
- package/dist/install/cli.d.ts +11 -0
- package/dist/install/cli.d.ts.map +1 -0
- package/dist/install/cli.js +365 -0
- package/dist/install/cli.js.map +1 -0
- package/dist/install/index.d.ts +4 -0
- package/dist/install/index.d.ts.map +1 -0
- package/dist/install/index.js +3 -0
- package/dist/install/index.js.map +1 -0
- package/dist/install/install-helper.d.ts +35 -0
- package/dist/install/install-helper.d.ts.map +1 -0
- package/dist/install/install-helper.js +227 -0
- package/dist/install/install-helper.js.map +1 -0
- package/dist/install/key-validator.d.ts +19 -0
- package/dist/install/key-validator.d.ts.map +1 -0
- package/dist/install/key-validator.js +122 -0
- package/dist/install/key-validator.js.map +1 -0
- package/dist/install/migrate.d.ts +12 -0
- package/dist/install/migrate.d.ts.map +1 -0
- package/dist/install/migrate.js +115 -0
- package/dist/install/migrate.js.map +1 -0
- package/dist/install/schedule.d.ts +11 -0
- package/dist/install/schedule.d.ts.map +1 -0
- package/dist/install/schedule.js +114 -0
- package/dist/install/schedule.js.map +1 -0
- package/dist/install/setup-wizard.d.ts +40 -0
- package/dist/install/setup-wizard.d.ts.map +1 -0
- package/dist/install/setup-wizard.js +489 -0
- package/dist/install/setup-wizard.js.map +1 -0
- package/dist/lib/date.d.ts +54 -0
- package/dist/lib/date.d.ts.map +1 -0
- package/dist/lib/date.js +85 -0
- package/dist/lib/date.js.map +1 -0
- package/dist/metrics/anti-patterns.d.ts +62 -0
- package/dist/metrics/anti-patterns.d.ts.map +1 -0
- package/dist/metrics/anti-patterns.js +301 -0
- package/dist/metrics/anti-patterns.js.map +1 -0
- package/dist/metrics/api-failure-tracker.d.ts +82 -0
- package/dist/metrics/api-failure-tracker.d.ts.map +1 -0
- package/dist/metrics/api-failure-tracker.js +202 -0
- package/dist/metrics/api-failure-tracker.js.map +1 -0
- package/dist/metrics/budget-tracker.d.ts +60 -0
- package/dist/metrics/budget-tracker.d.ts.map +1 -0
- package/dist/metrics/budget-tracker.js +130 -0
- package/dist/metrics/budget-tracker.js.map +1 -0
- package/dist/metrics/claudemd-tracker.d.ts +108 -0
- package/dist/metrics/claudemd-tracker.d.ts.map +1 -0
- package/dist/metrics/claudemd-tracker.js +337 -0
- package/dist/metrics/claudemd-tracker.js.map +1 -0
- package/dist/metrics/collaboration-profile.d.ts +65 -0
- package/dist/metrics/collaboration-profile.d.ts.map +1 -0
- package/dist/metrics/collaboration-profile.js +231 -0
- package/dist/metrics/collaboration-profile.js.map +1 -0
- package/dist/metrics/context-composition-tracker.d.ts +74 -0
- package/dist/metrics/context-composition-tracker.d.ts.map +1 -0
- package/dist/metrics/context-composition-tracker.js +202 -0
- package/dist/metrics/context-composition-tracker.js.map +1 -0
- package/dist/metrics/context-tracker.d.ts +78 -0
- package/dist/metrics/context-tracker.d.ts.map +1 -0
- package/dist/metrics/context-tracker.js +222 -0
- package/dist/metrics/context-tracker.js.map +1 -0
- package/dist/metrics/context-window-tracker.d.ts +18 -0
- package/dist/metrics/context-window-tracker.d.ts.map +1 -0
- package/dist/metrics/context-window-tracker.js +35 -0
- package/dist/metrics/context-window-tracker.js.map +1 -0
- package/dist/metrics/cost-forecast.d.ts +36 -0
- package/dist/metrics/cost-forecast.d.ts.map +1 -0
- package/dist/metrics/cost-forecast.js +91 -0
- package/dist/metrics/cost-forecast.js.map +1 -0
- package/dist/metrics/cost-per-outcome.d.ts +102 -0
- package/dist/metrics/cost-per-outcome.d.ts.map +1 -0
- package/dist/metrics/cost-per-outcome.js +266 -0
- package/dist/metrics/cost-per-outcome.js.map +1 -0
- package/dist/metrics/cost-tracker.d.ts +78 -0
- package/dist/metrics/cost-tracker.d.ts.map +1 -0
- package/dist/metrics/cost-tracker.js +169 -0
- package/dist/metrics/cost-tracker.js.map +1 -0
- package/dist/metrics/decision-tracker.d.ts +49 -0
- package/dist/metrics/decision-tracker.d.ts.map +1 -0
- package/dist/metrics/decision-tracker.js +161 -0
- package/dist/metrics/decision-tracker.js.map +1 -0
- package/dist/metrics/efficiency-score.d.ts +80 -0
- package/dist/metrics/efficiency-score.d.ts.map +1 -0
- package/dist/metrics/efficiency-score.js +219 -0
- package/dist/metrics/efficiency-score.js.map +1 -0
- package/dist/metrics/git-efficiency-tracker.d.ts +165 -0
- package/dist/metrics/git-efficiency-tracker.d.ts.map +1 -0
- package/dist/metrics/git-efficiency-tracker.js +1056 -0
- package/dist/metrics/git-efficiency-tracker.js.map +1 -0
- package/dist/metrics/index.d.ts +26 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +14 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/instruction-drift-tracker.d.ts +69 -0
- package/dist/metrics/instruction-drift-tracker.d.ts.map +1 -0
- package/dist/metrics/instruction-drift-tracker.js +213 -0
- package/dist/metrics/instruction-drift-tracker.js.map +1 -0
- package/dist/metrics/latency-decomposition.d.ts +50 -0
- package/dist/metrics/latency-decomposition.d.ts.map +1 -0
- package/dist/metrics/latency-decomposition.js +112 -0
- package/dist/metrics/latency-decomposition.js.map +1 -0
- package/dist/metrics/latency-tracker.d.ts +33 -0
- package/dist/metrics/latency-tracker.d.ts.map +1 -0
- package/dist/metrics/latency-tracker.js +93 -0
- package/dist/metrics/latency-tracker.js.map +1 -0
- package/dist/metrics/live-session-registry.d.ts +29 -0
- package/dist/metrics/live-session-registry.d.ts.map +1 -0
- package/dist/metrics/live-session-registry.js +103 -0
- package/dist/metrics/live-session-registry.js.map +1 -0
- package/dist/metrics/model-usage-tracker.d.ts +21 -0
- package/dist/metrics/model-usage-tracker.d.ts.map +1 -0
- package/dist/metrics/model-usage-tracker.js +53 -0
- package/dist/metrics/model-usage-tracker.js.map +1 -0
- package/dist/metrics/percentile.d.ts +5 -0
- package/dist/metrics/percentile.d.ts.map +1 -0
- package/dist/metrics/percentile.js +10 -0
- package/dist/metrics/percentile.js.map +1 -0
- package/dist/metrics/personal-coach.d.ts +47 -0
- package/dist/metrics/personal-coach.d.ts.map +1 -0
- package/dist/metrics/personal-coach.js +241 -0
- package/dist/metrics/personal-coach.js.map +1 -0
- package/dist/metrics/prompt-feedback.d.ts +75 -0
- package/dist/metrics/prompt-feedback.d.ts.map +1 -0
- package/dist/metrics/prompt-feedback.js +286 -0
- package/dist/metrics/prompt-feedback.js.map +1 -0
- package/dist/metrics/proxy-metrics.d.ts +54 -0
- package/dist/metrics/proxy-metrics.d.ts.map +1 -0
- package/dist/metrics/proxy-metrics.js +228 -0
- package/dist/metrics/proxy-metrics.js.map +1 -0
- package/dist/metrics/quality-proxy-tracker.d.ts +51 -0
- package/dist/metrics/quality-proxy-tracker.d.ts.map +1 -0
- package/dist/metrics/quality-proxy-tracker.js +162 -0
- package/dist/metrics/quality-proxy-tracker.js.map +1 -0
- package/dist/metrics/recommendation-engine.d.ts +72 -0
- package/dist/metrics/recommendation-engine.d.ts.map +1 -0
- package/dist/metrics/recommendation-engine.js +207 -0
- package/dist/metrics/recommendation-engine.js.map +1 -0
- package/dist/metrics/retry-detector.d.ts +43 -0
- package/dist/metrics/retry-detector.d.ts.map +1 -0
- package/dist/metrics/retry-detector.js +179 -0
- package/dist/metrics/retry-detector.js.map +1 -0
- package/dist/metrics/session-tracker.d.ts +75 -0
- package/dist/metrics/session-tracker.d.ts.map +1 -0
- package/dist/metrics/session-tracker.js +249 -0
- package/dist/metrics/session-tracker.js.map +1 -0
- package/dist/metrics/task-completion-tracker.d.ts +15 -0
- package/dist/metrics/task-completion-tracker.d.ts.map +1 -0
- package/dist/metrics/task-completion-tracker.js +27 -0
- package/dist/metrics/task-completion-tracker.js.map +1 -0
- package/dist/metrics/task-detector.d.ts +84 -0
- package/dist/metrics/task-detector.d.ts.map +1 -0
- package/dist/metrics/task-detector.js +302 -0
- package/dist/metrics/task-detector.js.map +1 -0
- package/dist/metrics/tool-selection-scorer.d.ts +39 -0
- package/dist/metrics/tool-selection-scorer.d.ts.map +1 -0
- package/dist/metrics/tool-selection-scorer.js +193 -0
- package/dist/metrics/tool-selection-scorer.js.map +1 -0
- package/dist/metrics/trend-analyzer.d.ts +92 -0
- package/dist/metrics/trend-analyzer.d.ts.map +1 -0
- package/dist/metrics/trend-analyzer.js +293 -0
- package/dist/metrics/trend-analyzer.js.map +1 -0
- package/dist/metrics/turn-cost-attributor.d.ts +41 -0
- package/dist/metrics/turn-cost-attributor.d.ts.map +1 -0
- package/dist/metrics/turn-cost-attributor.js +118 -0
- package/dist/metrics/turn-cost-attributor.js.map +1 -0
- package/dist/metrics/turn-tracker.d.ts +49 -0
- package/dist/metrics/turn-tracker.d.ts.map +1 -0
- package/dist/metrics/turn-tracker.js +192 -0
- package/dist/metrics/turn-tracker.js.map +1 -0
- package/dist/platforms/amazon-q-adapter.d.ts +10 -0
- package/dist/platforms/amazon-q-adapter.d.ts.map +1 -0
- package/dist/platforms/amazon-q-adapter.js +75 -0
- package/dist/platforms/amazon-q-adapter.js.map +1 -0
- package/dist/platforms/claude-code-adapter.d.ts +10 -0
- package/dist/platforms/claude-code-adapter.d.ts.map +1 -0
- package/dist/platforms/claude-code-adapter.js +48 -0
- package/dist/platforms/claude-code-adapter.js.map +1 -0
- package/dist/platforms/continue-adapter.d.ts +10 -0
- package/dist/platforms/continue-adapter.d.ts.map +1 -0
- package/dist/platforms/continue-adapter.js +73 -0
- package/dist/platforms/continue-adapter.js.map +1 -0
- package/dist/platforms/copilot-adapter.d.ts +37 -0
- package/dist/platforms/copilot-adapter.d.ts.map +1 -0
- package/dist/platforms/copilot-adapter.js +66 -0
- package/dist/platforms/copilot-adapter.js.map +1 -0
- package/dist/platforms/cursor-adapter.d.ts +10 -0
- package/dist/platforms/cursor-adapter.d.ts.map +1 -0
- package/dist/platforms/cursor-adapter.js +60 -0
- package/dist/platforms/cursor-adapter.js.map +1 -0
- package/dist/platforms/generic-mcp-adapter.d.ts +113 -0
- package/dist/platforms/generic-mcp-adapter.d.ts.map +1 -0
- package/dist/platforms/generic-mcp-adapter.js +139 -0
- package/dist/platforms/generic-mcp-adapter.js.map +1 -0
- package/dist/platforms/index.d.ts +15 -0
- package/dist/platforms/index.d.ts.map +1 -0
- package/dist/platforms/index.js +12 -0
- package/dist/platforms/index.js.map +1 -0
- package/dist/platforms/platform-registry.d.ts +11 -0
- package/dist/platforms/platform-registry.d.ts.map +1 -0
- package/dist/platforms/platform-registry.js +54 -0
- package/dist/platforms/platform-registry.js.map +1 -0
- package/dist/platforms/types.d.ts +36 -0
- package/dist/platforms/types.d.ts.map +1 -0
- package/dist/platforms/types.js +2 -0
- package/dist/platforms/types.js.map +1 -0
- package/dist/platforms/windsurf-adapter.d.ts +10 -0
- package/dist/platforms/windsurf-adapter.d.ts.map +1 -0
- package/dist/platforms/windsurf-adapter.js +63 -0
- package/dist/platforms/windsurf-adapter.js.map +1 -0
- package/dist/platforms/zed-adapter.d.ts +10 -0
- package/dist/platforms/zed-adapter.d.ts.map +1 -0
- package/dist/platforms/zed-adapter.js +72 -0
- package/dist/platforms/zed-adapter.js.map +1 -0
- package/dist/proxy/index.d.ts +7 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +5 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/otlp-receiver.d.ts +28 -0
- package/dist/proxy/otlp-receiver.d.ts.map +1 -0
- package/dist/proxy/otlp-receiver.js +319 -0
- package/dist/proxy/otlp-receiver.js.map +1 -0
- package/dist/proxy/proxy-manager.d.ts +47 -0
- package/dist/proxy/proxy-manager.d.ts.map +1 -0
- package/dist/proxy/proxy-manager.js +338 -0
- package/dist/proxy/proxy-manager.js.map +1 -0
- package/dist/proxy/types.d.ts +72 -0
- package/dist/proxy/types.d.ts.map +1 -0
- package/dist/proxy/types.js +33 -0
- package/dist/proxy/types.js.map +1 -0
- package/dist/proxy/upstream-http.d.ts +26 -0
- package/dist/proxy/upstream-http.d.ts.map +1 -0
- package/dist/proxy/upstream-http.js +209 -0
- package/dist/proxy/upstream-http.js.map +1 -0
- package/dist/proxy/upstream-stdio.d.ts +25 -0
- package/dist/proxy/upstream-stdio.d.ts.map +1 -0
- package/dist/proxy/upstream-stdio.js +256 -0
- package/dist/proxy/upstream-stdio.js.map +1 -0
- package/dist/security/audit-trail.d.ts +74 -0
- package/dist/security/audit-trail.d.ts.map +1 -0
- package/dist/security/audit-trail.js +338 -0
- package/dist/security/audit-trail.js.map +1 -0
- package/dist/security/index.d.ts +5 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +4 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/ssrf.d.ts +2 -0
- package/dist/security/ssrf.d.ts.map +1 -0
- package/dist/security/ssrf.js +126 -0
- package/dist/security/ssrf.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +117 -0
- package/dist/server.js.map +1 -0
- package/dist/shared/__test-utils__/log-output.d.ts +49 -0
- package/dist/shared/__test-utils__/log-output.d.ts.map +1 -0
- package/dist/shared/__test-utils__/log-output.js +38 -0
- package/dist/shared/__test-utils__/log-output.js.map +1 -0
- package/dist/shared/config.d.ts +56 -0
- package/dist/shared/config.d.ts.map +1 -0
- package/dist/shared/config.js +290 -0
- package/dist/shared/config.js.map +1 -0
- package/dist/shared/errors.d.ts +139 -0
- package/dist/shared/errors.d.ts.map +1 -0
- package/dist/shared/errors.js +406 -0
- package/dist/shared/errors.js.map +1 -0
- package/dist/shared/events/factory.d.ts +143 -0
- package/dist/shared/events/factory.d.ts.map +1 -0
- package/dist/shared/events/factory.js +351 -0
- package/dist/shared/events/factory.js.map +1 -0
- package/dist/shared/events/index.d.ts +6 -0
- package/dist/shared/events/index.d.ts.map +1 -0
- package/dist/shared/events/index.js +3 -0
- package/dist/shared/events/index.js.map +1 -0
- package/dist/shared/events/serialize.d.ts +87 -0
- package/dist/shared/events/serialize.d.ts.map +1 -0
- package/dist/shared/events/serialize.js +510 -0
- package/dist/shared/events/serialize.js.map +1 -0
- package/dist/shared/events/types.d.ts +139 -0
- package/dist/shared/events/types.d.ts.map +1 -0
- package/dist/shared/events/types.js +2 -0
- package/dist/shared/events/types.js.map +1 -0
- package/dist/shared/harvest/event-buffer.d.ts +59 -0
- package/dist/shared/harvest/event-buffer.d.ts.map +1 -0
- package/dist/shared/harvest/event-buffer.js +100 -0
- package/dist/shared/harvest/event-buffer.js.map +1 -0
- package/dist/shared/harvest/harvest-scheduler.d.ts +200 -0
- package/dist/shared/harvest/harvest-scheduler.d.ts.map +1 -0
- package/dist/shared/harvest/harvest-scheduler.js +647 -0
- package/dist/shared/harvest/harvest-scheduler.js.map +1 -0
- package/dist/shared/harvest/index.d.ts +7 -0
- package/dist/shared/harvest/index.d.ts.map +1 -0
- package/dist/shared/harvest/index.js +4 -0
- package/dist/shared/harvest/index.js.map +1 -0
- package/dist/shared/harvest/metric-aggregator.d.ts +115 -0
- package/dist/shared/harvest/metric-aggregator.d.ts.map +1 -0
- package/dist/shared/harvest/metric-aggregator.js +247 -0
- package/dist/shared/harvest/metric-aggregator.js.map +1 -0
- package/dist/shared/index.d.ts +22 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +13 -0
- package/dist/shared/index.js.map +1 -0
- package/dist/shared/logger.d.ts +57 -0
- package/dist/shared/logger.d.ts.map +1 -0
- package/dist/shared/logger.js +166 -0
- package/dist/shared/logger.js.map +1 -0
- package/dist/shared/pricing-data.d.ts +4 -0
- package/dist/shared/pricing-data.d.ts.map +1 -0
- package/dist/shared/pricing-data.js +473 -0
- package/dist/shared/pricing-data.js.map +1 -0
- package/dist/shared/pricing.d.ts +148 -0
- package/dist/shared/pricing.d.ts.map +1 -0
- package/dist/shared/pricing.js +528 -0
- package/dist/shared/pricing.js.map +1 -0
- package/dist/shared/redact.d.ts +33 -0
- package/dist/shared/redact.d.ts.map +1 -0
- package/dist/shared/redact.js +110 -0
- package/dist/shared/redact.js.map +1 -0
- package/dist/shared/timing.d.ts +96 -0
- package/dist/shared/timing.d.ts.map +1 -0
- package/dist/shared/timing.js +173 -0
- package/dist/shared/timing.js.map +1 -0
- package/dist/shared/tokens.d.ts +145 -0
- package/dist/shared/tokens.d.ts.map +1 -0
- package/dist/shared/tokens.js +492 -0
- package/dist/shared/tokens.js.map +1 -0
- package/dist/shared/transport/events-api.d.ts +14 -0
- package/dist/shared/transport/events-api.d.ts.map +1 -0
- package/dist/shared/transport/events-api.js +29 -0
- package/dist/shared/transport/events-api.js.map +1 -0
- package/dist/shared/transport/http-client.d.ts +49 -0
- package/dist/shared/transport/http-client.d.ts.map +1 -0
- package/dist/shared/transport/http-client.js +381 -0
- package/dist/shared/transport/http-client.js.map +1 -0
- package/dist/shared/transport/index.d.ts +10 -0
- package/dist/shared/transport/index.d.ts.map +1 -0
- package/dist/shared/transport/index.js +6 -0
- package/dist/shared/transport/index.js.map +1 -0
- package/dist/shared/transport/logs-api.d.ts +29 -0
- package/dist/shared/transport/logs-api.d.ts.map +1 -0
- package/dist/shared/transport/logs-api.js +40 -0
- package/dist/shared/transport/logs-api.js.map +1 -0
- package/dist/shared/transport/metric-api.d.ts +9 -0
- package/dist/shared/transport/metric-api.d.ts.map +1 -0
- package/dist/shared/transport/metric-api.js +39 -0
- package/dist/shared/transport/metric-api.js.map +1 -0
- package/dist/shared/transport/otlp-event-bridge.d.ts +22 -0
- package/dist/shared/transport/otlp-event-bridge.d.ts.map +1 -0
- package/dist/shared/transport/otlp-event-bridge.js +50 -0
- package/dist/shared/transport/otlp-event-bridge.js.map +1 -0
- package/dist/shared/transport/otlp-shared.d.ts +14 -0
- package/dist/shared/transport/otlp-shared.d.ts.map +1 -0
- package/dist/shared/transport/otlp-shared.js +49 -0
- package/dist/shared/transport/otlp-shared.js.map +1 -0
- package/dist/shared/transport/otlp-transport.d.ts +58 -0
- package/dist/shared/transport/otlp-transport.d.ts.map +1 -0
- package/dist/shared/transport/otlp-transport.js +236 -0
- package/dist/shared/transport/otlp-transport.js.map +1 -0
- package/dist/shared/transport/types.d.ts +129 -0
- package/dist/shared/transport/types.d.ts.map +1 -0
- package/dist/shared/transport/types.js +2 -0
- package/dist/shared/transport/types.js.map +1 -0
- package/dist/shared/version.d.ts +2 -0
- package/dist/shared/version.d.ts.map +1 -0
- package/dist/shared/version.js +2 -0
- package/dist/shared/version.js.map +1 -0
- package/dist/storage/index.d.ts +7 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +4 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/local-store.d.ts +153 -0
- package/dist/storage/local-store.d.ts.map +1 -0
- package/dist/storage/local-store.js +719 -0
- package/dist/storage/local-store.js.map +1 -0
- package/dist/storage/retention.d.ts +2 -0
- package/dist/storage/retention.d.ts.map +1 -0
- package/dist/storage/retention.js +53 -0
- package/dist/storage/retention.js.map +1 -0
- package/dist/storage/session-store.d.ts +97 -0
- package/dist/storage/session-store.d.ts.map +1 -0
- package/dist/storage/session-store.js +391 -0
- package/dist/storage/session-store.js.map +1 -0
- package/dist/storage/types.d.ts +64 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +2 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/storage/weekly-summary.d.ts +61 -0
- package/dist/storage/weekly-summary.d.ts.map +1 -0
- package/dist/storage/weekly-summary.js +243 -0
- package/dist/storage/weekly-summary.js.map +1 -0
- package/dist/tools/analytics-tools.d.ts +101 -0
- package/dist/tools/analytics-tools.d.ts.map +1 -0
- package/dist/tools/analytics-tools.js +71 -0
- package/dist/tools/analytics-tools.js.map +1 -0
- package/dist/tools/cost-tools.d.ts +121 -0
- package/dist/tools/cost-tools.d.ts.map +1 -0
- package/dist/tools/cost-tools.js +174 -0
- package/dist/tools/cost-tools.js.map +1 -0
- package/dist/tools/cross-session-tools.d.ts +376 -0
- package/dist/tools/cross-session-tools.d.ts.map +1 -0
- package/dist/tools/cross-session-tools.js +820 -0
- package/dist/tools/cross-session-tools.js.map +1 -0
- package/dist/tools/extended-analytics-tools.d.ts +164 -0
- package/dist/tools/extended-analytics-tools.d.ts.map +1 -0
- package/dist/tools/extended-analytics-tools.js +121 -0
- package/dist/tools/extended-analytics-tools.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +4 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/session-stats.d.ts +162 -0
- package/dist/tools/session-stats.d.ts.map +1 -0
- package/dist/tools/session-stats.js +1054 -0
- package/dist/tools/session-stats.js.map +1 -0
- package/dist/tools/workflow-tools.d.ts +126 -0
- package/dist/tools/workflow-tools.d.ts.map +1 -0
- package/dist/tools/workflow-tools.js +274 -0
- package/dist/tools/workflow-tools.js.map +1 -0
- package/dist/tracing/mcp-tracer.d.ts +4 -0
- package/dist/tracing/mcp-tracer.d.ts.map +1 -0
- package/dist/tracing/mcp-tracer.js +14 -0
- package/dist/tracing/mcp-tracer.js.map +1 -0
- package/dist/tracing/session-span.d.ts +14 -0
- package/dist/tracing/session-span.d.ts.map +1 -0
- package/dist/tracing/session-span.js +53 -0
- package/dist/tracing/session-span.js.map +1 -0
- package/dist/tracing/task-span-tracker.d.ts +11 -0
- package/dist/tracing/task-span-tracker.d.ts.map +1 -0
- package/dist/tracing/task-span-tracker.js +59 -0
- package/dist/tracing/task-span-tracker.js.map +1 -0
- package/dist/tracing/tool-call-span.d.ts +4 -0
- package/dist/tracing/tool-call-span.d.ts.map +1 -0
- package/dist/tracing/tool-call-span.js +60 -0
- package/dist/tracing/tool-call-span.js.map +1 -0
- package/dist/transport/index.d.ts +3 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +2 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/log-ingest.d.ts +42 -0
- package/dist/transport/log-ingest.d.ts.map +1 -0
- package/dist/transport/log-ingest.js +151 -0
- package/dist/transport/log-ingest.js.map +1 -0
- package/dist/transport/nr-ingest.d.ts +171 -0
- package/dist/transport/nr-ingest.d.ts.map +1 -0
- package/dist/transport/nr-ingest.js +659 -0
- package/dist/transport/nr-ingest.js.map +1 -0
- package/dist/types.d.ts +45 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/web/assets/index-BrL281N-.css +2 -0
- package/dist/web/assets/index-CcaYZzXm.js +42 -0
- package/dist/web/favicon.svg +15 -0
- package/dist/web/index.html +15 -0
- package/examples/local-alert-rules.json +106 -0
- package/package.json +129 -1
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { createLogger } from '../logger.js';
|
|
3
|
+
import { EventBuffer } from './event-buffer.js';
|
|
4
|
+
import { MetricAggregator, snapshotsToNrMetrics } from './metric-aggregator.js';
|
|
5
|
+
const logger = createLogger('harvest');
|
|
6
|
+
/**
|
|
7
|
+
* Generate an 8-hex-char correlation ID for one harvest cycle
|
|
8
|
+
*. Stamped via `logger.child({ harvestId })` so every
|
|
9
|
+
* log line emitted during the cycle — including from `sendEvents*` /
|
|
10
|
+
* `sendMetrics*` helpers — carries the same ID. Operators can pivot on
|
|
11
|
+
* `harvestId` in stderr to trace one cycle through batch send, retry,
|
|
12
|
+
* and overflow logs.
|
|
13
|
+
*/
|
|
14
|
+
function newHarvestId() {
|
|
15
|
+
return randomUUID().slice(0, 8);
|
|
16
|
+
}
|
|
17
|
+
const DEFAULT_EVENT_HARVEST_MS = 5_000;
|
|
18
|
+
const DEFAULT_METRIC_HARVEST_MS = 60_000;
|
|
19
|
+
export class HarvestScheduler {
|
|
20
|
+
eventBuffer;
|
|
21
|
+
metricAggregator;
|
|
22
|
+
licenseKey;
|
|
23
|
+
transportOptions;
|
|
24
|
+
sendEventsFn;
|
|
25
|
+
sendMetricsFn;
|
|
26
|
+
eventHarvestIntervalMs;
|
|
27
|
+
metricHarvestIntervalMs;
|
|
28
|
+
otlpEventBridge;
|
|
29
|
+
otlpTransport;
|
|
30
|
+
transport;
|
|
31
|
+
allowProcessExit;
|
|
32
|
+
eventIntervalId = null;
|
|
33
|
+
metricIntervalId = null;
|
|
34
|
+
running = false;
|
|
35
|
+
stopPromise = null;
|
|
36
|
+
// 15 + §4.16 — track in-flight harvests so (a) overlapping
|
|
37
|
+
// interval ticks don't double-fire harvests on the same buffers (a slow
|
|
38
|
+
// network can otherwise produce N concurrent fetches if interval < latency),
|
|
39
|
+
// and (b) stop() can await any harvest already in progress before kicking
|
|
40
|
+
// off the final flush (avoids racing the in-flight one).
|
|
41
|
+
inFlightEventHarvest = null;
|
|
42
|
+
inFlightMetricHarvest = null;
|
|
43
|
+
// Per-transport retry buffers. In 'both' mode, NR and OTLP failures must
|
|
44
|
+
// be tracked independently — otherwise a NR-only failure causes the next
|
|
45
|
+
// harvest to re-send the batch to *both* transports, duplicating on OTLP.
|
|
46
|
+
retryNrEventBatch = [];
|
|
47
|
+
retryOtlpEventBatch = [];
|
|
48
|
+
// retry buffers hold pre-explosion bucket snapshots, not
|
|
49
|
+
// exploded NrMetric[] wire form. On the next harvest the failed snapshots
|
|
50
|
+
// are merged back into the aggregator (via a temporary one, per transport)
|
|
51
|
+
// so that overlapping (name, attributes) keys collapse into a single
|
|
52
|
+
// rolled-up data point with one timestamp instead of two separately
|
|
53
|
+
// exploded sets of count/sum/min/max.
|
|
54
|
+
retryNrMetricSnapshots = [];
|
|
55
|
+
retryOtlpMetricSnapshots = [];
|
|
56
|
+
maxRetryEvents;
|
|
57
|
+
maxRetryMetricSnapshotsCap;
|
|
58
|
+
// Snapshot cap: 500 buckets ≈ 2000 wire metrics post-explosion. Larger
|
|
59
|
+
// effective ceiling than the previous 500-NrMetric cap (~125 buckets) by
|
|
60
|
+
// design — retry pressure is rare and the previous limit was tighter than
|
|
61
|
+
// intended.
|
|
62
|
+
boundBeforeExit;
|
|
63
|
+
constructor(options) {
|
|
64
|
+
this.licenseKey = options.licenseKey;
|
|
65
|
+
this.transportOptions = options.transportOptions;
|
|
66
|
+
this.sendEventsFn = options.sendEventsFn;
|
|
67
|
+
this.sendMetricsFn = options.sendMetricsFn;
|
|
68
|
+
this.eventHarvestIntervalMs = this.validateInterval('eventHarvestIntervalMs', options.eventHarvestIntervalMs ?? DEFAULT_EVENT_HARVEST_MS);
|
|
69
|
+
this.metricHarvestIntervalMs = this.validateInterval('metricHarvestIntervalMs', options.metricHarvestIntervalMs ?? DEFAULT_METRIC_HARVEST_MS);
|
|
70
|
+
this.otlpEventBridge = options.otlpEventBridge;
|
|
71
|
+
this.otlpTransport = options.otlpTransport;
|
|
72
|
+
this.transport = options.transport ?? 'nr-events-api';
|
|
73
|
+
this.allowProcessExit = options.allowProcessExit ?? false;
|
|
74
|
+
// Warn when the transport configuration requires an OTLP component that
|
|
75
|
+
// was not provided — events/metrics would be silently dropped (§HVS4).
|
|
76
|
+
const wantOtlp = this.transport === 'otlp' || this.transport === 'both';
|
|
77
|
+
if (wantOtlp && !this.otlpEventBridge) {
|
|
78
|
+
logger.warn('HarvestScheduler: transport includes otlp but otlpEventBridge is not configured — OTLP events will be silently dropped');
|
|
79
|
+
}
|
|
80
|
+
if (wantOtlp && !this.otlpTransport) {
|
|
81
|
+
logger.warn('HarvestScheduler: transport includes otlp but otlpTransport is not configured — OTLP metrics will be silently dropped');
|
|
82
|
+
}
|
|
83
|
+
this.eventBuffer = new EventBuffer({ maxSize: options.maxEventBufferSize });
|
|
84
|
+
this.metricAggregator = new MetricAggregator();
|
|
85
|
+
// 21: maxRetryEvents defaults to maxEventBufferSize when
|
|
86
|
+
// not specified, preserving prior behavior. Operators that need a
|
|
87
|
+
// deeper retry cap (bursty failures, long downstream outages) can set
|
|
88
|
+
// it independently — peak in-memory event count is then roughly
|
|
89
|
+
// maxEventBufferSize + maxRetryEvents.
|
|
90
|
+
this.maxRetryEvents = options.maxRetryEvents ?? options.maxEventBufferSize ?? 1_000;
|
|
91
|
+
this.maxRetryMetricSnapshotsCap = options.maxRetryMetricSnapshots ?? 500;
|
|
92
|
+
this.boundBeforeExit = () => {
|
|
93
|
+
void this.stop();
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Buffer an event for the next harvest tick.
|
|
98
|
+
*
|
|
99
|
+
* Returns `true` when the event was added without evicting another, and
|
|
100
|
+
* `false` when the event buffer was already full and the oldest event
|
|
101
|
+
* was head-dropped. Callers ignoring the return
|
|
102
|
+
* value see the same behavior as before — the per-harvest
|
|
103
|
+
* `nr.ai.dropped_events` self-monitoring metric still counts the drops
|
|
104
|
+
* regardless. The boolean lets producers throttle or surface a custom
|
|
105
|
+
* backpressure metric instead of polling.
|
|
106
|
+
*/
|
|
107
|
+
addEvent(event) {
|
|
108
|
+
return this.eventBuffer.add(event);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Record a metric sample.
|
|
112
|
+
*
|
|
113
|
+
* Returns `true` when the sample was accepted into a bucket, and `false`
|
|
114
|
+
* when it was rejected (non-finite value or invalid attribute type) per
|
|
115
|
+
* the §4.8 strict validation contract. Existing
|
|
116
|
+
* callers that ignore the return value see unchanged behavior.
|
|
117
|
+
*/
|
|
118
|
+
recordMetric(name, value, attributes = {}) {
|
|
119
|
+
return this.metricAggregator.record(name, value, attributes);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Start periodic harvesting.
|
|
123
|
+
*
|
|
124
|
+
* **Signal handling is the consumer's responsibility.** This library does
|
|
125
|
+
* NOT register `SIGTERM` or `SIGINT` handlers — auto-registering process-level
|
|
126
|
+
* signal handlers from a library is an anti-pattern:
|
|
127
|
+
*
|
|
128
|
+
* 1. The consumer's main process likely has its own `SIGTERM`/`SIGINT`
|
|
129
|
+
* handler. If both fire and the consumer's `process.exit(0)` runs first,
|
|
130
|
+
* our in-flight `stop()` is killed mid-flush and events are lost on
|
|
131
|
+
* graceful K8s rollout.
|
|
132
|
+
* 2. The consumer can't know that *we* attached a handler, so they can't
|
|
133
|
+
* coordinate ordering.
|
|
134
|
+
*
|
|
135
|
+
* To flush cleanly on shutdown, register your own signal handler and
|
|
136
|
+
* `await scheduler.stop()` before exiting:
|
|
137
|
+
*
|
|
138
|
+
* ```ts
|
|
139
|
+
* for (const sig of ['SIGTERM', 'SIGINT'] as const) {
|
|
140
|
+
* process.once(sig, async () => {
|
|
141
|
+
* await scheduler.stop();
|
|
142
|
+
* process.exit(0);
|
|
143
|
+
* });
|
|
144
|
+
* }
|
|
145
|
+
* ```
|
|
146
|
+
*
|
|
147
|
+
* **Process exit semantics.** By default
|
|
148
|
+
* (`allowProcessExit: false`), the harvest intervals keep the Node event
|
|
149
|
+
* loop alive — your process will NOT exit until you call
|
|
150
|
+
* `await scheduler.stop()`. This is intentional: silently dropping
|
|
151
|
+
* buffered events on exit is worse than a process that won't quit.
|
|
152
|
+
* Pass `allowProcessExit: true` to opt into `unref()` + a best-effort
|
|
153
|
+
* `beforeExit` fallback (suitable for short-lived CLI tools where
|
|
154
|
+
* losing the last few buffered events is acceptable).
|
|
155
|
+
*/
|
|
156
|
+
start() {
|
|
157
|
+
if (this.running) {
|
|
158
|
+
logger.warn('HarvestScheduler already running');
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// 19: refuse start() while a previous stop() is still
|
|
162
|
+
// resolving. `running` flips to false at the top of doStop(), so without
|
|
163
|
+
// this guard a fast-cycling consumer could call start() before stop()
|
|
164
|
+
// has finished tearing down intervals / removing the beforeExit
|
|
165
|
+
// listener — the new start would then register a fresh beforeExit
|
|
166
|
+
// listener while the old stop is mid-flight clearing it. Caller must
|
|
167
|
+
// `await scheduler.stop()` before restarting. (`stopPromise` is cleared
|
|
168
|
+
// via `.finally()` in `stop()` once doStop resolves, so a fully-awaited
|
|
169
|
+
// stop unblocks the next start.)
|
|
170
|
+
if (this.stopPromise !== null) {
|
|
171
|
+
logger.warn('HarvestScheduler.start() called while a previous stop() is in flight — refusing. Await stop() before restarting.');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
this.running = true;
|
|
175
|
+
this.eventIntervalId = setInterval(() => {
|
|
176
|
+
// Re-entrancy guard: if the previous harvest is still running (slow
|
|
177
|
+
// network, big batch), skip this tick rather than fire concurrent
|
|
178
|
+
// fetches. Data isn't lost — the buffer still accumulates and the
|
|
179
|
+
// next clean tick picks it up.
|
|
180
|
+
if (this.inFlightEventHarvest)
|
|
181
|
+
return;
|
|
182
|
+
// .catch prevents unhandled-rejection process crash if the promise
|
|
183
|
+
// rejects unexpectedly before reaching its internal try/catch (§HV3).
|
|
184
|
+
this.harvestEvents().catch((err) => {
|
|
185
|
+
logger.error('Unexpected error in event harvest interval', {
|
|
186
|
+
error: err instanceof Error ? err.message : String(err),
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}, this.eventHarvestIntervalMs);
|
|
190
|
+
this.metricIntervalId = setInterval(() => {
|
|
191
|
+
if (this.inFlightMetricHarvest)
|
|
192
|
+
return;
|
|
193
|
+
this.harvestMetrics().catch((err) => {
|
|
194
|
+
logger.error('Unexpected error in metric harvest interval', {
|
|
195
|
+
error: err instanceof Error ? err.message : String(err),
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
}, this.metricHarvestIntervalMs);
|
|
199
|
+
if (this.allowProcessExit) {
|
|
200
|
+
// opt-in path for short-lived CLI tools. unref()'d
|
|
201
|
+
// intervals don't hold the loop open, and beforeExit is registered as
|
|
202
|
+
// a best-effort final-flush attempt. Both are best-effort: Node can
|
|
203
|
+
// exit before the fire-and-forget stop() finishes, so events may be
|
|
204
|
+
// lost. Consumers should still prefer awaiting stop() explicitly.
|
|
205
|
+
this.eventIntervalId.unref();
|
|
206
|
+
this.metricIntervalId.unref();
|
|
207
|
+
process.once('beforeExit', this.boundBeforeExit);
|
|
208
|
+
}
|
|
209
|
+
logger.info('Harvest scheduler started', {
|
|
210
|
+
eventIntervalMs: this.eventHarvestIntervalMs,
|
|
211
|
+
metricIntervalMs: this.metricHarvestIntervalMs,
|
|
212
|
+
allowProcessExit: this.allowProcessExit,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Stop the scheduler and run a final flush.
|
|
217
|
+
*
|
|
218
|
+
* Concurrent calls are coalesced — the first call drives the shutdown,
|
|
219
|
+
* subsequent calls receive the same `Promise`.
|
|
220
|
+
*
|
|
221
|
+
* Order of operations:
|
|
222
|
+
* 1. Clear the harvest intervals.
|
|
223
|
+
* 2. Await any in-flight harvest started by an interval tick.
|
|
224
|
+
* 3. Run a final `harvestEvents()` + `harvestMetrics()`.
|
|
225
|
+
* 4. `flush()` the optional `otlpEventBridge` and `otlpTransport` so
|
|
226
|
+
* pending OTLP batches are drained.
|
|
227
|
+
*
|
|
228
|
+
* **OTel SDK teardown is the consumer's responsibility.**
|
|
229
|
+
* `stop()` deliberately does not call `shutdown()` on `otlpTransport` /
|
|
230
|
+
* `otlpEventBridge` — the scheduler does not own those objects'
|
|
231
|
+
* lifecycles. If you do not call `shutdown()` yourself, you will leak
|
|
232
|
+
* keep-alive HTTP/2 connections, may drop in-flight OTLP batches sitting
|
|
233
|
+
* inside the SDK's batch processors, and SDK timer handles can keep the
|
|
234
|
+
* Node event loop alive past `stop()`. After `await scheduler.stop()`,
|
|
235
|
+
* call:
|
|
236
|
+
*
|
|
237
|
+
* ```ts
|
|
238
|
+
* await otlpTransport.shutdown(); // tracer + meter providers
|
|
239
|
+
* await otlpEventBridge.shutdown(); // logger provider
|
|
240
|
+
* ```
|
|
241
|
+
*
|
|
242
|
+
* Order matters: shut down only *after* `stop()` has resolved, otherwise
|
|
243
|
+
* the final flush above will fail mid-export.
|
|
244
|
+
*/
|
|
245
|
+
async stop() {
|
|
246
|
+
if (this.stopPromise)
|
|
247
|
+
return this.stopPromise;
|
|
248
|
+
// Do NOT short-circuit when !this.running — a caller who added events
|
|
249
|
+
// without ever calling start() should still get a final flush (§HVS1).
|
|
250
|
+
// The interval teardown steps in doStop() are all safe no-ops when the
|
|
251
|
+
// intervals were never started.
|
|
252
|
+
// 19: drive doStop to completion through stopPromise so
|
|
253
|
+
// concurrent stop() callers coalesce, then clear stopPromise once it
|
|
254
|
+
// resolves so a subsequent start() can pass the "no in-flight stop"
|
|
255
|
+
// guard. Without the clear, a restart-after-stop sequence would be
|
|
256
|
+
// refused (the §4.19 guard sees a non-null stopPromise from the prior
|
|
257
|
+
// session and treats the next start() as racing an in-flight stop).
|
|
258
|
+
const promise = this.doStop().finally(() => {
|
|
259
|
+
// Only clear if the promise we registered is still the current one —
|
|
260
|
+
// a brand-new stop() may have already overwritten it (unlikely under
|
|
261
|
+
// normal use, but defensive).
|
|
262
|
+
if (this.stopPromise === promise)
|
|
263
|
+
this.stopPromise = null;
|
|
264
|
+
});
|
|
265
|
+
this.stopPromise = promise;
|
|
266
|
+
return promise;
|
|
267
|
+
}
|
|
268
|
+
async doStop() {
|
|
269
|
+
this.running = false;
|
|
270
|
+
if (this.eventIntervalId !== null) {
|
|
271
|
+
clearInterval(this.eventIntervalId);
|
|
272
|
+
this.eventIntervalId = null;
|
|
273
|
+
}
|
|
274
|
+
if (this.metricIntervalId !== null) {
|
|
275
|
+
clearInterval(this.metricIntervalId);
|
|
276
|
+
this.metricIntervalId = null;
|
|
277
|
+
}
|
|
278
|
+
if (this.allowProcessExit) {
|
|
279
|
+
process.removeListener('beforeExit', this.boundBeforeExit);
|
|
280
|
+
}
|
|
281
|
+
// wait for any harvest started by an interval tick
|
|
282
|
+
// to complete before we initiate the final flush. Without this, stop()'s
|
|
283
|
+
// own harvestEvents call could race the in-flight one (the re-entrancy
|
|
284
|
+
// guard would make it a no-op, returning the existing promise — but if
|
|
285
|
+
// we don't await first, we'd queue a second harvest immediately after
|
|
286
|
+
// and lose the chance to flush newly-buffered events from after the
|
|
287
|
+
// in-flight one started).
|
|
288
|
+
if (this.inFlightEventHarvest) {
|
|
289
|
+
await this.inFlightEventHarvest.catch((err) => {
|
|
290
|
+
logger.warn('In-flight event harvest errored during stop()', {
|
|
291
|
+
error: err instanceof Error ? err.message : String(err),
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
if (this.inFlightMetricHarvest) {
|
|
296
|
+
await this.inFlightMetricHarvest.catch((err) => {
|
|
297
|
+
logger.warn('In-flight metric harvest errored during stop()', {
|
|
298
|
+
error: err instanceof Error ? err.message : String(err),
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
// Final flush of in-memory buffers
|
|
303
|
+
await Promise.all([this.harvestEvents(), this.harvestMetrics()]);
|
|
304
|
+
// Flush OTLP-managed buffers (they live inside the OTel SDK batch
|
|
305
|
+
// processors; otherwise events/metrics already enqueued but not yet
|
|
306
|
+
// exported are dropped on process exit).
|
|
307
|
+
const flushes = [];
|
|
308
|
+
if (this.otlpEventBridge) {
|
|
309
|
+
flushes.push(this.otlpEventBridge.flush().catch((err) => {
|
|
310
|
+
logger.warn('Error flushing OTLP event bridge', {
|
|
311
|
+
error: err instanceof Error ? err.message : String(err),
|
|
312
|
+
});
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
315
|
+
if (this.otlpTransport) {
|
|
316
|
+
flushes.push(this.otlpTransport.flush().catch((err) => {
|
|
317
|
+
logger.warn('Error flushing OTLP transport', {
|
|
318
|
+
error: err instanceof Error ? err.message : String(err),
|
|
319
|
+
});
|
|
320
|
+
}));
|
|
321
|
+
}
|
|
322
|
+
if (flushes.length > 0)
|
|
323
|
+
await Promise.all(flushes);
|
|
324
|
+
logger.info('Harvest scheduler stopped');
|
|
325
|
+
}
|
|
326
|
+
async harvestEvents() {
|
|
327
|
+
// If already in flight, return the same promise — callers (interval
|
|
328
|
+
// ticks, manual invocations, stop's final flush) all wait on the
|
|
329
|
+
// same operation rather than racing.
|
|
330
|
+
if (this.inFlightEventHarvest)
|
|
331
|
+
return this.inFlightEventHarvest;
|
|
332
|
+
this.inFlightEventHarvest = this.doHarvestEvents().finally(() => {
|
|
333
|
+
this.inFlightEventHarvest = null;
|
|
334
|
+
});
|
|
335
|
+
return this.inFlightEventHarvest;
|
|
336
|
+
}
|
|
337
|
+
validateInterval(name, ms) {
|
|
338
|
+
if (!Number.isFinite(ms) || ms < 100) {
|
|
339
|
+
throw new RangeError(`HarvestScheduler: ${name} must be a finite number >= 100ms, got ${String(ms)}`);
|
|
340
|
+
}
|
|
341
|
+
return ms;
|
|
342
|
+
}
|
|
343
|
+
async doHarvestEvents() {
|
|
344
|
+
// 5: scoped child logger stamps `harvestId` on every
|
|
345
|
+
// log line emitted during this cycle (overflow, retry, requeue,
|
|
346
|
+
// OTLP-export failures). Operators searching stderr for one harvest's
|
|
347
|
+
// story have a single pivot.
|
|
348
|
+
const harvestLog = logger.child({ harvestId: newHarvestId(), scope: 'events' });
|
|
349
|
+
const fresh = this.eventBuffer.flush();
|
|
350
|
+
// §4.1: surface event-buffer head-drops as a self-monitoring metric so
|
|
351
|
+
// overflow loss is visible in the consumer's own NR dashboards. Drained
|
|
352
|
+
// once per harvest, paired with a single warn log.
|
|
353
|
+
const dropped = this.eventBuffer.drainDropCount();
|
|
354
|
+
if (dropped > 0) {
|
|
355
|
+
harvestLog.warn('EventBuffer overflow — oldest entries dropped', { dropped });
|
|
356
|
+
this.metricAggregator.record('nr.ai.dropped_events', dropped, {
|
|
357
|
+
source: 'event_buffer',
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
// Drain the add counter each cycle so totalAdded represents adds-since-
|
|
361
|
+
// last-flush (§HVS3). Not emitted as a metric today; wired here so a
|
|
362
|
+
// future nr.ai.added_events metric reads the correct per-interval value.
|
|
363
|
+
this.eventBuffer.drainAddCount();
|
|
364
|
+
const wantNr = this.transport === 'nr-events-api' || this.transport === 'both';
|
|
365
|
+
const wantOtlp = this.transport === 'otlp' || this.transport === 'both';
|
|
366
|
+
const sends = [];
|
|
367
|
+
if (wantNr) {
|
|
368
|
+
const nrBatch = this.retryNrEventBatch.length > 0 ? [...this.retryNrEventBatch, ...fresh] : [...fresh];
|
|
369
|
+
this.retryNrEventBatch = [];
|
|
370
|
+
if (nrBatch.length > 0) {
|
|
371
|
+
sends.push(this.sendEventsToNr(nrBatch, harvestLog));
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (wantOtlp) {
|
|
375
|
+
const otlpBatch = this.retryOtlpEventBatch.length > 0 ? [...this.retryOtlpEventBatch, ...fresh] : [...fresh];
|
|
376
|
+
this.retryOtlpEventBatch = [];
|
|
377
|
+
if (otlpBatch.length > 0) {
|
|
378
|
+
sends.push(this.sendEventsToOtlp(otlpBatch, harvestLog));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (sends.length > 0)
|
|
382
|
+
await Promise.all(sends);
|
|
383
|
+
}
|
|
384
|
+
async sendEventsToNr(batch, log = logger) {
|
|
385
|
+
try {
|
|
386
|
+
const result = await this.sendEventsFn(batch, this.licenseKey, this.transportOptions);
|
|
387
|
+
if (!result.success) {
|
|
388
|
+
log.warn('Failed to send events to NR — re-queuing batch for retry', {
|
|
389
|
+
batchSize: batch.length,
|
|
390
|
+
error: result.error,
|
|
391
|
+
});
|
|
392
|
+
this.requeueNrEvents(batch, log);
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
// §4.24 — debug-level success log so operators tailing stderr can
|
|
396
|
+
// distinguish "harvest completed cleanly with N events" from
|
|
397
|
+
// "harvest never ran". Debug (not info) keeps the steady-state
|
|
398
|
+
// happy-path output quiet at default log levels.
|
|
399
|
+
log.debug('Sent events to NR', { batchSize: batch.length });
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
catch (err) {
|
|
403
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
404
|
+
log.warn('Unexpected error sending events to NR — re-queuing batch for retry', {
|
|
405
|
+
batchSize: batch.length,
|
|
406
|
+
error: message,
|
|
407
|
+
});
|
|
408
|
+
this.requeueNrEvents(batch, log);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
async sendEventsToOtlp(batch, log = logger) {
|
|
412
|
+
try {
|
|
413
|
+
if (this.otlpEventBridge) {
|
|
414
|
+
this.otlpEventBridge.sendEvents(batch);
|
|
415
|
+
// §4.24 — paired with the NR-side success log. Note: this only
|
|
416
|
+
// confirms enqueue into the OTel BatchLogRecordProcessor, not
|
|
417
|
+
// export — the SDK decides when the wire send actually fires.
|
|
418
|
+
log.debug('Enqueued events to OTLP bridge', { batchSize: batch.length });
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
// Bridge absent: the constructor already warned once (§HVS4). Warn
|
|
422
|
+
// at debug here so repeated harvests are traceable without flooding
|
|
423
|
+
// stderr (§HV2).
|
|
424
|
+
log.debug('OTLP event bridge not configured — batch discarded', {
|
|
425
|
+
batchSize: batch.length,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
catch (err) {
|
|
430
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
431
|
+
log.warn('Error sending events to OTLP — re-queuing batch for retry', {
|
|
432
|
+
batchSize: batch.length,
|
|
433
|
+
error: message,
|
|
434
|
+
});
|
|
435
|
+
this.requeueOtlpEvents(batch, log);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async harvestMetrics() {
|
|
439
|
+
if (this.inFlightMetricHarvest)
|
|
440
|
+
return this.inFlightMetricHarvest;
|
|
441
|
+
this.inFlightMetricHarvest = this.doHarvestMetrics().finally(() => {
|
|
442
|
+
this.inFlightMetricHarvest = null;
|
|
443
|
+
});
|
|
444
|
+
return this.inFlightMetricHarvest;
|
|
445
|
+
}
|
|
446
|
+
async doHarvestMetrics() {
|
|
447
|
+
// 5: scoped child logger stamps a `harvestId` on every
|
|
448
|
+
// log line emitted during this metric harvest cycle.
|
|
449
|
+
const harvestLog = logger.child({ harvestId: newHarvestId(), scope: 'metrics' });
|
|
450
|
+
// 25 / §4.11 — self-monitoring: drain MetricAggregator's
|
|
451
|
+
// drop counter and emit it as a metric so non-finite-value rejections
|
|
452
|
+
// and invalid-attribute rejections are visible in the consumer's own
|
|
453
|
+
// NR dashboards. Mirrors §4.1's `nr.ai.dropped_events` pattern from
|
|
454
|
+
// doHarvestEvents. Note this is recorded BEFORE harvestSnapshots() so
|
|
455
|
+
// the dropped_metrics gauge itself flows through this same harvest tick
|
|
456
|
+
// (one extra bucket, one extra summary on the wire).
|
|
457
|
+
const droppedMetrics = this.metricAggregator.drainDropCount();
|
|
458
|
+
if (droppedMetrics > 0) {
|
|
459
|
+
harvestLog.warn('MetricAggregator overflow — non-finite or invalid samples dropped', {
|
|
460
|
+
dropped: droppedMetrics,
|
|
461
|
+
});
|
|
462
|
+
this.metricAggregator.record('nr.ai.dropped_metrics', droppedMetrics, {
|
|
463
|
+
source: 'metric_aggregator',
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
// drain the aggregator as snapshots, then per
|
|
467
|
+
// transport: merge the previous failed-send snapshots with the fresh
|
|
468
|
+
// ones (so duplicate name+attrs buckets accumulate), then explode to
|
|
469
|
+
// wire form. Each transport gets its own merged set so a NR-only
|
|
470
|
+
// failure doesn't double-send to OTLP (preserves §4.5 semantics).
|
|
471
|
+
const fresh = this.metricAggregator.harvestSnapshots();
|
|
472
|
+
const wantNr = this.transport === 'nr-events-api' || this.transport === 'both';
|
|
473
|
+
const wantOtlp = this.transport === 'otlp' || this.transport === 'both';
|
|
474
|
+
const sends = [];
|
|
475
|
+
if (wantNr) {
|
|
476
|
+
const nrSnapshots = this.mergeSnapshots(this.retryNrMetricSnapshots, fresh);
|
|
477
|
+
this.retryNrMetricSnapshots = [];
|
|
478
|
+
if (nrSnapshots.length > 0) {
|
|
479
|
+
sends.push(this.sendMetricsToNr(snapshotsToNrMetrics(nrSnapshots, this.metricHarvestIntervalMs), nrSnapshots, harvestLog));
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (wantOtlp) {
|
|
483
|
+
const otlpSnapshots = this.mergeSnapshots(this.retryOtlpMetricSnapshots, fresh);
|
|
484
|
+
this.retryOtlpMetricSnapshots = [];
|
|
485
|
+
if (otlpSnapshots.length > 0) {
|
|
486
|
+
sends.push(this.sendMetricsToOtlp(snapshotsToNrMetrics(otlpSnapshots, this.metricHarvestIntervalMs), otlpSnapshots, harvestLog));
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (sends.length > 0)
|
|
490
|
+
await Promise.all(sends);
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Combine retry snapshots with a fresh harvest's snapshots, accumulating
|
|
494
|
+
* any duplicate (name, attributes) buckets. Returns a new snapshot list
|
|
495
|
+
* with one entry per unique key. Uses a throwaway {@link MetricAggregator}
|
|
496
|
+
* as the merge engine so the bucket-key logic is not duplicated here.
|
|
497
|
+
*
|
|
498
|
+
* without this re-merge, a failed-send retry plus a
|
|
499
|
+
* fresh harvest hitting the same metric+attrs would produce two wire
|
|
500
|
+
* data points with different timestamps, breaking downstream NRQL
|
|
501
|
+
* aggregation that expects one bucket per harvest interval.
|
|
502
|
+
*/
|
|
503
|
+
mergeSnapshots(retry, fresh) {
|
|
504
|
+
if (retry.length === 0)
|
|
505
|
+
return [...fresh];
|
|
506
|
+
if (fresh.length === 0)
|
|
507
|
+
return [...retry];
|
|
508
|
+
const merger = new MetricAggregator();
|
|
509
|
+
merger.merge(retry);
|
|
510
|
+
merger.merge(fresh);
|
|
511
|
+
return merger.harvestSnapshots();
|
|
512
|
+
}
|
|
513
|
+
async sendMetricsToNr(batch, snapshots, log = logger) {
|
|
514
|
+
try {
|
|
515
|
+
const result = await this.sendMetricsFn(batch, this.licenseKey, this.transportOptions);
|
|
516
|
+
if (!result.success) {
|
|
517
|
+
log.warn('Failed to send metrics to NR — re-queuing batch for retry', {
|
|
518
|
+
batchSize: batch.length,
|
|
519
|
+
error: result.error,
|
|
520
|
+
});
|
|
521
|
+
this.requeueNrMetrics(snapshots, log);
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
// §4.24 — debug-level success log so operators tailing stderr can
|
|
525
|
+
// distinguish "harvest completed cleanly with N metrics" from
|
|
526
|
+
// "harvest never ran". Includes both the wire batch count and
|
|
527
|
+
// the underlying snapshot count so a future cardinality-explosion
|
|
528
|
+
// signal is visible at debug.
|
|
529
|
+
log.debug('Sent metrics to NR', {
|
|
530
|
+
batchSize: batch.length,
|
|
531
|
+
snapshotCount: snapshots.length,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
catch (err) {
|
|
536
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
537
|
+
log.warn('Unexpected error sending metrics to NR — re-queuing batch for retry', {
|
|
538
|
+
batchSize: batch.length,
|
|
539
|
+
error: message,
|
|
540
|
+
});
|
|
541
|
+
this.requeueNrMetrics(snapshots, log);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
async sendMetricsToOtlp(batch, snapshots, log = logger) {
|
|
545
|
+
try {
|
|
546
|
+
if (this.otlpTransport) {
|
|
547
|
+
await this.otlpTransport.exportMetrics(batch);
|
|
548
|
+
// §4.24 — paired with the NR-side success log.
|
|
549
|
+
log.debug('Sent metrics to OTLP', {
|
|
550
|
+
batchSize: batch.length,
|
|
551
|
+
snapshotCount: snapshots.length,
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
// Transport absent: the constructor already warned once (§HVS4).
|
|
556
|
+
log.debug('OTLP transport not configured — metric batch discarded', {
|
|
557
|
+
batchSize: batch.length,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
catch (err) {
|
|
562
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
563
|
+
// §11.1 — OtlpTransport.exportMetrics throws { code: 'OTLP_BAD_REQUEST' } for
|
|
564
|
+
// HTTP 400 to signal that the payload is permanently malformed and will always
|
|
565
|
+
// fail. Drop the batch instead of requeuing so it does not occupy the retry
|
|
566
|
+
// buffer indefinitely. All other errors are retried normally.
|
|
567
|
+
const isNonRetryable = err instanceof Error && err.code === 'OTLP_BAD_REQUEST';
|
|
568
|
+
if (isNonRetryable) {
|
|
569
|
+
log.error('OTLP metric export rejected (bad request) — dropping batch, will not retry', {
|
|
570
|
+
batchSize: batch.length,
|
|
571
|
+
snapshotCount: snapshots.length,
|
|
572
|
+
error: message,
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
log.warn('Error sending metrics to OTLP — re-queuing batch for retry', {
|
|
577
|
+
batchSize: batch.length,
|
|
578
|
+
error: message,
|
|
579
|
+
});
|
|
580
|
+
this.requeueOtlpMetrics(snapshots, log);
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
// NOTE (§HVS5): the self-monitoring metrics recorded below (nr.ai.dropped_events,
|
|
585
|
+
// nr.ai.dropped_metrics) are written to metricAggregator AFTER the current
|
|
586
|
+
// harvest cycle has already drained it via harvestSnapshots(). They will not
|
|
587
|
+
// be sent until the NEXT harvest cycle — operators see overflow counts one
|
|
588
|
+
// cycle late. This is an accepted trade-off; documenting so the lag is not
|
|
589
|
+
// mistaken for a bug.
|
|
590
|
+
requeueNrEvents(batch, log = logger) {
|
|
591
|
+
// for-of push avoids two hazards: (a) the O(n+m) intermediate array from
|
|
592
|
+
// [...old, ...new] (§HVS7), and (b) push(...batch) throwing RangeError when
|
|
593
|
+
// batch.length exceeds the engine's argument-count limit (~65k) (§HV1).
|
|
594
|
+
for (const e of batch)
|
|
595
|
+
this.retryNrEventBatch.push(e);
|
|
596
|
+
if (this.retryNrEventBatch.length > this.maxRetryEvents) {
|
|
597
|
+
const dropped = this.retryNrEventBatch.length - this.maxRetryEvents;
|
|
598
|
+
this.retryNrEventBatch.splice(0, dropped);
|
|
599
|
+
log.warn('NR event retry buffer overflow — oldest entries dropped', { dropped });
|
|
600
|
+
// Surface as self-monitoring metric so NR dashboards show retry-buffer drops (§HS1).
|
|
601
|
+
this.metricAggregator.record('nr.ai.dropped_events', dropped, {
|
|
602
|
+
source: 'retry_buffer',
|
|
603
|
+
transport: 'nr-events-api',
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
requeueOtlpEvents(batch, log = logger) {
|
|
608
|
+
for (const e of batch)
|
|
609
|
+
this.retryOtlpEventBatch.push(e);
|
|
610
|
+
if (this.retryOtlpEventBatch.length > this.maxRetryEvents) {
|
|
611
|
+
const dropped = this.retryOtlpEventBatch.length - this.maxRetryEvents;
|
|
612
|
+
this.retryOtlpEventBatch.splice(0, dropped);
|
|
613
|
+
log.warn('OTLP event retry buffer overflow — oldest entries dropped', { dropped });
|
|
614
|
+
this.metricAggregator.record('nr.ai.dropped_events', dropped, {
|
|
615
|
+
source: 'retry_buffer',
|
|
616
|
+
transport: 'otlp',
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
requeueNrMetrics(snapshots, log = logger) {
|
|
621
|
+
for (const s of snapshots)
|
|
622
|
+
this.retryNrMetricSnapshots.push(s);
|
|
623
|
+
if (this.retryNrMetricSnapshots.length > this.maxRetryMetricSnapshotsCap) {
|
|
624
|
+
const dropped = this.retryNrMetricSnapshots.length - this.maxRetryMetricSnapshotsCap;
|
|
625
|
+
this.retryNrMetricSnapshots.splice(0, dropped);
|
|
626
|
+
log.warn('NR metric retry buffer overflow — oldest entries dropped', { dropped });
|
|
627
|
+
this.metricAggregator.record('nr.ai.dropped_metrics', dropped, {
|
|
628
|
+
source: 'retry_buffer',
|
|
629
|
+
transport: 'nr-metrics-api',
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
requeueOtlpMetrics(snapshots, log = logger) {
|
|
634
|
+
for (const s of snapshots)
|
|
635
|
+
this.retryOtlpMetricSnapshots.push(s);
|
|
636
|
+
if (this.retryOtlpMetricSnapshots.length > this.maxRetryMetricSnapshotsCap) {
|
|
637
|
+
const dropped = this.retryOtlpMetricSnapshots.length - this.maxRetryMetricSnapshotsCap;
|
|
638
|
+
this.retryOtlpMetricSnapshots.splice(0, dropped);
|
|
639
|
+
log.warn('OTLP metric retry buffer overflow — oldest entries dropped', { dropped });
|
|
640
|
+
this.metricAggregator.record('nr.ai.dropped_metrics', dropped, {
|
|
641
|
+
source: 'retry_buffer',
|
|
642
|
+
transport: 'otlp',
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
//# sourceMappingURL=harvest-scheduler.js.map
|