Merged in feature/wrap-logging (pull request #2511)

Feature/wrap logging

* feat: change all logging to go through our own logger function so that we can control log levels

* move packages/trpc to using our own logger

* merge


Approved-by: Linus Flood
This commit is contained in:
Joakim Jäderberg
2025-07-03 12:37:04 +00:00
parent 7e32ed294d
commit daf765f3d5
110 changed files with 681 additions and 441 deletions

View File

@@ -2,6 +2,7 @@ import { type NextRequest, NextResponse } from "next/server"
import { AuthError } from "next-auth"
import { Lang } from "@scandic-hotels/common/constants/language"
import { logger } from "@scandic-hotels/common/logger"
import { env } from "@/env/server"
import { internalServerError } from "@/server/errors/next"
@@ -20,7 +21,7 @@ export async function GET(
const returnUrl = request.headers.get("x-returnurl")
const isSeamless = request.headers.get("x-logout-source") === "seamless"
console.log(
logger.debug(
`[logout] source: ${request.headers.get("x-logout-source") || "normal"}`
)
@@ -32,7 +33,7 @@ export async function GET(
if (returnUrl) {
redirectTo = returnUrl
} else {
console.log(
logger.debug(
`[login] missing returnUrl, using fallback: ${redirectToFallback}`
)
redirectTo = redirectToFallback
@@ -42,9 +43,9 @@ export async function GET(
// Make relative URL to absolute URL
if (redirectTo.startsWith("/")) {
console.log(`[logout] make redirectTo absolute, from ${redirectTo}`)
logger.debug(`[logout] make redirectTo absolute, from ${redirectTo}`)
redirectTo = new URL(redirectTo, publicURL).href
console.log(`[logout] make redirectTo absolute, to ${redirectTo}`)
logger.debug(`[logout] make redirectTo absolute, to ${redirectTo}`)
}
try {
@@ -72,21 +73,21 @@ export async function GET(
break
}
const redirectUrl = new URL(redirectUrlValue)
console.log(
logger.debug(
`[logout] creating redirect to seamless logout: ${redirectUrl}`
)
redirectTo = redirectUrl.toString()
} catch (e) {
console.error(
"Unable to create URL for seamless logout, proceeding without it."
logger.error(
"Unable to create URL for seamless logout, proceeding without it.",
e
)
console.error(e)
}
}
try {
redirectTo = `${env.CURITY_ISSUER_USER}/authn/authenticate/logout?redirect_uri=${encodeURIComponent(redirectTo)}`
console.log(`[logout] final redirectUrl: ${redirectTo}`)
logger.debug(`[logout] final redirectUrl: ${redirectTo}`)
/**
* Passing `redirect: false` to `signOut` will return a result object
@@ -99,16 +100,16 @@ export async function GET(
})
if (redirectUrlObj) {
console.log(`[logout] redirecting to: ${redirectUrlObj.redirect}`)
logger.debug(`[logout] redirecting to: ${redirectUrlObj.redirect}`)
return NextResponse.redirect(redirectUrlObj.redirect)
} else {
console.error(`[logout] missing redirectUrlObj reponse from signOut()`)
logger.error(`[logout] missing redirectUrlObj reponse from signOut()`)
}
} catch (error) {
if (error instanceof AuthError) {
console.log({ signOutAuthError: error })
logger.error("signOutAuthError", { signOutAuthError: error })
} else {
console.log({ signOutError: error })
logger.error("signOutError", { signOutError: error })
}
}

View File

@@ -4,6 +4,8 @@ import * as Sentry from "@sentry/nextjs"
import { useEffect } from "react"
import { useIntl } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
export default function Error({
error,
}: {
@@ -14,7 +16,7 @@ export default function Error({
useEffect(() => {
if (!error) return
console.error({ breadcrumbsError: error })
logger.error("Breadcrumbs Error", { breadcrumbsError: error })
Sentry.captureException(error)
}, [error])

View File

@@ -4,6 +4,8 @@ import * as Sentry from "@sentry/nextjs"
import { useEffect } from "react"
import { useIntl } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
export default function Error({
error,
}: {
@@ -14,7 +16,7 @@ export default function Error({
useEffect(() => {
if (!error) return
console.error(error)
logger.error("My Pages", error)
Sentry.captureException(error)
}, [error])

View File

@@ -4,6 +4,8 @@ import * as Sentry from "@sentry/nextjs"
import { useEffect } from "react"
import { useIntl } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
import { DestinationOverviewPageError } from "@/components/ContentType/DestinationPage/DestinationOverviewPage/error"
export default function Error({
@@ -16,7 +18,7 @@ export default function Error({
useEffect(() => {
if (!error) return
console.error(error)
logger.error("Destination overview page", error)
Sentry.captureException(error)
}, [error])

View File

@@ -1,6 +1,8 @@
import { NextResponse } from "next/server"
import { AuthError } from "next-auth"
import { logger } from "@scandic-hotels/common/logger"
import { dtmcApiCallback } from "@/constants/routes/dtmc"
import { env } from "@/env/server"
import { internalServerError, serviceUnavailable } from "@/server/errors/next"
@@ -21,22 +23,22 @@ export async function GET() {
)
if (redirectUrl) {
console.log(`[dtmc] redirecting to: ${redirectUrl}`)
logger.debug(`[dtmc] redirecting to: ${redirectUrl}`)
return NextResponse.redirect(redirectUrl)
} else {
console.error(`[dtmc] missing redirectUrl response from signIn()`)
logger.error(`[dtmc] missing redirectUrl response from signIn()`)
return internalServerError(
"[dtmc] Missing redirect URL from authentication service"
)
}
} catch (error) {
if (error instanceof AuthError) {
console.error({ signInAuthError: error })
logger.error("signInAuthError", { signInAuthError: error })
return serviceUnavailable(
"[dtmc] Microsoft authentication service unavailable"
)
} else {
console.error({ signInError: error })
logger.error("signInError", { signInError: error })
return internalServerError(
"[dtmc] Unexpected error during authentication"
)

View File

@@ -1,6 +1,7 @@
import { notFound } from "next/navigation"
import { myStay } from "@scandic-hotels/common/constants/routes/myStay"
import { logger } from "@scandic-hotels/common/logger"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import { PaymentCallbackStatusEnum } from "@/constants/booking"
@@ -25,7 +26,7 @@ export default async function GuaranteePaymentCallbackPage(
) {
const searchParams = await props.searchParams
const params = await props.params
console.log(`[gla-payment-callback] callback started`)
logger.debug(`[gla-payment-callback] callback started`)
const lang = params.lang
const status = searchParams.status
const confirmationNumber = searchParams.confirmationNumber
@@ -49,7 +50,7 @@ export default async function GuaranteePaymentCallbackPage(
/>
)
}
console.log(`[gla-payment-callback] redirecting to: ${myStayUrl}`)
logger.debug(`[gla-payment-callback] redirecting to: ${myStayUrl}`)
return <TrackGuarantee status={status} redirectUrl={myStayUrl} />
}
@@ -76,7 +77,7 @@ export default async function GuaranteePaymentCallbackPage(
: BookingErrorCodeEnum.TransactionFailed
)
} catch {
console.error(
logger.error(
`[gla-payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
)
if (status === PaymentCallbackStatusEnum.Cancel) {
@@ -86,7 +87,10 @@ export default async function GuaranteePaymentCallbackPage(
errorMessage = `Failed to get booking status for ${confirmationNumber}, status: ${status}`
}
}
console.log(errorMessage)
if (errorMessage) {
logger.error(errorMessage)
}
if (isAncillaryFlow) {
searchObject.set("ancillary", "ancillary")

View File

@@ -1,5 +1,6 @@
import { notFound } from "next/navigation"
import { logger } from "@scandic-hotels/common/logger"
import { getServiceToken } from "@scandic-hotels/common/tokenManager"
import { BookingErrorCodeEnum } from "@scandic-hotels/trpc/enums/bookingErrorCode"
import { getBooking } from "@scandic-hotels/trpc/routers/booking/utils"
@@ -31,13 +32,13 @@ export default async function PaymentCallbackPage(
) {
const searchParams = await props.searchParams
const params = await props.params
console.log(`[payment-callback] callback started`)
logger.debug(`[payment-callback] callback started`)
const lang = params.lang
const status = searchParams.status
const confirmationNumber = searchParams.confirmationNumber
if (!status || !confirmationNumber) {
console.error(
logger.error(
`[payment-callback] missing status or confirmationNumber in search params`
)
notFound()
@@ -71,7 +72,7 @@ export default async function PaymentCallbackPage(
}
if (!token) {
console.error(
logger.error(
`[payment-callback] no token found for user, cannot fetch booking`
)
notFound()
@@ -89,7 +90,7 @@ export default async function PaymentCallbackPage(
const expire = Math.floor(Date.now() / 1000) + 60
const sig = encrypt(expire.toString())
const confirmationUrl = `${bookingConfirmation(lang)}?RefId=${encodeURIComponent(refId)}`
console.log(
logger.debug(
`[payment-callback] rendering success callback with confirmation number: ${confirmationNumber}`
)
@@ -126,7 +127,7 @@ export default async function PaymentCallbackPage(
: BookingErrorCodeEnum.TransactionFailed
)
} catch {
console.error(
logger.error(
`[payment-callback] failed to get booking status for ${confirmationNumber}, status: ${status}`
)
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)
@@ -135,7 +136,7 @@ export default async function PaymentCallbackPage(
}
if (status === PaymentCallbackStatusEnum.Error) {
console.error(
logger.error(
`[payment-callback] error status received for ${confirmationNumber}, status: ${status}`
)
searchObject.set("errorCode", BookingErrorCodeEnum.TransactionFailed)

View File

@@ -2,6 +2,7 @@ import { type NextRequest, NextResponse } from "next/server"
import { AuthError } from "next-auth"
import { Lang } from "@scandic-hotels/common/constants/language"
import { logger } from "@scandic-hotels/common/logger"
import { env } from "@/env/server"
import { internalServerError } from "@/server/errors/next"
@@ -25,7 +26,7 @@ export async function GET(
const isSeamlessMagicLink =
request.headers.get("x-login-source") === "seamless-magiclink"
console.log(
logger.debug(
`[login] source: ${request.headers.get("x-login-source") || "normal"}`
)
@@ -34,8 +35,8 @@ export async function GET(
request.nextUrl.searchParams.get("redirectTo")
const redirectToFallback = "/"
console.log(`[login] redirectTo cookie value: ${redirectToCookieValue}`)
console.log(
logger.debug(`[login] redirectTo cookie value: ${redirectToCookieValue}`)
logger.debug(
`[login] redirectTo search param value: ${redirectToSearchParamValue}`
)
@@ -43,7 +44,7 @@ export async function GET(
if (returnUrl) {
redirectTo = returnUrl
} else {
console.log(
logger.debug(
`[login] missing returnUrl, using fallback: ${redirectToFallback}`
)
redirectTo = redirectToFallback
@@ -54,9 +55,9 @@ export async function GET(
// Make relative URL to absolute URL
if (redirectTo.startsWith("/")) {
console.log(`[login] make redirectTo absolute, from ${redirectTo}`)
logger.debug(`[login] make redirectTo absolute, from ${redirectTo}`)
redirectTo = new URL(redirectTo, publicURL).href
console.log(`[login] make redirectTo absolute, to ${redirectTo}`)
logger.debug(`[login] make redirectTo absolute, to ${redirectTo}`)
}
// Clean up cookie from authRequired middleware
@@ -90,9 +91,11 @@ export async function GET(
break
}
const redirectUrl = new URL(redirectUrlValue)
console.log(`[login] creating redirect to seamless login: ${redirectUrl}`)
logger.debug(
`[login] creating redirect to seamless login: ${redirectUrl}`
)
redirectUrl.searchParams.set("returnurl", redirectTo)
console.log(
logger.debug(
`[login] returnurl for seamless login: ${redirectUrl.searchParams.get("returnurl")}`
)
redirectTo = redirectUrl.toString()
@@ -105,7 +108,7 @@ export async function GET(
"; Max-Age=300; Path=/; HttpOnly; SameSite=Lax"
)
} catch (e) {
console.error(
logger.error(
"[login] unable to create URL for seamless login, proceeding without it.",
e
)
@@ -113,7 +116,7 @@ export async function GET(
}
try {
console.log(`[login] final redirectUrl: ${redirectTo}`)
logger.debug(`[login] final redirectUrl: ${redirectTo}`)
/** Record<string, any> is next-auth typings */
const params: Record<string, any> = {
@@ -174,16 +177,16 @@ export async function GET(
const redirectOpts = {
headers: redirectHeaders,
}
console.log(`[login] redirecting to: ${redirectUrl}`, redirectOpts)
logger.debug(`[login] redirecting to: ${redirectUrl}`, redirectOpts)
return NextResponse.redirect(redirectUrl, redirectOpts)
} else {
console.error(`[login] missing redirectUrl reponse from signIn()`)
logger.error(`[login] missing redirectUrl reponse from signIn()`)
}
} catch (error) {
if (error instanceof AuthError) {
console.error({ signInAuthError: error })
logger.error("signInAuthError", { signInAuthError: error })
} else {
console.error({ signInError: error })
logger.error("signInError", { signInError: error })
}
}

View File

@@ -1,6 +1,8 @@
import { type NextRequest, NextResponse } from "next/server"
import { AuthError } from "next-auth"
import { logger } from "@scandic-hotels/common/logger"
import { badRequest, internalServerError } from "@/server/errors/next"
import { getPublicURL } from "@/server/utils"
@@ -16,7 +18,7 @@ export async function GET(
const loginKey = request.nextUrl.searchParams.get("loginKey")
if (!loginKey) {
console.log(
logger.debug(
`[verifymagiclink] missing required loginKey, aborting bad request`
)
return badRequest()
@@ -24,14 +26,14 @@ export async function GET(
let redirectTo: string
console.log(`[verifymagiclink] verifying callback`)
logger.debug(`[verifymagiclink] verifying callback`)
const redirectToCookieValue = request.cookies.get(
"magicLinkRedirectTo"
)?.value // Set redirect url from the magicLinkRedirect Cookie which is set when intiating login
const redirectToFallback = "/"
console.log(
logger.debug(
`[verifymagiclink] magicLinkRedirectTo cookie value: ${redirectToCookieValue}`
)
@@ -39,11 +41,11 @@ export async function GET(
// Make relative URL to absolute URL
if (redirectTo.startsWith("/")) {
console.log(
logger.debug(
`[verifymagiclink] make redirectTo absolute, from ${redirectTo}`
)
redirectTo = new URL(redirectTo, publicURL).href
console.log(`[verifymagiclink] make redirectTo absolute, to ${redirectTo}`)
logger.debug(`[verifymagiclink] make redirectTo absolute, to ${redirectTo}`)
}
// Update Seamless login url as Magic link login has a different authenticator in Curity
@@ -59,7 +61,7 @@ export async function GET(
)
try {
console.log(`[verifymagiclink] final redirectUrl: ${redirectTo}`)
logger.debug(`[verifymagiclink] final redirectUrl: ${redirectTo}`)
const params = await context.params
/**
@@ -92,18 +94,18 @@ export async function GET(
)
if (redirectUrl) {
console.log(`[verifymagiclink] redirecting to: ${redirectUrl}`)
logger.debug(`[verifymagiclink] redirecting to: ${redirectUrl}`)
return NextResponse.redirect(redirectUrl)
} else {
console.error(
logger.error(
`[verifymagiclink] missing redirectUrl reponse from signIn()`
)
}
} catch (error) {
if (error instanceof AuthError) {
console.error({ signInAuthError: error })
logger.error("signInAuthError", { signInAuthError: error })
} else {
console.error({ signInError: error })
logger.error("signInError", { signInError: error })
}
}

View File

@@ -5,6 +5,7 @@ import { useParams, useRouter, useSearchParams } from "next/navigation"
import { startTransition, useEffect, useRef } from "react"
import { useIntl } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
import { SESSION_EXPIRED } from "@scandic-hotels/trpc/errors"
import { login } from "@/constants/routes/handleAuth"
@@ -31,14 +32,13 @@ export default function Error({
useEffect(() => {
if (!error) return
console.error(error)
if (error.message === SESSION_EXPIRED) {
const loginUrl = login[params.lang]
window.location.assign(loginUrl)
return
}
logger.error("(live)/error", error)
Sentry.captureException(error)
}, [error, params.lang])

View File

@@ -3,6 +3,8 @@
import * as Sentry from "@sentry/nextjs"
import { useEffect } from "react"
import { logger } from "@scandic-hotels/common/logger"
export default function Error({
error,
}: {
@@ -11,7 +13,7 @@ export default function Error({
useEffect(() => {
if (!error) return
console.error(error)
logger.error("header", error)
Sentry.captureException(error)
}, [error])

View File

@@ -1,5 +1,6 @@
import { notFound } from "next/navigation"
import { logger } from "@scandic-hotels/common/logger"
import { GetCurrentBlockPage } from "@scandic-hotels/trpc/graphql/Query/Current/CurrentBlockPage.graphql"
import { GetCurrentBlockPageTrackingData } from "@scandic-hotels/trpc/graphql/Query/Current/CurrentBlockPageTrackingData.graphql"
import { request } from "@scandic-hotels/trpc/graphql/request"
@@ -34,9 +35,12 @@ export default async function CurrentContentPage(
)
if (!response.data?.all_current_blocks_page?.total) {
console.log("#### DATA ####")
console.log(response.data)
console.log("SearchParams URI: ", searchParams.uri)
logger.debug(
"#### DATA ####",
response.data,
"SearchParams URI: ",
searchParams.uri
)
throw new Error("Not found")
}

View File

@@ -2,6 +2,7 @@ import { cookies } from "next/headers"
import { redirect } from "next/navigation"
import { z } from "zod"
import { logger } from "@scandic-hotels/common/logger"
import { safeTry } from "@scandic-hotels/common/utils/safeTry"
import { SAS_TOKEN_STORAGE_KEY } from "@scandic-hotels/trpc/constants/partnerSAS"
@@ -35,7 +36,7 @@ export async function GET(
})
if (!result.success) {
console.error("[SAS] Invalid search params", result.error)
logger.error("[SAS] Invalid search params", result.error)
redirect(`/${lang}/sas-x-scandic/error?errorCode=invalid_query`)
}
const { code, state } = result.data
@@ -62,7 +63,7 @@ export async function GET(
if (!tokenResponse.ok) {
const error = await tokenResponse.text()
console.error("[SAS] Failed to get token", error)
logger.error("[SAS] Failed to get token", error)
redirect(`/${lang}/sas-x-scandic/error?errorCode=token_error`)
}
@@ -90,7 +91,7 @@ export async function GET(
const caller = await serverClient()
const [data, error] = await safeTry(caller.partner.sas.requestOtp())
if (!data || error) {
console.error("[SAS] Failed to request OTP", error)
logger.error("[SAS] Failed to request OTP", error)
redirect(`/${lang}/sas-x-scandic/error`)
}
@@ -107,7 +108,7 @@ export async function GET(
throw new Error(`Unhandled request OTP status ${data.status}`)
}
console.log("[SAS] Request OTP response", data)
logger.debug("[SAS] Request OTP response", data)
const otpUrl = new URL(
`/${lang}/sas-x-scandic/otp`,

View File

@@ -4,6 +4,7 @@ import * as Sentry from "@sentry/nextjs"
import { useEffect } from "react"
import { useIntl } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
import { Typography } from "@scandic-hotels/design-system/Typography"
import { GenericError } from "./components/GenericError"
@@ -19,7 +20,7 @@ export default function Error({
useEffect(() => {
if (!error) return
console.error(error)
logger.error("sas-x-scandic", error)
Sentry.captureException(error)
}, [error])

View File

@@ -3,6 +3,7 @@ import { redirect } from "next/navigation"
import { z } from "zod"
import { myPages } from "@scandic-hotels/common/constants/routes/myPages"
import { logger } from "@scandic-hotels/common/logger"
import { safeTry } from "@scandic-hotels/common/utils/safeTry"
import {
SAS_TOKEN_STORAGE_KEY,
@@ -173,7 +174,7 @@ async function handleLinkAccount({
const caller = await serverClient()
const [res, error] = await safeTry(caller.partner.sas.linkAccount())
if (!res || error) {
console.error("[SAS] link account error", error)
logger.error("[SAS] link account error", error)
return {
url: `/${lang}/sas-x-scandic/error`,
}
@@ -216,7 +217,7 @@ async function handleUnlinkAccount({
const caller = await serverClient()
const [res, error] = await safeTry(caller.partner.sas.unlinkAccount())
if (!res || error) {
console.error("[SAS] unlink account error", error)
logger.error("[SAS] unlink account error", error)
return {
url: `/${lang}/sas-x-scandic/error`,
}
@@ -266,7 +267,7 @@ async function handleTransferPoints({
)
if (!res || error || res.transferState === "error") {
console.error("[SAS] transfer points error", error)
logger.error("[SAS] transfer points error", error)
return {
url: `/${lang}/sas-x-scandic/error`,
type: "replace",
@@ -274,14 +275,14 @@ async function handleTransferPoints({
}
if (res.transferState === "notLinked") {
console.warn("[SAS] transfer points not linked")
logger.warn("[SAS] transfer points not linked")
return {
url: `/${lang}/sas-x-scandic/link`,
type: "replace",
}
}
console.log("[SAS] transfer points response", res)
logger.debug("[SAS] transfer points response", res)
return {
url: `/${lang}/sas-x-scandic/transfer/success?p=${points}`,

View File

@@ -1,5 +1,7 @@
import { notFound } from "next/navigation"
import { logger } from "@scandic-hotels/common/logger"
import AccountPage from "@/components/Webviews/AccountPage"
import LoyaltyPage from "@/components/Webviews/LoyaltyPage"
@@ -22,7 +24,7 @@ export default async function ContentTypePage(
return <AccountPage />
default:
const type: never = params.contentType
console.error(`Unsupported content type given: ${type}`)
logger.error(`Unsupported content type given: ${type}`)
notFound()
}
}

View File

@@ -1,6 +1,9 @@
import * as Sentry from "@sentry/nextjs"
import { headers } from "next/headers"
import { redirect } from "next/navigation"
import { logger } from "@scandic-hotels/common/logger"
import { getProfile } from "@/lib/trpc/memoizedRequests"
import { getIntl } from "@/i18n"
@@ -17,7 +20,7 @@ export default async function Layout(
const user = await getProfile()
if (!user) {
console.log(`[webview:page] unable to load user`)
logger.debug(`[webview:page] unable to load user`)
return (
<p>
{intl.formatMessage({
@@ -35,7 +38,9 @@ export default async function Layout(
const headersList = await headers()
const returnURL = `/${params.lang}/webview${headersList.get("x-pathname")!}`
const redirectURL = `/${params.lang}/webview/refresh?returnUrl=${encodeURIComponent(returnURL)}`
console.log(`[webview:page] user error, redirecting to: ${redirectURL}`)
logger.debug(
`[webview:page] user error, redirecting to: ${redirectURL}`
)
redirect(redirectURL)
case "notfound":
return (
@@ -55,7 +60,8 @@ export default async function Layout(
)
default:
const u: never = user
console.log("[webview:page] unhandled user loading error", u)
logger.error("[webview:page] unhandled user loading error", u)
Sentry.captureMessage("[webview:page] unhandled user loading error", u)
}
}