@jcit/signoz 0.1.2 → 0.2.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/dist/cli.mjs +109 -30
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -2,11 +2,19 @@
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import { formatOutput, addExamples, credential, installErrorHandler, defineCliApp } from '@jcit/core';
|
|
4
4
|
import { c as createSignozClient } from './shared/signoz.DH9MeMIj.mjs';
|
|
5
|
+
import { Option } from 'commander';
|
|
5
6
|
import { consola } from 'consola';
|
|
6
7
|
import { readFileSync } from 'node:fs';
|
|
7
8
|
|
|
9
|
+
function addCommonOptions(cmd) {
|
|
10
|
+
return cmd.addOption(
|
|
11
|
+
new Option("--format <format>", "Output format: json | table | text").default("json")
|
|
12
|
+
).addOption(new Option("--url <url>", "SigNoz API base URL").hideHelp()).addOption(new Option("--token <token>", "SigNoz API token").hideHelp());
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
function registerAlerts(program) {
|
|
9
|
-
const cmd = program.command("alerts").description("List all alert rules")
|
|
16
|
+
const cmd = program.command("alerts").description("List all alert rules");
|
|
17
|
+
addCommonOptions(cmd).action(async (opts) => {
|
|
10
18
|
const client = createSignozClient({ url: opts.url, token: opts.token });
|
|
11
19
|
const alerts = await client("/api/v1/rules");
|
|
12
20
|
formatOutput(alerts, opts.format);
|
|
@@ -35,6 +43,60 @@ function registerAuth(program) {
|
|
|
35
43
|
});
|
|
36
44
|
}
|
|
37
45
|
|
|
46
|
+
const LIST_SQL = `SELECT now() as ts,
|
|
47
|
+
metric_name, temporality, type, unit,
|
|
48
|
+
count(*) as value
|
|
49
|
+
FROM signoz_metrics.distributed_metadata
|
|
50
|
+
GROUP BY metric_name, temporality, type, unit
|
|
51
|
+
ORDER BY metric_name`;
|
|
52
|
+
function extractMetrics(result) {
|
|
53
|
+
const series = result.data?.data?.results?.[0]?.aggregations?.[0]?.series;
|
|
54
|
+
if (!series) return [];
|
|
55
|
+
return series.map((s) => {
|
|
56
|
+
const labels = {};
|
|
57
|
+
for (const l of s.labels ?? []) {
|
|
58
|
+
labels[l.key.name] = l.value;
|
|
59
|
+
}
|
|
60
|
+
const temporality = labels.temporality ?? "";
|
|
61
|
+
const promqlOk = temporality === "Cumulative" || temporality === "Unspecified";
|
|
62
|
+
return {
|
|
63
|
+
name: labels.metric_name ?? "",
|
|
64
|
+
temporality,
|
|
65
|
+
type: labels.type ?? "",
|
|
66
|
+
unit: labels.unit ?? "",
|
|
67
|
+
promql: promqlOk ? "yes" : "no"
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function registerMetrics(program) {
|
|
72
|
+
const cmd = program.command("metrics").description("List available metrics with temporality and type");
|
|
73
|
+
addCommonOptions(cmd).action(async (opts) => {
|
|
74
|
+
const client = createSignozClient({ url: opts.url, token: opts.token });
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
const result = await client("/api/v5/query_range", {
|
|
77
|
+
method: "POST",
|
|
78
|
+
body: {
|
|
79
|
+
schemaVersion: "v1",
|
|
80
|
+
start: now - 36e5,
|
|
81
|
+
end: now,
|
|
82
|
+
requestType: "time_series",
|
|
83
|
+
compositeQuery: {
|
|
84
|
+
queries: [
|
|
85
|
+
{ type: "clickhouse_sql", spec: { name: "A", query: LIST_SQL, disabled: false } }
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
const metrics = extractMetrics(result);
|
|
91
|
+
if (opts.format === "json") {
|
|
92
|
+
formatOutput(metrics, "json");
|
|
93
|
+
} else {
|
|
94
|
+
formatOutput(metrics, opts.format);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
addExamples(cmd, ["signoz metrics", "signoz metrics --format table"]);
|
|
98
|
+
}
|
|
99
|
+
|
|
38
100
|
const DURATION_RE = /^(\d+)(s|m|h|d)$/;
|
|
39
101
|
const UNITS = {
|
|
40
102
|
s: 1e3,
|
|
@@ -59,37 +121,46 @@ function parseUntil(input) {
|
|
|
59
121
|
if (input === "now") return Date.now();
|
|
60
122
|
return parseSince(input);
|
|
61
123
|
}
|
|
124
|
+
function injectTimeVars(sql, startMs, endMs) {
|
|
125
|
+
const startS = Math.floor(startMs / 1e3);
|
|
126
|
+
const endS = Math.floor(endMs / 1e3);
|
|
127
|
+
const startNs = BigInt(startMs) * 1000000n;
|
|
128
|
+
const endNs = BigInt(endMs) * 1000000n;
|
|
129
|
+
return sql.replaceAll("{{start_ms}}", String(startMs)).replaceAll("{{end_ms}}", String(endMs)).replaceAll("{{start_s}}", String(startS)).replaceAll("{{end_s}}", String(endS)).replaceAll("{{start_ns}}", String(startNs)).replaceAll("{{end_ns}}", String(endNs));
|
|
130
|
+
}
|
|
62
131
|
|
|
63
|
-
|
|
64
|
-
function buildPromqlRequest(query, startNs, endNs, step) {
|
|
132
|
+
function buildPromqlRequest(query, start, end, step) {
|
|
65
133
|
return {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
134
|
+
schemaVersion: "v1",
|
|
135
|
+
start,
|
|
136
|
+
end,
|
|
137
|
+
requestType: "time_series",
|
|
69
138
|
compositeQuery: {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
chQueries: {},
|
|
74
|
-
builderQueries: {}
|
|
139
|
+
queries: [
|
|
140
|
+
{ type: "promql", spec: { name: "A", query, step, disabled: false, stats: false } }
|
|
141
|
+
]
|
|
75
142
|
}
|
|
76
143
|
};
|
|
77
144
|
}
|
|
78
|
-
function buildSqlRequest(query,
|
|
145
|
+
function buildSqlRequest(query, start, end) {
|
|
79
146
|
return {
|
|
80
|
-
|
|
81
|
-
|
|
147
|
+
schemaVersion: "v1",
|
|
148
|
+
start,
|
|
149
|
+
end,
|
|
150
|
+
requestType: "time_series",
|
|
82
151
|
compositeQuery: {
|
|
83
|
-
|
|
84
|
-
panelType: "time_series",
|
|
85
|
-
chQueries: { A: { query, disabled: false } },
|
|
86
|
-
promQueries: {},
|
|
87
|
-
builderQueries: {}
|
|
152
|
+
queries: [{ type: "clickhouse_sql", spec: { name: "A", query, disabled: false } }]
|
|
88
153
|
}
|
|
89
154
|
};
|
|
90
155
|
}
|
|
156
|
+
function isEmptyResult(res) {
|
|
157
|
+
const results = res.data?.data?.results;
|
|
158
|
+
if (!results) return true;
|
|
159
|
+
return results.every((r) => !r.aggregations?.some((a) => a.series && a.series.length > 0));
|
|
160
|
+
}
|
|
91
161
|
function registerQuery(program) {
|
|
92
|
-
const cmd = program.command("query").description("Query traces, logs, and metrics via the unified query API").option("--promql <expr>", "PromQL expression").option("--sql <query>", "ClickHouse SQL query (
|
|
162
|
+
const cmd = program.command("query").description("Query traces, logs, and metrics via the unified query API").option("--promql <expr>", "PromQL expression").option("--sql <query>", "ClickHouse SQL query (use {{start_ms}} etc. for time injection)").option("-f, --file <path>", "Load query from JSON file (v5 query_range format)").option("--since <time>", "Start time: duration ago (1h, 30m, 7d) or ISO date", "1h").option("--until <time>", "End time: 'now', duration ago, or ISO date", "now").option("--step <seconds>", "Step interval in seconds (PromQL only)", "60");
|
|
163
|
+
addCommonOptions(cmd).action(async (opts) => {
|
|
93
164
|
const modes = [opts.promql, opts.sql, opts.file].filter(Boolean);
|
|
94
165
|
if (modes.length === 0) {
|
|
95
166
|
cmd.error("specify one of --promql, --sql, or --file");
|
|
@@ -98,38 +169,45 @@ function registerQuery(program) {
|
|
|
98
169
|
cmd.error("specify only one of --promql, --sql, or --file");
|
|
99
170
|
}
|
|
100
171
|
const client = createSignozClient({ url: opts.url, token: opts.token });
|
|
101
|
-
const
|
|
102
|
-
const
|
|
172
|
+
const start = parseSince(opts.since);
|
|
173
|
+
const end = parseUntil(opts.until);
|
|
103
174
|
let body;
|
|
104
175
|
if (opts.promql) {
|
|
105
176
|
const step = Number(opts.step);
|
|
106
177
|
if (Number.isNaN(step) || step <= 0) {
|
|
107
178
|
cmd.error(`invalid --step value: "${opts.step}". Provide a positive number in seconds`);
|
|
108
179
|
}
|
|
109
|
-
body = buildPromqlRequest(opts.promql,
|
|
180
|
+
body = buildPromqlRequest(opts.promql, start, end, step);
|
|
110
181
|
} else if (opts.sql) {
|
|
111
|
-
body = buildSqlRequest(opts.sql,
|
|
182
|
+
body = buildSqlRequest(injectTimeVars(opts.sql, start, end), start, end);
|
|
112
183
|
} else {
|
|
113
184
|
body = JSON.parse(readFileSync(opts.file, "utf-8"));
|
|
114
|
-
body.
|
|
115
|
-
body.
|
|
185
|
+
body.schemaVersion ??= "v1";
|
|
186
|
+
body.start = start;
|
|
187
|
+
body.end = end;
|
|
116
188
|
}
|
|
117
|
-
const result = await client("/api/
|
|
189
|
+
const result = await client("/api/v5/query_range", {
|
|
118
190
|
method: "POST",
|
|
119
191
|
body
|
|
120
192
|
});
|
|
121
193
|
formatOutput(result, opts.format);
|
|
194
|
+
if (opts.promql && isEmptyResult(result)) {
|
|
195
|
+
consola.warn(
|
|
196
|
+
'PromQL returned no data. Common causes:\n \u2022 Delta-temporality metrics (e.g. signoz_calls_total) are not queryable via PromQL \u2014 use --sql or the builder API instead\n \u2022 OTel dot-separated names require {__name__="metric.name"} syntax'
|
|
197
|
+
);
|
|
198
|
+
}
|
|
122
199
|
});
|
|
123
200
|
addExamples(cmd, [
|
|
201
|
+
`signoz query --promql '{__name__="http.client.request.duration.bucket"}' --since 1h`,
|
|
124
202
|
"signoz query --promql 'rate(http_requests_total[5m])' --since 1h",
|
|
125
203
|
"signoz query --sql 'SELECT count(*) FROM signoz_logs.distributed_logs_v2 WHERE timestamp >= ...'",
|
|
126
|
-
"signoz query -f my-query.json --since 7d --until 1d"
|
|
127
|
-
"signoz query --promql 'up' --format table --step 30"
|
|
204
|
+
"signoz query -f my-query.json --since 7d --until 1d"
|
|
128
205
|
]);
|
|
129
206
|
}
|
|
130
207
|
|
|
131
208
|
function registerServices(program) {
|
|
132
|
-
const cmd = program.command("services").description("List all SigNoz services")
|
|
209
|
+
const cmd = program.command("services").description("List all SigNoz services");
|
|
210
|
+
addCommonOptions(cmd).action(async (opts) => {
|
|
133
211
|
const client = createSignozClient({ url: opts.url, token: opts.token });
|
|
134
212
|
const services = await client("/api/v1/services/list");
|
|
135
213
|
formatOutput(services, opts.format);
|
|
@@ -148,6 +226,7 @@ const program = defineCliApp({
|
|
|
148
226
|
});
|
|
149
227
|
registerAuth(program);
|
|
150
228
|
registerQuery(program);
|
|
229
|
+
registerMetrics(program);
|
|
151
230
|
registerAlerts(program);
|
|
152
231
|
registerServices(program);
|
|
153
232
|
program.parseAsync();
|