mirror of
https://github.com/versia-pub/server.git
synced 2026-03-13 05:49:16 +01:00
refactor: ♻️ Rewrite logging logic into a unified package
This commit is contained in:
parent
e1bd389bf1
commit
aff51b651c
32 changed files with 479 additions and 402 deletions
56
packages/logging/formatter.ts
Normal file
56
packages/logging/formatter.ts
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
import type { LogLevel, LogRecord } from "@logtape/logtape";
|
||||
import chalk, { type ChalkInstance } from "chalk";
|
||||
|
||||
const levelAbbreviations: Record<LogLevel, string> = {
|
||||
debug: "DBG",
|
||||
info: "INF",
|
||||
warning: "WRN",
|
||||
error: "ERR",
|
||||
fatal: "FTL",
|
||||
trace: "TRC",
|
||||
};
|
||||
|
||||
/**
|
||||
* The styles for the log level in the console.
|
||||
*/
|
||||
const logLevelStyles: Record<LogLevel, ChalkInstance> = {
|
||||
debug: chalk.white.bgGray,
|
||||
info: chalk.black.bgWhite,
|
||||
warning: chalk.black.bgYellow,
|
||||
error: chalk.white.bgRed,
|
||||
fatal: chalk.white.bgRedBright,
|
||||
trace: chalk.white.bgBlue,
|
||||
};
|
||||
|
||||
/**
|
||||
* Pretty colored console formatter.
|
||||
*
|
||||
* @param record The log record to format.
|
||||
* @returns The formatted log record, as an array of arguments for
|
||||
* {@link console.log}.
|
||||
*/
|
||||
export function consoleFormatter(record: LogRecord): string[] {
|
||||
const msg = record.message.join("");
|
||||
const date = new Date(record.timestamp);
|
||||
const time = `${date.getUTCHours().toString().padStart(2, "0")}:${date
|
||||
.getUTCMinutes()
|
||||
.toString()
|
||||
.padStart(
|
||||
2,
|
||||
"0",
|
||||
)}:${date.getUTCSeconds().toString().padStart(2, "0")}.${date
|
||||
.getUTCMilliseconds()
|
||||
.toString()
|
||||
.padStart(3, "0")}`;
|
||||
|
||||
const formattedTime = chalk.gray(time);
|
||||
const formattedLevel = logLevelStyles[record.level](
|
||||
levelAbbreviations[record.level],
|
||||
);
|
||||
const formattedCategory = chalk.gray(record.category.join("\xb7"));
|
||||
const formattedMsg = chalk.reset(msg);
|
||||
|
||||
return [
|
||||
`${formattedTime} ${formattedLevel} ${formattedCategory} ${formattedMsg}`,
|
||||
];
|
||||
}
|
||||
158
packages/logging/index.ts
Normal file
158
packages/logging/index.ts
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
import { mkdir } from "node:fs/promises";
|
||||
import { dirname } from "node:path";
|
||||
import { getFileSink, getRotatingFileSink } from "@logtape/file";
|
||||
import {
|
||||
configure,
|
||||
getConsoleSink,
|
||||
getLevelFilter,
|
||||
getLogger,
|
||||
type Sink,
|
||||
withFilter,
|
||||
} from "@logtape/logtape";
|
||||
import { getSentrySink } from "@logtape/sentry";
|
||||
import * as Sentry from "@sentry/bun";
|
||||
import { config } from "@versia-server/config";
|
||||
import { env } from "bun";
|
||||
import pkg from "../../package.json" with { type: "json" };
|
||||
import { consoleFormatter } from "./formatter.ts";
|
||||
|
||||
if (config.logging.file?.path) {
|
||||
// config.logging.file.path is a path to a file, create the directory if it doesn't exist
|
||||
await mkdir(dirname(config.logging.file.path), { recursive: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all configured sinks depending on the configuration.
|
||||
*/
|
||||
const getSinks = (): Record<"file" | "console" | "sentry", Sink> => {
|
||||
const sinks: Record<string, Sink> = {};
|
||||
|
||||
if (config.logging.file) {
|
||||
if (config.logging.file.rotation) {
|
||||
sinks.file = getRotatingFileSink(config.logging.file.path, {
|
||||
maxFiles: config.logging.file.rotation.max_files,
|
||||
maxSize: config.logging.file.rotation.max_size,
|
||||
});
|
||||
} else {
|
||||
sinks.file = getFileSink(config.logging.file.path);
|
||||
}
|
||||
|
||||
sinks.file = withFilter(
|
||||
sinks.file,
|
||||
getLevelFilter(config.logging.file.log_level),
|
||||
);
|
||||
}
|
||||
|
||||
if (config.logging.sentry) {
|
||||
sinks.sentry = getSentrySink(
|
||||
Sentry.init({
|
||||
dsn: config.logging.sentry.dsn.origin,
|
||||
debug: config.logging.sentry.debug,
|
||||
sampleRate: config.logging.sentry.sample_rate,
|
||||
maxBreadcrumbs: config.logging.sentry.max_breadcrumbs,
|
||||
tracesSampleRate: config.logging.sentry.traces_sample_rate,
|
||||
environment: config.logging.sentry.environment,
|
||||
tracePropagationTargets:
|
||||
config.logging.sentry.trace_propagation_targets,
|
||||
release: env.GIT_COMMIT
|
||||
? `${pkg.version}-${env.GIT_COMMIT}`
|
||||
: pkg.version,
|
||||
integrations: [Sentry.extraErrorDataIntegration()],
|
||||
}),
|
||||
);
|
||||
|
||||
sinks.sentry = withFilter(
|
||||
sinks.sentry,
|
||||
getLevelFilter(config.logging.sentry.log_level),
|
||||
);
|
||||
}
|
||||
|
||||
sinks.console = getConsoleSink({
|
||||
formatter: consoleFormatter,
|
||||
});
|
||||
|
||||
sinks.console = withFilter(
|
||||
sinks.console,
|
||||
getLevelFilter(config.logging.log_level),
|
||||
);
|
||||
|
||||
return sinks;
|
||||
};
|
||||
|
||||
const getSinkNames = (): ("file" | "console" | "sentry")[] => {
|
||||
const names = [] as ("file" | "console" | "sentry")[];
|
||||
|
||||
if (config.logging.file) {
|
||||
names.push("file");
|
||||
}
|
||||
|
||||
if (config.logging.sentry) {
|
||||
names.push("sentry");
|
||||
}
|
||||
|
||||
names.push("console");
|
||||
|
||||
return names;
|
||||
};
|
||||
|
||||
await configure({
|
||||
reset: true,
|
||||
sinks: getSinks(),
|
||||
loggers: [
|
||||
{
|
||||
category: "server",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "inbox"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "delivery"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "bridge"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "resolvers"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["federation", "messaging"],
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: "database",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: "webfinger",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: "sonic",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
{
|
||||
category: ["logtape", "meta"],
|
||||
lowestLevel: "error",
|
||||
},
|
||||
{
|
||||
category: "plugin",
|
||||
sinks: getSinkNames(),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
export const serverLogger = getLogger("server");
|
||||
export const federationInboxLogger = getLogger(["federation", "inbox"]);
|
||||
export const federationDeliveryLogger = getLogger(["federation", "delivery"]);
|
||||
export const federationBridgeLogger = getLogger(["federation", "bridge"]);
|
||||
export const federationResolversLogger = getLogger(["federation", "resolvers"]);
|
||||
export const federationMessagingLogger = getLogger(["federation", "messaging"]);
|
||||
export const databaseLogger = getLogger("database");
|
||||
export const webfingerLogger = getLogger("webfinger");
|
||||
export const sonicLogger = getLogger("sonic");
|
||||
export const pluginLogger = getLogger("plugin");
|
||||
22
packages/logging/package.json
Normal file
22
packages/logging/package.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "@versia-server/logging",
|
||||
"module": "index.ts",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./index.ts",
|
||||
"default": "./index.ts"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@versia-server/config": "workspace:*",
|
||||
"@logtape/logtape": "catalog:",
|
||||
"@logtape/file": "catalog:",
|
||||
"@logtape/sentry": "catalog:",
|
||||
"@logtape/otel": "catalog:",
|
||||
"@sentry/bun": "catalog:",
|
||||
"chalk": "catalog:"
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue