@hasna/logs 0.3.6 → 0.3.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/dist/cli/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // @bun
3
3
  import {
4
4
  runJob
5
- } from "../index-4hj4sakk.js";
5
+ } from "../index-7qhh666n.js";
6
6
  import {
7
7
  createPage,
8
8
  createProject,
@@ -12,7 +12,7 @@ import {
12
12
  listProjects,
13
13
  resolveProjectId,
14
14
  summarizeLogs
15
- } from "../index-86j0hn03.js";
15
+ } from "../index-1f2ghyhm.js";
16
16
  import {
17
17
  createJob,
18
18
  listJobs
@@ -20,7 +20,8 @@ import {
20
20
  import {
21
21
  searchLogs,
22
22
  tailLogs
23
- } from "../index-rbrsvsyh.js";
23
+ } from "../index-exeq2gs6.js";
24
+ import"../index-997bkzr2.js";
24
25
  import {
25
26
  __commonJS,
26
27
  __require,
@@ -2283,9 +2284,46 @@ program2.command("scan").description("Run an immediate scan for a job").option("
2283
2284
  await runJob(db, job.id, job.project_id, job.page_id ?? undefined);
2284
2285
  console.log("Scan complete.");
2285
2286
  });
2287
+ program2.command("diagnose").description("Health diagnosis: score, top errors, trends, failing pages").option("--project <name|id>", "Project name or ID").option("--since <time>", "Time window (1h, 24h, 7d)", "24h").option("--include <items>", "Comma-separated: top_errors,error_rate,failing_pages,perf").action(async (opts) => {
2288
+ const { diagnose } = await import("../diagnose-e0w5rwbc.js");
2289
+ const projectId = resolveProject(opts.project);
2290
+ if (!projectId) {
2291
+ console.error("--project required");
2292
+ process.exit(1);
2293
+ }
2294
+ const include = opts.include ? opts.include.split(",") : undefined;
2295
+ const result = diagnose(getDb(), projectId, opts.since, include);
2296
+ const scoreColor = result.health_score >= 80 ? "\x1B[32m" : result.health_score >= 50 ? "\x1B[33m" : "\x1B[31m";
2297
+ console.log(`
2298
+ ${C.bold}Health Score:${C.reset} ${scoreColor}${result.health_score}/100${C.reset}`);
2299
+ if (result.top_errors?.length) {
2300
+ console.log(`
2301
+ ${C.bold}Top Errors:${C.reset}`);
2302
+ for (const e of result.top_errors) {
2303
+ console.log(` ${C.red}${pad(String(e.count), 5)}x${C.reset} ${C.cyan}${pad(e.service ?? "-", 12)}${C.reset} ${e.message}`);
2304
+ }
2305
+ }
2306
+ if (result.error_rate !== undefined) {
2307
+ console.log(`
2308
+ ${C.bold}Error Rate:${C.reset} ${result.error_rate.toFixed(2)}%`);
2309
+ }
2310
+ if (result.failing_pages?.length) {
2311
+ console.log(`
2312
+ ${C.bold}Failing Pages:${C.reset}`);
2313
+ for (const p of result.failing_pages)
2314
+ console.log(` ${C.red}\u2717${C.reset} ${p.url} (${p.error_count} errors)`);
2315
+ }
2316
+ if (result.perf_regressions?.length) {
2317
+ console.log(`
2318
+ ${C.bold}Perf Regressions:${C.reset}`);
2319
+ for (const r of result.perf_regressions)
2320
+ console.log(` ${C.yellow}\u26A0${C.reset} ${r.page_url} p95=${r.p95_ms}ms`);
2321
+ }
2322
+ console.log("");
2323
+ });
2286
2324
  program2.command("watch").description("Stream new logs in real time with color coding").option("--project <name|id>", "Filter by project name or ID").option("--level <levels>", "Comma-separated levels (debug,info,warn,error,fatal)").option("--service <name>", "Filter by service name").option("--interval <ms>", "Poll interval in milliseconds (default: 500)", "500").option("--since <time>", "Start from this time (default: now)").action(async (opts) => {
2287
2325
  const db = getDb();
2288
- const { searchLogs: searchLogs2 } = await import("../query-shjjj67k.js");
2326
+ const { searchLogs: searchLogs2 } = await import("../query-6s5gqkck.js");
2289
2327
  let projectId = opts.project;
2290
2328
  if (projectId) {
2291
2329
  const proj = db.query("SELECT id FROM projects WHERE id = ? OR name = ?").get(projectId, projectId);
@@ -2341,6 +2379,31 @@ Errors: ${errorCount} Warnings: ${warnCount}`);
2341
2379
  process.exit(0);
2342
2380
  });
2343
2381
  });
2382
+ program2.command("count").description("Count logs with optional breakdown by level or service").option("--project <name|id>", "Project name or ID").option("--service <name>", "Filter by service").option("--level <level>", "Filter by level").option("--since <time>", "Since (1h, 24h, 7d)").option("--until <time>", "Until").option("--group-by <field>", "Breakdown: level | service").action(async (opts) => {
2383
+ const { countLogs } = await import("../count-x3n7qg3c.js");
2384
+ const result = countLogs(getDb(), {
2385
+ project_id: resolveProject(opts.project),
2386
+ service: opts.service,
2387
+ level: opts.level,
2388
+ since: opts.since,
2389
+ until: opts.until,
2390
+ group_by: opts.groupBy
2391
+ });
2392
+ console.log(`Total: ${result.total} ${C.red}Errors: ${result.errors}${C.reset} ${C.yellow}Warns: ${result.warns}${C.reset} Fatals: ${result.fatals}`);
2393
+ if (result.by_service) {
2394
+ console.log(`
2395
+ By Service:`);
2396
+ for (const [svc, cnt] of Object.entries(result.by_service)) {
2397
+ console.log(` ${C.cyan}${pad(svc, 20)}${C.reset} ${cnt}`);
2398
+ }
2399
+ } else if (opts.groupBy === "level") {
2400
+ console.log(`
2401
+ By Level:`);
2402
+ for (const [lvl, cnt] of Object.entries(result.by_level)) {
2403
+ console.log(` ${colorLevel(lvl)} ${cnt}`);
2404
+ }
2405
+ }
2406
+ });
2344
2407
  program2.command("export").description("Export logs to JSON or CSV").option("--project <name|id>", "Project name or ID").option("--since <time>", "Relative time or ISO").option("--level <level>").option("--service <name>").option("--format <fmt>", "json or csv", "json").option("--output <file>", "Output file (default: stdout)").option("--limit <n>", "Max rows", "100000").action(async (opts) => {
2345
2408
  const { exportToCsv, exportToJson } = await import("../export-c3eqjste.js");
2346
2409
  const { createWriteStream } = await import("fs");
@@ -0,0 +1,9 @@
1
+ // @bun
2
+ import {
3
+ countLogs
4
+ } from "./index-edn08m6f.js";
5
+ import"./index-997bkzr2.js";
6
+ import"./index-re3ntm60.js";
7
+ export {
8
+ countLogs
9
+ };
@@ -0,0 +1,9 @@
1
+ // @bun
2
+ import {
3
+ diagnose
4
+ } from "./index-7w7v7hnr.js";
5
+ import"./index-997bkzr2.js";
6
+ import"./index-re3ntm60.js";
7
+ export {
8
+ diagnose
9
+ };
@@ -0,0 +1,45 @@
1
+ // @bun
2
+ import {
3
+ parseTime
4
+ } from "./index-997bkzr2.js";
5
+
6
+ // src/lib/count.ts
7
+ function countLogs(db, opts) {
8
+ const conditions = [];
9
+ const params = {};
10
+ if (opts.project_id) {
11
+ conditions.push("project_id = $p");
12
+ params.$p = opts.project_id;
13
+ }
14
+ if (opts.service) {
15
+ conditions.push("service = $service");
16
+ params.$service = opts.service;
17
+ }
18
+ if (opts.level) {
19
+ conditions.push("level = $level");
20
+ params.$level = opts.level;
21
+ }
22
+ const since = parseTime(opts.since);
23
+ const until = parseTime(opts.until);
24
+ if (since) {
25
+ conditions.push("timestamp >= $since");
26
+ params.$since = since;
27
+ }
28
+ if (until) {
29
+ conditions.push("timestamp <= $until");
30
+ params.$until = until;
31
+ }
32
+ const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
33
+ const byLevel = db.prepare(`SELECT level, COUNT(*) as c FROM logs ${where} GROUP BY level`).all(params);
34
+ const by_level = Object.fromEntries(byLevel.map((r) => [r.level, r.c]));
35
+ const total = byLevel.reduce((s, r) => s + r.c, 0);
36
+ return {
37
+ total,
38
+ errors: by_level["error"] ?? 0,
39
+ warns: by_level["warn"] ?? 0,
40
+ fatals: by_level["fatal"] ?? 0,
41
+ by_level
42
+ };
43
+ }
44
+
45
+ export { countLogs };