feat: use logger

This commit is contained in:
max_richter 2023-08-05 22:16:14 +02:00
parent 46cd823b6c
commit 32a7f89309
15 changed files with 99 additions and 37 deletions

View File

@ -22,7 +22,6 @@ export const createNewArticle: MenuEntry = {
state.activeState.value = "loading"; state.activeState.value = "loading";
fetchStream("/api/articles/create?url=" + value, (chunk) => { fetchStream("/api/articles/create?url=" + value, (chunk) => {
console.log({ chunk: chunk.split("\n") });
if (chunk.startsWith("id:")) { if (chunk.startsWith("id:")) {
state.loadingText.value = "Finished"; state.loadingText.value = "Finished";
setTimeout(() => { setTimeout(() => {

View File

@ -32,7 +32,6 @@ export const createNewMovie: MenuEntry = {
let currentQuery: string; let currentQuery: string;
const search = debounce(async function search(query: string) { const search = debounce(async function search(query: string) {
currentQuery = query; currentQuery = query;
console.log({ query });
if (query.length < 2) { if (query.length < 2) {
return; return;
} }

9
lib/cache/cache.ts vendored
View File

@ -5,11 +5,14 @@ import {
RedisConnectOptions, RedisConnectOptions,
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";
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") || "";
const REDIS_PORT = Deno.env.get("REDIS_PORT"); const REDIS_PORT = Deno.env.get("REDIS_PORT");
const log = createLogger("cache");
async function createCache<T>(): Promise<Redis> { async function createCache<T>(): Promise<Redis> {
if (REDIS_HOST) { if (REDIS_HOST) {
const conf: RedisConnectOptions = { const conf: RedisConnectOptions = {
@ -22,10 +25,10 @@ async function createCache<T>(): Promise<Redis> {
} }
try { try {
const client = await connect(conf); const client = await connect(conf);
console.log("[redis] connected"); log.info("redis connected");
return client; return client;
} catch (_err) { } catch (_err) {
console.log("[cache] cant connect to redis, falling back to mock"); log.info("cant connect to redis, falling back to mock");
} }
} }
@ -94,7 +97,7 @@ export async function set<T extends RedisValue>(
content: T, content: T,
options?: RedisOptions, options?: RedisOptions,
) { ) {
console.log("[cache] storing ", { id }); log.debug("storing ", { id });
const res = await cache.set(id, content); const res = await cache.set(id, content);
if (options?.expires) { if (options?.expires) {
await expire(id, options.expires); await expire(id, options.expires);

4
lib/cache/image.ts vendored
View File

@ -1,6 +1,7 @@
import { hash } from "@lib/string.ts"; import { hash } from "@lib/string.ts";
import * as cache from "@lib/cache/cache.ts"; import * as cache from "@lib/cache/cache.ts";
import { ImageMagick } from "https://deno.land/x/imagemagick_deno@0.0.25/mod.ts"; import { ImageMagick } from "https://deno.land/x/imagemagick_deno@0.0.25/mod.ts";
import { createLogger } from "@lib/log.ts";
type ImageCacheOptions = { type ImageCacheOptions = {
url: string; url: string;
@ -10,6 +11,7 @@ type ImageCacheOptions = {
}; };
const CACHE_KEY = "images"; const CACHE_KEY = "images";
const log = createLogger("cache/image");
function getCacheKey({ url: _url, width, height }: ImageCacheOptions) { function getCacheKey({ url: _url, width, height }: ImageCacheOptions) {
const url = new URL(_url); const url = new URL(_url);
@ -63,7 +65,7 @@ export async function setImage(
const imageCorrect = await verifyImage(clone); const imageCorrect = await verifyImage(clone);
if (!imageCorrect) { if (!imageCorrect) {
console.log("[cache/image] failed to store image", { url }); log.info("failed to store image", { url });
return; return;
} }

View File

@ -11,6 +11,7 @@ import remarkFrontmatter, {
import * as cache from "@lib/cache/documents.ts"; import * as cache from "@lib/cache/documents.ts";
import { SILVERBULLET_SERVER } from "@lib/env.ts"; import { SILVERBULLET_SERVER } from "@lib/env.ts";
import { fixRenderedMarkdown } from "@lib/helpers.ts"; import { fixRenderedMarkdown } from "@lib/helpers.ts";
import { createLogger } from "@lib/log.ts";
export type Document = { export type Document = {
name: string; name: string;
@ -20,13 +21,15 @@ export type Document = {
perm: string; perm: string;
}; };
const log = createLogger("documents");
export async function getDocuments(): Promise<Document[]> { export async function getDocuments(): Promise<Document[]> {
const cachedDocuments = await cache.getDocuments(); const cachedDocuments = await cache.getDocuments();
if (cachedDocuments) return cachedDocuments; if (cachedDocuments) return cachedDocuments;
const headers = new Headers(); const headers = new Headers();
headers.append("Accept", "application/json"); headers.append("Accept", "application/json");
console.log("[documents] fetching all documents"); log.debug("fetching all documents");
const response = await fetch(`${SILVERBULLET_SERVER}/index.json`, { const response = await fetch(`${SILVERBULLET_SERVER}/index.json`, {
headers: headers, headers: headers,
}); });
@ -48,7 +51,7 @@ export function createDocument(
headers.append("Content-Type", mediaType); headers.append("Content-Type", mediaType);
} }
console.log("[documents] creating document", { name }); log.info("creating document", { name });
return fetch(SILVERBULLET_SERVER + "/" + name, { return fetch(SILVERBULLET_SERVER + "/" + name, {
body: content, body: content,
@ -61,7 +64,7 @@ export async function getDocument(name: string): Promise<string> {
const cachedDocument = await cache.getDocument(name); const cachedDocument = await cache.getDocument(name);
if (cachedDocument) return cachedDocument; if (cachedDocument) return cachedDocument;
console.log("[documents] fetching document", { name }); log.debug("fetching document", { name });
const response = await fetch(SILVERBULLET_SERVER + "/" + name); const response = await fetch(SILVERBULLET_SERVER + "/" + name);
const text = await response.text(); const text = await response.text();

53
lib/log.ts Normal file
View File

@ -0,0 +1,53 @@
enum LOG_LEVEL {
DEBUG,
INFO,
WARN,
ERROR,
}
let longestScope = 0;
let logLevel = LOG_LEVEL.WARN;
export function setLogLevel(level: LOG_LEVEL) {
logLevel = level;
}
const getPrefix = (scope: string) => `[${scope.padEnd(longestScope, " ")}]`;
type LoggerOptions = {
enabled?: boolean;
};
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,
};
}
const log = createLogger("");

View File

@ -100,7 +100,6 @@ function parseArticle(original: string, id: string): Article {
export const getAllArticles = crud.readAll; export const getAllArticles = crud.readAll;
export const getArticle = crud.read; export const getArticle = crud.read;
export const createArticle = (article: Article) => { export const createArticle = (article: Article) => {
console.log("creating article", { article });
const content = renderArticle(article); const content = renderArticle(article);
return crud.create(article.id, content); return crud.create(article.id, content);
}; };

View File

@ -103,7 +103,6 @@ const crud = createCrud<Movie>({
export const getMovie = crud.read; export const getMovie = crud.read;
export const getAllMovies = crud.readAll; export const getAllMovies = crud.readAll;
export const createMovie = (movie: Movie) => { export const createMovie = (movie: Movie) => {
console.log("creating movie", { movie });
const content = renderMovie(movie); const content = renderMovie(movie);
return crud.create(movie.id, content); return crud.create(movie.id, content);
}; };

View File

@ -3,6 +3,9 @@ import { TYPESENSE_API_KEY, TYPESENSE_URL } from "@lib/env.ts";
import { getAllMovies } from "@lib/resource/movies.ts"; import { getAllMovies } from "@lib/resource/movies.ts";
import { getAllRecipes } from "@lib/resource/recipes.ts"; import { getAllRecipes } from "@lib/resource/recipes.ts";
import { getAllArticles } from "@lib/resource/articles.ts"; import { getAllArticles } from "@lib/resource/articles.ts";
import { createLogger } from "@lib/log.ts";
const log = createLogger("typesense");
function sanitizeStringForTypesense(input: string) { function sanitizeStringForTypesense(input: string) {
// Remove backslashes // Remove backslashes
@ -82,12 +85,12 @@ async function initializeTypesense() {
], ],
default_sorting_field: "rating", // Default field for sorting default_sorting_field: "rating", // Default field for sorting
}); });
console.log('[typesense] created "resources" collection'); log.info('created "resources" collection');
} else { } else {
console.log('[typesense] collection "resources" already exists.'); log.info('collection "resources" already exists.');
} }
} catch (error) { } catch (error) {
console.error("[typesense] error initializing", error); log.error("error initializing", error);
} }
} }
@ -143,9 +146,9 @@ async function synchronizeWithTypesense() {
// ), // ),
// ); // );
console.log("Data synchronized with Typesense."); log.info("data synchronized");
} catch (error) { } catch (error) {
console.error("Error synchronizing data with Typesense:", error); log.error("error synchronizing", error);
} }
} }

View File

@ -9,10 +9,11 @@ import tds from "https://cdn.skypack.dev/turndown@7.1.1";
import { Article, createArticle } from "@lib/resource/articles.ts"; import { Article, createArticle } from "@lib/resource/articles.ts";
import { getYoutubeVideoDetails } from "@lib/youtube.ts"; import { getYoutubeVideoDetails } from "@lib/youtube.ts";
import { extractYoutubeId, isYoutubeLink } from "@lib/string.ts"; import { extractYoutubeId, isYoutubeLink } from "@lib/string.ts";
import { createLogger } from "@lib/log.ts";
const parser = new DOMParser(); const parser = new DOMParser();
//service.use(gfm); const log = createLogger("api/article");
async function processCreateArticle( async function processCreateArticle(
{ fetchUrl, streamResponse }: { { fetchUrl, streamResponse }: {
@ -20,7 +21,7 @@ async function processCreateArticle(
streamResponse: ReturnType<typeof createStreamResponse>; streamResponse: ReturnType<typeof createStreamResponse>;
}, },
) { ) {
console.log("[api/article] create article from url", { url: fetchUrl }); log.info("create article from url", { url: fetchUrl });
streamResponse.enqueue("downloading article"); streamResponse.enqueue("downloading article");
@ -48,7 +49,7 @@ async function processCreateArticle(
const result = readable.parse(); const result = readable.parse();
console.log("[api/article] parsed ", { log.debug("parsed", {
url: fetchUrl, url: fetchUrl,
content: result.textContent, content: result.textContent,
}); });
@ -173,7 +174,7 @@ async function processCreateYoutubeVideo(
streamResponse: ReturnType<typeof createStreamResponse>; streamResponse: ReturnType<typeof createStreamResponse>;
}, },
) { ) {
console.log("[api/article] create youtube article from url", { log.info("create youtube article from url", {
url: fetchUrl, url: fetchUrl,
}); });
@ -187,6 +188,7 @@ async function processCreateYoutubeVideo(
const newId = await openai.shortenTitle(video.snippet.title); const newId = await openai.shortenTitle(video.snippet.title);
const newArticle: Article = { const newArticle: Article = {
type: "article",
name: video.snippet.title, name: video.snippet.title,
id: newId || video.snippet.title, id: newId || video.snippet.title,
content: video.snippet.description, content: video.snippet.description,
@ -227,18 +229,18 @@ export const handler: Handlers = {
if (isYoutubeLink(fetchUrl)) { if (isYoutubeLink(fetchUrl)) {
processCreateYoutubeVideo({ fetchUrl, streamResponse }).then( processCreateYoutubeVideo({ fetchUrl, streamResponse }).then(
(article) => { (article) => {
console.log({ article }); log.debug("created article from youtube", { article });
}, },
).catch((err) => { ).catch((err) => {
console.log(err); log.error(err);
}).finally(() => { }).finally(() => {
streamResponse.cancel(); streamResponse.cancel();
}); });
} else { } else {
processCreateArticle({ fetchUrl, streamResponse }).then((article) => { processCreateArticle({ fetchUrl, streamResponse }).then((article) => {
console.log({ article }); log.debug("created article from link", { article });
}).catch((err) => { }).catch((err) => {
console.log(err); log.error(err);
}).finally(() => { }).finally(() => {
streamResponse.cancel(); streamResponse.cancel();
}); });

View File

@ -11,6 +11,7 @@ import * as cache from "@lib/cache/image.ts";
import { SILVERBULLET_SERVER } from "@lib/env.ts"; import { SILVERBULLET_SERVER } from "@lib/env.ts";
import { PromiseQueue } from "@lib/promise.ts"; import { PromiseQueue } from "@lib/promise.ts";
import { BadRequestError } from "@lib/errors.ts"; import { BadRequestError } from "@lib/errors.ts";
import { createLogger } from "@lib/log.ts";
await initialize(); await initialize();
@ -19,8 +20,10 @@ type ImageParams = {
height: number; height: number;
}; };
const log = createLogger("api/image");
async function getRemoteImage(image: string) { async function getRemoteImage(image: string) {
console.log("[api/image] fetching", { image }); log.debug("[api/image] fetching", { image });
const sourceRes = await fetch(image); const sourceRes = await fetch(image);
if (!sourceRes.ok) { if (!sourceRes.ok) {
return "Error retrieving image from URL."; return "Error retrieving image from URL.";
@ -107,7 +110,7 @@ const queue = new PromiseQueue();
async function processImage(imageUrl: string, params: ImageParams) { async function processImage(imageUrl: string, params: ImageParams) {
const remoteImage = await getRemoteImage(imageUrl); const remoteImage = await getRemoteImage(imageUrl);
if (typeof remoteImage === "string") { if (typeof remoteImage === "string") {
console.log("[api/image] ERROR " + remoteImage); log.warn("error fetching ", { remoteImage });
throw new BadRequestError(); throw new BadRequestError();
} }
@ -117,7 +120,7 @@ async function processImage(imageUrl: string, params: ImageParams) {
mode: "resize", mode: "resize",
}); });
console.log("[api/image] resized image", { log.debug("resized image", {
imageUrl, imageUrl,
length: modifiedImage.length, length: modifiedImage.length,
}); });
@ -144,14 +147,14 @@ const GET = async (
height: params.height, height: params.height,
}); });
if (cachedResponse) { if (cachedResponse) {
console.log("[api/image] cached: " + imageUrl); log.debug("cached", { imageUrl });
return new Response(cachedResponse.buffer.slice(), { return new Response(cachedResponse.buffer.slice(), {
headers: { headers: {
"Content-Type": cachedResponse.mediaType, "Content-Type": cachedResponse.mediaType,
}, },
}); });
} else { } else {
console.log("[api/image] no image in cache"); log.debug("no image in cache");
} }
const [resizedImage, mediaType] = await queue.enqueue(() => const [resizedImage, mediaType] = await queue.enqueue(() =>
@ -165,8 +168,7 @@ const GET = async (
mediaType: mediaType, mediaType: mediaType,
}); });
console.log("[api/image] not-cached: " + imageUrl); log.debug("not-cached", { imageUrl, resizedImage });
console.log({ imageUrl, resizedImage });
return new Response(new Uint8Array(resizedImage), { return new Response(new Uint8Array(resizedImage), {
headers: { headers: {

View File

@ -37,7 +37,6 @@ export const handler: Handlers = {
const authors = url.searchParams?.get("author")?.split(","); const authors = url.searchParams?.get("author")?.split(",");
if (authors?.length) { if (authors?.length) {
console.log({ authors });
resources = resources.filter((r) => { resources = resources.filter((r) => {
return r?.meta?.author && authors.includes(r?.meta?.author); return r?.meta?.author && authors.includes(r?.meta?.author);
}); });

View File

@ -2,6 +2,7 @@ import { HandlerContext } from "$fresh/server.ts";
import { getMovieCredits } from "@lib/tmdb.ts"; import { getMovieCredits } from "@lib/tmdb.ts";
import * as cache from "@lib/cache/cache.ts"; import * as cache from "@lib/cache/cache.ts";
import { json } from "@lib/helpers.ts"; import { json } from "@lib/helpers.ts";
import { createLogger } from "@lib/log.ts";
type CachedMovieCredits = { type CachedMovieCredits = {
lastUpdated: number; lastUpdated: number;
@ -10,6 +11,8 @@ type CachedMovieCredits = {
const CACHE_INTERVAL = 1000 * 60 * 24 * 30; const CACHE_INTERVAL = 1000 * 60 * 24 * 30;
const log = createLogger("api/tmdb");
export const handler = async ( export const handler = async (
_req: Request, _req: Request,
_ctx: HandlerContext, _ctx: HandlerContext,
@ -22,7 +25,7 @@ export const handler = async (
}); });
} }
console.log("[api] getting movie credits"); log.debug("getting movie credits");
const cacheId = `/movie/credits/${id}`; const cacheId = `/movie/credits/${id}`;

View File

@ -19,8 +19,6 @@ export default function Greet(props: PageProps<Article>) {
const { author = "", date = "" } = article.meta; const { author = "", date = "" } = article.meta;
console.log({ tags: article.tags });
return ( return (
<MainLayout url={props.url} title={`Article > ${article.name}`}> <MainLayout url={props.url} title={`Article > ${article.name}`}>
<RecipeHero <RecipeHero

View File

@ -17,8 +17,6 @@ export default function Greet(props: PageProps<Movie>) {
const { author = "", date = "" } = movie.meta; const { author = "", date = "" } = movie.meta;
console.log(movie.description);
return ( return (
<MainLayout url={props.url} title={`Movie > ${movie.name}`}> <MainLayout url={props.url} title={`Movie > ${movie.name}`}>
<RecipeHero <RecipeHero