@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 CHANGED
@@ -11,7 +11,10 @@ npm install @sentienguard/apm
11
11
  ## Quick Start
12
12
 
13
13
  ```js
14
- // Add this as the FIRST import in your app
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentienguard/apm",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "SentienGuard APM SDK - Minimal, production-safe application performance monitoring",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
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
- const config = {
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
- // Debug mode (logs SDK activity)
35
- debug: process.env.SENTIENGUARD_DEBUG === 'true',
36
-
37
- // MongoDB monitoring configuration
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
- // Enable/disable MongoDB instrumentation (default: true)
40
- enabled: process.env.SENTIENGUARD_MONGODB_ENABLED !== 'false',
41
- // Slow query threshold in milliseconds (default: 100ms)
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
- // Enable/disable circuit breaker (default: false - opt-in)
50
- enabled: process.env.SENTIENGUARD_CIRCUIT_BREAKER_ENABLED === 'true',
51
- // Timeout for operations in milliseconds (default: 3s)
52
- timeout: parseInt(process.env.SENTIENGUARD_CIRCUIT_TIMEOUT_MS, 10) || 3000,
53
- // Error threshold percentage to trip circuit (default: 50%)
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
- // Enable/disable OpenAI instrumentation (default: true)
64
- enabled: process.env.SENTIENGUARD_OPENAI_ENABLED !== 'false',
65
- // Track token usage (default: true)
66
- trackTokens: process.env.SENTIENGUARD_OPENAI_TRACK_TOKENS !== 'false',
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
- * Check if SDK is properly configured and should be active
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
- // SDK disables itself silently if no API key or service name
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
- // Auto-initialize on import
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