feat: add logging
This commit is contained in:
parent
59ddcb64a6
commit
830b33462d
140
fresh.gen.ts
140
fresh.gen.ts
@ -5,40 +5,42 @@
|
|||||||
import * as $0 from "./routes/_404.tsx";
|
import * as $0 from "./routes/_404.tsx";
|
||||||
import * as $1 from "./routes/_app.tsx";
|
import * as $1 from "./routes/_app.tsx";
|
||||||
import * as $2 from "./routes/_middleware.ts";
|
import * as $2 from "./routes/_middleware.ts";
|
||||||
import * as $3 from "./routes/admin/performance/api.ts";
|
import * as $3 from "./routes/admin/log/index.tsx";
|
||||||
import * as $4 from "./routes/admin/performance/index.tsx";
|
import * as $4 from "./routes/admin/performance/api.ts";
|
||||||
import * as $5 from "./routes/api/articles/[name].ts";
|
import * as $5 from "./routes/admin/performance/index.tsx";
|
||||||
import * as $6 from "./routes/api/articles/create/index.ts";
|
import * as $6 from "./routes/api/articles/[name].ts";
|
||||||
import * as $7 from "./routes/api/articles/index.ts";
|
import * as $7 from "./routes/api/articles/create/index.ts";
|
||||||
import * as $8 from "./routes/api/auth/callback.ts";
|
import * as $8 from "./routes/api/articles/index.ts";
|
||||||
import * as $9 from "./routes/api/auth/login.ts";
|
import * as $9 from "./routes/api/auth/callback.ts";
|
||||||
import * as $10 from "./routes/api/auth/logout.ts";
|
import * as $10 from "./routes/api/auth/login.ts";
|
||||||
import * as $11 from "./routes/api/cache/index.ts";
|
import * as $11 from "./routes/api/auth/logout.ts";
|
||||||
import * as $12 from "./routes/api/images/index.ts";
|
import * as $12 from "./routes/api/cache/index.ts";
|
||||||
import * as $13 from "./routes/api/index.ts";
|
import * as $13 from "./routes/api/images/index.ts";
|
||||||
import * as $14 from "./routes/api/movies/[name].ts";
|
import * as $14 from "./routes/api/index.ts";
|
||||||
import * as $15 from "./routes/api/movies/enhance/[name].ts";
|
import * as $15 from "./routes/api/logs.ts";
|
||||||
import * as $16 from "./routes/api/movies/index.ts";
|
import * as $16 from "./routes/api/movies/[name].ts";
|
||||||
import * as $17 from "./routes/api/query/index.ts";
|
import * as $17 from "./routes/api/movies/enhance/[name].ts";
|
||||||
import * as $18 from "./routes/api/query/sync.ts";
|
import * as $18 from "./routes/api/movies/index.ts";
|
||||||
import * as $19 from "./routes/api/recipes/[name].ts";
|
import * as $19 from "./routes/api/query/index.ts";
|
||||||
import * as $20 from "./routes/api/recipes/index.ts";
|
import * as $20 from "./routes/api/query/sync.ts";
|
||||||
import * as $21 from "./routes/api/resources.ts";
|
import * as $21 from "./routes/api/recipes/[name].ts";
|
||||||
import * as $22 from "./routes/api/series/[name].ts";
|
import * as $22 from "./routes/api/recipes/index.ts";
|
||||||
import * as $23 from "./routes/api/series/enhance/[name].ts";
|
import * as $23 from "./routes/api/resources.ts";
|
||||||
import * as $24 from "./routes/api/series/index.ts";
|
import * as $24 from "./routes/api/series/[name].ts";
|
||||||
import * as $25 from "./routes/api/tmdb/[id].ts";
|
import * as $25 from "./routes/api/series/enhance/[name].ts";
|
||||||
import * as $26 from "./routes/api/tmdb/credits/[id].ts";
|
import * as $26 from "./routes/api/series/index.ts";
|
||||||
import * as $27 from "./routes/api/tmdb/query.ts";
|
import * as $27 from "./routes/api/tmdb/[id].ts";
|
||||||
import * as $28 from "./routes/articles/[name].tsx";
|
import * as $28 from "./routes/api/tmdb/credits/[id].ts";
|
||||||
import * as $29 from "./routes/articles/index.tsx";
|
import * as $29 from "./routes/api/tmdb/query.ts";
|
||||||
import * as $30 from "./routes/index.tsx";
|
import * as $30 from "./routes/articles/[name].tsx";
|
||||||
import * as $31 from "./routes/movies/[name].tsx";
|
import * as $31 from "./routes/articles/index.tsx";
|
||||||
import * as $32 from "./routes/movies/index.tsx";
|
import * as $32 from "./routes/index.tsx";
|
||||||
import * as $33 from "./routes/recipes/[name].tsx";
|
import * as $33 from "./routes/movies/[name].tsx";
|
||||||
import * as $34 from "./routes/recipes/index.tsx";
|
import * as $34 from "./routes/movies/index.tsx";
|
||||||
import * as $35 from "./routes/series/[name].tsx";
|
import * as $35 from "./routes/recipes/[name].tsx";
|
||||||
import * as $36 from "./routes/series/index.tsx";
|
import * as $36 from "./routes/recipes/index.tsx";
|
||||||
|
import * as $37 from "./routes/series/[name].tsx";
|
||||||
|
import * as $38 from "./routes/series/index.tsx";
|
||||||
import * as $$0 from "./islands/Counter.tsx";
|
import * as $$0 from "./islands/Counter.tsx";
|
||||||
import * as $$1 from "./islands/IngredientsList.tsx";
|
import * as $$1 from "./islands/IngredientsList.tsx";
|
||||||
import * as $$2 from "./islands/KMenu.tsx";
|
import * as $$2 from "./islands/KMenu.tsx";
|
||||||
@ -56,40 +58,42 @@ const manifest = {
|
|||||||
"./routes/_404.tsx": $0,
|
"./routes/_404.tsx": $0,
|
||||||
"./routes/_app.tsx": $1,
|
"./routes/_app.tsx": $1,
|
||||||
"./routes/_middleware.ts": $2,
|
"./routes/_middleware.ts": $2,
|
||||||
"./routes/admin/performance/api.ts": $3,
|
"./routes/admin/log/index.tsx": $3,
|
||||||
"./routes/admin/performance/index.tsx": $4,
|
"./routes/admin/performance/api.ts": $4,
|
||||||
"./routes/api/articles/[name].ts": $5,
|
"./routes/admin/performance/index.tsx": $5,
|
||||||
"./routes/api/articles/create/index.ts": $6,
|
"./routes/api/articles/[name].ts": $6,
|
||||||
"./routes/api/articles/index.ts": $7,
|
"./routes/api/articles/create/index.ts": $7,
|
||||||
"./routes/api/auth/callback.ts": $8,
|
"./routes/api/articles/index.ts": $8,
|
||||||
"./routes/api/auth/login.ts": $9,
|
"./routes/api/auth/callback.ts": $9,
|
||||||
"./routes/api/auth/logout.ts": $10,
|
"./routes/api/auth/login.ts": $10,
|
||||||
"./routes/api/cache/index.ts": $11,
|
"./routes/api/auth/logout.ts": $11,
|
||||||
"./routes/api/images/index.ts": $12,
|
"./routes/api/cache/index.ts": $12,
|
||||||
"./routes/api/index.ts": $13,
|
"./routes/api/images/index.ts": $13,
|
||||||
"./routes/api/movies/[name].ts": $14,
|
"./routes/api/index.ts": $14,
|
||||||
"./routes/api/movies/enhance/[name].ts": $15,
|
"./routes/api/logs.ts": $15,
|
||||||
"./routes/api/movies/index.ts": $16,
|
"./routes/api/movies/[name].ts": $16,
|
||||||
"./routes/api/query/index.ts": $17,
|
"./routes/api/movies/enhance/[name].ts": $17,
|
||||||
"./routes/api/query/sync.ts": $18,
|
"./routes/api/movies/index.ts": $18,
|
||||||
"./routes/api/recipes/[name].ts": $19,
|
"./routes/api/query/index.ts": $19,
|
||||||
"./routes/api/recipes/index.ts": $20,
|
"./routes/api/query/sync.ts": $20,
|
||||||
"./routes/api/resources.ts": $21,
|
"./routes/api/recipes/[name].ts": $21,
|
||||||
"./routes/api/series/[name].ts": $22,
|
"./routes/api/recipes/index.ts": $22,
|
||||||
"./routes/api/series/enhance/[name].ts": $23,
|
"./routes/api/resources.ts": $23,
|
||||||
"./routes/api/series/index.ts": $24,
|
"./routes/api/series/[name].ts": $24,
|
||||||
"./routes/api/tmdb/[id].ts": $25,
|
"./routes/api/series/enhance/[name].ts": $25,
|
||||||
"./routes/api/tmdb/credits/[id].ts": $26,
|
"./routes/api/series/index.ts": $26,
|
||||||
"./routes/api/tmdb/query.ts": $27,
|
"./routes/api/tmdb/[id].ts": $27,
|
||||||
"./routes/articles/[name].tsx": $28,
|
"./routes/api/tmdb/credits/[id].ts": $28,
|
||||||
"./routes/articles/index.tsx": $29,
|
"./routes/api/tmdb/query.ts": $29,
|
||||||
"./routes/index.tsx": $30,
|
"./routes/articles/[name].tsx": $30,
|
||||||
"./routes/movies/[name].tsx": $31,
|
"./routes/articles/index.tsx": $31,
|
||||||
"./routes/movies/index.tsx": $32,
|
"./routes/index.tsx": $32,
|
||||||
"./routes/recipes/[name].tsx": $33,
|
"./routes/movies/[name].tsx": $33,
|
||||||
"./routes/recipes/index.tsx": $34,
|
"./routes/movies/index.tsx": $34,
|
||||||
"./routes/series/[name].tsx": $35,
|
"./routes/recipes/[name].tsx": $35,
|
||||||
"./routes/series/index.tsx": $36,
|
"./routes/recipes/index.tsx": $36,
|
||||||
|
"./routes/series/[name].tsx": $37,
|
||||||
|
"./routes/series/index.tsx": $38,
|
||||||
},
|
},
|
||||||
islands: {
|
islands: {
|
||||||
"./islands/Counter.tsx": $$0,
|
"./islands/Counter.tsx": $$0,
|
||||||
|
4
lib/cache/cache.ts
vendored
4
lib/cache/cache.ts
vendored
@ -6,6 +6,7 @@ import {
|
|||||||
RedisValue,
|
RedisValue,
|
||||||
} from "https://deno.land/x/redis@v0.31.0/mod.ts";
|
} from "https://deno.land/x/redis@v0.31.0/mod.ts";
|
||||||
import { createLogger } from "@lib/log.ts";
|
import { createLogger } from "@lib/log.ts";
|
||||||
|
import { getTimeCacheKey } from "@lib/string.ts";
|
||||||
|
|
||||||
const REDIS_HOST = Deno.env.get("REDIS_HOST");
|
const REDIS_HOST = Deno.env.get("REDIS_HOST");
|
||||||
const REDIS_PASS = Deno.env.get("REDIS_PASS") || "";
|
const REDIS_PASS = Deno.env.get("REDIS_PASS") || "";
|
||||||
@ -82,6 +83,7 @@ export function expire(id: string, seconds: number) {
|
|||||||
|
|
||||||
type RedisOptions = {
|
type RedisOptions = {
|
||||||
expires?: number;
|
expires?: number;
|
||||||
|
noLog?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function del(key: string) {
|
export function del(key: string) {
|
||||||
@ -97,7 +99,7 @@ export function set<T extends RedisValue>(
|
|||||||
content: T,
|
content: T,
|
||||||
options?: RedisOptions,
|
options?: RedisOptions,
|
||||||
) {
|
) {
|
||||||
log.debug("storing ", { id });
|
if (options?.noLog !== true) log.debug("storing ", { id });
|
||||||
return cache.set(id, content, { ex: options?.expires || undefined });
|
return cache.set(id, content, { ex: options?.expires || undefined });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
37
lib/cache/logs.ts
vendored
Normal file
37
lib/cache/logs.ts
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { createLogger } from "@lib/log.ts";
|
||||||
|
import * as cache from "@lib/cache/cache.ts";
|
||||||
|
import { getTimeCacheKey, parseTimeCacheKey } from "@lib/string.ts";
|
||||||
|
|
||||||
|
const log = createLogger("");
|
||||||
|
log.addEventListener("log", (data) => {
|
||||||
|
cache.set(`log:${getTimeCacheKey()}`, JSON.stringify(data.detail), {
|
||||||
|
noLog: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Log = {
|
||||||
|
scope: string;
|
||||||
|
level: number;
|
||||||
|
date: Date;
|
||||||
|
args: unknown[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getLogs() {
|
||||||
|
const d = new Date();
|
||||||
|
const year = d.getFullYear();
|
||||||
|
const month = d.getMonth().toString().padStart(2, "0");
|
||||||
|
const day = d.getDate().toString().padStart(2, "0");
|
||||||
|
|
||||||
|
const keys = await cache.keys(
|
||||||
|
`log:${year}:${month}:${day}:*`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const logs = await Promise.all(
|
||||||
|
keys.map(async (key) => {
|
||||||
|
const date = parseTimeCacheKey(key);
|
||||||
|
return { ...JSON.parse(await cache.get<string>(key)), date } as Log;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return logs.sort((a, b) => a.date.getTime() > b.date.getTime() ? -1 : 1);
|
||||||
|
}
|
11
lib/cache/performance.ts
vendored
11
lib/cache/performance.ts
vendored
@ -1,4 +1,5 @@
|
|||||||
import * as cache from "@lib/cache/cache.ts";
|
import * as cache from "@lib/cache/cache.ts";
|
||||||
|
import { getTimeCacheKey } from "@lib/string.ts";
|
||||||
|
|
||||||
export type PerformancePoint = {
|
export type PerformancePoint = {
|
||||||
path: string;
|
path: string;
|
||||||
@ -16,20 +17,14 @@ export type PerformanceRes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const savePerformance = (url: string, milliseconds: number) => {
|
export const savePerformance = (url: string, milliseconds: number) => {
|
||||||
const d = new Date();
|
const cacheKey = getTimeCacheKey();
|
||||||
const year = d.getFullYear();
|
|
||||||
const month = d.getMonth().toString().padStart(2, "0");
|
|
||||||
const day = d.getDay().toString().padStart(2, "0");
|
|
||||||
const hour = d.getHours().toString().padStart(2, "0");
|
|
||||||
const minute = d.getMinutes().toString().padStart(2, "0");
|
|
||||||
const seconds = d.getSeconds().toString().padStart(2, "0");
|
|
||||||
|
|
||||||
const u = new URL(url);
|
const u = new URL(url);
|
||||||
if (u.pathname.includes("_frsh/")) return;
|
if (u.pathname.includes("_frsh/")) return;
|
||||||
u.searchParams.delete("__frsh_c");
|
u.searchParams.delete("__frsh_c");
|
||||||
|
|
||||||
cache.set(
|
cache.set(
|
||||||
`performance:${year}:${month}:${day}:${hour}:${minute}:${seconds}`,
|
`performance:${cacheKey}`,
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
path: decodeURIComponent(u.pathname),
|
path: decodeURIComponent(u.pathname),
|
||||||
search: u.search,
|
search: u.search,
|
||||||
|
63
lib/log.ts
63
lib/log.ts
@ -1,51 +1,50 @@
|
|||||||
|
import { EventEmitter } from "https://deno.land/x/evtemitter@v3.0.0/mod.ts";
|
||||||
|
|
||||||
enum LOG_LEVEL {
|
enum LOG_LEVEL {
|
||||||
DEBUG,
|
DEBUG = 0,
|
||||||
INFO,
|
INFO = 1,
|
||||||
WARN,
|
WARN = 2,
|
||||||
ERROR,
|
ERROR = 3,
|
||||||
}
|
}
|
||||||
|
const logFuncs = {
|
||||||
|
[LOG_LEVEL.DEBUG]: console.debug,
|
||||||
|
[LOG_LEVEL.INFO]: console.info,
|
||||||
|
[LOG_LEVEL.WARN]: console.warn,
|
||||||
|
[LOG_LEVEL.ERROR]: console.error,
|
||||||
|
} as const;
|
||||||
|
|
||||||
let longestScope = 0;
|
let longestScope = 0;
|
||||||
let logLevel = LOG_LEVEL.WARN;
|
let logLevel = LOG_LEVEL.WARN;
|
||||||
|
|
||||||
|
const ee = new EventEmitter<{
|
||||||
|
log: { level: LOG_LEVEL; scope: string; args: unknown[] };
|
||||||
|
}>();
|
||||||
|
|
||||||
export function setLogLevel(level: LOG_LEVEL) {
|
export function setLogLevel(level: LOG_LEVEL) {
|
||||||
logLevel = level;
|
logLevel = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getPrefix = (scope: string) => `[${scope.padEnd(longestScope, " ")}]`;
|
|
||||||
|
|
||||||
type LoggerOptions = {
|
type LoggerOptions = {
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createLogFunction =
|
||||||
|
(scope: string, level: LOG_LEVEL) => (...data: unknown[]) => {
|
||||||
|
ee.emit("log", { level, scope, args: data });
|
||||||
|
if (level <= logLevel) return;
|
||||||
|
logFuncs[level](`[${scope.padEnd(longestScope, " ")}]`, ...data);
|
||||||
|
};
|
||||||
|
|
||||||
export function createLogger(scope: string, _options?: LoggerOptions) {
|
export function createLogger(scope: string, _options?: LoggerOptions) {
|
||||||
longestScope = Math.max(scope.length, longestScope);
|
longestScope = Math.max(scope.length, longestScope);
|
||||||
|
|
||||||
function debug(...data: unknown[]) {
|
|
||||||
if (logLevel !== LOG_LEVEL.DEBUG) return;
|
|
||||||
console.debug(getPrefix(scope), ...data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function info(...data: unknown[]) {
|
|
||||||
if (logLevel !== LOG_LEVEL.DEBUG && logLevel !== LOG_LEVEL.INFO) return;
|
|
||||||
console.info(getPrefix(scope), ...data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function warn(...data: unknown[]) {
|
|
||||||
if (
|
|
||||||
logLevel !== LOG_LEVEL.DEBUG && logLevel !== LOG_LEVEL.INFO &&
|
|
||||||
logLevel !== LOG_LEVEL.WARN
|
|
||||||
) return;
|
|
||||||
console.warn(getPrefix(scope), ...data);
|
|
||||||
}
|
|
||||||
|
|
||||||
function error(...data: unknown[]) {
|
|
||||||
console.error(getPrefix(scope), ...data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
debug,
|
debug: createLogFunction(scope, LOG_LEVEL.DEBUG),
|
||||||
info,
|
info: createLogFunction(scope, LOG_LEVEL.INFO),
|
||||||
error,
|
error: createLogFunction(scope, LOG_LEVEL.ERROR),
|
||||||
warn,
|
warn: createLogFunction(scope, LOG_LEVEL.WARN),
|
||||||
|
addEventListener:
|
||||||
|
((type, cb) =>
|
||||||
|
ee.addEventListener(type, cb)) as typeof ee.addEventListener,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,30 @@ function componentToHex(c: number) {
|
|||||||
return hex.length == 1 ? "0" + hex : hex;
|
return hex.length == 1 ? "0" + hex : hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTimeCacheKey() {
|
||||||
|
const d = new Date();
|
||||||
|
const year = d.getFullYear();
|
||||||
|
const month = d.getMonth().toString().padStart(2, "0");
|
||||||
|
const day = d.getDate().toString().padStart(2, "0");
|
||||||
|
const hour = d.getHours().toString().padStart(2, "0");
|
||||||
|
const minute = d.getMinutes().toString().padStart(2, "0");
|
||||||
|
const seconds = d.getSeconds().toString().padStart(2, "0");
|
||||||
|
return `${year}:${month}:${day}:${hour}:${minute}:${seconds}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseTimeCacheKey(key: string) {
|
||||||
|
const [_year, _month, _day, _hour, _minute, _second] = key.split(":")
|
||||||
|
.slice(1).map((s) => parseInt(s));
|
||||||
|
const d = new Date();
|
||||||
|
d.setFullYear(_year);
|
||||||
|
d.setMonth(_month);
|
||||||
|
d.setDate(_day);
|
||||||
|
d.setHours(_hour);
|
||||||
|
d.setMinutes(_minute);
|
||||||
|
d.setSeconds(_second);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
export function rgbToHex(r: number, g: number, b: number) {
|
export function rgbToHex(r: number, g: number, b: number) {
|
||||||
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
|
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
|
||||||
}
|
}
|
||||||
|
81
routes/admin/log/index.tsx
Normal file
81
routes/admin/log/index.tsx
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import { MainLayout } from "@components/layouts/main.tsx";
|
||||||
|
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||||
|
import { AccessDeniedError } from "@lib/errors.ts";
|
||||||
|
import { getLogs, Log } from "@lib/cache/logs.ts";
|
||||||
|
import { formatDate } from "@lib/string.ts";
|
||||||
|
import { renderMarkdown } from "@lib/documents.ts";
|
||||||
|
|
||||||
|
const renderLog = (t: unknown) =>
|
||||||
|
renderMarkdown(`\`\`\`js
|
||||||
|
${typeof t === "string" ? t : JSON.stringify(t).trim()}
|
||||||
|
\`\`\``);
|
||||||
|
|
||||||
|
export const handler: Handlers = {
|
||||||
|
async GET(_, ctx) {
|
||||||
|
const logs = await getLogs();
|
||||||
|
if (!("session" in ctx.state)) {
|
||||||
|
throw new AccessDeniedError();
|
||||||
|
}
|
||||||
|
return ctx.render({
|
||||||
|
logs: logs.map((l) => {
|
||||||
|
return {
|
||||||
|
...l,
|
||||||
|
html: l.args.map(renderLog).join("\n"),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function LogLine(
|
||||||
|
{ log }: {
|
||||||
|
log: Log;
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
class="mt-4 flex flex-col gap-2 bg-gray-900 px-4 py-2 rounded-2xl"
|
||||||
|
style={{ background: "var(--light)" }}
|
||||||
|
>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<span class="bg-gray-600 py-1 px-2 text-xs rounded-xl text-white">
|
||||||
|
{log.date.getHours().toString().padStart(2, "0")}:{log.date
|
||||||
|
.getMinutes().toString().padStart(2, "0")}:{log.date.getSeconds()
|
||||||
|
.toString().padStart(2, "0")} {formatDate(log.date)}
|
||||||
|
</span>
|
||||||
|
<span class="bg-gray-600 py-1 px-2 text-xs rounded-xl text-white">
|
||||||
|
{log.scope}
|
||||||
|
</span>
|
||||||
|
<span class="bg-gray-600 py-1 px-2 text-xs rounded-xl text-white">
|
||||||
|
{log.level}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="flex gap-1 text-white"
|
||||||
|
dangerouslySetInnerHTML={{ __html: log.html }}
|
||||||
|
>
|
||||||
|
<pre>
|
||||||
|
{log.html}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Greet(
|
||||||
|
{ data: { logs }, url }: PageProps<{ logs: Log[] }>,
|
||||||
|
) {
|
||||||
|
return (
|
||||||
|
<MainLayout url={url}>
|
||||||
|
<h1 class="text-white text-4xl ">Performance</h1>
|
||||||
|
|
||||||
|
{logs.map((r) => {
|
||||||
|
return (
|
||||||
|
<LogLine
|
||||||
|
log={r}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</MainLayout>
|
||||||
|
);
|
||||||
|
}
|
@ -1,10 +1,6 @@
|
|||||||
import { MainLayout } from "@components/layouts/main.tsx";
|
import { MainLayout } from "@components/layouts/main.tsx";
|
||||||
import { Handlers, PageProps } from "$fresh/server.ts";
|
import { Handlers, PageProps } from "$fresh/server.ts";
|
||||||
import {
|
import { getPerformances, PerformanceRes } from "@lib/cache/performance.ts";
|
||||||
getPerformances,
|
|
||||||
PerformancePoint,
|
|
||||||
PerformanceRes,
|
|
||||||
} from "@lib/cache/performance.ts";
|
|
||||||
import { AccessDeniedError } from "@lib/errors.ts";
|
import { AccessDeniedError } from "@lib/errors.ts";
|
||||||
|
|
||||||
export const handler: Handlers = {
|
export const handler: Handlers = {
|
||||||
|
14
routes/api/logs.ts
Normal file
14
routes/api/logs.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Handlers } from "$fresh/server.ts";
|
||||||
|
import { createStreamResponse } from "@lib/helpers.ts";
|
||||||
|
|
||||||
|
const activeResponses: ReturnType<typeof createStreamResponse>[] = [];
|
||||||
|
|
||||||
|
export const handler: Handlers = {
|
||||||
|
GET() {
|
||||||
|
const r = createStreamResponse();
|
||||||
|
|
||||||
|
activeResponses.push(r);
|
||||||
|
|
||||||
|
return r.response;
|
||||||
|
},
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user