obzervo-agent 1.0.0
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/bin/index.js +147 -0
- package/package.json +32 -0
package/bin/index.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { Command } = require('commander');
|
|
4
|
+
const axios = require('axios');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
const program = new Command();
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('obzervo')
|
|
12
|
+
.description('Obzervo Local CLI Observability and Monitoring Agent')
|
|
13
|
+
.version('1.0.0');
|
|
14
|
+
|
|
15
|
+
program
|
|
16
|
+
.command('monitor')
|
|
17
|
+
.description('Monitor a local or private HTTP endpoint and stream telemetry to Obzervo')
|
|
18
|
+
.option('-e, --endpointId <id>', 'Target endpoint ID in Obzervo')
|
|
19
|
+
.option('-t, --targetUrl <url>', 'Local target URL, for example http://localhost:3000/health')
|
|
20
|
+
.option('-k, --key <apiKey>', 'Obzervo API key, for example obz_live_...')
|
|
21
|
+
.option('-m, --method <httpMethod>', 'HTTP method (GET, POST, PUT, etc.)', 'GET')
|
|
22
|
+
.option('-c, --expectedStatus <status>', 'Expected HTTP status code', 200)
|
|
23
|
+
.option('-s, --serverUrl <serverUrl>', 'Obzervo API server URL', 'http://localhost:5000')
|
|
24
|
+
.option('-i, --interval <intervalMs>', 'Ping interval in milliseconds', 10000)
|
|
25
|
+
.action(async (options) => {
|
|
26
|
+
let { endpointId, targetUrl, key, method, expectedStatus, serverUrl, interval } = options;
|
|
27
|
+
|
|
28
|
+
const configPath = path.join(process.cwd(), 'obzervo.json');
|
|
29
|
+
if (fs.existsSync(configPath)) {
|
|
30
|
+
try {
|
|
31
|
+
const fileConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
32
|
+
endpointId = endpointId || fileConfig.endpointId;
|
|
33
|
+
targetUrl = targetUrl || fileConfig.targetUrl;
|
|
34
|
+
key = key || fileConfig.apiKey;
|
|
35
|
+
method = method || fileConfig.method || 'GET';
|
|
36
|
+
expectedStatus = expectedStatus || fileConfig.expectedStatus || 200;
|
|
37
|
+
serverUrl = serverUrl || fileConfig.serverUrl || 'http://localhost:5000';
|
|
38
|
+
interval = interval || fileConfig.interval || 10000;
|
|
39
|
+
console.log('[Obzervo Agent] Loaded local configuration from obzervo.json');
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error('[Obzervo Agent Error] Failed to parse obzervo.json:', err.message);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!endpointId || !targetUrl || !key) {
|
|
46
|
+
console.error('\nMissing required parameters.');
|
|
47
|
+
console.error('Usage: obzervo monitor --endpointId <ID> --targetUrl <URL> --key <API_KEY>');
|
|
48
|
+
console.error('Or create an obzervo.json config file in the project directory.\n');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const targetExpectedStatus = Number(expectedStatus) || 200;
|
|
53
|
+
const targetMethod = (method || 'GET').toUpperCase();
|
|
54
|
+
|
|
55
|
+
console.log('\n======================================================');
|
|
56
|
+
console.log('Obzervo Local CLI Monitoring Agent Started');
|
|
57
|
+
console.log('======================================================');
|
|
58
|
+
console.log(` Target URL: ${targetUrl}`);
|
|
59
|
+
console.log(` Method: ${targetMethod}`);
|
|
60
|
+
console.log(` Expected Status: ${targetExpectedStatus}`);
|
|
61
|
+
console.log(` Endpoint ID: ${endpointId}`);
|
|
62
|
+
console.log(` API Server: ${serverUrl}`);
|
|
63
|
+
console.log(` Interval: Every ${Number(interval) / 1000}s`);
|
|
64
|
+
console.log('======================================================\n');
|
|
65
|
+
|
|
66
|
+
const runPingCycle = async () => {
|
|
67
|
+
const startTime = process.hrtime();
|
|
68
|
+
let statusCode = 0;
|
|
69
|
+
let latencyMs = 0;
|
|
70
|
+
let responseSize = 0;
|
|
71
|
+
let isSuccess = false;
|
|
72
|
+
let errorMessage = null;
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const response = await axios({
|
|
76
|
+
method: targetMethod,
|
|
77
|
+
url: targetUrl,
|
|
78
|
+
timeout: 5000,
|
|
79
|
+
validateStatus: () => true
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const diff = process.hrtime(startTime);
|
|
83
|
+
latencyMs = parseFloat((diff[0] * 1000 + diff[1] / 1e6).toFixed(2));
|
|
84
|
+
statusCode = response.status;
|
|
85
|
+
|
|
86
|
+
const rawData = JSON.stringify(response.data || '');
|
|
87
|
+
responseSize = Buffer.byteLength(rawData, 'utf8');
|
|
88
|
+
|
|
89
|
+
isSuccess = statusCode === targetExpectedStatus;
|
|
90
|
+
if (!isSuccess) {
|
|
91
|
+
errorMessage = `HTTP Status ${statusCode} did not match expected status ${targetExpectedStatus}`;
|
|
92
|
+
}
|
|
93
|
+
} catch (err) {
|
|
94
|
+
const diff = process.hrtime(startTime);
|
|
95
|
+
latencyMs = parseFloat((diff[0] * 1000 + diff[1] / 1e6).toFixed(2));
|
|
96
|
+
|
|
97
|
+
if (err.response) {
|
|
98
|
+
statusCode = err.response.status;
|
|
99
|
+
errorMessage = `HTTP ${statusCode}: ${err.response.statusText}`;
|
|
100
|
+
} else if (err.code === 'ECONNABORTED') {
|
|
101
|
+
statusCode = 408;
|
|
102
|
+
errorMessage = 'Local HTTP request timeout after 5000ms';
|
|
103
|
+
} else {
|
|
104
|
+
statusCode = 0;
|
|
105
|
+
errorMessage = err.message || 'Connection refused or DNS failure';
|
|
106
|
+
}
|
|
107
|
+
isSuccess = false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
await axios.post(
|
|
113
|
+
`${serverUrl}/api/v1/telemetry/push`,
|
|
114
|
+
{
|
|
115
|
+
endpointId,
|
|
116
|
+
statusCode,
|
|
117
|
+
latencyMs,
|
|
118
|
+
responseSize,
|
|
119
|
+
isSuccess,
|
|
120
|
+
errorMessage
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
headers: {
|
|
124
|
+
'x-api-key': key,
|
|
125
|
+
'Content-Type': 'application/json'
|
|
126
|
+
},
|
|
127
|
+
timeout: 5000
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
132
|
+
if (isSuccess) {
|
|
133
|
+
console.log(`[${timestamp}] PING OK | ${statusCode} | ${latencyMs}ms | ${responseSize} B`);
|
|
134
|
+
} else {
|
|
135
|
+
console.log(`[${timestamp}] PING FAIL | ${statusCode} | ${latencyMs}ms | Error: ${errorMessage}`);
|
|
136
|
+
}
|
|
137
|
+
} catch (pushErr) {
|
|
138
|
+
const timestamp = new Date().toLocaleTimeString();
|
|
139
|
+
console.error(`[${timestamp}] Telemetry Push Failed:`, pushErr.response?.data?.message || pushErr.message);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
await runPingCycle();
|
|
144
|
+
setInterval(runPingCycle, Number(interval));
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
program.parse(process.argv);
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "obzervo-agent",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Obzervo Local & VPC Microservice Monitoring CLI Agent",
|
|
5
|
+
"bin": {
|
|
6
|
+
"obzervo": "./bin/index.js",
|
|
7
|
+
"obzervo-agent": "./bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./bin/index.js",
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/"
|
|
12
|
+
],
|
|
13
|
+
"preferGlobal": true,
|
|
14
|
+
"scripts": {
|
|
15
|
+
"start": "node ./bin/index.js"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"obzervo",
|
|
19
|
+
"monitoring",
|
|
20
|
+
"cli",
|
|
21
|
+
"observability",
|
|
22
|
+
"sre",
|
|
23
|
+
"telemetry",
|
|
24
|
+
"agent"
|
|
25
|
+
],
|
|
26
|
+
"author": "Obzervo SRE Team",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"axios": "^1.6.8",
|
|
30
|
+
"commander": "^12.0.0"
|
|
31
|
+
}
|
|
32
|
+
}
|