@sentienguard/apm 1.0.6 → 1.0.8
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 +3 -1
- package/src/config.js +67 -61
- package/src/index.d.ts +102 -0
- 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
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentienguard/apm",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "SentienGuard APM SDK - Minimal, production-safe application performance monitoring",
|
|
5
5
|
"main": "src/index.js",
|
|
6
|
+
"types": "src/index.d.ts",
|
|
6
7
|
"type": "module",
|
|
7
8
|
"browser": "./src/browser.js",
|
|
8
9
|
"exports": {
|
|
9
10
|
".": {
|
|
11
|
+
"types": "./src/index.d.ts",
|
|
10
12
|
"browser": "./src/browser.js",
|
|
11
13
|
"default": "./src/index.js"
|
|
12
14
|
}
|
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.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SentienGuard APM SDK TypeScript Declarations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Configuration types
|
|
6
|
+
export interface ApmConfig {
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
service?: string;
|
|
9
|
+
environment?: string;
|
|
10
|
+
endpoint?: string;
|
|
11
|
+
flushInterval?: number;
|
|
12
|
+
maxRoutes?: number;
|
|
13
|
+
maxPayloadSize?: number;
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
debug?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface BrowserApmConfig {
|
|
19
|
+
apiKey: string;
|
|
20
|
+
service: string;
|
|
21
|
+
endpoint?: string;
|
|
22
|
+
environment?: string;
|
|
23
|
+
flushInterval?: number;
|
|
24
|
+
debug?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ApmStatus {
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
initialized: boolean;
|
|
30
|
+
config: {
|
|
31
|
+
service: string;
|
|
32
|
+
environment: string;
|
|
33
|
+
flushInterval: number;
|
|
34
|
+
};
|
|
35
|
+
stats: {
|
|
36
|
+
requestMetrics?: number;
|
|
37
|
+
dependencyMetrics?: number;
|
|
38
|
+
webVitals?: number;
|
|
39
|
+
jsErrors?: number;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Core functions
|
|
44
|
+
export function initialize(options?: BrowserApmConfig): void;
|
|
45
|
+
export function init(options?: BrowserApmConfig): void;
|
|
46
|
+
export function shutdown(): Promise<void>;
|
|
47
|
+
export function getStatus(): ApmStatus;
|
|
48
|
+
export function flush(): Promise<void>;
|
|
49
|
+
export function getConfig(): ApmConfig;
|
|
50
|
+
export function isEnabled(): boolean;
|
|
51
|
+
|
|
52
|
+
// Express middleware (no-ops in browser)
|
|
53
|
+
export function expressMiddleware(): (req: any, res: any, next: any) => void;
|
|
54
|
+
export function expressErrorMiddleware(): (err: any, req: any, res: any, next: any) => void;
|
|
55
|
+
|
|
56
|
+
// Fastify plugin (no-ops in browser)
|
|
57
|
+
export function fastifyPlugin(): any;
|
|
58
|
+
export function fastifyErrorHandler(): any;
|
|
59
|
+
|
|
60
|
+
// Route utilities (no-ops in browser)
|
|
61
|
+
export function normalizeRoute(route: string): string;
|
|
62
|
+
export function extractRoute(req: any): string;
|
|
63
|
+
export const RouteRegistry: any;
|
|
64
|
+
|
|
65
|
+
// Aggregator
|
|
66
|
+
export function getAggregator(): any;
|
|
67
|
+
|
|
68
|
+
// MongoDB instrumentation (no-ops in browser)
|
|
69
|
+
export function instrumentMongoDB(): void;
|
|
70
|
+
|
|
71
|
+
// OpenAI instrumentation (no-ops in browser)
|
|
72
|
+
export function instrumentOpenAI(): void;
|
|
73
|
+
|
|
74
|
+
// Circuit breaker (no-ops in browser)
|
|
75
|
+
export function createBreaker(fn: any): any;
|
|
76
|
+
export function wrapMongoOperation(fn: any): any;
|
|
77
|
+
export function getBreakerStats(): any;
|
|
78
|
+
|
|
79
|
+
// Default export
|
|
80
|
+
declare const SentienGuard: {
|
|
81
|
+
init: typeof init;
|
|
82
|
+
initialize: typeof initialize;
|
|
83
|
+
shutdown: typeof shutdown;
|
|
84
|
+
getStatus: typeof getStatus;
|
|
85
|
+
flush: typeof flush;
|
|
86
|
+
getConfig: typeof getConfig;
|
|
87
|
+
isEnabled: typeof isEnabled;
|
|
88
|
+
getAggregator: typeof getAggregator;
|
|
89
|
+
expressMiddleware: typeof expressMiddleware;
|
|
90
|
+
expressErrorMiddleware: typeof expressErrorMiddleware;
|
|
91
|
+
fastifyPlugin: typeof fastifyPlugin;
|
|
92
|
+
fastifyErrorHandler: typeof fastifyErrorHandler;
|
|
93
|
+
normalizeRoute: typeof normalizeRoute;
|
|
94
|
+
extractRoute: typeof extractRoute;
|
|
95
|
+
instrumentMongoDB: typeof instrumentMongoDB;
|
|
96
|
+
instrumentOpenAI: typeof instrumentOpenAI;
|
|
97
|
+
createBreaker: typeof createBreaker;
|
|
98
|
+
wrapMongoOperation: typeof wrapMongoOperation;
|
|
99
|
+
getBreakerStats: typeof getBreakerStats;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export default SentienGuard;
|
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
|