feat: add logging
This commit is contained in:
4
lib/cache/cache.ts
vendored
4
lib/cache/cache.ts
vendored
@ -6,6 +6,7 @@ import {
|
||||
RedisValue,
|
||||
} from "https://deno.land/x/redis@v0.31.0/mod.ts";
|
||||
import { createLogger } from "@lib/log.ts";
|
||||
import { getTimeCacheKey } from "@lib/string.ts";
|
||||
|
||||
const REDIS_HOST = Deno.env.get("REDIS_HOST");
|
||||
const REDIS_PASS = Deno.env.get("REDIS_PASS") || "";
|
||||
@ -82,6 +83,7 @@ export function expire(id: string, seconds: number) {
|
||||
|
||||
type RedisOptions = {
|
||||
expires?: number;
|
||||
noLog?: boolean;
|
||||
};
|
||||
|
||||
export function del(key: string) {
|
||||
@ -97,7 +99,7 @@ export function set<T extends RedisValue>(
|
||||
content: T,
|
||||
options?: RedisOptions,
|
||||
) {
|
||||
log.debug("storing ", { id });
|
||||
if (options?.noLog !== true) log.debug("storing ", { id });
|
||||
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 { getTimeCacheKey } from "@lib/string.ts";
|
||||
|
||||
export type PerformancePoint = {
|
||||
path: string;
|
||||
@ -16,20 +17,14 @@ export type PerformanceRes = {
|
||||
};
|
||||
|
||||
export const savePerformance = (url: string, milliseconds: number) => {
|
||||
const d = new Date();
|
||||
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 cacheKey = getTimeCacheKey();
|
||||
|
||||
const u = new URL(url);
|
||||
if (u.pathname.includes("_frsh/")) return;
|
||||
u.searchParams.delete("__frsh_c");
|
||||
|
||||
cache.set(
|
||||
`performance:${year}:${month}:${day}:${hour}:${minute}:${seconds}`,
|
||||
`performance:${cacheKey}`,
|
||||
JSON.stringify({
|
||||
path: decodeURIComponent(u.pathname),
|
||||
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 {
|
||||
DEBUG,
|
||||
INFO,
|
||||
WARN,
|
||||
ERROR,
|
||||
DEBUG = 0,
|
||||
INFO = 1,
|
||||
WARN = 2,
|
||||
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 logLevel = LOG_LEVEL.WARN;
|
||||
|
||||
const ee = new EventEmitter<{
|
||||
log: { level: LOG_LEVEL; scope: string; args: unknown[] };
|
||||
}>();
|
||||
|
||||
export function setLogLevel(level: LOG_LEVEL) {
|
||||
logLevel = level;
|
||||
}
|
||||
|
||||
const getPrefix = (scope: string) => `[${scope.padEnd(longestScope, " ")}]`;
|
||||
|
||||
type LoggerOptions = {
|
||||
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) {
|
||||
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 {
|
||||
debug,
|
||||
info,
|
||||
error,
|
||||
warn,
|
||||
debug: createLogFunction(scope, LOG_LEVEL.DEBUG),
|
||||
info: createLogFunction(scope, LOG_LEVEL.INFO),
|
||||
error: createLogFunction(scope, LOG_LEVEL.ERROR),
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
|
||||
}
|
||||
|
Reference in New Issue
Block a user