@sentienguard/apm 1.0.6 → 1.0.7
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 +6 -1
- package/package.json +1 -1
- package/src/config.js +67 -61
- package/src/index.js +18 -2
package/README.md
CHANGED
|
@@ -11,7 +11,10 @@ npm install @sentienguard/apm
|
|
|
11
11
|
## Quick Start
|
|
12
12
|
|
|
13
13
|
```js
|
|
14
|
-
//
|
|
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)
|
|
15
18
|
import '@sentienguard/apm';
|
|
16
19
|
|
|
17
20
|
// Your app code
|
|
@@ -56,6 +59,7 @@ All configuration is via environment variables:
|
|
|
56
59
|
For better route extraction, add the middleware:
|
|
57
60
|
|
|
58
61
|
```js
|
|
62
|
+
import 'dotenv/config'; // if using dotenv
|
|
59
63
|
import '@sentienguard/apm';
|
|
60
64
|
import { expressMiddleware, expressErrorMiddleware } from '@sentienguard/apm';
|
|
61
65
|
import express from 'express';
|
|
@@ -75,6 +79,7 @@ app.use(expressErrorMiddleware());
|
|
|
75
79
|
### Fastify
|
|
76
80
|
|
|
77
81
|
```js
|
|
82
|
+
import 'dotenv/config'; // if using dotenv
|
|
78
83
|
import '@sentienguard/apm';
|
|
79
84
|
import { fastifyPlugin, fastifyErrorHandler } from '@sentienguard/apm';
|
|
80
85
|
import Fastify from 'fastify';
|
package/package.json
CHANGED
package/src/config.js
CHANGED
|
@@ -2,87 +2,93 @@
|
|
|
2
2
|
* SDK Configuration
|
|
3
3
|
* Environment-driven configuration with sensible defaults.
|
|
4
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.
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
// API key for authentication (required)
|
|
9
|
-
apiKey: process.env.SENTIENGUARD_APM_KEY || '',
|
|
10
|
-
|
|
11
|
-
// Service name (required for data to be meaningful)
|
|
12
|
-
service: process.env.SENTIENGUARD_SERVICE || '',
|
|
13
|
-
|
|
14
|
-
// Environment (production, staging, development)
|
|
15
|
-
environment: process.env.SENTIENGUARD_ENV || 'production',
|
|
16
|
-
|
|
17
|
-
// Backend endpoint for data ingestion
|
|
18
|
-
// Production: https://sentienguard-dev.the-algo.com/api/v1/apm/ingest
|
|
19
|
-
// Local: http://localhost:4000/api/v1/apm/ingest
|
|
20
|
-
endpoint: process.env.SENTIENGUARD_ENDPOINT || 'https://sentienguard-dev.the-algo.com/api/v1/apm/ingest',
|
|
21
|
-
|
|
22
|
-
// Flush interval in seconds (default: 10s)
|
|
23
|
-
flushInterval: parseInt(process.env.SENTIENGUARD_FLUSH_INTERVAL, 10) || 10,
|
|
24
|
-
|
|
25
|
-
// Max unique routes to track per service (prevents memory bloat)
|
|
26
|
-
maxRoutes: parseInt(process.env.SENTIENGUARD_MAX_ROUTES, 10) || 100,
|
|
27
|
-
|
|
28
|
-
// Max payload size in bytes (prevent oversized payloads)
|
|
29
|
-
maxPayloadSize: parseInt(process.env.SENTIENGUARD_MAX_PAYLOAD_SIZE, 10) || 1024 * 1024, // 1MB
|
|
30
|
-
|
|
31
|
-
// Enable/disable SDK (auto-disabled if no API key)
|
|
32
|
-
enabled: process.env.SENTIENGUARD_ENABLED !== 'false',
|
|
10
|
+
let configLoaded = false;
|
|
33
11
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
12
|
+
const config = {
|
|
13
|
+
apiKey: '',
|
|
14
|
+
service: '',
|
|
15
|
+
environment: 'production',
|
|
16
|
+
endpoint: 'https://sentienguard-dev.the-algo.com/api/v1/apm/ingest',
|
|
17
|
+
flushInterval: 10,
|
|
18
|
+
maxRoutes: 100,
|
|
19
|
+
maxPayloadSize: 1024 * 1024,
|
|
20
|
+
enabled: true,
|
|
21
|
+
debug: false,
|
|
38
22
|
mongodb: {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
slowQueryMs: parseInt(process.env.SENTIENGUARD_MONGODB_SLOW_QUERY_MS, 10) || 100,
|
|
43
|
-
// Interval for collecting pool stats in milliseconds (default: 10s)
|
|
44
|
-
poolStatsInterval: parseInt(process.env.SENTIENGUARD_MONGODB_POOL_INTERVAL, 10) || 10000
|
|
23
|
+
enabled: true,
|
|
24
|
+
slowQueryMs: 100,
|
|
25
|
+
poolStatsInterval: 10000
|
|
45
26
|
},
|
|
46
|
-
|
|
47
|
-
// Circuit breaker configuration
|
|
48
27
|
circuitBreaker: {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
errorThresholdPercentage: parseInt(process.env.SENTIENGUARD_CIRCUIT_ERROR_THRESHOLD, 10) || 50,
|
|
55
|
-
// Time to wait before attempting recovery in milliseconds (default: 30s)
|
|
56
|
-
resetTimeout: parseInt(process.env.SENTIENGUARD_CIRCUIT_RESET_TIMEOUT, 10) || 30000,
|
|
57
|
-
// Minimum requests before calculating error percentage (default: 5)
|
|
58
|
-
volumeThreshold: parseInt(process.env.SENTIENGUARD_CIRCUIT_VOLUME_THRESHOLD, 10) || 5
|
|
28
|
+
enabled: false,
|
|
29
|
+
timeout: 3000,
|
|
30
|
+
errorThresholdPercentage: 50,
|
|
31
|
+
resetTimeout: 30000,
|
|
32
|
+
volumeThreshold: 5
|
|
59
33
|
},
|
|
60
|
-
|
|
61
|
-
// OpenAI monitoring configuration
|
|
62
34
|
openai: {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// Track estimated costs (default: true)
|
|
68
|
-
trackCosts: process.env.SENTIENGUARD_OPENAI_TRACK_COSTS !== 'false',
|
|
69
|
-
// Slow API call threshold in milliseconds (default: 5000ms / 5s)
|
|
70
|
-
slowCallMs: parseInt(process.env.SENTIENGUARD_OPENAI_SLOW_CALL_MS, 10) || 5000
|
|
35
|
+
enabled: true,
|
|
36
|
+
trackTokens: true,
|
|
37
|
+
trackCosts: true,
|
|
38
|
+
slowCallMs: 5000
|
|
71
39
|
}
|
|
72
40
|
};
|
|
73
41
|
|
|
74
42
|
/**
|
|
75
|
-
*
|
|
43
|
+
* Load configuration from environment variables.
|
|
44
|
+
* Called lazily on first use (e.g., from initialize() or isEnabled()).
|
|
45
|
+
* Safe to call multiple times — only reads env vars once.
|
|
46
|
+
*/
|
|
47
|
+
export function loadConfig({ force = false } = {}) {
|
|
48
|
+
if (configLoaded && !force) return;
|
|
49
|
+
configLoaded = true;
|
|
50
|
+
|
|
51
|
+
config.apiKey = process.env.SENTIENGUARD_APM_KEY || '';
|
|
52
|
+
config.service = process.env.SENTIENGUARD_SERVICE || '';
|
|
53
|
+
config.environment = process.env.SENTIENGUARD_ENV || 'production';
|
|
54
|
+
config.endpoint = process.env.SENTIENGUARD_ENDPOINT || 'https://sentienguard-dev.the-algo.com/api/v1/apm/ingest';
|
|
55
|
+
config.flushInterval = parseInt(process.env.SENTIENGUARD_FLUSH_INTERVAL, 10) || 10;
|
|
56
|
+
config.maxRoutes = parseInt(process.env.SENTIENGUARD_MAX_ROUTES, 10) || 100;
|
|
57
|
+
config.maxPayloadSize = parseInt(process.env.SENTIENGUARD_MAX_PAYLOAD_SIZE, 10) || 1024 * 1024;
|
|
58
|
+
config.enabled = process.env.SENTIENGUARD_ENABLED !== 'false';
|
|
59
|
+
config.debug = process.env.SENTIENGUARD_DEBUG === 'true';
|
|
60
|
+
|
|
61
|
+
config.mongodb.enabled = process.env.SENTIENGUARD_MONGODB_ENABLED !== 'false';
|
|
62
|
+
config.mongodb.slowQueryMs = parseInt(process.env.SENTIENGUARD_MONGODB_SLOW_QUERY_MS, 10) || 100;
|
|
63
|
+
config.mongodb.poolStatsInterval = parseInt(process.env.SENTIENGUARD_MONGODB_POOL_INTERVAL, 10) || 10000;
|
|
64
|
+
|
|
65
|
+
config.circuitBreaker.enabled = process.env.SENTIENGUARD_CIRCUIT_BREAKER_ENABLED === 'true';
|
|
66
|
+
config.circuitBreaker.timeout = parseInt(process.env.SENTIENGUARD_CIRCUIT_TIMEOUT_MS, 10) || 3000;
|
|
67
|
+
config.circuitBreaker.errorThresholdPercentage = parseInt(process.env.SENTIENGUARD_CIRCUIT_ERROR_THRESHOLD, 10) || 50;
|
|
68
|
+
config.circuitBreaker.resetTimeout = parseInt(process.env.SENTIENGUARD_CIRCUIT_RESET_TIMEOUT, 10) || 30000;
|
|
69
|
+
config.circuitBreaker.volumeThreshold = parseInt(process.env.SENTIENGUARD_CIRCUIT_VOLUME_THRESHOLD, 10) || 5;
|
|
70
|
+
|
|
71
|
+
config.openai.enabled = process.env.SENTIENGUARD_OPENAI_ENABLED !== 'false';
|
|
72
|
+
config.openai.trackTokens = process.env.SENTIENGUARD_OPENAI_TRACK_TOKENS !== 'false';
|
|
73
|
+
config.openai.trackCosts = process.env.SENTIENGUARD_OPENAI_TRACK_COSTS !== 'false';
|
|
74
|
+
config.openai.slowCallMs = parseInt(process.env.SENTIENGUARD_OPENAI_SLOW_CALL_MS, 10) || 5000;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Check if SDK is properly configured and should be active.
|
|
79
|
+
* Triggers lazy config load if not yet loaded.
|
|
76
80
|
*/
|
|
77
81
|
export function isEnabled() {
|
|
78
|
-
|
|
82
|
+
loadConfig();
|
|
79
83
|
return config.enabled && !!config.apiKey && !!config.service;
|
|
80
84
|
}
|
|
81
85
|
|
|
82
86
|
/**
|
|
83
|
-
* Get validated configuration
|
|
87
|
+
* Get validated configuration.
|
|
88
|
+
* Triggers lazy config load if not yet loaded.
|
|
84
89
|
*/
|
|
85
90
|
export function getConfig() {
|
|
91
|
+
loadConfig();
|
|
86
92
|
return { ...config };
|
|
87
93
|
}
|
|
88
94
|
|
package/src/index.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* No config → SDK disables itself silently.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import config, { isEnabled, debug, getConfig } from './config.js';
|
|
22
|
+
import config, { isEnabled, debug, getConfig, loadConfig } from './config.js';
|
|
23
23
|
import { instrumentHttp, expressMiddleware, fastifyPlugin } from './instrumentation.js';
|
|
24
24
|
import { instrumentDependencies } from './dependencies.js';
|
|
25
25
|
import { startErrorCapture, expressErrorMiddleware, fastifyErrorHandler } from './errors.js';
|
|
@@ -42,6 +42,9 @@ function initialize() {
|
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Load config from env vars (lazy — allows dotenv to load first)
|
|
46
|
+
loadConfig();
|
|
47
|
+
|
|
45
48
|
// Check if SDK should be enabled
|
|
46
49
|
if (!isEnabled()) {
|
|
47
50
|
// Silently disable - this is expected behavior
|
|
@@ -146,9 +149,22 @@ function getStatus() {
|
|
|
146
149
|
};
|
|
147
150
|
}
|
|
148
151
|
|
|
149
|
-
//
|
|
152
|
+
// Phase 1: Immediately patch http/https (config-independent, must happen before servers are created)
|
|
153
|
+
instrumentHttp();
|
|
154
|
+
instrumentDependencies();
|
|
155
|
+
|
|
156
|
+
// Phase 2: Try full initialization (works if env vars are already set at system/process level)
|
|
150
157
|
initialize();
|
|
151
158
|
|
|
159
|
+
// Phase 3: If init failed (env vars not loaded yet, e.g. dotenv.config() hasn't run),
|
|
160
|
+
// retry after the importing module's synchronous code completes
|
|
161
|
+
if (!isInitialized) {
|
|
162
|
+
process.nextTick(() => {
|
|
163
|
+
loadConfig({ force: true });
|
|
164
|
+
initialize();
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
152
168
|
// Export for advanced usage
|
|
153
169
|
export {
|
|
154
170
|
// Core functions
|