social bookmarking for atproto

[appview] implement logging using winston

hexmani.ac 9bdb1d41 babfc59a

verified
Changed files
+68 -18
backend
+2 -1
backend/src/db/database.ts
··· 6 6 7 7 import { drizzle } from "drizzle-orm/libsql"; 8 8 import { Config } from "../config.ts"; 9 + import Logger from "../logger.js"; 9 10 10 11 const config = Config.getInstance(); 11 12 const dbname = config.get("database.name"); ··· 23 24 try { 24 25 Database.instance = new Database(); 25 26 } catch (e) { 26 - console.error(e); 27 + Logger.error(e); 27 28 process.exit(1); 28 29 } 29 30 }
+38
backend/src/logger.ts
··· 1 + /* 2 + * clippr: a social bookmarking service for the AT Protocol 3 + * Copyright (c) 2025 clippr contributors. 4 + * SPDX-License-Identifier: AGPL-3.0-only 5 + */ 6 + 7 + import { createLogger, format, transports } from "winston"; 8 + 9 + // TODO: I can't seem to actually get the config setting for the log level yet. 10 + const loglevel: string = "debug"; 11 + 12 + const Logger = createLogger({ 13 + level: loglevel, 14 + transports: [ 15 + new transports.Console({ 16 + format: format.combine( 17 + format.colorize(), 18 + format.timestamp(), 19 + format.printf(({ level, message, timestamp }) => { 20 + return `[${timestamp}] ${level}: ${message}`; 21 + }), 22 + ), 23 + }), 24 + new transports.File({ 25 + dirname: "logs", 26 + filename: "clippr-error.log", 27 + level: "error", 28 + }), 29 + new transports.File({ 30 + dirname: "logs", 31 + filename: "clippr-combined.log", 32 + }), 33 + ], 34 + format: format.combine(format.timestamp(), format.json()), 35 + defaultMeta: { service: "clippr-backend" }, 36 + }); 37 + 38 + export default Logger;
+11 -9
backend/src/main.ts
··· 9 9 import { readFromFirehose, startFirehose, stopFirehose } from "./network/jetstream.ts"; 10 10 import app from "./server.ts"; 11 11 import { Database } from "./db/database.js"; 12 + import Logger from "./logger.js"; 12 13 13 - function main() { 14 - console.log("clippr-BE starting..."); 14 + async function main() { 15 + let logger = Logger; 16 + logger.info("clippr-BE starting..."); 15 17 16 - console.log("initializing config"); 17 - const config: Config = Config.getInstance(); // Get config from config.toml 18 + logger.info("initializing config"); 19 + const config = Config.getInstance(); 18 20 19 - console.log("initializing database"); 21 + logger.info("initializing database"); 20 22 Database.getInstance(); 21 23 22 - console.log("initializing firehose connection"); 24 + logger.info("initializing firehose connection"); 23 25 startFirehose(); 24 26 readFromFirehose(); 25 27 ··· 29 31 fetch: app.fetch, 30 32 }); 31 33 32 - console.log( 34 + logger.info( 33 35 `server started at http://${config.get("hostname")}:${config.get("port")}`, 34 36 ); 35 37 ··· 40 42 process.once("SIGTERM", () => gracefulShutdown("SIGTERM")); 41 43 42 44 function gracefulShutdown(signal: string) { 43 - console.log(`\nreceived ${signal}, shutting down...`); 45 + logger.info(`\nreceived ${signal}, shutting down...`); 44 46 stopFirehose(); 45 47 server.close(); 46 - console.log("server shut down, bye!"); 48 + logger.info("server shut down, bye!"); 47 49 process.exit(0); 48 50 } 49 51 }
+5 -4
backend/src/network/commit.ts
··· 10 10 import { is } from "@atcute/lexicons"; 11 11 import { SocialClipprFeedClip, SocialClipprFeedTag } from "@clipprjs/lexicons"; 12 12 import type { At } from "@atcute/client/lexicons"; 13 + import Logger from "../logger.js"; 13 14 14 15 const db = Database.getInstance().getDb(); 15 16 ··· 27 28 if (event.commit.operation !== "create") return; // We currently do not handle these. 28 29 29 30 if (!is(SocialClipprFeedClip.mainSchema, event.commit.record)) { 30 - console.log("invalid clip", event.commit.record); 31 + Logger.verbose("Invalid clip", event.commit.record); 31 32 return; 32 33 } 33 34 ··· 60 61 languages: record.languages, 61 62 }); 62 63 63 - console.log("inserted new clip"); 64 + Logger.verbose("Indexed new clip:", event.did, event.commit.rkey); 64 65 } 65 66 66 67 export async function handleTag(event: CommitEvent<`social.clippr.${string}`>) { 67 68 if (event.commit.operation !== "create") return; // We currently do not handle these. 68 69 69 70 if (!is(SocialClipprFeedTag.mainSchema, event.commit.record)) { 70 - console.log("invalid tag", event.commit.record); 71 + Logger.verbose("Invalid tag", event.commit.record); 71 72 return; 72 73 } 73 74 ··· 87 88 color: record.color, 88 89 }); 89 90 90 - console.log("inserted new tag"); 91 + Logger.verbose("Indexed new tag:", event.did, event.commit.rkey); 91 92 }
+5 -4
backend/src/network/jetstream.ts
··· 7 7 import { Jetstream } from "@skyware/jetstream"; 8 8 import { Config } from "../config.ts"; 9 9 import { handleClip, handleTag } from "./commit.js"; 10 + import Logger from "../logger.js"; 10 11 11 12 const config = Config.getInstance(); 12 13 const hostname = config.get("network.firehose"); ··· 34 35 handleTag(e); 35 36 break; 36 37 default: 37 - console.log( 38 + Logger.debug( 38 39 `commit for ${e.commit.collection} is not relevant, dropping`, 39 40 ); 40 41 break; ··· 42 43 }); 43 44 44 45 jetstream.on("account", (e) => { 45 - console.log("account update", e.account.did); 46 + Logger.debug(`Received account update for ${e.account.did}`); 46 47 }); 47 48 48 49 jetstream.on("identity", (e) => { 49 - console.log("identity update", e.identity.did); 50 + Logger.debug(`Received identity update for ${e.identity.did}`); 50 51 }); 51 52 52 53 jetstream.on("error", (e) => { 53 - console.log(e); 54 + Logger.warn(e); 54 55 }); 55 56 }
+7
backend/src/server.ts
··· 6 6 7 7 import { Hono } from "hono"; 8 8 import misc from "./routes/misc.ts"; 9 + import Logger from "./logger.js"; 10 + import { logger } from "hono/logger"; 11 + 12 + export function winstonLogger(message: string, ...rest: unknown[]) { 13 + Logger.http(message, ...rest); 14 + } 9 15 10 16 const app = new Hono(); 17 + app.use(logger(winstonLogger)); 11 18 12 19 // Link all routes up 13 20 app.route("/", misc);