@sentienguard/apm 1.0.21 → 1.0.22-debug.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/README.md CHANGED
@@ -1,141 +1,141 @@
1
- # @sentienguard/apm
2
-
3
- Minimal, production-safe APM SDK for Node.js applications. Zero-config setup with automatic instrumentation.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install @sentienguard/apm
9
- ```
10
-
11
- ## Quick Start
12
-
13
- ```js
14
- // If using dotenv, import it first
15
- import 'dotenv/config';
16
-
17
- // Then import the SDK (before other app modules for best instrumentation coverage)
18
- import '@sentienguard/apm';
19
-
20
- // Your app code
21
- import express from 'express';
22
- const app = express();
23
- // ...
24
- ```
25
-
26
- Set environment variables:
27
-
28
- ```bash
29
- SENTIENGUARD_APM_KEY=your-app-key
30
- SENTIENGUARD_SERVICE=my-api
31
- ```
32
-
33
- That's it. The SDK automatically instruments your application and sends metrics to SentienGuard.
34
-
35
- ## Configuration
36
-
37
- All configuration is via environment variables:
38
-
39
- | Variable | Required | Default | Description |
40
- |----------|----------|---------|-------------|
41
- | `SENTIENGUARD_APM_KEY` | Yes | - | Your application's APM key |
42
- | `SENTIENGUARD_SERVICE` | Yes | - | Service name (e.g., `orders-api`) |
43
- | `SENTIENGUARD_ENV` | No | `production` | Environment (`production`, `staging`, `development`) |
44
- | `SENTIENGUARD_ENDPOINT` | No | `https://sentienguard-dev.the-algo.com/api/v1` | SentienGuard backend URL |
45
- | `SENTIENGUARD_FLUSH_INTERVAL` | No | `10` | Metrics flush interval in seconds |
46
-
47
- > **Note:** If `SENTIENGUARD_APM_KEY` or `SENTIENGUARD_SERVICE` is missing, the SDK disables itself silently without affecting your application.
48
-
49
- ## What Gets Tracked
50
-
51
- - **HTTP Requests** - Incoming requests with method, route, status, and latency
52
- - **Dependencies** - Outgoing HTTP/HTTPS calls to external services
53
- - **Errors** - Uncaught exceptions and unhandled rejections
54
-
55
- ## Framework Integration
56
-
57
- ### Express
58
-
59
- For better route extraction, add the middleware:
60
-
61
- ```js
62
- import 'dotenv/config'; // if using dotenv
63
- import '@sentienguard/apm';
64
- import { expressMiddleware, expressErrorMiddleware } from '@sentienguard/apm';
65
- import express from 'express';
66
-
67
- const app = express();
68
-
69
- // Add early in middleware chain
70
- app.use(expressMiddleware());
71
-
72
- // Your routes
73
- app.get('/users/:id', (req, res) => { ... });
74
-
75
- // Add error middleware last
76
- app.use(expressErrorMiddleware());
77
- ```
78
-
79
- ### Fastify
80
-
81
- ```js
82
- import 'dotenv/config'; // if using dotenv
83
- import '@sentienguard/apm';
84
- import { fastifyPlugin, fastifyErrorHandler } from '@sentienguard/apm';
85
- import Fastify from 'fastify';
86
-
87
- const app = Fastify();
88
-
89
- // Register plugin
90
- app.register(fastifyPlugin);
91
-
92
- // Add error handler
93
- app.setErrorHandler(fastifyErrorHandler);
94
- ```
95
-
96
- ## API Reference
97
-
98
- ### Functions
99
-
100
- ```js
101
- import {
102
- shutdown, // Graceful shutdown (flushes pending metrics)
103
- getStatus, // Get SDK status and stats
104
- flush, // Force flush metrics now
105
- isEnabled // Check if SDK is enabled
106
- } from '@sentienguard/apm';
107
- ```
108
-
109
- ### Graceful Shutdown
110
-
111
- The SDK automatically handles `SIGTERM` and `SIGINT` signals. For manual shutdown:
112
-
113
- ```js
114
- import { shutdown } from '@sentienguard/apm';
115
-
116
- process.on('exit', async () => {
117
- await shutdown();
118
- });
119
- ```
120
-
121
- ### Check Status
122
-
123
- ```js
124
- import { getStatus } from '@sentienguard/apm';
125
-
126
- console.log(getStatus());
127
- // {
128
- // enabled: true,
129
- // initialized: true,
130
- // config: { service: 'my-api', environment: 'production', flushInterval: 10 },
131
- // stats: { requests: 150, dependencies: 45, errors: 2 }
132
- // }
133
- ```
134
-
135
- ## Requirements
136
-
137
- - Node.js >= 16.0.0
138
-
139
- ## License
140
-
141
- MIT
1
+ # @sentienguard/apm
2
+
3
+ Minimal, production-safe APM SDK for Node.js applications. Zero-config setup with automatic instrumentation.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @sentienguard/apm
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```js
14
+ // If using dotenv, import it first
15
+ import 'dotenv/config';
16
+
17
+ // Then import the SDK (before other app modules for best instrumentation coverage)
18
+ import '@sentienguard/apm';
19
+
20
+ // Your app code
21
+ import express from 'express';
22
+ const app = express();
23
+ // ...
24
+ ```
25
+
26
+ Set environment variables:
27
+
28
+ ```bash
29
+ SENTIENGUARD_APM_KEY=your-app-key
30
+ SENTIENGUARD_SERVICE=my-api
31
+ ```
32
+
33
+ That's it. The SDK automatically instruments your application and sends metrics to SentienGuard.
34
+
35
+ ## Configuration
36
+
37
+ All configuration is via environment variables:
38
+
39
+ | Variable | Required | Default | Description |
40
+ |----------|----------|---------|-------------|
41
+ | `SENTIENGUARD_APM_KEY` | Yes | - | Your application's APM key |
42
+ | `SENTIENGUARD_SERVICE` | Yes | - | Service name (e.g., `orders-api`) |
43
+ | `SENTIENGUARD_ENV` | No | `production` | Environment (`production`, `staging`, `development`) |
44
+ | `SENTIENGUARD_ENDPOINT` | No | `https://sentienguard-dev.the-algo.com/api/v1` | SentienGuard backend URL |
45
+ | `SENTIENGUARD_FLUSH_INTERVAL` | No | `10` | Metrics flush interval in seconds |
46
+
47
+ > **Note:** If `SENTIENGUARD_APM_KEY` or `SENTIENGUARD_SERVICE` is missing, the SDK disables itself silently without affecting your application.
48
+
49
+ ## What Gets Tracked
50
+
51
+ - **HTTP Requests** - Incoming requests with method, route, status, and latency
52
+ - **Dependencies** - Outgoing HTTP/HTTPS calls to external services
53
+ - **Errors** - Uncaught exceptions and unhandled rejections
54
+
55
+ ## Framework Integration
56
+
57
+ ### Express
58
+
59
+ For better route extraction, add the middleware:
60
+
61
+ ```js
62
+ import 'dotenv/config'; // if using dotenv
63
+ import '@sentienguard/apm';
64
+ import { expressMiddleware, expressErrorMiddleware } from '@sentienguard/apm';
65
+ import express from 'express';
66
+
67
+ const app = express();
68
+
69
+ // Add early in middleware chain
70
+ app.use(expressMiddleware());
71
+
72
+ // Your routes
73
+ app.get('/users/:id', (req, res) => { ... });
74
+
75
+ // Add error middleware last
76
+ app.use(expressErrorMiddleware());
77
+ ```
78
+
79
+ ### Fastify
80
+
81
+ ```js
82
+ import 'dotenv/config'; // if using dotenv
83
+ import '@sentienguard/apm';
84
+ import { fastifyPlugin, fastifyErrorHandler } from '@sentienguard/apm';
85
+ import Fastify from 'fastify';
86
+
87
+ const app = Fastify();
88
+
89
+ // Register plugin
90
+ app.register(fastifyPlugin);
91
+
92
+ // Add error handler
93
+ app.setErrorHandler(fastifyErrorHandler);
94
+ ```
95
+
96
+ ## API Reference
97
+
98
+ ### Functions
99
+
100
+ ```js
101
+ import {
102
+ shutdown, // Graceful shutdown (flushes pending metrics)
103
+ getStatus, // Get SDK status and stats
104
+ flush, // Force flush metrics now
105
+ isEnabled // Check if SDK is enabled
106
+ } from '@sentienguard/apm';
107
+ ```
108
+
109
+ ### Graceful Shutdown
110
+
111
+ The SDK automatically handles `SIGTERM` and `SIGINT` signals. For manual shutdown:
112
+
113
+ ```js
114
+ import { shutdown } from '@sentienguard/apm';
115
+
116
+ process.on('exit', async () => {
117
+ await shutdown();
118
+ });
119
+ ```
120
+
121
+ ### Check Status
122
+
123
+ ```js
124
+ import { getStatus } from '@sentienguard/apm';
125
+
126
+ console.log(getStatus());
127
+ // {
128
+ // enabled: true,
129
+ // initialized: true,
130
+ // config: { service: 'my-api', environment: 'production', flushInterval: 10 },
131
+ // stats: { requests: 150, dependencies: 45, errors: 2 }
132
+ // }
133
+ ```
134
+
135
+ ## Requirements
136
+
137
+ - Node.js >= 16.0.0
138
+
139
+ ## License
140
+
141
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentienguard/apm",
3
- "version": "1.0.21",
3
+ "version": "1.0.22-debug.1",
4
4
  "description": "SentienGuard APM SDK - Minimal, production-safe application performance monitoring",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
