@hasna/logs 0.3.5 → 0.3.7

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-fzmz9aqs.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-5tvnhvgr.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,
@@ -2174,15 +2175,17 @@ function resolveProject(nameOrId) {
2174
2175
  return resolveProjectId(getDb(), nameOrId) ?? nameOrId;
2175
2176
  }
2176
2177
  var program2 = new Command().name("logs").description("@hasna/logs \u2014 log aggregation and monitoring").version("0.0.1");
2177
- program2.command("list").description("Search and list logs").option("--project <name|id>", "Filter by project name or ID").option("--page <id>", "Filter by page ID").option("--level <levels>", "Comma-separated levels (error,warn,info,debug,fatal)").option("--service <name>", "Filter by service").option("--since <iso>", "Since timestamp or relative (1h, 24h, 7d)").option("--text <query>", "Full-text search").option("--limit <n>", "Max results", "100").option("--format <fmt>", "Output format: table|json|compact", "table").action((opts) => {
2178
+ program2.command("list").description("Search and list logs").option("--project <name|id>", "Filter by project name or ID").option("--page <id>", "Filter by page ID").option("--level <levels>", "Comma-separated levels (error,warn,info,debug,fatal)").option("--service <name>", "Filter by service").option("--since <iso>", "Since timestamp or relative (1h, 24h, 7d)").option("--until <iso>", "Until timestamp or relative (e.g. logs list --since 2h --until 1h)").option("--text <query>", "Full-text search").option("--limit <n>", "Max results", "100").option("--format <fmt>", "Output format: table|json|compact", "table").action((opts) => {
2178
2179
  const db = getDb();
2179
2180
  const since = parseRelativeTime(opts.since);
2181
+ const until = parseRelativeTime(opts.until);
2180
2182
  const rows = searchLogs(db, {
2181
2183
  project_id: resolveProject(opts.project),
2182
2184
  page_id: opts.page,
2183
2185
  level: opts.level ? opts.level.split(",") : undefined,
2184
2186
  service: opts.service,
2185
2187
  since,
2188
+ until,
2186
2189
  text: opts.text,
2187
2190
  limit: Number(opts.limit)
2188
2191
  });
@@ -2207,8 +2210,8 @@ program2.command("tail").description("Show most recent logs").option("--project
2207
2210
  for (const r of rows)
2208
2211
  console.log(colorRow(r.timestamp, r.level, r.service ?? "-", r.message));
2209
2212
  });
2210
- program2.command("summary").description("Error/warn summary by service").option("--project <name|id>", "Project name or ID").option("--since <time>", "Relative time (1h, 24h, 7d)", "24h").action((opts) => {
2211
- const summary = summarizeLogs(getDb(), resolveProject(opts.project), parseRelativeTime(opts.since));
2213
+ program2.command("summary").description("Error/warn summary by service").option("--project <name|id>", "Project name or ID").option("--since <time>", "Relative time (1h, 24h, 7d)", "24h").option("--until <time>", "Upper bound time").action((opts) => {
2214
+ const summary = summarizeLogs(getDb(), resolveProject(opts.project), parseRelativeTime(opts.since), parseRelativeTime(opts.until));
2212
2215
  if (!summary.length) {
2213
2216
  console.log("No errors/warnings in this window.");
2214
2217
  return;
@@ -2281,9 +2284,46 @@ program2.command("scan").description("Run an immediate scan for a job").option("
2281
2284
  await runJob(db, job.id, job.project_id, job.page_id ?? undefined);
2282
2285
  console.log("Scan complete.");
2283
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
+ });
2284
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) => {
2285
2325
  const db = getDb();
2286
- const { searchLogs: searchLogs2 } = await import("../query-shjjj67k.js");
2326
+ const { searchLogs: searchLogs2 } = await import("../query-6s5gqkck.js");
2287
2327
  let projectId = opts.project;
2288
2328
  if (projectId) {
2289
2329
  const proj = db.query("SELECT id FROM projects WHERE id = ? OR name = ?").get(projectId, projectId);
@@ -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 };