@merittdev/horus 0.8.11 → 0.8.12
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/index.cjs +76 -12
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -65880,7 +65880,7 @@ init_cjs_shims();
|
|
|
65880
65880
|
|
|
65881
65881
|
// ../../packages/core/src/version.ts
|
|
65882
65882
|
init_cjs_shims();
|
|
65883
|
-
var HORUS_VERSION = true ? "0.8.
|
|
65883
|
+
var HORUS_VERSION = true ? "0.8.12" : "dev";
|
|
65884
65884
|
var PINNED_SOURCE_VERSION = "1.5.1";
|
|
65885
65885
|
|
|
65886
65886
|
// ../../packages/core/src/evidence.ts
|
|
@@ -71571,6 +71571,18 @@ function buildLevelFilter(m5, minLevel) {
|
|
|
71571
71571
|
);
|
|
71572
71572
|
return { terms: { [m5.levelField]: stringLevels } };
|
|
71573
71573
|
}
|
|
71574
|
+
function buildWhereFilters(where) {
|
|
71575
|
+
if (where === void 0 || where.length === 0) return [];
|
|
71576
|
+
return where.map((w4) => ({
|
|
71577
|
+
bool: {
|
|
71578
|
+
should: [
|
|
71579
|
+
{ term: { [w4.field]: w4.value } },
|
|
71580
|
+
{ term: { [`${w4.field}.keyword`]: w4.value } }
|
|
71581
|
+
],
|
|
71582
|
+
minimum_should_match: 1
|
|
71583
|
+
}
|
|
71584
|
+
}));
|
|
71585
|
+
}
|
|
71574
71586
|
function buildTextMust(q2, mapping = MERITT_FIELD_MAPPING) {
|
|
71575
71587
|
if (q2.text === void 0) return [{ match_all: {} }];
|
|
71576
71588
|
if (q2.broadText === true) {
|
|
@@ -71595,6 +71607,7 @@ function buildSearchBody(q2, mapping = MERITT_FIELD_MAPPING) {
|
|
|
71595
71607
|
if (q2.service !== void 0) {
|
|
71596
71608
|
filters.push({ term: { [serviceTermField(mapping)]: q2.service } });
|
|
71597
71609
|
}
|
|
71610
|
+
filters.push(...buildWhereFilters(q2.where));
|
|
71598
71611
|
const mustClause = buildTextMust(q2, mapping);
|
|
71599
71612
|
return {
|
|
71600
71613
|
query: {
|
|
@@ -71623,6 +71636,7 @@ function buildErrorAggBody(q2, field, mapping = MERITT_FIELD_MAPPING) {
|
|
|
71623
71636
|
if (q2.eventCode !== void 0) {
|
|
71624
71637
|
filters.push({ term: { [signatureTermField(mapping)]: q2.eventCode } });
|
|
71625
71638
|
}
|
|
71639
|
+
filters.push(...buildWhereFilters(q2.where));
|
|
71626
71640
|
const aggField = field === mapping.eventCodeField ? signatureTermField(mapping) : `${field}.keyword`;
|
|
71627
71641
|
return {
|
|
71628
71642
|
size: 0,
|
|
@@ -71857,6 +71871,7 @@ function buildErrorAnalysisBody(q2, field = "event_code", mapping = MERITT_FIELD
|
|
|
71857
71871
|
if (q2.eventCode !== void 0) {
|
|
71858
71872
|
filters.push({ term: { [sigTerm]: q2.eventCode } });
|
|
71859
71873
|
}
|
|
71874
|
+
filters.push(...buildWhereFilters(q2.where));
|
|
71860
71875
|
const mustClause = buildTextMust(q2, mapping);
|
|
71861
71876
|
return {
|
|
71862
71877
|
size: 0,
|
|
@@ -112108,6 +112123,19 @@ function resolveRawLevel(opts) {
|
|
|
112108
112123
|
if (opts.allLevels) return void 0;
|
|
112109
112124
|
return opts.level ?? "error";
|
|
112110
112125
|
}
|
|
112126
|
+
function parseWhere(raw) {
|
|
112127
|
+
if (raw === void 0) return [];
|
|
112128
|
+
return raw.map((entry2) => {
|
|
112129
|
+
const eq2 = entry2.indexOf("=");
|
|
112130
|
+
if (eq2 <= 0) {
|
|
112131
|
+
throw new Error(
|
|
112132
|
+
`Invalid --where "${entry2}" \u2014 expected field=value, e.g. --where context.brand_id=42`
|
|
112133
|
+
);
|
|
112134
|
+
}
|
|
112135
|
+
return { field: entry2.slice(0, eq2).trim(), value: entry2.slice(eq2 + 1).trim() };
|
|
112136
|
+
});
|
|
112137
|
+
}
|
|
112138
|
+
var whereLabel = (where) => where.map((w4) => `${w4.field}=${w4.value}`).join(", ");
|
|
112111
112139
|
function levelColor(level, text2) {
|
|
112112
112140
|
if (level === "error" || level === "fatal") return import_picocolors27.default.red(text2);
|
|
112113
112141
|
if (level === "warn") return import_picocolors27.default.yellow(text2);
|
|
@@ -112123,6 +112151,15 @@ function matchServiceLabel(requested, candidates) {
|
|
|
112123
112151
|
}
|
|
112124
112152
|
async function runLogs(service, opts) {
|
|
112125
112153
|
try {
|
|
112154
|
+
let where;
|
|
112155
|
+
try {
|
|
112156
|
+
where = parseWhere(opts.where);
|
|
112157
|
+
} catch (err2) {
|
|
112158
|
+
console.error(import_picocolors27.default.red(err2.message));
|
|
112159
|
+
return 1;
|
|
112160
|
+
}
|
|
112161
|
+
const hasWhere = where.length > 0;
|
|
112162
|
+
const whereArg = hasWhere ? where : void 0;
|
|
112126
112163
|
const config2 = await loadConfig(opts.config, { name: opts.name });
|
|
112127
112164
|
let renv;
|
|
112128
112165
|
try {
|
|
@@ -112174,8 +112211,9 @@ async function runLogs(service, opts) {
|
|
|
112174
112211
|
const from = sinceToIso(opts.since) ?? new Date(Date.now() - 7 * 864e5).toISOString();
|
|
112175
112212
|
const fromDisplay = from.slice(0, 16).replace("T", " ");
|
|
112176
112213
|
if (opts.raw === true) {
|
|
112177
|
-
const rawLevel = resolveRawLevel(opts);
|
|
112178
|
-
const
|
|
112214
|
+
const rawLevel = hasWhere && opts.level === void 0 ? void 0 : resolveRawLevel(opts);
|
|
112215
|
+
const defaultLimit = hasWhere ? 200 : 20;
|
|
112216
|
+
const limit = opts.limit !== void 0 ? Math.min(Number(opts.limit), 1e3) : defaultLimit;
|
|
112179
112217
|
const records = await logs.searchLogs({
|
|
112180
112218
|
service: resolvedService,
|
|
112181
112219
|
from,
|
|
@@ -112184,8 +112222,10 @@ async function runLogs(service, opts) {
|
|
|
112184
112222
|
// grep searches message + detail + context.* so error strings buried
|
|
112185
112223
|
// outside the message field are still found (HOR-216).
|
|
112186
112224
|
broadText: opts.grep !== void 0,
|
|
112225
|
+
where: whereArg,
|
|
112187
112226
|
limit
|
|
112188
112227
|
});
|
|
112228
|
+
const truncated = records.length === limit;
|
|
112189
112229
|
if (opts.json) {
|
|
112190
112230
|
console.log(
|
|
112191
112231
|
JSON.stringify(
|
|
@@ -112197,7 +112237,9 @@ async function runLogs(service, opts) {
|
|
|
112197
112237
|
index: indexPattern,
|
|
112198
112238
|
from,
|
|
112199
112239
|
level: rawLevel ?? "all",
|
|
112200
|
-
grep: opts.grep ?? null
|
|
112240
|
+
grep: opts.grep ?? null,
|
|
112241
|
+
where: whereArg ?? null,
|
|
112242
|
+
truncated
|
|
112201
112243
|
},
|
|
112202
112244
|
records: records.map((r) => ({
|
|
112203
112245
|
timestamp: r.timestamp,
|
|
@@ -112219,6 +112261,11 @@ async function runLogs(service, opts) {
|
|
|
112219
112261
|
);
|
|
112220
112262
|
return 0;
|
|
112221
112263
|
}
|
|
112264
|
+
console.error(
|
|
112265
|
+
import_picocolors27.default.dim(
|
|
112266
|
+
`logs: ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + ` \xB7 from ${fromDisplay} UTC \xB7 level ${rawLevel ?? "all"}` + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (rawLevel === "error" && !hasWhere ? " (error+ only \u2014 use --all-levels for info/debug)" : "")
|
|
112267
|
+
)
|
|
112268
|
+
);
|
|
112222
112269
|
if (records.length === 0) {
|
|
112223
112270
|
console.log(import_picocolors27.default.dim("No logs matched."));
|
|
112224
112271
|
return 0;
|
|
@@ -112233,6 +112280,13 @@ async function runLogs(service, opts) {
|
|
|
112233
112280
|
console.log(import_picocolors27.default.dim(` ${key}: ${value.slice(0, 200)}`));
|
|
112234
112281
|
}
|
|
112235
112282
|
}
|
|
112283
|
+
if (truncated) {
|
|
112284
|
+
console.error(
|
|
112285
|
+
import_picocolors27.default.yellow(
|
|
112286
|
+
`[warn] showing the first ${limit} record(s) \u2014 results may be truncated. Narrow with --where/--since/--level or raise --limit (max 1000).`
|
|
112287
|
+
)
|
|
112288
|
+
);
|
|
112289
|
+
}
|
|
112236
112290
|
return 0;
|
|
112237
112291
|
}
|
|
112238
112292
|
if (opts.groupBy !== void 0) {
|
|
@@ -112242,7 +112296,8 @@ async function runLogs(service, opts) {
|
|
|
112242
112296
|
service: resolvedService,
|
|
112243
112297
|
from,
|
|
112244
112298
|
text: opts.grep,
|
|
112245
|
-
broadText: opts.grep !== void 0
|
|
112299
|
+
broadText: opts.grep !== void 0,
|
|
112300
|
+
where: whereArg
|
|
112246
112301
|
},
|
|
112247
112302
|
field
|
|
112248
112303
|
);
|
|
@@ -112258,6 +112313,7 @@ async function runLogs(service, opts) {
|
|
|
112258
112313
|
from,
|
|
112259
112314
|
severity: "error+",
|
|
112260
112315
|
grep: opts.grep ?? null,
|
|
112316
|
+
where: whereArg ?? null,
|
|
112261
112317
|
groupBy: field
|
|
112262
112318
|
},
|
|
112263
112319
|
buckets
|
|
@@ -112270,7 +112326,7 @@ async function runLogs(service, opts) {
|
|
|
112270
112326
|
}
|
|
112271
112327
|
console.log(
|
|
112272
112328
|
import_picocolors27.default.bold(`Top ${field}`) + import_picocolors27.default.dim(
|
|
112273
|
-
` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
|
|
112329
|
+
` \u2014 ${renv.project}/${renv.env}` + (resolvedService ? ` \xB7 service ${resolvedService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
|
|
112274
112330
|
)
|
|
112275
112331
|
);
|
|
112276
112332
|
console.log(import_picocolors27.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
@@ -112291,12 +112347,13 @@ async function runLogs(service, opts) {
|
|
|
112291
112347
|
service: resolvedService,
|
|
112292
112348
|
from,
|
|
112293
112349
|
text: opts.grep,
|
|
112294
|
-
broadText: opts.grep !== void 0
|
|
112350
|
+
broadText: opts.grep !== void 0,
|
|
112351
|
+
where: whereArg
|
|
112295
112352
|
});
|
|
112296
112353
|
let scopeService = resolvedService;
|
|
112297
112354
|
let broadeningNote;
|
|
112298
112355
|
if (analysis.signatures.length === 0 && resolvedService !== void 0) {
|
|
112299
|
-
const broader = await logs.analyzeErrors({ from, text: opts.grep, broadText: opts.grep !== void 0 });
|
|
112356
|
+
const broader = await logs.analyzeErrors({ from, text: opts.grep, broadText: opts.grep !== void 0, where: whereArg });
|
|
112300
112357
|
if (broader.signatures.length > 0) {
|
|
112301
112358
|
const match = matchServiceLabel(resolvedService, broader.affectedServices);
|
|
112302
112359
|
if (match.kind === "unique" && match.matched !== void 0 && match.matched !== resolvedService) {
|
|
@@ -112304,7 +112361,8 @@ async function runLogs(service, opts) {
|
|
|
112304
112361
|
service: match.matched,
|
|
112305
112362
|
from,
|
|
112306
112363
|
text: opts.grep,
|
|
112307
|
-
broadText: opts.grep !== void 0
|
|
112364
|
+
broadText: opts.grep !== void 0,
|
|
112365
|
+
where: whereArg
|
|
112308
112366
|
});
|
|
112309
112367
|
if (scoped.signatures.length > 0) {
|
|
112310
112368
|
analysis = scoped;
|
|
@@ -112337,7 +112395,8 @@ async function runLogs(service, opts) {
|
|
|
112337
112395
|
index: indexPattern,
|
|
112338
112396
|
from,
|
|
112339
112397
|
severity: "error+",
|
|
112340
|
-
grep: opts.grep ?? null
|
|
112398
|
+
grep: opts.grep ?? null,
|
|
112399
|
+
where: whereArg ?? null
|
|
112341
112400
|
},
|
|
112342
112401
|
totalErrors: analysis.totalErrors,
|
|
112343
112402
|
signatures: analysis.signatures,
|
|
@@ -112352,7 +112411,7 @@ async function runLogs(service, opts) {
|
|
|
112352
112411
|
}
|
|
112353
112412
|
console.log(
|
|
112354
112413
|
import_picocolors27.default.bold(`Error analysis`) + import_picocolors27.default.dim(
|
|
112355
|
-
` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "")
|
|
112414
|
+
` \u2014 ${renv.project}/${renv.env}` + (scopeService ? ` \xB7 service ${scopeService}` : "") + (opts.grep ? ` \xB7 grep "${opts.grep}"` : "") + (hasWhere ? ` \xB7 where ${whereLabel(where)}` : "")
|
|
112356
112415
|
)
|
|
112357
112416
|
);
|
|
112358
112417
|
console.log(import_picocolors27.default.dim(` index: ${indexPattern} \xB7 from: ${fromDisplay} UTC \xB7 severity: error+`));
|
|
@@ -116652,7 +116711,12 @@ Examples:
|
|
|
116652
116711
|
});
|
|
116653
116712
|
program2.command("logs [service]").description(
|
|
116654
116713
|
"Synthesize error evidence from logs (signatures, first/last, affected services); --raw for lines"
|
|
116655
|
-
).option("-c, --config <path>", "path to horus.config.ts").option("--name <name>", "registered project name (resolves via the registry)").option("--project <name>", "project name").option("--env <name>", "environment name (e.g. production)").option("--since <when>", "time window, e.g. 24h, 7d, or an ISO date").option("--level <level>", "minimum level (with --raw): trace|debug|info|warn|error|fatal").option("--grep <text>", "match text in the message").option("--raw", "dump individual log lines instead of synthesized evidence (error+ by default)").option("--all-levels", "with --raw: show all severity levels, not just error+").option("--group-by <field>", "aggregate error counts by a context field, e.g. context.brand_id").option(
|
|
116714
|
+
).option("-c, --config <path>", "path to horus.config.ts").option("--name <name>", "registered project name (resolves via the registry)").option("--project <name>", "project name").option("--env <name>", "environment name (e.g. production)").option("--since <when>", "time window, e.g. 24h, 7d, or an ISO date").option("--level <level>", "minimum level (with --raw): trace|debug|info|warn|error|fatal").option("--grep <text>", "match text in the message").option("--raw", "dump individual log lines instead of synthesized evidence (error+ by default)").option("--all-levels", "with --raw: show all severity levels, not just error+").option("--group-by <field>", "aggregate error counts by a context field, e.g. context.brand_id").option(
|
|
116715
|
+
"--where <field=value>",
|
|
116716
|
+
"filter by a structured context field (repeatable, AND-combined), e.g. --where context.brand_id=42",
|
|
116717
|
+
(val, acc) => [...acc, val],
|
|
116718
|
+
[]
|
|
116719
|
+
).option("--limit <n>", "max records (with --raw)").option("--json", "output JSON").option("--ai", "append AI narration of error signatures (default mode only, not --raw or --json)").option("--ai-model <model>", "override the AI model (default: claude-opus-4-8)").action(
|
|
116656
116720
|
async (service, opts) => {
|
|
116657
116721
|
process.exitCode = await runLogs(service, opts);
|
|
116658
116722
|
}
|