refactor(logs): move to core package (#4586)

This commit is contained in:
Meier Lukas
2025-12-16 23:37:44 +01:00
committed by GitHub
parent d86af072bf
commit d348abfe4a
145 changed files with 971 additions and 708 deletions

View File

@@ -11,8 +11,9 @@ import { IntegrationProvider } from "@homarr/auth/client";
import { auth } from "@homarr/auth/next";
import { getIntegrationsWithPermissionsAsync } from "@homarr/auth/server";
import { isNullOrWhitespace } from "@homarr/common";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import type { WidgetKind } from "@homarr/definitions";
import { logger } from "@homarr/log";
import { getI18n } from "@homarr/translation/server";
import { prefetchForKindAsync } from "@homarr/widgets/prefetch";
@@ -22,6 +23,8 @@ import type { Board, Item } from "../_types";
import { DynamicClientBoard } from "./_dynamic-client";
import { BoardContentHeaderActions } from "./_header-actions";
const logger = createLogger({ module: "createBoardContentPage" });
export type Params = Record<string, unknown>;
interface Props<TParams extends Params> {
@@ -57,7 +60,13 @@ export const createBoardContentPage = <TParams extends Record<string, unknown>>(
for (const [kind, items] of itemsMap) {
await prefetchForKindAsync(kind, queryClient, items).catch((error) => {
logger.error(new Error("Failed to prefetch widget", { cause: error }));
logger.error(
new ErrorWithMetadata(
"Failed to prefetch widget",
{ widgetKind: kind, itemCount: items.length },
{ cause: error },
),
);
});
}

View File

@@ -6,7 +6,7 @@ import { TRPCError } from "@trpc/server";
import { auth } from "@homarr/auth/next";
import { BoardProvider } from "@homarr/boards/context";
import { EditModeProvider } from "@homarr/boards/edit-mode";
import { logger } from "@homarr/log";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { MainHeader } from "~/components/layout/header";
import { BoardLogoWithTitle } from "~/components/layout/logo/board-logo";
@@ -18,6 +18,8 @@ import { CustomCss } from "./(content)/_custom-css";
import { BoardReadyProvider } from "./(content)/_ready-context";
import { BoardMantineProvider } from "./(content)/_theme";
const logger = createLogger({ module: "createBoardLayout" });
interface CreateBoardLayoutProps<TParams extends Params> {
headerActions: JSX.Element;
getInitialBoardAsync: (params: TParams) => Promise<Board>;

View File

@@ -2,8 +2,8 @@
import { Select } from "@mantine/core";
import type { LogLevel } from "@homarr/log/constants";
import { logLevelConfiguration, logLevels } from "@homarr/log/constants";
import type { LogLevel } from "@homarr/core/infrastructure/logs/constants";
import { logLevelConfiguration, logLevels } from "@homarr/core/infrastructure/logs/constants";
import { useI18n } from "@homarr/translation/client";
import { useLogContext } from "./log-context";

View File

@@ -3,8 +3,8 @@
import type { PropsWithChildren } from "react";
import { createContext, useContext, useMemo, useState } from "react";
import type { LogLevel } from "@homarr/log/constants";
import { logLevels } from "@homarr/log/constants";
import type { LogLevel } from "@homarr/core/infrastructure/logs/constants";
import { logLevels } from "@homarr/core/infrastructure/logs/constants";
const LogContext = createContext<{
level: LogLevel;

View File

@@ -7,7 +7,7 @@ import "@xterm/xterm/css/xterm.css";
import { notFound } from "next/navigation";
import { auth } from "@homarr/auth/next";
import { env } from "@homarr/log/env";
import { logsEnv } from "@homarr/core/infrastructure/logs/env";
import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb";
import { fullHeightWithoutHeaderAndFooter } from "~/constants";
@@ -35,7 +35,7 @@ export default async function LogsManagementPage() {
}
return (
<LogContextProvider defaultLevel={env.LOG_LEVEL}>
<LogContextProvider defaultLevel={logsEnv.LEVEL}>
<Group justify="space-between" align="center" wrap="nowrap">
<DynamicBreadcrumb />
<LogLevelSelection />

View File

@@ -6,9 +6,12 @@ import { appRouter, createTRPCContext } from "@homarr/api";
import type { Session } from "@homarr/auth";
import { hashPasswordAsync } from "@homarr/auth";
import { createSessionAsync } from "@homarr/auth/server";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import { db, eq } from "@homarr/db";
import { apiKeys } from "@homarr/db/schema";
import { logger } from "@homarr/log";
const logger = createLogger({ module: "trpcOpenApiRoute" });
const handlerAsync = async (req: NextRequest) => {
const apiKeyHeaderValue = req.headers.get("ApiKey");
@@ -27,7 +30,7 @@ const handlerAsync = async (req: NextRequest) => {
router: appRouter,
createContext: () => createTRPCContext({ session, headers: req.headers }),
onError({ error, path, type }) {
logger.error(new Error(`tRPC Error with ${type} on '${path}'`, { cause: error.cause }));
logger.error(new ErrorWithMetadata("tRPC Error occured", { path, type }, { cause: error }));
},
});
};
@@ -48,9 +51,10 @@ const getSessionOrDefaultFromHeadersAsync = async (
const [apiKeyId, apiKey] = apiKeyHeaderValue.split(".");
if (!apiKeyId || !apiKey) {
logger.warn(
`An attempt to authenticate over API has failed due to invalid API key format ip='${ipAdress}' userAgent='${userAgent}'`,
);
logger.warn("An attempt to authenticate over API has failed due to invalid API key format", {
ipAdress,
userAgent,
});
return null;
}
@@ -74,18 +78,21 @@ const getSessionOrDefaultFromHeadersAsync = async (
});
if (!apiKeyFromDb) {
logger.warn(`An attempt to authenticate over API has failed ip='${ipAdress}' userAgent='${userAgent}'`);
logger.warn("An attempt to authenticate over API has failed", { ipAdress, userAgent });
return null;
}
const hashedApiKey = await hashPasswordAsync(apiKey, apiKeyFromDb.salt);
if (apiKeyFromDb.apiKey !== hashedApiKey) {
logger.warn(`An attempt to authenticate over API has failed ip='${ipAdress}' userAgent='${userAgent}'`);
logger.warn("An attempt to authenticate over API has failed", { ipAdress, userAgent });
return null;
}
logger.info(`Read session from API request and found user ${apiKeyFromDb.user.name} (${apiKeyFromDb.user.id})`);
logger.info("Read session from API request and found user", {
name: apiKeyFromDb.user.name,
id: apiKeyFromDb.user.id,
});
return await createSessionAsync(db, apiKeyFromDb.user);
};

View File

@@ -1,8 +1,10 @@
import { NextRequest } from "next/server";
import { createHandlersAsync } from "@homarr/auth";
import { createLogger } from "@homarr/core/infrastructure/logs";
import type { SupportedAuthProvider } from "@homarr/definitions";
import { logger } from "@homarr/log";
const logger = createLogger({ module: "nextAuthRoute" });
export const GET = async (req: NextRequest) => {
const { handlers } = await createHandlersAsync(extractProvider(req), isSecureCookieEnabled(req));

View File

@@ -1,13 +1,16 @@
import { performance } from "perf_hooks";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
import { db } from "@homarr/db";
import { logger } from "@homarr/log";
import { handshakeAsync } from "@homarr/redis";
const logger = createLogger({ module: "healthLiveRoute" });
export async function GET() {
const timeBeforeHealthCheck = performance.now();
const response = await executeAndAggregateAllHealthChecksAsync();
logger.info(`Completed healthcheck after ${performance.now() - timeBeforeHealthCheck}ms`);
logger.info("Completed healthcheck", { elapsed: `${performance.now() - timeBeforeHealthCheck}ms` });
if (response.status === "healthy") {
return new Response(JSON.stringify(response), {
@@ -73,7 +76,7 @@ const executeHealthCheckSafelyAsync = async (
};
} catch (error) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
logger.error(`Healthcheck '${name}' has failed: ${error}`);
logger.error(new ErrorWithMetadata("Healthcheck failed", { name }, { cause: error }));
return {
status: "unhealthy",
values: {

View File

@@ -3,7 +3,10 @@ import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter, createTRPCContext } from "@homarr/api";
import { trpcPath } from "@homarr/api/shared";
import { auth } from "@homarr/auth/next";
import { logger } from "@homarr/log";
import { createLogger } from "@homarr/core/infrastructure/logs";
import { ErrorWithMetadata } from "@homarr/core/infrastructure/logs/error";
const logger = createLogger({ module: "trpcRoute" });
/**
* Configure basic CORS headers
@@ -31,7 +34,7 @@ const handler = auth(async (req) => {
req,
createContext: () => createTRPCContext({ session: req.auth, headers: req.headers }),
onError({ error, path, type }) {
logger.error(new Error(`tRPC Error with ${type} on '${path}'`, { cause: error.cause }));
logger.error(new ErrorWithMetadata("tRPC Error occured", { path, type }, { cause: error }));
},
});

View File

@@ -3,7 +3,9 @@ import "server-only";
import { notFound, redirect } from "next/navigation";
import { TRPCError } from "@trpc/server";
import { logger } from "@homarr/log";
import { createLogger } from "@homarr/core/infrastructure/logs";
const logger = createLogger({ module: "trpcCatchError" });
export const catchTrpcNotFound = (err: unknown) => {
if (err instanceof TRPCError && err.code === "NOT_FOUND") {