package/src/config.js CHANGED
@@ -1,179 +1,179 @@
1
- /**
2
- * SDK Configuration
3
- * Environment-driven configuration with sensible defaults.
4
- * No config → SDK disables itself silently.
5
- *
6
- * Config is loaded lazily (at initialize() time, not import time)
7
- * so that dotenv or other env loaders can run first.
8
- */
9
-
10
- let configLoaded = false;
11
-
12
- const config = {
13
- apiKey: '',
14
- service: '',
15
- environment: 'production',
16
- endpoint: 'https://sentienguard-dev.the-algo.com/api/v1/apm/ingest',
17
- tracesEndpoint: '',
18
- flushInterval: 10,
19
- maxRoutes: 100,
20
- maxPayloadSize: 1024 * 1024,
21
- enabled: true,
22
- debug: false,
23
- mongodb: {
24
- enabled: true,
25
- slowQueryMs: 100,
26
- poolStatsInterval: 10000
27
- },
28
- circuitBreaker: {
29
- enabled: false,
30
- timeout: 3000,
31
- errorThresholdPercentage: 50,
32
- resetTimeout: 30000,
33
- volumeThreshold: 5
34
- },
35
- openai: {
36
- enabled: true,
37
- trackTokens: true,
38
- trackCosts: true,
39
- slowCallMs: 5000
40
- },
41
- /** OpenTelemetry + W3C trace context; set SENTIENGUARD_TRACING=false for legacy HTTP patches only */
42
- tracing: {
43
- enabled: true,
44
- /** When true, outgoing HTTP to localhost is traced (for multi-service dev). Default false. */
45
- traceLocalHttp: false,
46
- /** Port -> display name for local peers, from SENTIENGUARD_PEER_SERVICE_MAP */
47
- peerServiceMap: {},
48
- /** Sampling rate for exporting raw spans (0..1). Parent-based. */
49
- sampleRate: 0.05,
50
- /** Span export queue + batch sizes (drop-on-pressure). */
51
- maxQueueSize: 2048,
52
- maxBatchSize: 256
53
- }
54
- };
55
-
56
- /**
57
- * Load configuration from environment variables.
58
- * Called lazily on first use (e.g., from initialize() or isEnabled()).
59
- * Safe to call multiple times — only reads env vars once.
60
- */
61
- export function loadConfig({ force = false } = {}) {
62
- if (configLoaded && !force) return;
63
- configLoaded = true;
64
-
65
- config.apiKey = process.env.SENTIENGUARD_APM_KEY || '';
66
- config.service = process.env.SENTIENGUARD_SERVICE || '';
67
- config.environment = process.env.SENTIENGUARD_ENV || 'production';
68
- config.endpoint = process.env.SENTIENGUARD_ENDPOINT || 'https://sentienguard-dev.the-algo.com/api/v1/apm/ingest';
69
- config.tracesEndpoint =
70
- process.env.SENTIENGUARD_TRACES_ENDPOINT ||
71
- deriveTracesEndpoint(config.endpoint) ||
72
- 'https://sentienguard-dev.the-algo.com/api/v1/apm/traces';
73
- config.flushInterval = parseInt(process.env.SENTIENGUARD_FLUSH_INTERVAL, 10) || 10;
74
- config.maxRoutes = parseInt(process.env.SENTIENGUARD_MAX_ROUTES, 10) || 100;
75
- config.maxPayloadSize = parseInt(process.env.SENTIENGUARD_MAX_PAYLOAD_SIZE, 10) || 1024 * 1024;
76
- config.enabled = process.env.SENTIENGUARD_ENABLED !== 'false';
77
- config.debug = process.env.SENTIENGUARD_DEBUG === 'true';
78
-
79
- config.mongodb.enabled = process.env.SENTIENGUARD_MONGODB_ENABLED !== 'false';
80
- config.mongodb.slowQueryMs = parseInt(process.env.SENTIENGUARD_MONGODB_SLOW_QUERY_MS, 10) || 100;
81
- config.mongodb.poolStatsInterval = parseInt(process.env.SENTIENGUARD_MONGODB_POOL_INTERVAL, 10) || 10000;
82
-
83
- config.circuitBreaker.enabled = process.env.SENTIENGUARD_CIRCUIT_BREAKER_ENABLED === 'true';
84
- config.circuitBreaker.timeout = parseInt(process.env.SENTIENGUARD_CIRCUIT_TIMEOUT_MS, 10) || 3000;
85
- config.circuitBreaker.errorThresholdPercentage = parseInt(process.env.SENTIENGUARD_CIRCUIT_ERROR_THRESHOLD, 10) || 50;
86
- config.circuitBreaker.resetTimeout = parseInt(process.env.SENTIENGUARD_CIRCUIT_RESET_TIMEOUT, 10) || 30000;
87
- config.circuitBreaker.volumeThreshold = parseInt(process.env.SENTIENGUARD_CIRCUIT_VOLUME_THRESHOLD, 10) || 5;
88
-
89
- config.openai.enabled = process.env.SENTIENGUARD_OPENAI_ENABLED !== 'false';
90
- config.openai.trackTokens = process.env.SENTIENGUARD_OPENAI_TRACK_TOKENS !== 'false';
91
- config.openai.trackCosts = process.env.SENTIENGUARD_OPENAI_TRACK_COSTS !== 'false';
92
- config.openai.slowCallMs = parseInt(process.env.SENTIENGUARD_OPENAI_SLOW_CALL_MS, 10) || 5000;
93
-
94
- config.tracing.enabled = process.env.SENTIENGUARD_TRACING !== 'false';
95
- const sampleRaw = process.env.SENTIENGUARD_TRACE_SAMPLE_RATE;
96
- const sample = sampleRaw != null ? Number(sampleRaw) : NaN;
97
- if (!Number.isNaN(sample) && sample >= 0 && sample <= 1) {
98
- config.tracing.sampleRate = sample;
99
- }
100
- config.tracing.maxQueueSize = parseInt(process.env.SENTIENGUARD_TRACE_MAX_QUEUE_SIZE, 10) || config.tracing.maxQueueSize;
101
- config.tracing.maxBatchSize = parseInt(process.env.SENTIENGUARD_TRACE_MAX_BATCH_SIZE, 10) || config.tracing.maxBatchSize;
102
- // Default behavior:
103
- // - production: do NOT record localhost dependency edges (noise + self-calls)
104
- // - non-production: DO record localhost edges (local multi-service dev "just works")
105
- // Can always be overridden explicitly via SENTIENGUARD_TRACE_LOCAL_HTTP=true/false.
106
- const explicitLocal =
107
- process.env.SENTIENGUARD_TRACE_LOCAL_HTTP === 'true'
108
- ? true
109
- : process.env.SENTIENGUARD_TRACE_LOCAL_HTTP === 'false'
110
- ? false
111
- : undefined;
112
- const isProd = (config.environment || '').toLowerCase() === 'production' ||
113
- (process.env.NODE_ENV || '').toLowerCase() === 'production';
114
- config.tracing.traceLocalHttp = explicitLocal ?? !isProd;
115
- config.tracing.peerServiceMap = parsePeerServiceMap(process.env.SENTIENGUARD_PEER_SERVICE_MAP || '');
116
- }
117
-
118
- /** "3001:payments-api,3002:auth-api" -> { "3001": "payments-api", ... } */
119
- function parsePeerServiceMap(raw) {
120
- const map = Object.create(null);
121
- if (!raw || typeof raw !== 'string') return map;
122
- for (const part of raw.split(',')) {
123
- const trimmed = part.trim();
124
- if (!trimmed) continue;
125
- const idx = trimmed.indexOf(':');
126
- if (idx === -1) continue;
127
- const port = trimmed.slice(0, idx).trim();
128
- const name = trimmed.slice(idx + 1).trim();
129
- if (port && name) map[port] = name;
130
- }
131
- return map;
132
- }
133
-
134
- function deriveTracesEndpoint(ingestEndpoint) {
135
- try {
136
- const u = new URL(String(ingestEndpoint));
137
- // Common default: /api/v1/apm/ingest -> /api/v1/apm/traces
138
- u.pathname = u.pathname.replace(/\/apm\/ingest\/?$/i, '/apm/traces');
139
- return u.toString();
140
- } catch {
141
- return '';
142
- }
143
- }
144
-
145
- /**
146
- * Check if SDK is properly configured and should be active.
147
- * Triggers lazy config load if not yet loaded.
148
- */
149
- export function isEnabled() {
150
- loadConfig();
151
- return config.enabled && !!config.apiKey && !!config.service;
152
- }
153
-
154
- /**
155
- * Get validated configuration.
156
- * Triggers lazy config load if not yet loaded.
157
- */
158
- export function getConfig() {
159
- loadConfig();
160
- return { ...config };
161
- }
162
-
163
- /**
164
- * Log debug message if debug mode is enabled
165
- */
166
- export function debug(...args) {
167
- if (config.debug) {
168
- console.log('[SentienGuard APM]', ...args);
169
- }
170
- }
171
-
172
- /**
173
- * Log warning (always shown)
174
- */
175
- export function warn(...args) {
176
- console.warn('[SentienGuard APM]', ...args);
177
- }
178
-
179
- export default config;
1
+ /**
2
+ * SDK Configuration
3
+ * Environment-driven configuration with sensible defaults.
4
+ * No config → SDK disables itself silently.
5
+ *
6
+ * Config is loaded lazily (at initialize() time, not import time)
7
+ * so that dotenv or other env loaders can run first.
8
+ */
9
+
10
+ let configLoaded = false;
11
+
12
+ const config = {
13
+ apiKey: '',
14
+ service: '',
15
+ environment: 'production',
16
+ endpoint: 'https://sentienguard-dev.the-algo.com/api/v1/apm/ingest',
17
+ tracesEndpoint: '',
18
+ flushInterval: 10,
19
+ maxRoutes: 100,
20
+ maxPayloadSize: 1024 * 1024,
21
+ enabled: true,
22
+ debug: false,
23
+ mongodb: {
24
+ enabled: true,
25
+ slowQueryMs: 100,
26
+ poolStatsInterval: 10000
27
+ },
28
+ circuitBreaker: {
29
+ enabled: false,
30
+ timeout: 3000,
31
+ errorThresholdPercentage: 50,
32
+ resetTimeout: 30000,
33
+ volumeThreshold: 5
34
+ },
35
+ openai: {
36
+ enabled: true,
37
+ trackTokens: true,
38
+ trackCosts: true,
39
+ slowCallMs: 5000
40
+ },
41
+ /** OpenTelemetry + W3C trace context; set SENTIENGUARD_TRACING=false for legacy HTTP patches only */
42
+ tracing: {
43
+ enabled: true,
44
+ /** When true, outgoing HTTP to localhost is traced (for multi-service dev). Default false. */
45
+ traceLocalHttp: false,
46
+ /** Port -> display name for local peers, from SENTIENGUARD_PEER_SERVICE_MAP */
47
+ peerServiceMap: {},
48
+ /** Sampling rate for exporting raw spans (0..1). Parent-based. */
49
+ sampleRate: 0.05,
50
+ /** Span export queue + batch sizes (drop-on-pressure). */
51
+ maxQueueSize: 2048,
52
+ maxBatchSize: 256
53
+ }
54
+ };
55
+
56
+ /**
57
+ * Load configuration from environment variables.
58
+ * Called lazily on first use (e.g., from initialize() or isEnabled()).
59
+ * Safe to call multiple times — only reads env vars once.
60
+ */
61
+ export function loadConfig({ force = false } = {}) {
62
+ if (configLoaded && !force) return;
63
+ configLoaded = true;
64
+
65
+ config.apiKey = process.env.SENTIENGUARD_APM_KEY || '';
66
+ config.service = process.env.SENTIENGUARD_SERVICE || '';
67
+ config.environment = process.env.SENTIENGUARD_ENV || 'production';
68
+ config.endpoint = process.env.SENTIENGUARD_ENDPOINT || 'https://sentienguard-dev.the-algo.com/api/v1/apm/ingest';
69
+ config.tracesEndpoint =
70
+ process.env.SENTIENGUARD_TRACES_ENDPOINT ||
71
+ deriveTracesEndpoint(config.endpoint) ||
72
+ 'https://sentienguard-dev.the-algo.com/api/v1/apm/traces';
73
+ config.flushInterval = parseInt(process.env.SENTIENGUARD_FLUSH_INTERVAL, 10) || 10;
74
+ config.maxRoutes = parseInt(process.env.SENTIENGUARD_MAX_ROUTES, 10) || 100;
75
+ config.maxPayloadSize = parseInt(process.env.SENTIENGUARD_MAX_PAYLOAD_SIZE, 10) || 1024 * 1024;
76
+ config.enabled = process.env.SENTIENGUARD_ENABLED !== 'false';
77
+ config.debug = process.env.SENTIENGUARD_DEBUG === 'true';
78
+
79
+ config.mongodb.enabled = process.env.SENTIENGUARD_MONGODB_ENABLED !== 'false';
80
+ config.mongodb.slowQueryMs = parseInt(process.env.SENTIENGUARD_MONGODB_SLOW_QUERY_MS, 10) || 100;
81
+ config.mongodb.poolStatsInterval = parseInt(process.env.SENTIENGUARD_MONGODB_POOL_INTERVAL, 10) || 10000;
82
+
83
+ config.circuitBreaker.enabled = process.env.SENTIENGUARD_CIRCUIT_BREAKER_ENABLED === 'true';
84
+ config.circuitBreaker.timeout = parseInt(process.env.SENTIENGUARD_CIRCUIT_TIMEOUT_MS, 10) || 3000;
85
+ config.circuitBreaker.errorThresholdPercentage = parseInt(process.env.SENTIENGUARD_CIRCUIT_ERROR_THRESHOLD, 10) || 50;
86
+ config.circuitBreaker.resetTimeout = parseInt(process.env.SENTIENGUARD_CIRCUIT_RESET_TIMEOUT, 10) || 30000;
87
+ config.circuitBreaker.volumeThreshold = parseInt(process.env.SENTIENGUARD_CIRCUIT_VOLUME_THRESHOLD, 10) || 5;
88
+
89
+ config.openai.enabled = process.env.SENTIENGUARD_OPENAI_ENABLED !== 'false';
90
+ config.openai.trackTokens = process.env.SENTIENGUARD_OPENAI_TRACK_TOKENS !== 'false';
91
+ config.openai.trackCosts = process.env.SENTIENGUARD_OPENAI_TRACK_COSTS !== 'false';
92
+ config.openai.slowCallMs = parseInt(process.env.SENTIENGUARD_OPENAI_SLOW_CALL_MS, 10) || 5000;
93
+
94
+ config.tracing.enabled = process.env.SENTIENGUARD_TRACING !== 'false';
95
+ const sampleRaw = process.env.SENTIENGUARD_TRACE_SAMPLE_RATE;
96
+ const sample = sampleRaw != null ? Number(sampleRaw) : NaN;
97
+ if (!Number.isNaN(sample) && sample >= 0 && sample <= 1) {
98
+ config.tracing.sampleRate = sample;
99
+ }
100
+ config.tracing.maxQueueSize = parseInt(process.env.SENTIENGUARD_TRACE_MAX_QUEUE_SIZE, 10) || config.tracing.maxQueueSize;
101
+ config.tracing.maxBatchSize = parseInt(process.env.SENTIENGUARD_TRACE_MAX_BATCH_SIZE, 10) || config.tracing.maxBatchSize;
102
+ // Default behavior:
103
+ // - production: do NOT record localhost dependency edges (noise + self-calls)
104
+ // - non-production: DO record localhost edges (local multi-service dev "just works")
105
+ // Can always be overridden explicitly via SENTIENGUARD_TRACE_LOCAL_HTTP=true/false.
106
+ const explicitLocal =
107
+ process.env.SENTIENGUARD_TRACE_LOCAL_HTTP === 'true'
108
+ ? true
109
+ : process.env.SENTIENGUARD_TRACE_LOCAL_HTTP === 'false'
110
+ ? false
111
+ : undefined;
112
+ const isProd = (config.environment || '').toLowerCase() === 'production' ||
113
+ (process.env.NODE_ENV || '').toLowerCase() === 'production';
114
+ config.tracing.traceLocalHttp = explicitLocal ?? !isProd;
115
+ config.tracing.peerServiceMap = parsePeerServiceMap(process.env.SENTIENGUARD_PEER_SERVICE_MAP || '');
116
+ }
117
+
118
+ /** "3001:payments-api,3002:auth-api" -> { "3001": "payments-api", ... } */
119
+ function parsePeerServiceMap(raw) {
120
+ const map = Object.create(null);
121
+ if (!raw || typeof raw !== 'string') return map;
122
+ for (const part of raw.split(',')) {
123
+ const trimmed = part.trim();
124
+ if (!trimmed) continue;
125
+ const idx = trimmed.indexOf(':');
126
+ if (idx === -1) continue;
127
+ const port = trimmed.slice(0, idx).trim();
128
+ const name = trimmed.slice(idx + 1).trim();
129
+ if (port && name) map[port] = name;
130
+ }
131
+ return map;
132
+ }
133
+
134
+ function deriveTracesEndpoint(ingestEndpoint) {
135
+ try {
136
+ const u = new URL(String(ingestEndpoint));
137
+ // Common default: /api/v1/apm/ingest -> /api/v1/apm/traces
138
+ u.pathname = u.pathname.replace(/\/apm\/ingest\/?$/i, '/apm/traces');
139
+ return u.toString();
140
+ } catch {
141
+ return '';
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Check if SDK is properly configured and should be active.
147
+ * Triggers lazy config load if not yet loaded.
148
+ */
149
+ export function isEnabled() {
150
+ loadConfig();
151
+ return config.enabled && !!config.apiKey && !!config.service;
152
+ }
153
+
154
+ /**
155
+ * Get validated configuration.
156
+ * Triggers lazy config load if not yet loaded.
157
+ */
158
+ export function getConfig() {
159
+ loadConfig();
160
+ return { ...config };
161
+ }
162
+
163
+ /**
164
+ * Log debug message if debug mode is enabled
165
+ */
166
+ export function debug(...args) {
167
+ if (config.debug) {
168
+ console.log('[SentienGuard APM]', ...args);
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Log warning (always shown)
174
+ */
175
+ export function warn(...args) {
176
+ console.warn('[SentienGuard APM]', ...args);
177
+ }
178
+
179
+ export default config;