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 { z } from "zod" import { z } from "zod"
import { logger } from "@scandic-hotels/common/logger"
import { phoneValidator } from "@scandic-hotels/common/utils/zod/phoneValidator" import { phoneValidator } from "@scandic-hotels/common/utils/zod/phoneValidator"
import * as api from "@scandic-hotels/trpc/api" import * as api from "@scandic-hotels/trpc/api"
import { ApiLang } from "@scandic-hotels/trpc/constants/apiLang" import { ApiLang } from "@scandic-hotels/trpc/constants/apiLang"
@@ -47,7 +48,7 @@ export const editProfile = protectedServerActionProcedure
const intl = await getIntl() const intl = await getIntl()
const payload = editProfilePayload.safeParse(input) const payload = editProfilePayload.safeParse(input)
if (!payload.success) { if (!payload.success) {
console.error( logger.error(
"editProfile payload validation error", "editProfile payload validation error",
JSON.stringify({ JSON.stringify({
query: input, query: input,
@@ -70,7 +71,7 @@ export const editProfile = protectedServerActionProcedure
const profile = await getProfile() const profile = await getProfile()
if (!profile || "error" in profile) { if (!profile || "error" in profile) {
console.error( logger.error(
"editProfile profile fetch error", "editProfile profile fetch error",
JSON.stringify({ JSON.stringify({
query: input, query: input,
@@ -144,7 +145,7 @@ export const editProfile = protectedServerActionProcedure
status: Status.success, status: Status.success,
} }
} else { } else {
console.log( logger.debug(
`[edit profile: ${profile.membershipNumber}] body keys: ${JSON.stringify(Object.keys(body))}` `[edit profile: ${profile.membershipNumber}] body keys: ${JSON.stringify(Object.keys(body))}`
) )
} }
@@ -159,7 +160,7 @@ export const editProfile = protectedServerActionProcedure
if (!apiResponse.ok) { if (!apiResponse.ok) {
const text = await apiResponse.text() const text = await apiResponse.text()
console.error( logger.error(
"editProfile api patch error", "editProfile api patch error",
JSON.stringify({ JSON.stringify({
query: input, query: input,
@@ -184,7 +185,7 @@ export const editProfile = protectedServerActionProcedure
const json = await apiResponse.json() const json = await apiResponse.json()
if (json.errors?.length) { if (json.errors?.length) {
json.errors.forEach((error: any) => { json.errors.forEach((error: any) => {
console.warn( logger.warn(
"editProfile api patch errors (not aborting)", "editProfile api patch errors (not aborting)",
JSON.stringify({ JSON.stringify({
query: input, query: input,
@@ -196,7 +197,7 @@ export const editProfile = protectedServerActionProcedure
const validatedData = editProfileSchema.safeParse(json.data.attributes) const validatedData = editProfileSchema.safeParse(json.data.attributes)
if (!validatedData.success) { if (!validatedData.success) {
console.error( logger.error(
"editProfile validation error", "editProfile validation error",
JSON.stringify({ JSON.stringify({
query: input, query: input,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import { logger } from "@scandic-hotels/common/logger"
import { GetCurrentBlockPage } from "@scandic-hotels/trpc/graphql/Query/Current/CurrentBlockPage.graphql" import { GetCurrentBlockPage } from "@scandic-hotels/trpc/graphql/Query/Current/CurrentBlockPage.graphql"
import { GetCurrentBlockPageTrackingData } from "@scandic-hotels/trpc/graphql/Query/Current/CurrentBlockPageTrackingData.graphql" import { GetCurrentBlockPageTrackingData } from "@scandic-hotels/trpc/graphql/Query/Current/CurrentBlockPageTrackingData.graphql"
import { request } from "@scandic-hotels/trpc/graphql/request" 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) { if (!response.data?.all_current_blocks_page?.total) {
console.log("#### DATA ####") logger.debug(
console.log(response.data) "#### DATA ####",
console.log("SearchParams URI: ", searchParams.uri) response.data,
"SearchParams URI: ",
searchParams.uri
)
throw new Error("Not found") throw new Error("Not found")
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import { NextResponse } from "next/server" import { NextResponse } from "next/server"
import { logger } from "@scandic-hotels/common/logger"
import { env } from "@/env/server" import { env } from "@/env/server"
import { auth } from "@/auth" import { auth } from "@/auth"
@@ -11,6 +13,6 @@ export const GET = async () => {
} }
const user = await auth() const user = await auth()
console.log("[DEBUG] access-token", user?.token) logger.debug("[DEBUG] access-token", user?.token)
return NextResponse.json(user) return NextResponse.json(user)
} }

View File

@@ -1,5 +1,6 @@
import { type NextRequest, NextResponse } from "next/server" import { type NextRequest, NextResponse } from "next/server"
import { logger } from "@scandic-hotels/common/logger"
import { languageSchema } from "@scandic-hotels/common/utils/languages" import { languageSchema } from "@scandic-hotels/common/utils/languages"
import { env } from "@/env/server" import { env } from "@/env/server"
@@ -9,7 +10,7 @@ export const dynamic = "force-dynamic"
export async function GET(request: NextRequest) { export async function GET(request: NextRequest) {
if (!env.ENABLE_WARMUP_HOTEL) { if (!env.ENABLE_WARMUP_HOTEL) {
console.log("[WARMUP] Warmup hotel data is disabled") logger.info("[WARMUP] Warmup hotel data is disabled")
return NextResponse.json( return NextResponse.json(
{ message: "Warmup hotel data is disabled" }, { message: "Warmup hotel data is disabled" },
{ status: 200 } { status: 200 }
@@ -32,7 +33,7 @@ export async function GET(request: NextRequest) {
}) })
return NextResponse.json(hotels) return NextResponse.json(hotels)
} catch (error) { } catch (error) {
console.error("[WARMUP] error", error) logger.error("[WARMUP] error", error)
return NextResponse.json( return NextResponse.json(
{ {
error: "Failed to fetch all hotels", error: "Failed to fetch all hotels",

View File

@@ -1,5 +1,6 @@
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { profile } from "@scandic-hotels/common/constants/routes/myPages" import { profile } from "@scandic-hotels/common/constants/routes/myPages"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { serverClient } from "@/lib/trpc/server" import { serverClient } from "@/lib/trpc/server"
import { getPublicURL } from "@/server/utils" import { getPublicURL } from "@/server/utils"
@@ -10,10 +11,12 @@ export async function GET(
request: NextRequest, request: NextRequest,
props: { params: Promise<{ lang: string }> } props: { params: Promise<{ lang: string }> }
) { ) {
const addCardLogger = createLogger("add-card")
const params = await props.params const params = await props.params
const publicURL = getPublicURL(request) const publicURL = getPublicURL(request)
console.log(`[add-card] callback started`) addCardLogger.debug(`[add-card] callback started`)
const lang = params.lang as Lang const lang = params.lang as Lang
const returnUrl = new URL(`${publicURL}/${profile[lang ?? Lang.en]}`) const returnUrl = new URL(`${publicURL}/${profile[lang ?? Lang.en]}`)
@@ -32,28 +35,28 @@ export async function GET(
}) })
if (saveCardSuccess) { if (saveCardSuccess) {
console.log(`[add-card] planet success: card saved success`) addCardLogger.debug(`[add-card] planet success: card saved success`)
returnUrl.searchParams.set("success", "true") returnUrl.searchParams.set("success", "true")
} else { } else {
console.log(`[add-card] planet success: card saved fail`) addCardLogger.debug(`[add-card] planet success: card saved fail`)
returnUrl.searchParams.set("failure", "true") returnUrl.searchParams.set("failure", "true")
} }
} else { } else {
console.log(`[add-card] planet success: missing datatransTrxId`) addCardLogger.debug(`[add-card] planet success: missing datatransTrxId`)
returnUrl.searchParams.set("error", "true") returnUrl.searchParams.set("error", "true")
} }
} else if (failure) { } else if (failure) {
console.log(`[add-card] planet fail`) addCardLogger.debug(`[add-card] planet fail`)
returnUrl.searchParams.set("failure", "true") returnUrl.searchParams.set("failure", "true")
} else if (cancel) { } else if (cancel) {
console.log(`[add-card] planet cancel`) addCardLogger.debug(`[add-card] planet cancel`)
returnUrl.searchParams.set("cancel", "true") returnUrl.searchParams.set("cancel", "true")
} }
} catch (e) { } catch (e) {
console.error(`[add-card] error saving credit card`, e) addCardLogger.error(`[add-card] error saving credit card`, e)
returnUrl.searchParams.set("error", "true") returnUrl.searchParams.set("error", "true")
} }
console.log(`[add-card] redirecting to: ${returnUrl}`) addCardLogger.debug(`[add-card] redirecting to: ${returnUrl}`)
return Response.redirect(returnUrl) return Response.redirect(returnUrl)
} }

View File

@@ -1,6 +1,7 @@
import { type NextRequest, NextResponse } from "next/server" import { type NextRequest, NextResponse } from "next/server"
import { overview } from "@scandic-hotels/common/constants/routes/myPages" import { overview } from "@scandic-hotels/common/constants/routes/myPages"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import * as api from "@scandic-hotels/trpc/api" import * as api from "@scandic-hotels/trpc/api"
import { isValidSession } from "@scandic-hotels/trpc/utils/session" import { isValidSession } from "@scandic-hotels/trpc/utils/session"
@@ -13,6 +14,7 @@ import { auth } from "@/auth"
import { auth as dtmcAuth } from "@/auth.dtmc" import { auth as dtmcAuth } from "@/auth.dtmc"
import { getLang } from "@/i18n/serverContext" import { getLang } from "@/i18n/serverContext"
const dtmcLogger = createLogger("dtmc")
interface LinkEmployeeSuccessResult { interface LinkEmployeeSuccessResult {
success: true success: true
} }
@@ -32,7 +34,7 @@ async function linkEmployeeToUser(
employeeId: string, employeeId: string,
accessToken: string accessToken: string
): Promise<LinkEmployeeResult> { ): Promise<LinkEmployeeResult> {
console.log(`[dtmc] Linking employee ID ${employeeId}`) dtmcLogger.debug(`Linking employee ID ${employeeId}`)
let response: Response let response: Response
try { try {
response = await api.post( response = await api.post(
@@ -45,7 +47,7 @@ async function linkEmployeeToUser(
} }
) )
} catch (networkError) { } catch (networkError) {
console.error("[dtmc] Network error during API request:", networkError) dtmcLogger.error("Network error during API request:", networkError)
return { return {
success: false, success: false,
statusCode: 0, statusCode: 0,
@@ -53,17 +55,17 @@ async function linkEmployeeToUser(
} }
if (!response.ok) { if (!response.ok) {
console.error(`[dtmc] API returned error status ${response.status}`) dtmcLogger.error(`API returned error status ${response.status}`)
try { try {
const errorResponse = await response.json() const errorResponse = await response.json()
console.error(`[dtmc] API error response:`, errorResponse) dtmcLogger.error(`API error response:`, errorResponse)
} catch (parseError) { } catch (parseError) {
console.warn(`[dtmc] Could not parse API error response:`, parseError) dtmcLogger.warn(`Could not parse API error response:`, parseError)
try { try {
const errorText = await response.text() const errorText = await response.text()
console.error(`[dtmc] Raw error response:`, errorText) dtmcLogger.error(`Raw error response:`, errorText)
} catch { } catch {
console.error(`[dtmc] Could not read error response body`) dtmcLogger.error(`Could not read error response body`)
} }
} }
@@ -87,19 +89,19 @@ async function linkEmployeeToUser(
} }
} }
console.log(`[dtmc] API call successful - Status: ${response.status}`) dtmcLogger.debug(`API call successful - Status: ${response.status}`)
console.log( dtmcLogger.debug(
`[dtmc] Response headers:`, `Response headers:`,
Object.fromEntries(response.headers.entries()) Object.fromEntries(response.headers.entries())
) )
try { try {
const responseBody = await response.json() const responseBody = await response.json()
console.log(`[dtmc] Response body:`, responseBody) dtmcLogger.debug(`Response body:`, responseBody)
} catch (parseError) { } catch (parseError) {
console.warn(`[dtmc] Could not parse success response body:`, parseError) dtmcLogger.warn(`Could not parse success response body:`, parseError)
} }
console.log(`[dtmc] Successfully linked employee ID ${employeeId}`) dtmcLogger.debug(`Successfully linked employee ID ${employeeId}`)
return { success: true } return { success: true }
} }
@@ -114,20 +116,18 @@ export async function GET(request: NextRequest) {
const dtmcSession = await dtmcAuth() const dtmcSession = await dtmcAuth()
const session = await auth() const session = await auth()
const baseUrl = getPublicURL(request) const baseUrl = getPublicURL(request)
console.log("[dtmc] DTMC Callback handler - using baseUrl:", baseUrl) dtmcLogger.debug("DTMC Callback handler - using baseUrl:", baseUrl)
if (!isValidSession(session)) { if (!isValidSession(session)) {
console.error( dtmcLogger.error("DTMC Callback handler - No valid user session found")
"[dtmc] DTMC Callback handler - No valid user session found"
)
const errorUrl = new URL(linkEmploymentError[lang], baseUrl) const errorUrl = new URL(linkEmploymentError[lang], baseUrl)
errorUrl.searchParams.set("error", "no_session") errorUrl.searchParams.set("error", "no_session")
return NextResponse.redirect(errorUrl) return NextResponse.redirect(errorUrl)
} }
if (!isValidSession(dtmcSession)) { if (!isValidSession(dtmcSession)) {
console.error( dtmcLogger.error(
"[dtmc] DTMC Callback handler - No valid entra id session found" "DTMC Callback handler - No valid entra id session found"
) )
const errorUrl = new URL(linkEmploymentError[lang], baseUrl) const errorUrl = new URL(linkEmploymentError[lang], baseUrl)
errorUrl.searchParams.set("error", "no_entra_id_session") errorUrl.searchParams.set("error", "no_entra_id_session")
@@ -136,40 +136,40 @@ export async function GET(request: NextRequest) {
const employeeId = dtmcSession.employeeId const employeeId = dtmcSession.employeeId
console.log( dtmcLogger.debug(
"[dtmc] DTMC Callback handler - Extracted employeeId:", "DTMC Callback handler - Extracted employeeId:",
employeeId employeeId
) )
if (!employeeId) { if (!employeeId) {
console.error("[dtmc] DTMC Callback handler - No employeeId in session") dtmcLogger.error("DTMC Callback handler - No employeeId in session")
const errorUrl = new URL(linkEmploymentError[lang], baseUrl) const errorUrl = new URL(linkEmploymentError[lang], baseUrl)
errorUrl.searchParams.set("error", "missing_employee_id") errorUrl.searchParams.set("error", "missing_employee_id")
return NextResponse.redirect(errorUrl) return NextResponse.redirect(errorUrl)
} }
console.log( dtmcLogger.debug(
"[dtmc] DTMC Callback handler - Calling linkEmployeeToUser with ID:", "DTMC Callback handler - Calling linkEmployeeToUser with ID:",
employeeId employeeId
) )
const accessToken = session.token.access_token const accessToken = session.token.access_token
if (!accessToken) { if (!accessToken) {
console.error("[dtmc] DTMC Callback handler - No access token in session") dtmcLogger.error("DTMC Callback handler - No access token in session")
const errorUrl = new URL(linkEmploymentError[lang], baseUrl) const errorUrl = new URL(linkEmploymentError[lang], baseUrl)
errorUrl.searchParams.set("error", "missing_access_token") errorUrl.searchParams.set("error", "missing_access_token")
return NextResponse.redirect(errorUrl) return NextResponse.redirect(errorUrl)
} }
const result = await linkEmployeeToUser(employeeId, accessToken) const result = await linkEmployeeToUser(employeeId, accessToken)
console.log( dtmcLogger.debug(
"[dtmc] DTMC Callback handler - linkEmployeeToUser result:", "DTMC Callback handler - linkEmployeeToUser result:",
result result
) )
if (!result.success) { if (!result.success) {
console.error( dtmcLogger.error(
"[dtmc] DTMC Callback handler - Failed to verify employment:", "DTMC Callback handler - Failed to verify employment:",
`Status: ${result.statusCode}, Error: ${result.queryParam}` `Status: ${result.statusCode}, Error: ${result.queryParam}`
) )
@@ -182,21 +182,21 @@ export async function GET(request: NextRequest) {
return NextResponse.redirect(errorUrl) return NextResponse.redirect(errorUrl)
} }
console.log( dtmcLogger.debug(
"[dtmc] DTMC Callback handler - Success! Employee linked with ID:", "DTMC Callback handler - Success! Employee linked with ID:",
employeeId employeeId
) )
console.log("[dtmc] overview[lang]:", overview[lang]) dtmcLogger.debug("overview[lang]:", overview[lang])
const successUrl = new URL(overview[lang], baseUrl) const successUrl = new URL(overview[lang], baseUrl)
successUrl.searchParams.set(DTMC_SUCCESS_BANNER_KEY, "true") successUrl.searchParams.set(DTMC_SUCCESS_BANNER_KEY, "true")
console.log( dtmcLogger.debug(
"[dtmc] DTMC Callback handler - Redirecting to success URL:", "DTMC Callback handler - Redirecting to success URL:",
successUrl.toString() successUrl.toString()
) )
return NextResponse.redirect(successUrl) return NextResponse.redirect(successUrl)
} catch (error) { } catch (error) {
console.error("[dtmc] DTMC Callback handler - Error in handler:", error) dtmcLogger.error("DTMC Callback handler - Error in handler:", error)
return internalServerError() return internalServerError()
} }
} }

View File

@@ -4,6 +4,7 @@ import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { generateHotelUrlTag } from "@scandic-hotels/trpc/utils/generateTag" import { generateHotelUrlTag } from "@scandic-hotels/trpc/utils/generateTag"
import { env } from "@/env/server" import { env } from "@/env/server"
@@ -24,22 +25,25 @@ const validateJsonBody = z.object({
}), }),
}) })
const revalidateHotelLogger = createLogger("revalidate-hotel")
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
try { try {
const headersList = await headers() const headersList = await headers()
const secret = headersList.get("x-revalidate-secret") const secret = headersList.get("x-revalidate-secret")
if (secret !== env.REVALIDATE_SECRET) { if (secret !== env.REVALIDATE_SECRET) {
console.error(`Invalid Secret`) revalidateHotelLogger.error(`Invalid Secret`, { secret })
console.error({ secret })
return badRequest({ revalidated: false, now: Date.now() }) return badRequest({ revalidated: false, now: Date.now() })
} }
const data = await request.json() const data = await request.json()
const validatedData = validateJsonBody.safeParse(data) const validatedData = validateJsonBody.safeParse(data)
if (!validatedData.success) { if (!validatedData.success) {
console.error("Bad validation for `validatedData` in hotel revalidation") revalidateHotelLogger.error(
console.error(validatedData.error) "Bad validation for `validatedData` in hotel revalidation",
validatedData.error
)
return internalServerError({ revalidated: false, now: Date.now() }) return internalServerError({ revalidated: false, now: Date.now() })
} }
@@ -56,21 +60,20 @@ export async function POST(request: NextRequest) {
if (content_type.uid === "hotel_page") { if (content_type.uid === "hotel_page") {
tag = generateHotelUrlTag(locale, entry.hotel_page_id) tag = generateHotelUrlTag(locale, entry.hotel_page_id)
} else { } else {
console.error( revalidateHotelLogger.error(
`Invalid content_type, received ${content_type.uid}, expected "hotel_page"` `Invalid content_type, received ${content_type.uid}, expected "hotel_page"`
) )
return notFound({ revalidated: false, now: Date.now() }) return notFound({ revalidated: false, now: Date.now() })
} }
console.info(`Revalidating hotel url tag: ${tag}`) revalidateHotelLogger.info(`Revalidating hotel url tag: ${tag}`)
revalidateTag(tag) revalidateTag(tag)
const cacheClient = await getCacheClient() const cacheClient = await getCacheClient()
await cacheClient.deleteKey(tag, { fuzzy: true }) await cacheClient.deleteKey(tag, { fuzzy: true })
return Response.json({ revalidated: true, now: Date.now() }) return Response.json({ revalidated: true, now: Date.now() })
} catch (error) { } catch (error) {
console.error("Failed to revalidate tag(s) for hotel") revalidateHotelLogger.error("Failed to revalidate tag(s) for hotel", error)
console.error(error)
return internalServerError({ revalidated: false, now: Date.now() }) return internalServerError({ revalidated: false, now: Date.now() })
} }
} }

View File

@@ -4,6 +4,7 @@ import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { generateLoyaltyConfigTag } from "@scandic-hotels/trpc/utils/generateTag" import { generateLoyaltyConfigTag } from "@scandic-hotels/trpc/utils/generateTag"
import { env } from "@/env/server" import { env } from "@/env/server"
@@ -16,6 +17,8 @@ enum LoyaltyConfigContentTypes {
reward = "reward", reward = "reward",
} }
const loyaltyRevalidateLogger = createLogger("loyalty-revalidate")
const validateJsonBody = z.object({ const validateJsonBody = z.object({
data: z.object({ data: z.object({
content_type: z.object({ content_type: z.object({
@@ -35,18 +38,18 @@ export async function POST(request: NextRequest) {
const secret = headersList.get("x-revalidate-secret") const secret = headersList.get("x-revalidate-secret")
if (secret !== env.REVALIDATE_SECRET) { if (secret !== env.REVALIDATE_SECRET) {
console.error(`Invalid Secret`) loyaltyRevalidateLogger.error(`Invalid Secret`, { secret })
console.error({ secret })
return badRequest({ revalidated: false, now: Date.now() }) return badRequest({ revalidated: false, now: Date.now() })
} }
const data = await request.json() const data = await request.json()
const validatedData = validateJsonBody.safeParse(data) const validatedData = validateJsonBody.safeParse(data)
if (!validatedData.success) { if (!validatedData.success) {
console.error( loyaltyRevalidateLogger.error(
"Bad validation for `validatedData` in loyaltyConfig revalidation" "Bad validation for `validatedData` in loyaltyConfig revalidation",
validatedData.error
) )
console.error(validatedData.error)
return internalServerError({ revalidated: false, now: Date.now() }) return internalServerError({ revalidated: false, now: Date.now() })
} }
@@ -76,11 +79,11 @@ export async function POST(request: NextRequest) {
entry.reward_id entry.reward_id
) )
} else { } else {
console.error("Invalid content_type") loyaltyRevalidateLogger.error("Invalid content_type")
return notFound({ revalidated: false, now: Date.now() }) return notFound({ revalidated: false, now: Date.now() })
} }
console.info(`Revalidating loyalty config tag: ${tag}`) loyaltyRevalidateLogger.info(`Revalidating loyalty config tag: ${tag}`)
revalidateTag(tag) revalidateTag(tag)
const cacheClient = await getCacheClient() const cacheClient = await getCacheClient()
@@ -88,8 +91,10 @@ export async function POST(request: NextRequest) {
return Response.json({ revalidated: true, now: Date.now() }) return Response.json({ revalidated: true, now: Date.now() })
} catch (error) { } catch (error) {
console.error("Failed to revalidate tag(s) for loyalty config") loyaltyRevalidateLogger.error(
console.error(error) "Failed to revalidate tag(s) for loyalty config",
error
)
return internalServerError({ revalidated: false, now: Date.now() }) return internalServerError({ revalidated: false, now: Date.now() })
} }
} }

View File

@@ -2,6 +2,7 @@ import { revalidateTag } from "next/cache"
import { headers } from "next/headers" import { headers } from "next/headers"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { generateTag } from "@scandic-hotels/trpc/utils/generateTag" import { generateTag } from "@scandic-hotels/trpc/utils/generateTag"
import { env } from "@/env/server" import { env } from "@/env/server"
@@ -9,6 +10,7 @@ import { badRequest, internalServerError } from "@/server/errors/next"
import type { Lang } from "@scandic-hotels/common/constants/language" import type { Lang } from "@scandic-hotels/common/constants/language"
const revalidateManuallyLogger = createLogger("revalidate-manually")
// This file is primarily to be used locally to test // This file is primarily to be used locally to test
// purging your cache for new (and old) requests // purging your cache for new (and old) requests
export async function POST() { export async function POST() {
@@ -17,8 +19,7 @@ export async function POST() {
const secret = headersList.get("x-revalidate-secret") const secret = headersList.get("x-revalidate-secret")
if (secret !== env.REVALIDATE_SECRET) { if (secret !== env.REVALIDATE_SECRET) {
console.error(`Invalid Secret`) revalidateManuallyLogger.error(`Invalid Secret`, { secret })
console.error({ secret })
return badRequest({ return badRequest({
now: Date.now(), now: Date.now(),
revalidated: false, revalidated: false,
@@ -30,8 +31,8 @@ export async function POST() {
const lang = headersList.get("x-lang") const lang = headersList.get("x-lang")
if (!lang || !identifier) { if (!lang || !identifier) {
console.info(`Missing lang and/or identifier`) revalidateManuallyLogger.info(`Missing lang and/or identifier`)
console.info(`lang: ${lang}, identifier: ${identifier}`) revalidateManuallyLogger.info(`lang: ${lang}, identifier: ${identifier}`)
return badRequest({ return badRequest({
now: Date.now(), now: Date.now(),
revalidated: false, revalidated: false,
@@ -42,18 +43,17 @@ export async function POST() {
const tag = generateTag(lang as Lang, identifier, affix) const tag = generateTag(lang as Lang, identifier, affix)
console.info( revalidateManuallyLogger.info(
`Revalidated tag for [lang: ${lang}, identifier: ${identifier}${affix ? `, affix: ${affix}` : ""}]` `Revalidated tag for [lang: ${lang}, identifier: ${identifier}${affix ? `, affix: ${affix}` : ""}]`
) )
console.info(`Tag: ${tag}`) revalidateManuallyLogger.info(`Tag: ${tag}`)
revalidateTag(tag) revalidateTag(tag)
cacheClient.deleteKey(tag, { fuzzy: true }) cacheClient.deleteKey(tag, { fuzzy: true })
return Response.json({ revalidated: true, now: Date.now() }) return Response.json({ revalidated: true, now: Date.now() })
} catch (error) { } catch (error) {
console.error("Failed to revalidate tag(s)") revalidateManuallyLogger.error("Failed to revalidate tag(s)", error)
console.error(error)
return internalServerError({ revalidated: false, now: Date.now() }) return internalServerError({ revalidated: false, now: Date.now() })
} }
} }

View File

@@ -4,6 +4,7 @@ import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { affix as breadcrumbsAffix } from "@scandic-hotels/trpc/routers/contentstack/breadcrumbs/utils" import { affix as breadcrumbsAffix } from "@scandic-hotels/trpc/routers/contentstack/breadcrumbs/utils"
import { destinationCityPageDestinationSettingsSchema } from "@scandic-hotels/trpc/routers/contentstack/destinationCityPage/output" import { destinationCityPageDestinationSettingsSchema } from "@scandic-hotels/trpc/routers/contentstack/destinationCityPage/output"
import { languageSwitcherAffix } from "@scandic-hotels/trpc/routers/contentstack/languageSwitcher/utils" import { languageSwitcherAffix } from "@scandic-hotels/trpc/routers/contentstack/languageSwitcher/utils"
@@ -47,14 +48,14 @@ const validateJsonBody = z.object({
}), }),
}) })
const revalidateLogger = createLogger("revalidate")
export async function POST(request: NextRequest) { export async function POST(request: NextRequest) {
try { try {
const headersList = await headers() const headersList = await headers()
const secret = headersList.get("x-revalidate-secret") const secret = headersList.get("x-revalidate-secret")
if (secret !== env.REVALIDATE_SECRET) { if (secret !== env.REVALIDATE_SECRET) {
console.error(`Invalid Secret`) revalidateLogger.error(`Invalid Secret`, { secret })
console.error({ secret })
return badRequest({ return badRequest({
now: Date.now(), now: Date.now(),
revalidated: false, revalidated: false,
@@ -64,8 +65,10 @@ export async function POST(request: NextRequest) {
const data = await request.json() const data = await request.json()
const validatedData = validateJsonBody.safeParse(data) const validatedData = validateJsonBody.safeParse(data)
if (!validatedData.success) { if (!validatedData.success) {
console.error("Bad validation for `validatedData`") revalidateLogger.error(
console.error(validatedData.error) "Bad validation for `validatedData`",
validatedData.error
)
return internalServerError({ revalidated: false, now: Date.now() }) return internalServerError({ revalidated: false, now: Date.now() })
} }
@@ -95,37 +98,41 @@ export async function POST(request: NextRequest) {
const cacheClient = await getCacheClient() const cacheClient = await getCacheClient()
const contentTypeUidTag = generateTag(entryLocale, content_type.uid) const contentTypeUidTag = generateTag(entryLocale, content_type.uid)
console.info(`Revalidating tag by content_type_uid: ${contentTypeUidTag}`) revalidateLogger.info(
`Revalidating tag by content_type_uid: ${contentTypeUidTag}`
)
revalidateTag(contentTypeUidTag) revalidateTag(contentTypeUidTag)
await cacheClient.deleteKey(contentTypeUidTag, { fuzzy: true }) await cacheClient.deleteKey(contentTypeUidTag, { fuzzy: true })
console.info(`Revalidating refsTag: ${refsTag}`) revalidateLogger.info(`Revalidating refsTag: ${refsTag}`)
revalidateTag(refsTag) revalidateTag(refsTag)
await cacheClient.deleteKey(refsTag, { fuzzy: true }) await cacheClient.deleteKey(refsTag, { fuzzy: true })
console.info(`Revalidating refTag: ${refTag}`) revalidateLogger.info(`Revalidating refTag: ${refTag}`)
revalidateTag(refTag) revalidateTag(refTag)
await cacheClient.deleteKey(refTag, { fuzzy: true }) await cacheClient.deleteKey(refTag, { fuzzy: true })
console.info(`Revalidating tag: ${tag}`) revalidateLogger.info(`Revalidating tag: ${tag}`)
revalidateTag(tag) revalidateTag(tag)
await cacheClient.deleteKey(tag, { fuzzy: true }) await cacheClient.deleteKey(tag, { fuzzy: true })
console.info(`Revalidating language switcher tag: ${languageSwitcherTag}`) revalidateLogger.info(
`Revalidating language switcher tag: ${languageSwitcherTag}`
)
revalidateTag(languageSwitcherTag) revalidateTag(languageSwitcherTag)
await cacheClient.deleteKey(languageSwitcherTag, { fuzzy: true }) await cacheClient.deleteKey(languageSwitcherTag, { fuzzy: true })
console.info(`Revalidating metadataTag: ${metadataTag}`) revalidateLogger.info(`Revalidating metadataTag: ${metadataTag}`)
revalidateTag(metadataTag) revalidateTag(metadataTag)
await cacheClient.deleteKey(metadataTag, { fuzzy: true }) await cacheClient.deleteKey(metadataTag, { fuzzy: true })
console.info(`Revalidating contentEntryTag: ${contentEntryTag}`) revalidateLogger.info(`Revalidating contentEntryTag: ${contentEntryTag}`)
revalidateTag(contentEntryTag) revalidateTag(contentEntryTag)
await cacheClient.deleteKey(contentEntryTag, { fuzzy: true }) await cacheClient.deleteKey(contentEntryTag, { fuzzy: true })
if (entry.url) { if (entry.url) {
const resolveEntryTag = resolveEntryCacheKey(entryLocale, entry.url) const resolveEntryTag = resolveEntryCacheKey(entryLocale, entry.url)
console.info(`Revalidating url: ${resolveEntryTag}`) revalidateLogger.info(`Revalidating url: ${resolveEntryTag}`)
await cacheClient.deleteKey(resolveEntryTag, { fuzzy: true }) await cacheClient.deleteKey(resolveEntryTag, { fuzzy: true })
} }
@@ -142,11 +149,13 @@ export async function POST(request: NextRequest) {
breadcrumbsAffix breadcrumbsAffix
) )
console.info(`Revalidating breadcrumbsRefsTag: ${breadcrumbsRefsTag}`) revalidateLogger.info(
`Revalidating breadcrumbsRefsTag: ${breadcrumbsRefsTag}`
)
revalidateTag(breadcrumbsRefsTag) revalidateTag(breadcrumbsRefsTag)
await cacheClient.deleteKey(breadcrumbsRefsTag, { fuzzy: true }) await cacheClient.deleteKey(breadcrumbsRefsTag, { fuzzy: true })
console.info(`Revalidating breadcrumbsTag: ${breadcrumbsTag}`) revalidateLogger.info(`Revalidating breadcrumbsTag: ${breadcrumbsTag}`)
revalidateTag(breadcrumbsTag) revalidateTag(breadcrumbsTag)
await cacheClient.deleteKey(breadcrumbsTag, { fuzzy: true }) await cacheClient.deleteKey(breadcrumbsTag, { fuzzy: true })
} }
@@ -158,7 +167,7 @@ export async function POST(request: NextRequest) {
pageSettingsAffix pageSettingsAffix
) )
console.info(`Revalidating pageSettingsTag: ${pageSettingsTag}`) revalidateLogger.info(`Revalidating pageSettingsTag: ${pageSettingsTag}`)
revalidateTag(pageSettingsTag) revalidateTag(pageSettingsTag)
await cacheClient.deleteKey(pageSettingsTag, { fuzzy: true }) await cacheClient.deleteKey(pageSettingsTag, { fuzzy: true })
} }
@@ -177,8 +186,7 @@ export async function POST(request: NextRequest) {
return Response.json({ revalidated: true, now: Date.now() }) return Response.json({ revalidated: true, now: Date.now() })
} catch (error) { } catch (error) {
console.error("Failed to revalidate tag(s)") revalidateLogger.error("Failed to revalidate tag(s)", error)
console.error(error)
return internalServerError({ revalidated: false, now: Date.now() }) return internalServerError({ revalidated: false, now: Date.now() })
} }
} }

View File

@@ -1,14 +1,15 @@
import { type NextRequest, NextResponse } from "next/server" import { type NextRequest, NextResponse } from "next/server"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { env } from "@/env/server" import { env } from "@/env/server"
import { warmup } from "@/services/warmup" import { warmup } from "@/services/warmup"
import { isWarmupKey } from "@/services/warmup/warmupKeys" import { isWarmupKey } from "@/services/warmup/warmupKeys"
import { createLogger } from "@/utils/logger"
export const dynamic = "force-dynamic" export const dynamic = "force-dynamic"
const logger = createLogger("Warmup") const logger = createLogger("warmup")
export async function GET(req: NextRequest) { export async function GET(req: NextRequest) {
const url = new URL(req.url) const url = new URL(req.url)

View File

@@ -3,6 +3,8 @@
import * as Sentry from "@sentry/nextjs" import * as Sentry from "@sentry/nextjs"
import { useEffect } from "react" import { useEffect } from "react"
import { logger } from "@scandic-hotels/common/logger"
import Image from "@/components/Image" import Image from "@/components/Image"
/* eslint-disable formatjs/no-literal-string-in-jsx */ /* eslint-disable formatjs/no-literal-string-in-jsx */
@@ -11,7 +13,7 @@ export default function GlobalError({
}: { }: {
error: Error & { digest?: string } error: Error & { digest?: string }
}) { }) {
console.log({ global_error: error }) logger.error("Global Error", { global_error: error })
useEffect(() => { useEffect(() => {
Sentry.captureException(error) Sentry.captureException(error)

View File

@@ -1,11 +1,14 @@
import { notFound } from "next/navigation" import { notFound } from "next/navigation"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { getSitemapDataById } from "@/utils/sitemap" import { getSitemapDataById } from "@/utils/sitemap"
import type { NextRequest } from "next/server" import type { NextRequest } from "next/server"
export const dynamic = "force-dynamic" export const dynamic = "force-dynamic"
const sitemapLogger = createLogger("sitemap")
export async function GET( export async function GET(
_request: NextRequest, _request: NextRequest,
context: { params: Promise<{ sitemapId: string }> } context: { params: Promise<{ sitemapId: string }> }
@@ -13,7 +16,7 @@ export async function GET(
const params = await context.params const params = await context.params
const sitemapId = params.sitemapId const sitemapId = params.sitemapId
console.log("[SITEMAP] Fetching sitemap by ID", sitemapId) sitemapLogger.debug("Fetching sitemap by ID", sitemapId)
if (!sitemapId) { if (!sitemapId) {
return notFound() return notFound()

View File

@@ -1,11 +1,14 @@
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { env } from "@/env/server" import { env } from "@/env/server"
import { getLastUpdated, getSitemapIds } from "@/utils/sitemap" import { getLastUpdated, getSitemapIds } from "@/utils/sitemap"
export const dynamic = "force-dynamic" export const dynamic = "force-dynamic"
const sitemapLogger = createLogger("sitemap")
export async function GET() { export async function GET() {
console.log(`[SITEMAP] Fetching sitemap`) sitemapLogger.debug(`Fetching sitemap`)
const lastUpdated = await getLastUpdated() const lastUpdated = await getLastUpdated()
@@ -15,7 +18,7 @@ export async function GET() {
return new Response("No sitemaps found", { status: 404 }) return new Response("No sitemaps found", { status: 404 })
} }
console.log(`[SITEMAP] Sitemaps retrieved: ${sitemaps.length}`) sitemapLogger.debug(`Sitemaps retrieved: ${sitemaps.length}`)
const urls = sitemaps.map( const urls = sitemaps.map(
(id) => `<sitemap> (id) => `<sitemap>

View File

@@ -1,6 +1,8 @@
import NextAuth, { type NextAuthConfig } from "next-auth" import NextAuth, { type NextAuthConfig } from "next-auth"
import MicrosoftEntraID from "next-auth/providers/microsoft-entra-id" import MicrosoftEntraID from "next-auth/providers/microsoft-entra-id"
import { logger } from "@scandic-hotels/common/logger"
import { env } from "@/env/server" import { env } from "@/env/server"
const config = { const config = {
@@ -40,9 +42,9 @@ const config = {
return session return session
}, },
async redirect({ baseUrl, url }) { async redirect({ baseUrl, url }) {
console.log(`[auth.dtmc] deciding redirect URL`, { baseUrl, url }) logger.debug(`[auth.dtmc] deciding redirect URL`, { baseUrl, url })
if (url.startsWith("/")) { if (url.startsWith("/")) {
console.log( logger.debug(
`[auth.dtmc] relative URL accepted, returning: ${baseUrl}${url}` `[auth.dtmc] relative URL accepted, returning: ${baseUrl}${url}`
) )
// Allows relative callback URLs // Allows relative callback URLs
@@ -54,22 +56,24 @@ const config = {
if ( if (
/\.scandichotels\.(dk|de|com|fi|no|se)$/.test(parsedUrl.hostname) /\.scandichotels\.(dk|de|com|fi|no|se)$/.test(parsedUrl.hostname)
) { ) {
console.log(`[auth.dtmc] subdomain URL accepted, returning: ${url}`) logger.debug(
`[auth.dtmc] subdomain URL accepted, returning: ${url}`
)
// Allows any subdomains on all top level domains above // Allows any subdomains on all top level domains above
return url return url
} else if (parsedUrl.origin === baseUrl) { } else if (parsedUrl.origin === baseUrl) {
// Allows callback URLs on the same origin // Allows callback URLs on the same origin
console.log(`[auth.dtmc] origin URL accepted, returning: ${url}`) logger.debug(`[auth.dtmc] origin URL accepted, returning: ${url}`)
return url return url
} }
} catch (e) { } catch (e) {
console.error( logger.error(
`[auth.dtmc] error parsing incoming URL for redirection`, `[auth.dtmc] error parsing incoming URL for redirection`,
e e
) )
} }
} }
console.log(`[auth.dtmc] URL denied, returning base URL: ${baseUrl}`) logger.debug(`[auth.dtmc] URL denied, returning base URL: ${baseUrl}`)
return baseUrl return baseUrl
}, },
async authorized() { async authorized() {

View File

@@ -1,5 +1,6 @@
import NextAuth, { type NextAuthConfig, type User } from "next-auth" import NextAuth, { type NextAuthConfig, type User } from "next-auth"
import { logger } from "@scandic-hotels/common/logger"
import { LoginTypeEnum } from "@scandic-hotels/trpc/types/loginType" import { LoginTypeEnum } from "@scandic-hotels/trpc/types/loginType"
import { PRE_REFRESH_TIME_IN_SECONDS } from "@/constants/auth" import { PRE_REFRESH_TIME_IN_SECONDS } from "@/constants/auth"
@@ -24,7 +25,7 @@ async function refreshTokens(token: JWT) {
throw "Refresh token missing." throw "Refresh token missing."
} }
console.log("token-debug Access token expired, trying to refresh it.", { logger.debug("token-debug Access token expired, trying to refresh it.", {
expires_at: token.expires_at, expires_at: token.expires_at,
sub: token.sub, sub: token.sub,
token: token.access_token, token: token.access_token,
@@ -46,7 +47,7 @@ async function refreshTokens(token: JWT) {
const new_tokens = await response.json() const new_tokens = await response.json()
if (!response.ok) { if (!response.ok) {
console.log("token-debug Token response was not ok", { logger.debug("token-debug Token response was not ok", {
status: response.status, status: response.status,
statusText: response.statusText, statusText: response.statusText,
sub: token.sub, sub: token.sub,
@@ -54,7 +55,7 @@ async function refreshTokens(token: JWT) {
throw new_tokens throw new_tokens
} }
console.log("token-debug Successfully got new token(s)", { logger.debug("token-debug Successfully got new token(s)", {
expires_at: new_tokens.expires_at, expires_at: new_tokens.expires_at,
got_new_refresh_token: new_tokens.refresh_token !== token.refresh_token, got_new_refresh_token: new_tokens.refresh_token !== token.refresh_token,
got_new_access_token: new_tokens.access_token !== token.access_token, got_new_access_token: new_tokens.access_token !== token.access_token,
@@ -72,7 +73,7 @@ async function refreshTokens(token: JWT) {
refresh_token: new_tokens.refresh_token, refresh_token: new_tokens.refresh_token,
} }
} catch (error) { } catch (error) {
console.log("token-debug Error thrown when trying to refresh", { logger.error("token-debug Error thrown when trying to refresh", {
error, error,
sub: token.sub, sub: token.sub,
}) })
@@ -141,9 +142,11 @@ const baseConfig = {
return session return session
}, },
async redirect({ baseUrl, url }) { async redirect({ baseUrl, url }) {
console.log(`[auth] deciding redirect URL`, { baseUrl, url }) logger.debug(`[auth] deciding redirect URL`, { baseUrl, url })
if (url.startsWith("/")) { if (url.startsWith("/")) {
console.log(`[auth] relative URL accepted, returning: ${baseUrl}${url}`) logger.debug(
`[auth] relative URL accepted, returning: ${baseUrl}${url}`
)
// Allows relative callback URLs // Allows relative callback URLs
return `${baseUrl}${url}` return `${baseUrl}${url}`
} else { } else {
@@ -153,19 +156,19 @@ const baseConfig = {
if ( if (
/\.scandichotels\.(dk|de|com|fi|no|se)$/.test(parsedUrl.hostname) /\.scandichotels\.(dk|de|com|fi|no|se)$/.test(parsedUrl.hostname)
) { ) {
console.log(`[auth] subdomain URL accepted, returning: ${url}`) logger.debug(`[auth] subdomain URL accepted, returning: ${url}`)
// Allows any subdomains on all top level domains above // Allows any subdomains on all top level domains above
return url return url
} else if (parsedUrl.origin === baseUrl) { } else if (parsedUrl.origin === baseUrl) {
// Allows callback URLs on the same origin // Allows callback URLs on the same origin
console.log(`[auth] origin URL accepted, returning: ${url}`) logger.debug(`[auth] origin URL accepted, returning: ${url}`)
return url return url
} }
} catch (e) { } catch (e) {
console.error(`[auth] error parsing incoming URL for redirection`, e) logger.error(`[auth] error parsing incoming URL for redirection`, e)
} }
} }
console.log(`[auth] URL denied, returning base URL: ${baseUrl}`) logger.debug(`[auth] URL denied, returning base URL: ${baseUrl}`)
return baseUrl return baseUrl
}, },
async authorized() { async authorized() {

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import { getFilteredJobs, getFiltersFromJobs } from "./utils" import { getFilteredJobs, getFiltersFromJobs } from "./utils"
import { import {
@@ -55,7 +57,7 @@ export function reducer(state: State, action: Action) {
} }
default: default:
const unhandledActionType: never = type const unhandledActionType: never = type
console.info(`Unhandled type: ${unhandledActionType}`) logger.info(`Unhandled JobylonFeed type: ${unhandledActionType}`)
return state return state
} }
} }

View File

@@ -10,6 +10,7 @@ import {
} from "react-aria-components" } from "react-aria-components"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { trpc } from "@scandic-hotels/trpc/client" import { trpc } from "@scandic-hotels/trpc/client"
@@ -177,7 +178,7 @@ function getRedeemFlow(reward: Reward, membershipNumber: string) {
case "Tier": case "Tier":
return <RedeemTier reward={reward} membershipNumber={membershipNumber} /> return <RedeemTier reward={reward} membershipNumber={membershipNumber} />
default: default:
console.warn("Unsupported reward type for redeem:", rewardType) logger.warn("Unsupported reward type for redeem:", rewardType)
return null return null
} }
} }

View File

@@ -2,6 +2,7 @@
import { createContext, useCallback, useContext, useEffect } from "react" import { createContext, useCallback, useContext, useEffect } from "react"
import { logger } from "@scandic-hotels/common/logger"
import { trpc } from "@scandic-hotels/trpc/client" import { trpc } from "@scandic-hotels/trpc/client"
import { getFirstRedeemableCoupon } from "@/utils/rewards" import { getFirstRedeemableCoupon } from "@/utils/rewards"
@@ -41,7 +42,7 @@ export default function useRedeemFlow() {
setRedeemStep("redeemed") setRedeemStep("redeemed")
}, },
onError(error) { onError(error) {
console.error("Failed to redeem", error) logger.error("Failed to redeem", error)
}, },
} }
) )

View File

@@ -6,6 +6,7 @@ import { useState } from "react"
import { Button as ButtonRAC } from "react-aria-components" import { Button as ButtonRAC } from "react-aria-components"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { PointOfInterestGroupEnum } from "@scandic-hotels/trpc/enums/pointOfInterest" import { PointOfInterestGroupEnum } from "@scandic-hotels/trpc/enums/pointOfInterest"
@@ -120,7 +121,7 @@ export default function Sidebar({
}) })
default: default:
const option: never = group const option: never = group
console.warn(`Unsupported group given: ${option}`) logger.warn(`Unsupported group given: ${option}`)
return intl.formatMessage({ return intl.formatMessage({
defaultMessage: "N/A", defaultMessage: "N/A",

View File

@@ -4,6 +4,8 @@ import "dialogshift-webchat-sdk/bundles/dialogshift-webchat-sdk.min.css"
import { useEffect, useRef } from "react" import { useEffect, useRef } from "react"
import { logger } from "@scandic-hotels/common/logger"
import type { Lang } from "@scandic-hotels/common/constants/language" import type { Lang } from "@scandic-hotels/common/constants/language"
interface DialogshiftWidgetClientProps { interface DialogshiftWidgetClientProps {
@@ -26,7 +28,7 @@ export default function DialogshiftWidgetClient({
locale: language, locale: language,
}) })
} catch (error) { } catch (error) {
console.error("Failed to load Dialogshift chat:", error) logger.error("Failed to load Dialogshift chat:", error)
} }
} }

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { isFacilityCard, setFacilityCardGrids } from "@/utils/facilityCards" import { isFacilityCard, setFacilityCardGrids } from "@/utils/facilityCards"
@@ -55,7 +57,7 @@ export default async function Facilities({
defaultMessage: "Read more", defaultMessage: "Read more",
}) })
default: default:
console.warn(`Unsupported option given: ${text}`) logger.warn(`Unsupported option given: ${text}`)
return intl.formatMessage({ return intl.formatMessage({
defaultMessage: "Read more", defaultMessage: "Read more",
}) })

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import { getIntl } from "@/i18n" import { getIntl } from "@/i18n"
import { import {
@@ -112,7 +114,7 @@ export async function translateWellnessDetails({
.sort() .sort()
default: default:
console.warn(`Unsupported type given: ${type}`) logger.warn(`Unsupported type given: ${type}`)
} }
} }

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import type { Lang } from "@scandic-hotels/common/constants/language" import type { Lang } from "@scandic-hotels/common/constants/language"
import type { Hotel, HotelData } from "@scandic-hotels/trpc/types/hotel" import type { Hotel, HotelData } from "@scandic-hotels/trpc/types/hotel"
import type { HotelPage } from "@scandic-hotels/trpc/types/hotelPage" import type { HotelPage } from "@scandic-hotels/trpc/types/hotelPage"
@@ -83,7 +85,7 @@ export function translateWellnessType(type: string, intl: IntlShape) {
defaultMessage: "Sauna", defaultMessage: "Sauna",
}) })
default: default:
console.warn(`Unsupported group given: ${type}`) logger.warn(`Unsupported wellnessType: ${type}`)
return intl.formatMessage({ return intl.formatMessage({
defaultMessage: "Wellness", defaultMessage: "Wellness",
}) })

View File

@@ -3,6 +3,8 @@
import { usePathname } from "next/navigation" import { usePathname } from "next/navigation"
import { useCallback, useEffect } from "react" import { useCallback, useEffect } from "react"
import { logger } from "@scandic-hotels/common/logger"
import { webviews } from "@/constants/routes/webviews" import { webviews } from "@/constants/routes/webviews"
export default function CookieBotConsent() { export default function CookieBotConsent() {
@@ -20,7 +22,7 @@ export default function CookieBotConsent() {
window.adobe.optIn.deny(window.adobe.OptInCategories.ANALYTICS, true) window.adobe.optIn.deny(window.adobe.OptInCategories.ANALYTICS, true)
} }
window.adobe.optIn.complete() window.adobe.optIn.complete()
console.warn("window.load event explicitly dispatched.") logger.warn("window.load event explicitly dispatched.")
window.dispatchEvent(new Event("load")) window.dispatchEvent(new Event("load"))
} }
}, [isWebview]) }, [isWebview])

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import List from "./List" import List from "./List"
import Puffs from "./Puffs" import Puffs from "./Puffs"
import Text from "./Text" import Text from "./Text"
@@ -24,7 +26,7 @@ export default function Blocks({ blocks }: BlocksProps) {
case BlocksTypenameEnum.CurrentBlocksPageBlocksText: case BlocksTypenameEnum.CurrentBlocksPageBlocksText:
return <Text key={`${block.__typename}-${idx}`} {...block} /> return <Text key={`${block.__typename}-${idx}`} {...block} />
default: default:
console.log(`Unknown type: (${type})`) logger.error(`Unknown type: (${type})`)
return null return null
} }
})} })}

View File

@@ -3,6 +3,8 @@
import { usePathname, useSearchParams } from "next/navigation" import { usePathname, useSearchParams } from "next/navigation"
import { useEffect } from "react" import { useEffect } from "react"
import { logger } from "@scandic-hotels/common/logger"
import type { import type {
SiteSectionObject, SiteSectionObject,
TrackingData, TrackingData,
@@ -82,7 +84,7 @@ export default function Tracking({ pageData }: TrackingProps) {
window.adobe.optIn.deny(window.adobe.OptInCategories.ANALYTICS, true) window.adobe.optIn.deny(window.adobe.OptInCategories.ANALYTICS, true)
} }
window.adobe.optIn.complete() window.adobe.optIn.complete()
console.warn("window.load event explicitly dispatched.") logger.warn("window.load event explicitly dispatched.")
window.dispatchEvent(new Event("load")) window.dispatchEvent(new Event("load"))
} }
} }

View File

@@ -1,6 +1,8 @@
import * as Sentry from "@sentry/nextjs" import * as Sentry from "@sentry/nextjs"
import React from "react" import React from "react"
import { logger } from "@scandic-hotels/common/logger"
type ErrorBoundaryProps = { type ErrorBoundaryProps = {
children: React.ReactNode children: React.ReactNode
fallback?: React.ReactNode fallback?: React.ReactNode
@@ -21,7 +23,7 @@ class ErrorBoundary extends React.Component<
} }
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
console.error("ErrorBoundary caught an error:", error, errorInfo) logger.error("ErrorBoundary caught an error:", error, errorInfo)
Sentry.captureException(error, { extra: { errorInfo } }) Sentry.captureException(error, { extra: { errorInfo } })
} }

View File

@@ -6,6 +6,7 @@ import { type ChangeEvent, type FormEvent, useId } from "react"
import { useFormContext, useWatch } from "react-hook-form" import { useFormContext, useWatch } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
import { Button } from "@scandic-hotels/design-system/Button" import { Button } from "@scandic-hotels/design-system/Button"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
@@ -78,7 +79,7 @@ export function Search({
setValue("city", undefined) setValue("city", undefined)
break break
default: default:
console.error("Unhandled type:", selectedItem.type) logger.error("Unhandled type:", selectedItem.type)
break break
} }

View File

@@ -6,6 +6,7 @@ import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { profile } from "@scandic-hotels/common/constants/routes/myPages" import { profile } from "@scandic-hotels/common/constants/routes/myPages"
import { logger } from "@scandic-hotels/common/logger"
import { trpc } from "@scandic-hotels/trpc/client" import { trpc } from "@scandic-hotels/trpc/client"
import { langToApiLang } from "@scandic-hotels/trpc/constants/apiLang" import { langToApiLang } from "@scandic-hotels/trpc/constants/apiLang"
@@ -84,7 +85,7 @@ export default function Form({ user }: EditFormProps) {
case Status.error: case Status.error:
if (response.issues?.length) { if (response.issues?.length) {
response.issues.forEach((issue) => { response.issues.forEach((issue) => {
console.error(issue) logger.error("Profile edit error:", issue)
}) })
} }
toast.error(response.message) toast.error(response.message)

View File

@@ -6,6 +6,7 @@ import { useRouter } from "next/navigation"
import { FormProvider, useForm } from "react-hook-form" import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
import { Button } from "@scandic-hotels/design-system/Button" import { Button } from "@scandic-hotels/design-system/Button"
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox" import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
@@ -69,7 +70,7 @@ export default function SignupForm({ title }: SignUpFormProps) {
defaultMessage: "Something went wrong!", defaultMessage: "Something went wrong!",
}) })
) )
console.error("Component Signup error:", error) logger.error("Component Signup error:", error)
}, },
}) })

View File

@@ -3,6 +3,7 @@ import { createEvent } from "ics"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { dt } from "@scandic-hotels/common/dt" import { dt } from "@scandic-hotels/common/dt"
import { logger } from "@scandic-hotels/common/logger"
import { toast } from "@/components/TempDesignSystem/Toasts" import { toast } from "@/components/TempDesignSystem/Toasts"
import useLang from "@/hooks/useLang" import useLang from "@/hooks/useLang"
@@ -25,7 +26,7 @@ export default function AddToCalendar({
createEvent(event, (error, value) => { createEvent(event, (error, value) => {
if (error) { if (error) {
console.error("ICS Error:", error) logger.error("ICS Error:", error)
toast.error( toast.error(
intl.formatMessage({ intl.formatMessage({
defaultMessage: "Failed to add to calendar", defaultMessage: "Failed to add to calendar",
@@ -45,7 +46,7 @@ export default function AddToCalendar({
URL.revokeObjectURL(url) URL.revokeObjectURL(url)
}) })
} catch (error) { } catch (error) {
console.error("Download error:", error) logger.error("Download error:", error)
toast.error( toast.error(
intl.formatMessage({ intl.formatMessage({
defaultMessage: "Failed to add to calendar", defaultMessage: "Failed to add to calendar",

View File

@@ -1,5 +1,7 @@
import "client-only" import "client-only"
import { logger } from "@scandic-hotels/common/logger"
export const glaStorageName = "gla-storage" export const glaStorageName = "gla-storage"
type GlaSessionData = { type GlaSessionData = {
@@ -15,7 +17,7 @@ export function readGlaFromSessionStorage(): GlaSessionData | null {
if (!glaSessionData) return null if (!glaSessionData) return null
return JSON.parse(glaSessionData) return JSON.parse(glaSessionData)
} catch (error) { } catch (error) {
console.error("Error reading from session storage:", error) logger.error("Error reading from session storage:", error)
return null return null
} }
} }
@@ -37,7 +39,7 @@ export function writeGlaToSessionStorage(
}) })
) )
} catch (error) { } catch (error) {
console.error("Error writing to session storage:", error) logger.error("Error writing to session storage:", error)
} }
} }

View File

@@ -10,6 +10,7 @@ import { useIntl } from "react-intl"
import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod" import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation" import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { logger } from "@scandic-hotels/common/logger"
import Body from "@scandic-hotels/design-system/Body" import Body from "@scandic-hotels/design-system/Body"
import { Button } from "@scandic-hotels/design-system/Button" import { Button } from "@scandic-hotels/design-system/Button"
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox" import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
@@ -180,7 +181,7 @@ export default function PaymentClient({
} }
}, },
onError: (error) => { onError: (error) => {
console.error("Error", error) logger.error("Booking error", error)
handlePaymentError(error.message) handlePaymentError(error.message)
}, },
}) })
@@ -195,7 +196,7 @@ export default function PaymentClient({
setPriceChangeData(null) setPriceChangeData(null)
}, },
onError: (error) => { onError: (error) => {
console.error("Error", error) logger.error("Price change error", error)
setPriceChangeData(null) setPriceChangeData(null)
handlePaymentError(error.message) handlePaymentError(error.message)
}, },

View File

@@ -1,4 +1,5 @@
import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod" import { PaymentMethodEnum } from "@scandic-hotels/common/constants/paymentMethod"
import { logger } from "@scandic-hotels/common/logger"
import type { RoomState } from "@/types/stores/enter-details" import type { RoomState } from "@/types/stores/enter-details"
@@ -63,7 +64,7 @@ export function readPaymentInfoFromSessionStorage():
if (!paymentInfoSessionData) return undefined if (!paymentInfoSessionData) return undefined
return JSON.parse(paymentInfoSessionData) return JSON.parse(paymentInfoSessionData)
} catch (error) { } catch (error) {
console.error("Error reading from session storage:", error) logger.error("Error reading from session storage:", error)
return undefined return undefined
} }
} }
@@ -81,7 +82,7 @@ export function writePaymentInfoToSessionStorage(
}) })
) )
} catch (error) { } catch (error) {
console.error("Error writing to session storage:", error) logger.error("Error writing to session storage:", error)
} }
} }

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import { sumPackages } from "@/components/HotelReservation/utils" import { sumPackages } from "@/components/HotelReservation/utils"
import type { RoomState } from "@/types/stores/enter-details" import type { RoomState } from "@/types/stores/enter-details"
@@ -133,7 +135,7 @@ export function mapToPrice(rooms: RoomState[], isMember: boolean) {
} }
} }
console.error(room.roomRate) logger.error("Unknown roomRate", room.roomRate)
throw new Error(`Unknown roomRate`) throw new Error(`Unknown roomRate`)
}) })
} }

View File

@@ -6,6 +6,7 @@ import { FormProvider, useForm } from "react-hook-form"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { myStay } from "@scandic-hotels/common/constants/routes/myStay" import { myStay } from "@scandic-hotels/common/constants/routes/myStay"
import { logger } from "@scandic-hotels/common/logger"
import Body from "@scandic-hotels/design-system/Body" import Body from "@scandic-hotels/design-system/Body"
import Caption from "@scandic-hotels/design-system/Caption" import Caption from "@scandic-hotels/design-system/Caption"
import { trpc } from "@scandic-hotels/trpc/client" import { trpc } from "@scandic-hotels/trpc/client"
@@ -48,7 +49,7 @@ export default function FindMyBooking() {
router.push(`${myStay[lang]}?RefId=${encodeURIComponent(result.refId)}`) router.push(`${myStay[lang]}?RefId=${encodeURIComponent(result.refId)}`)
}, },
onError: (error) => { onError: (error) => {
console.error("Failed to create ref id", error) logger.error("Failed to create ref id", error)
toast.error( toast.error(
intl.formatMessage({ intl.formatMessage({
defaultMessage: "Failed to submit form, please try again later.", defaultMessage: "Failed to submit form, please try again later.",

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import type { import type {
Ancillary, Ancillary,
SelectedAncillary, SelectedAncillary,
@@ -59,7 +61,7 @@ export const getAncillarySessionData = ():
const storedData = sessionStorage.getItem(ancillarySessionKey) const storedData = sessionStorage.getItem(ancillarySessionKey)
return storedData ? JSON.parse(storedData) : undefined return storedData ? JSON.parse(storedData) : undefined
} catch (error) { } catch (error) {
console.error("Error reading from session storage:", error) logger.error("Error reading from session storage:", error)
return undefined return undefined
} }
} }
@@ -96,7 +98,7 @@ export function setAncillarySessionData({
}) })
) )
} catch (error) { } catch (error) {
console.error("Error writing to session storage:", error) logger.error("Error writing to session storage:", error)
} }
} }

View File

@@ -4,6 +4,7 @@ import { useIntl } from "react-intl"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency" import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { dt } from "@scandic-hotels/common/dt" import { dt } from "@scandic-hotels/common/dt"
import { logger } from "@scandic-hotels/common/logger"
import Body from "@scandic-hotels/design-system/Body" import Body from "@scandic-hotels/design-system/Body"
import Caption from "@scandic-hotels/design-system/Caption" import Caption from "@scandic-hotels/design-system/Caption"
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon" import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
@@ -122,7 +123,7 @@ export default function SelectedRoomPanel() {
} }
if (!selectedProduct) { if (!selectedProduct) {
console.error("Selected product is unknown") logger.error("Selected product is unknown")
return null return null
} }

View File

@@ -5,6 +5,7 @@ import { useEffect, useRef } from "react"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { partnerSas } from "@scandic-hotels/common/constants/routes/myPages" import { partnerSas } from "@scandic-hotels/common/constants/routes/myPages"
import { logger } from "@scandic-hotels/common/logger"
import { trpc } from "@scandic-hotels/trpc/client" import { trpc } from "@scandic-hotels/trpc/client"
import { TIER_TO_FRIEND_MAP } from "@/constants/membershipLevels" import { TIER_TO_FRIEND_MAP } from "@/constants/membershipLevels"
@@ -42,7 +43,7 @@ export function SASLevelUpgradeCheck() {
} }
}, },
onError() { onError() {
console.log("[sas] something went wrong") logger.error("[sas] something went wrong")
}, },
}) })

View File

@@ -7,6 +7,7 @@ import { Dialog, Modal, ModalOverlay } from "react-aria-components"
import { useIntl } from "react-intl" import { useIntl } from "react-intl"
import { benefits } from "@scandic-hotels/common/constants/routes/myPages" import { benefits } from "@scandic-hotels/common/constants/routes/myPages"
import { logger } from "@scandic-hotels/common/logger"
import { Typography } from "@scandic-hotels/design-system/Typography" import { Typography } from "@scandic-hotels/design-system/Typography"
import { trpc } from "@scandic-hotels/trpc/client" import { trpc } from "@scandic-hotels/trpc/client"
@@ -91,7 +92,7 @@ export default function SurprisesNotification({
) )
}, },
onError: (error) => { onError: (error) => {
console.error("Failed to unwrap surprise", error) logger.error("Failed to unwrap surprise", error)
toast.error( toast.error(
<> <>
{intl.formatMessage( {intl.formatMessage(

View File

@@ -2,6 +2,7 @@ import { headers } from "next/headers"
import { redirect } from "next/navigation" import { redirect } from "next/navigation"
import { overview } from "@scandic-hotels/common/constants/routes/myPages" import { overview } from "@scandic-hotels/common/constants/routes/myPages"
import { logger } from "@scandic-hotels/common/logger"
import { isValidSession } from "@scandic-hotels/trpc/utils/session" import { isValidSession } from "@scandic-hotels/trpc/utils/session"
import { getProfile } from "@/lib/trpc/memoizedRequests" import { getProfile } from "@/lib/trpc/memoizedRequests"
@@ -26,7 +27,9 @@ export async function ProtectedLayout({ children }: React.PropsWithChildren) {
const redirectURL = `/${lang}/login?redirectTo=${redirectTo}` const redirectURL = `/${lang}/login?redirectTo=${redirectTo}`
if (!isValidSession(session)) { if (!isValidSession(session)) {
console.log(`[layout:protected] no session, redirecting to: ${redirectURL}`) logger.debug(
`[layout:protected] no session, redirecting to: ${redirectURL}`
)
redirect(redirectURL) redirect(redirectURL)
} }
@@ -34,27 +37,23 @@ export async function ProtectedLayout({ children }: React.PropsWithChildren) {
if (user && "error" in user) { if (user && "error" in user) {
// redirect(redirectURL) // redirect(redirectURL)
console.error("[layout:protected] error in user", user) logger.error("[layout:protected] error in user", user)
console.error(
"[layout:protected] full user: ",
JSON.stringify(user, null, 4)
)
switch (user.cause) { switch (user.cause) {
case "unauthorized": // fall through case "unauthorized": // fall through
case "forbidden": // fall through case "forbidden": // fall through
case "token_expired": case "token_expired":
console.error( logger.error(
`[layout:protected] user error, redirecting to: ${redirectURL}` `[layout:protected] user error, redirecting to: ${redirectURL}`
) )
redirect(redirectURL) redirect(redirectURL)
case "notfound": case "notfound":
console.error(`[layout:protected] notfound user loading error`) logger.error(`[layout:protected] notfound user loading error`)
break break
case "unknown": case "unknown":
console.error(`[layout:protected] unknown user loading error`) logger.error(`[layout:protected] unknown user loading error`)
break break
default: default:
console.error(`[layout:protected] unhandled user loading error`) logger.error(`[layout:protected] unhandled user loading error`)
break break
} }
return ( return (
@@ -67,7 +66,7 @@ export async function ProtectedLayout({ children }: React.PropsWithChildren) {
} }
if (!user) { if (!user) {
console.error( logger.error(
"[layout:protected] no user found, redirecting to: ", "[layout:protected] no user found, redirecting to: ",
redirectURL redirectURL
) )

View File

@@ -6,6 +6,7 @@ import { useIntl } from "react-intl"
import { useMediaQuery } from "usehooks-ts" import { useMediaQuery } from "usehooks-ts"
import { dt } from "@scandic-hotels/common/dt" import { dt } from "@scandic-hotels/common/dt"
import { logger } from "@scandic-hotels/common/logger"
import { Select } from "@scandic-hotels/design-system/Select" import { Select } from "@scandic-hotels/design-system/Select"
import useLang from "@/hooks/useLang" import useLang from "@/hooks/useLang"
@@ -104,7 +105,7 @@ export default function DateSelect({ name, registerOptions = {} }: DateProps) {
? parseDate(currentDateValue) ? parseDate(currentDateValue)
: null : null
} catch (error) { } catch (error) {
console.warn("Known error for parse date in DateSelect: ", error) logger.warn("Known error for parse date in DateSelect: ", error)
} }
useEffect(() => { useEffect(() => {

View File

@@ -1,3 +1,4 @@
import { logger } from "@scandic-hotels/common/logger"
import { phoneErrors } from "@scandic-hotels/common/utils/zod/phoneValidator" import { phoneErrors } from "@scandic-hotels/common/utils/zod/phoneValidator"
import { signupErrors } from "@scandic-hotels/trpc/routers/user/schemas" import { signupErrors } from "@scandic-hotels/trpc/routers/user/schemas"
@@ -182,7 +183,7 @@ export function getErrorMessage(intl: IntlShape, errorCode?: string) {
"Reward nights can't be combined with codes or vouchers.", "Reward nights can't be combined with codes or vouchers.",
}) })
default: default:
console.warn("Error code not supported:", errorCode) logger.warn("Error code not supported:", errorCode)
return errorCode return errorCode
} }
} }

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import type { IntlShape } from "react-intl/src/types" import type { IntlShape } from "react-intl/src/types"
import { RoomLighting, SeatingType } from "@/types/enums/meetingRooms" import { RoomLighting, SeatingType } from "@/types/enums/meetingRooms"
@@ -33,7 +35,7 @@ export function translateRoomLighting(option: string, intl: IntlShape) {
defaultMessage: "Windows natural daylight and excellent view", defaultMessage: "Windows natural daylight and excellent view",
}) })
default: default:
console.warn(`Unsupported conference room ligthing option: ${option}`) logger.warn(`Unsupported conference room ligthing option: ${option}`)
return intl.formatMessage({ return intl.formatMessage({
defaultMessage: "N/A", defaultMessage: "N/A",
}) })
@@ -75,7 +77,7 @@ export function translateSeatingType(type: string, intl: IntlShape) {
defaultMessage: "U-shape", defaultMessage: "U-shape",
}) })
default: default:
console.warn(`Unsupported conference room type : ${type}`) logger.warn(`Unsupported conference room type : ${type}`)
return intl.formatMessage({ return intl.formatMessage({
defaultMessage: "N/A", defaultMessage: "N/A",
}) })

View File

@@ -15,6 +15,7 @@ import {
type UseFromSubscribe, type UseFromSubscribe,
} from "react-hook-form" } from "react-hook-form"
import { logger } from "@scandic-hotels/common/logger"
import { trpc } from "@scandic-hotels/trpc/client" import { trpc } from "@scandic-hotels/trpc/client"
import useRouterTransitionStore from "@/stores/router-transition" import useRouterTransitionStore from "@/stores/router-transition"
@@ -215,13 +216,13 @@ const trackPerformance = async ({
try { try {
pageLoadTime = await promiseWithTimeout(getPageLoadTimeEntry(), 3000) pageLoadTime = await promiseWithTimeout(getPageLoadTimeEntry(), 3000)
} catch (error) { } catch (error) {
console.error("Error obtaining pageLoadTime:", error) logger.error("Error obtaining pageLoadTime:", error)
} }
try { try {
lcpTime = await promiseWithTimeout(getLCPTimeEntry(), 3000) lcpTime = await promiseWithTimeout(getLCPTimeEntry(), 3000)
} catch (error) { } catch (error) {
console.error("Error obtaining lcpTime:", error) logger.error("Error obtaining lcpTime:", error)
} }
const trackingData = { const trackingData = {

View File

@@ -1,5 +1,6 @@
import { useCallback, useEffect, useState } from "react" import { useCallback, useEffect, useState } from "react"
import { logger } from "@scandic-hotels/common/logger"
import { import {
type AutoCompleteLocation, type AutoCompleteLocation,
autoCompleteLocationSchema, autoCompleteLocationSchema,
@@ -25,7 +26,7 @@ export function useSearchHistory() {
return existingHistory return existingHistory
} catch (error) { } catch (error) {
console.error("Failed to parse search history:", error) logger.error("Failed to parse search history:", error)
localStorage.removeItem(KEY) localStorage.removeItem(KEY)
return [] return []

View File

@@ -2,6 +2,8 @@
import { IntlProvider } from "react-intl" import { IntlProvider } from "react-intl"
import { logger } from "@scandic-hotels/common/logger"
import type { ClientIntlProviderProps } from "@/types/i18n" import type { ClientIntlProviderProps } from "@/types/i18n"
const logged: Record<string, boolean> = {} const logged: Record<string, boolean> = {}
@@ -29,7 +31,7 @@ export default function ClientIntlProvider({
if (!logged[msg]) { if (!logged[msg]) {
logged[msg] = true logged[msg] = true
console.warn(err) logger.warn("IntlProvider", err)
} }
}} }}
> >

View File

@@ -1,5 +1,7 @@
// https://docs.lokalise.com/en/articles/3229161-structured-json // https://docs.lokalise.com/en/articles/3229161-structured-json
import { logger } from "@scandic-hotels/common/logger"
import type { LokaliseMessageDescriptor } from "@/types/intl" import type { LokaliseMessageDescriptor } from "@/types/intl"
type TranslationEntry = { type TranslationEntry = {
@@ -28,7 +30,7 @@ export function format(
if (description) { if (description) {
if (typeof description === "string") { if (typeof description === "string") {
console.warn( logger.warn(
`Unsupported type for description, expected 'object', got ${typeof context}. Skipping!`, `Unsupported type for description, expected 'object', got ${typeof context}. Skipping!`,
msg msg
) )
@@ -39,7 +41,7 @@ export function format(
if (typeof context === "string") { if (typeof context === "string") {
entry.context = context entry.context = context
} else { } else {
console.warn( logger.warn(
`Unsupported type for context, expected 'string', got ${typeof context}`, `Unsupported type for context, expected 'string', got ${typeof context}`,
msg msg
) )
@@ -50,7 +52,7 @@ export function format(
if (limit && typeof limit === "number") { if (limit && typeof limit === "number") {
entry.limit = limit entry.limit = limit
} else { } else {
console.warn( logger.warn(
`Unsupported type for limit, expected 'number', got ${typeof limit}`, `Unsupported type for limit, expected 'number', got ${typeof limit}`,
msg msg
) )
@@ -64,7 +66,7 @@ export function format(
entry.tags = tagArray entry.tags = tagArray
} }
} else { } else {
console.warn( logger.warn(
`Unsupported type for tags, expected Array, got ${typeof tags}`, `Unsupported type for tags, expected Array, got ${typeof tags}`,
msg msg
) )
@@ -75,7 +77,7 @@ export function format(
results[id] = entry results[id] = entry
} else { } else {
console.warn( logger.warn(
`Skipping message, unsupported type for defaultMessage, expected string, got ${typeof defaultMessage}`, `Skipping message, unsupported type for defaultMessage, expected string, got ${typeof defaultMessage}`,
{ {
id, id,

View File

@@ -4,16 +4,12 @@ import { performance, PerformanceObserver } from "node:perf_hooks"
import { LokaliseApi } from "@lokalise/node-api" import { LokaliseApi } from "@lokalise/node-api"
import AdmZip from "adm-zip" import AdmZip from "adm-zip"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
const projectId = "4194150766ff28c418f010.39532200" const projectId = "4194150766ff28c418f010.39532200"
const lokaliseApi = new LokaliseApi({ apiKey: process.env.LOKALISE_API_KEY }) const lokaliseApi = new LokaliseApi({ apiKey: process.env.LOKALISE_API_KEY })
function log(msg: string, ...args: any[]) { const lokaliseLogger = createLogger("lokalise")
console.log(`[lokalise] ${msg}`, ...args)
}
function error(msg: string, ...args: any[]) {
console.error(`[lokalise] ${msg}`, ...args)
}
let resolvePerf: (value?: unknown) => void let resolvePerf: (value?: unknown) => void
const performanceMetrics = new Promise((resolve) => { const performanceMetrics = new Promise((resolve) => {
@@ -31,7 +27,9 @@ const perf = new PerformanceObserver((items) => {
resolvePerf() resolvePerf()
} }
} else { } else {
log(`[metrics] ${entry.name} completed in ${entry.duration} ms`) lokaliseLogger.info(
`[metrics] ${entry.name} completed in ${entry.duration} ms`
)
} }
} }
performance.clearMeasures() performance.clearMeasures()
@@ -43,7 +41,7 @@ async function waitUntilUploadDone(processId: string) {
try { try {
performance.mark("waitUntilUploadDoneStart") performance.mark("waitUntilUploadDoneStart")
log("Checking upload status...") lokaliseLogger.debug("Checking upload status...")
performance.mark("getProcessStart") performance.mark("getProcessStart")
const process = await lokaliseApi.queuedProcesses().get(processId, { const process = await lokaliseApi.queuedProcesses().get(processId, {
@@ -56,7 +54,7 @@ async function waitUntilUploadDone(processId: string) {
"getProcessEnd" "getProcessEnd"
) )
log(`Status: ${process.status}`) lokaliseLogger.debug(`Status: ${process.status}`)
if (process.status === "finished") { if (process.status === "finished") {
clearInterval(interval) clearInterval(interval)
@@ -72,8 +70,8 @@ async function waitUntilUploadDone(processId: string) {
} }
} catch (e) { } catch (e) {
clearInterval(interval) clearInterval(interval)
error("An error occurred:", e) lokaliseLogger.error("An error occurred:", e)
performance.mark("waitUntilUploadDoneEnd", { detail: error }) performance.mark("waitUntilUploadDoneEnd", { detail: e })
performance.measure( performance.measure(
"Wait on upload", "Wait on upload",
"waitUntilUploadDoneStart", "waitUntilUploadDoneStart",
@@ -89,7 +87,7 @@ export async function upload(filepath: string) {
perf.observe({ type: "measure" }) perf.observe({ type: "measure" })
try { try {
log(`Uploading ${filepath}...`) lokaliseLogger.debug(`Uploading ${filepath}...`)
performance.mark("uploadStart") performance.mark("uploadStart")
@@ -130,9 +128,9 @@ export async function upload(filepath: string) {
"lokaliseUploadEnd" "lokaliseUploadEnd"
) )
log("Upload successful") lokaliseLogger.debug("Upload successful")
} catch (e) { } catch (e) {
error("Upload failed", e) lokaliseLogger.error("Upload failed", e)
} finally { } finally {
performance.mark("uploadEnd") performance.mark("uploadEnd")
@@ -148,7 +146,7 @@ export async function download(extractPath: string, all: boolean = false) {
perf.observe({ type: "measure" }) perf.observe({ type: "measure" })
try { try {
log( lokaliseLogger.debug(
all all
? "Downloading all translations..." ? "Downloading all translations..."
: "Downloading filtered translations..." : "Downloading filtered translations..."
@@ -199,12 +197,12 @@ export async function download(extractPath: string, all: boolean = false) {
"unpackTranslationsEnd" "unpackTranslationsEnd"
) )
log("Download successful") lokaliseLogger.debug("Download successful")
} else { } else {
throw bundleResponse throw bundleResponse
} }
} catch (e) { } catch (e) {
error("Download failed", e) lokaliseLogger.error("Download failed", e)
} finally { } finally {
performance.mark("downloadEnd") performance.mark("downloadEnd")
@@ -251,11 +249,13 @@ export async function deleteBulk(keyNames: string[]) {
.keys() .keys()
.bulk_delete(keysToDelete, { project_id: projectId }) .bulk_delete(keysToDelete, { project_id: projectId })
log(`Bulk delete successful, removed ${keysToDelete.length} keys`) lokaliseLogger.debug(
`Bulk delete successful, removed ${keysToDelete.length} keys`
)
return response return response
} catch (e) { } catch (e) {
error("Bulk delete failed", e) lokaliseLogger.error("Bulk delete failed", e)
} finally { } finally {
performance.mark("bulkDeleteEnd") performance.mark("bulkDeleteEnd")

View File

@@ -2,6 +2,7 @@
import { TRPCClientError } from "@trpc/client" import { TRPCClientError } from "@trpc/client"
import { logger } from "@scandic-hotels/common/logger"
import { SessionExpiredError } from "@scandic-hotels/trpc/errors" import { SessionExpiredError } from "@scandic-hotels/trpc/errors"
import { TrpcProvider as InternalTrpcProvider } from "@scandic-hotels/trpc/Provider" import { TrpcProvider as InternalTrpcProvider } from "@scandic-hotels/trpc/Provider"
@@ -23,7 +24,7 @@ export default function TrpcProvider({
onError={(error) => { onError={(error) => {
if (error instanceof TRPCClientError) { if (error instanceof TRPCClientError) {
const appError: TRPCClientError<AnyTRPCRouter> = error const appError: TRPCClientError<AnyTRPCRouter> = error
console.log({ appError }) logger.error("trpc error", { appError })
if (appError.data?.code === "UNAUTHORIZED") { if (appError.data?.code === "UNAUTHORIZED") {
if (appError.data?.cause instanceof SessionExpiredError) { if (appError.data?.cause instanceof SessionExpiredError) {
const loginUrl = login[lang] const loginUrl = login[lang]

View File

@@ -3,6 +3,7 @@ import { cookies, headers } from "next/headers"
import { redirect } from "next/navigation" import { redirect } from "next/navigation"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { createContext } from "@scandic-hotels/trpc/context" import { createContext } from "@scandic-hotels/trpc/context"
import { import {
appServerClient, appServerClient,
@@ -50,6 +51,7 @@ export async function createAppContext() {
configureServerClient(createAppContext) configureServerClient(createAppContext)
const serverClientLogger = createLogger("serverClient")
export async function serverClient() { export async function serverClient() {
const ctx = await createAppContext() const ctx = await createAppContext()
@@ -71,17 +73,18 @@ export async function serverClient() {
if (webviews.includes(fullPathname)) { if (webviews.includes(fullPathname)) {
const redirectUrl = `/${lang}/webview/refresh?returnurl=${encodeURIComponent(fullPathname)}` const redirectUrl = `/${lang}/webview/refresh?returnurl=${encodeURIComponent(fullPathname)}`
console.error( serverClientLogger.error(
"Unautorized in webview, redirecting to: ", "Unautorized in webview, redirecting to: ",
redirectUrl redirectUrl
) )
console.log(`[serverClient] onError redirecting to: ${redirectUrl}`)
redirect(redirectUrl) redirect(redirectUrl)
} }
const redirectUrl = `${login[lang]}?redirectTo=${encodeURIComponent(`/${lang}/${pathname}`)}` const redirectUrl = `${login[lang]}?redirectTo=${encodeURIComponent(`/${lang}/${pathname}`)}`
console.log(`[serverClient] onError redirecting to: ${redirectUrl}`) serverClientLogger.error(
`[serverClient] onError redirecting to: ${redirectUrl}`
)
redirect(redirectUrl) redirect(redirectUrl)
} }
} }

View File

@@ -2,6 +2,7 @@ import * as Sentry from "@sentry/nextjs"
import { type NextMiddleware, NextResponse } from "next/server" import { type NextMiddleware, NextResponse } from "next/server"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { logger } from "@scandic-hotels/common/logger"
import { findLang } from "@scandic-hotels/common/utils/languages" import { findLang } from "@scandic-hotels/common/utils/languages"
import * as authRequired from "@/middlewares/authRequired" import * as authRequired from "@/middlewares/authRequired"
@@ -92,7 +93,7 @@ export const middleware: NextMiddleware = async (request, event) => {
} catch (e) { } catch (e) {
if (e instanceof NextResponse && e.status) { if (e instanceof NextResponse && e.status) {
const cause = await e.json() const cause = await e.json()
console.error(`NextResponse Error in middleware`, cause) logger.error(`NextResponse Error in middleware`, cause)
Sentry.captureException(cause) Sentry.captureException(cause)
return NextResponse.rewrite( return NextResponse.rewrite(
@@ -107,7 +108,7 @@ export const middleware: NextMiddleware = async (request, event) => {
) )
} }
console.error(`Error in middleware`, e) logger.error(`Error in middleware`, e)
Sentry.captureException(e) Sentry.captureException(e)
return NextResponse.rewrite( return NextResponse.rewrite(

View File

@@ -1,5 +1,6 @@
import { type NextMiddleware, NextResponse } from "next/server" import { type NextMiddleware, NextResponse } from "next/server"
import { logger } from "@scandic-hotels/common/logger"
import { findLang } from "@scandic-hotels/common/utils/languages" import { findLang } from "@scandic-hotels/common/utils/languages"
import { authRequired, mfaRequired } from "@/constants/routes/authRequired" import { authRequired, mfaRequired } from "@/constants/routes/authRequired"
@@ -89,7 +90,7 @@ export const middleware = auth(async (request) => {
const redirectOpts = { const redirectOpts = {
headers, headers,
} }
console.log(`[authRequired] redirecting to: ${redirectUrl}`, redirectOpts) logger.debug(`[authRequired] redirecting to: ${redirectUrl}`, redirectOpts)
return NextResponse.redirect(redirectUrl, redirectOpts) return NextResponse.redirect(redirectUrl, redirectOpts)
}) as unknown as NextMiddleware // See comment above }) as unknown as NextMiddleware // See comment above

View File

@@ -6,6 +6,7 @@ import {
profile, profile,
profileEdit, profileEdit,
} from "@scandic-hotels/common/constants/routes/myPages" } from "@scandic-hotels/common/constants/routes/myPages"
import { logger } from "@scandic-hotels/common/logger"
import { findLang } from "@scandic-hotels/common/utils/languages" import { findLang } from "@scandic-hotels/common/utils/languages"
import { resolve as resolveEntry } from "@scandic-hotels/trpc/utils/entry" import { resolve as resolveEntry } from "@scandic-hotels/trpc/utils/entry"
@@ -25,7 +26,7 @@ export const middleware: NextMiddleware = async (request) => {
const nextUrlPublic = getPublicNextURL(request) const nextUrlPublic = getPublicNextURL(request)
const overviewUrl = overview[lang] const overviewUrl = overview[lang]
const redirectUrl = new URL(overviewUrl, nextUrlPublic) const redirectUrl = new URL(overviewUrl, nextUrlPublic)
console.log(`[myPages] redirecting to: ${redirectUrl}`) logger.debug(`[myPages] redirecting to: ${redirectUrl}`)
return NextResponse.redirect(redirectUrl, { return NextResponse.redirect(redirectUrl, {
status: 308, status: 308,
}) })

View File

@@ -1,6 +1,7 @@
import { type NextMiddleware, NextResponse } from "next/server" import { type NextMiddleware, NextResponse } from "next/server"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { logger } from "@scandic-hotels/common/logger"
import { findLang } from "@scandic-hotels/common/utils/languages" import { findLang } from "@scandic-hotels/common/utils/languages"
import { notFound } from "@/server/errors/next" import { notFound } from "@/server/errors/next"
@@ -65,7 +66,7 @@ export const middleware: NextMiddleware = async (request) => {
headers.set("x-continue", "1") headers.set("x-continue", "1")
return NextResponse.next({ headers }) return NextResponse.next({ headers })
} catch (e) { } catch (e) {
console.error("Redirect error: ", e) logger.error("Redirect error: ", e)
throw notFound() throw notFound()
} }
} }

View File

@@ -1,5 +1,6 @@
import { type NextMiddleware, NextResponse } from "next/server" import { type NextMiddleware, NextResponse } from "next/server"
import { logger } from "@scandic-hotels/common/logger"
import { findLang } from "@scandic-hotels/common/utils/languages" import { findLang } from "@scandic-hotels/common/utils/languages"
import { resolve as resolveEntry } from "@scandic-hotels/trpc/utils/entry" import { resolve as resolveEntry } from "@scandic-hotels/trpc/utils/entry"
@@ -92,7 +93,7 @@ export const middleware: NextMiddleware = async (request) => {
// Authorization header is required for webviews // Authorization header is required for webviews
// It should be base64 encoded // It should be base64 encoded
if (!authorizationToken) { if (!authorizationToken) {
console.error("Authorization header is missing") logger.error("Authorization header is missing")
return badRequest("Authorization header is missing") return badRequest("Authorization header is missing")
} }
@@ -100,7 +101,7 @@ export const middleware: NextMiddleware = async (request) => {
// It should be base64 encoded // It should be base64 encoded
const initializationVector = request.headers.get("X-AES-IV")! const initializationVector = request.headers.get("X-AES-IV")!
if (!initializationVector) { if (!initializationVector) {
console.error("initializationVector header is missing") logger.error("initializationVector header is missing")
return badRequest("initializationVector header is missing") return badRequest("initializationVector header is missing")
} }
@@ -121,8 +122,7 @@ export const middleware: NextMiddleware = async (request) => {
}) })
} catch (e) { } catch (e) {
if (e instanceof Error) { if (e instanceof Error) {
console.error("Error in webView middleware") logger.error(`Error in webView middleware - ${e.name}: ${e.message}`)
console.error(`${e.name}: ${e.message}`)
} }
return badRequest() return badRequest()

View File

@@ -1,8 +1,12 @@
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import type { Lang } from "@scandic-hotels/common/constants/language" import type { Lang } from "@scandic-hotels/common/constants/language"
export async function warmupHotelDataOnLang(lang: Lang) { export async function warmupHotelDataOnLang(lang: Lang) {
const warmupHotelDataOnLangLogger = createLogger("warmupHotelDataOnLang")
const PUBLIC_URL = Netlify.env.get("PUBLIC_URL") const PUBLIC_URL = Netlify.env.get("PUBLIC_URL")
console.info(
warmupHotelDataOnLangLogger.info(
`[WARMUP] Started warmup cache hoteldata for language ${lang} at: ${new Date().toISOString()}!` `[WARMUP] Started warmup cache hoteldata for language ${lang} at: ${new Date().toISOString()}!`
) )
@@ -22,9 +26,11 @@ export async function warmupHotelDataOnLang(lang: Lang) {
} }
const hotels = await hotelsResponse.json() const hotels = await hotelsResponse.json()
console.info(`[WARMUP] Retrieved ${hotels.length} hotels.`) warmupHotelDataOnLangLogger.info(
`[WARMUP] Retrieved ${hotels.length} hotels.`
)
} catch (error) { } catch (error) {
console.error( warmupHotelDataOnLangLogger.error(
`[WARMUP] Error warming cache with hoteldata on language ${lang} with error: ${error}` `[WARMUP] Error warming cache with hoteldata on language ${lang} with error: ${error}`
) )
} }

View File

@@ -1,7 +1,8 @@
import { NextRequest } from "next/server" import { NextRequest } from "next/server"
import { env } from "@/env/server" import { logger } from "@scandic-hotels/common/logger"
import { env } from "@/env/server"
/** /**
* Use this function when you want to create URLs that are public facing, for * Use this function when you want to create URLs that are public facing, for
* example for redirects or redirectTo query parameters. * example for redirects or redirectTo query parameters.
@@ -67,7 +68,7 @@ export function getInternalNextURL(request: NextRequest) {
const originHeader = request.headers.get("x-sh-origin") const originHeader = request.headers.get("x-sh-origin")
if (originHeader) { if (originHeader) {
console.log(`[internalNextUrl] using x-sh-origin header`, { logger.debug(`[internalNextUrl] using x-sh-origin header`, {
origin, origin,
originHeader, originHeader,
newOrigin: href.replace(origin, originHeader), newOrigin: href.replace(origin, originHeader),
@@ -78,7 +79,7 @@ export function getInternalNextURL(request: NextRequest) {
const hostHeader = request.headers.get("host") const hostHeader = request.headers.get("host")
if (hostHeader) { if (hostHeader) {
const inputHostOrigin = `${request.nextUrl.protocol}//${hostHeader}` const inputHostOrigin = `${request.nextUrl.protocol}//${hostHeader}`
console.log(`[internalNextUrl] using host header`, { logger.debug(`[internalNextUrl] using host header`, {
origin, origin,
hostHeader, hostHeader,
hostOrigin: inputHostOrigin, hostOrigin: inputHostOrigin,
@@ -88,6 +89,6 @@ export function getInternalNextURL(request: NextRequest) {
return new NextRequest(href.replace(origin, hostOrigin), request).nextUrl return new NextRequest(href.replace(origin, hostOrigin), request).nextUrl
} }
console.log(`[internalNextUrl] falling back to incoming request`) logger.debug(`[internalNextUrl] falling back to incoming request`)
return request.nextUrl return request.nextUrl
} }

View File

@@ -1,4 +1,5 @@
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { getServiceToken } from "@scandic-hotels/common/tokenManager" import { getServiceToken } from "@scandic-hotels/common/tokenManager"
import { safeTry } from "@scandic-hotels/common/utils/safeTry" import { safeTry } from "@scandic-hotels/common/utils/safeTry"
import { import {
@@ -10,6 +11,7 @@ import type { WarmupFunction, WarmupResult } from "."
export const warmupHotelIdsByCountry = export const warmupHotelIdsByCountry =
(): WarmupFunction => async (): Promise<WarmupResult> => { (): WarmupFunction => async (): Promise<WarmupResult> => {
const warmupLogger = createLogger("warmupHotelIdsByCountry")
try { try {
let serviceToken = await getServiceToken() let serviceToken = await getServiceToken()
@@ -39,7 +41,7 @@ export const warmupHotelIdsByCountry =
) )
if (error) { if (error) {
console.error( warmupLogger.error(
`[Warmup]: Error fetching hotel IDs for ${countryName}:`, `[Warmup]: Error fetching hotel IDs for ${countryName}:`,
error error
) )

View File

@@ -2,6 +2,7 @@ import isEqual from "fast-deep-equal"
import { parsePhoneNumberFromString } from "libphonenumber-js" import { parsePhoneNumberFromString } from "libphonenumber-js"
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency" import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
import { logger } from "@scandic-hotels/common/logger"
import { RateTypeEnum } from "@scandic-hotels/trpc/enums/rateType" import { RateTypeEnum } from "@scandic-hotels/trpc/enums/rateType"
import { import {
@@ -279,7 +280,7 @@ export function readFromSessionStorage(): PersistedState | undefined {
return parsedData return parsedData
} catch (error) { } catch (error) {
console.error("Error reading from session storage:", error) logger.error("Error reading from session storage:", error)
return undefined return undefined
} }
} }
@@ -292,7 +293,7 @@ export function writeToSessionStorage(state: PersistedState) {
try { try {
sessionStorage.setItem(detailsStorageName, JSON.stringify(state)) sessionStorage.setItem(detailsStorageName, JSON.stringify(state))
} catch (error) { } catch (error) {
console.error("Error writing to session storage:", error) logger.error("Error writing to session storage:", error)
} }
} }

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import type { Session } from "next-auth" import type { Session } from "next-auth"
export function isValidClientSession(session: Session | null) { export function isValidClientSession(session: Session | null) {
@@ -5,16 +7,16 @@ export function isValidClientSession(session: Session | null) {
return false return false
} }
if (session.error) { if (session.error) {
console.log(`Session error: ${session.error}`) logger.error(`Session error: ${session.error}`)
return false return false
} }
if (session.token.error) { if (session.token.error) {
console.log(`Session token error: ${session.token.error}`) logger.error(`Session token error: ${session.token.error}`)
return false return false
} }
if (session.token.expires_at && session.token.expires_at < Date.now()) { if (session.token.expires_at && session.token.expires_at < Date.now()) {
console.log(`Session expired: ${session.token.expires_at}`) logger.error(`Session expired: ${session.token.expires_at}`)
return false return false
} }

View File

@@ -1,16 +0,0 @@
export function createLogger(loggerPrefix: string | (() => Promise<string>)) {
const getLoggerPrefix: () => Promise<string> =
typeof loggerPrefix === "string" ? async () => loggerPrefix : loggerPrefix
return {
async debug(message: string, ...args: unknown[]): Promise<void> {
console.debug(`[${await getLoggerPrefix()}] ${message}`, ...args)
},
async warn(message: string, ...args: unknown[]): Promise<void> {
console.warn(`[${await getLoggerPrefix()}] Warning - ${message}`, ...args)
},
async error(message: string, ...args: unknown[]): Promise<void> {
console.error(`[${await getLoggerPrefix()}] Error - ${message}`, ...args)
},
}
}

View File

@@ -1,5 +1,7 @@
import parsePhoneNumberFromString, { type CountryCode } from "libphonenumber-js" import parsePhoneNumberFromString, { type CountryCode } from "libphonenumber-js"
import { logger } from "@scandic-hotels/common/logger"
export function formatPhoneNumber( export function formatPhoneNumber(
phoneNumber: string, phoneNumber: string,
phoneNumberCC?: string | null phoneNumberCC?: string | null
@@ -16,7 +18,7 @@ export function formatPhoneNumber(
: parsePhoneNumberFromString(normalized) : parsePhoneNumberFromString(normalized)
if (!parsedPhonenumber?.isValid()) { if (!parsedPhonenumber?.isValid()) {
console.warn( logger.warn(
`Invalid phone number: ${phoneNumber} with country code: ${phoneNumberCC}`, `Invalid phone number: ${phoneNumber} with country code: ${phoneNumberCC}`,
parsedPhonenumber parsedPhonenumber
) )

View File

@@ -1,3 +1,5 @@
import { logger } from "@scandic-hotels/common/logger"
import { SESSION_ID_KEY_NAME } from "@/hooks/useSessionId" import { SESSION_ID_KEY_NAME } from "@/hooks/useSessionId"
export function trackEvent(data: any) { export function trackEvent(data: any) {
@@ -11,7 +13,7 @@ export function trackEvent(data: any) {
try { try {
sessionId = sessionStorage.getItem(SESSION_ID_KEY_NAME) ?? "" sessionId = sessionStorage.getItem(SESSION_ID_KEY_NAME) ?? ""
} catch (e) { } catch (e) {
console.error("Error getting sessionId from sessionStorage", e) logger.error("Error getting sessionId from sessionStorage", e)
} }
data = { ...data, siteVersion: "new-web", sessionId } data = { ...data, siteVersion: "new-web", sessionId }

View File

@@ -1,5 +1,6 @@
import { z } from "zod" import { z } from "zod"
import { logger } from "@scandic-hotels/common/logger"
import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast" import { BreakfastPackageEnum } from "@scandic-hotels/trpc/enums/breakfast"
import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter" import { RoomPackageCodeEnum } from "@scandic-hotels/trpc/enums/roomFilter"
@@ -81,7 +82,7 @@ export function parseBookingWidgetSearchParams(
return result return result
} catch (error) { } catch (error) {
console.log("[URL] Error parsing search params for booking widget:", error) logger.error("[URL] Error parsing search params for booking widget:", error)
return {} return {}
} }
} }
@@ -118,7 +119,7 @@ export function parseSelectHotelSearchParams(
return result return result
} catch (error) { } catch (error) {
console.log("[URL] Error parsing search params for select hotel:", error) logger.error("[URL] Error parsing search params for select hotel:", error)
return null return null
} }
@@ -168,7 +169,7 @@ export function parseSelectRateSearchParams(
return result return result
} catch (error) { } catch (error) {
console.log("[URL] Error parsing search params for select rate:", error) logger.error("[URL] Error parsing search params for select rate:", error)
return null return null
} }
@@ -216,7 +217,7 @@ export function parseDetailsSearchParams(
return result return result
} catch (error) { } catch (error) {
console.log("[URL] Error parsing search params for details:", error) logger.error("[URL] Error parsing search params for details:", error)
return null return null
} }

View File

@@ -1,14 +1,6 @@
export const cacheLogger = { import { createLogger } from "../logger/createLogger"
async debug(message: string, ...args: unknown[]): Promise<void> {
console.debug(`${await loggerPrefix()} ${message}`, ...args) export const cacheLogger = createLogger(loggerPrefix)
},
async warn(message: string, ...args: unknown[]): Promise<void> {
console.warn(`${await loggerPrefix()} Warning - ${message}`, ...args)
},
async error(message: string, ...args: unknown[]): Promise<void> {
console.error(`${await loggerPrefix()} Error - ${message}`, ...args)
},
}
async function loggerPrefix() { async function loggerPrefix() {
const instancePrefix = await getCachePrefix() const instancePrefix = await getCachePrefix()

View File

@@ -0,0 +1,44 @@
export function createLogger(loggerPrefix: string | (() => Promise<string>)) {
const asyncWrapper: () => Promise<string> =
typeof loggerPrefix === "string" ? async () => loggerPrefix : loggerPrefix
const getLoggerPrefix = async () => {
const prefix = await asyncWrapper()
if (!prefix) {
return ""
}
return `[${prefix}]`
}
return {
async debug(message: string, ...args: unknown[]): Promise<void> {
// TODO: Make this configurable
if (process.env.NODE_ENV !== "development") {
return
}
console.debug(
"\x1b[36m%s\x1b[0m",
`${await getLoggerPrefix()} ${message}`.trim(),
...args
)
},
async info(message: string, ...args: unknown[]): Promise<void> {
console.info(`${await getLoggerPrefix()} ${message}`.trim(), ...args)
},
async warn(message: string, ...args: unknown[]): Promise<void> {
console.warn(
`${await getLoggerPrefix()} [warn] - ${message}`.trim(),
...args
)
},
async error(message: string, ...args: unknown[]): Promise<void> {
console.error(
`${await getLoggerPrefix()} [error] - ${message}`.trim(),
...args
)
},
}
}

View File

@@ -0,0 +1,3 @@
import { createLogger } from "./createLogger"
export const logger = createLogger("")

View File

@@ -16,6 +16,8 @@
"./telemetry": "./telemetry/index.ts", "./telemetry": "./telemetry/index.ts",
"./tokenManager": "./tokenManager/index.ts", "./tokenManager": "./tokenManager/index.ts",
"./dt": "./dt/dt.ts", "./dt": "./dt/dt.ts",
"./logger": "./logger/index.ts",
"./logger/*": "./logger/*.ts",
"./utils/isEdge": "./utils/isEdge.ts", "./utils/isEdge": "./utils/isEdge.ts",
"./utils/safeTry": "./utils/safeTry.ts", "./utils/safeTry": "./utils/safeTry.ts",
"./utils/url": "./utils/url.ts", "./utils/url": "./utils/url.ts",

View File

@@ -23,6 +23,8 @@ import {
mapValues, mapValues,
} from "lodash-es" } from "lodash-es"
import { logger } from "../logger"
import type { ZodError } from "zod" import type { ZodError } from "zod"
type AttributesInput = Record<string, unknown> type AttributesInput = Record<string, unknown>
@@ -88,7 +90,7 @@ export function isValidAttributeValue(value: unknown): value is AttributeValue {
* }; * };
* *
* const sanitized = sanitize(input); * const sanitized = sanitize(input);
* console.log(sanitized); * logger.log(sanitized);
* // { * // {
* // key1: "Example", * // key1: "Example",
* // key2: 10, * // key2: 10,
@@ -162,7 +164,7 @@ export function createCounter(meterName: string, counterName: string) {
const finalAttrs = sanitize(mergedAttrs) const finalAttrs = sanitize(mergedAttrs)
counter.add(1, finalAttrs) counter.add(1, finalAttrs)
console.info(`[${fullName}] start:`, finalAttrs) logger.info(`[${fullName}] start:`, finalAttrs)
}, },
/** /**
@@ -175,7 +177,7 @@ export function createCounter(meterName: string, counterName: string) {
const finalAttrs = sanitize(mergedAttrs) const finalAttrs = sanitize(mergedAttrs)
success.add(1, finalAttrs) success.add(1, finalAttrs)
console.info(`[${fullName}] success:`, finalAttrs) logger.info(`[${fullName}] success:`, finalAttrs)
}, },
/** /**
@@ -199,7 +201,7 @@ export function createCounter(meterName: string, counterName: string) {
const finalAttrs = sanitize(mergedAttrs) const finalAttrs = sanitize(mergedAttrs)
fail.add(1, finalAttrs) fail.add(1, finalAttrs)
console.error(`[${fullName}] dataError:`, finalAttrs) logger.error(`[${fullName}] dataError:`, finalAttrs)
}, },
/** /**
@@ -221,7 +223,7 @@ export function createCounter(meterName: string, counterName: string) {
const finalAttrs = sanitize(mergedAttrs) const finalAttrs = sanitize(mergedAttrs)
fail.add(1, finalAttrs) fail.add(1, finalAttrs)
console.error(`[${fullName}] noDataError:`, finalAttrs) logger.error(`[${fullName}] noDataError:`, finalAttrs)
}, },
/** /**
@@ -241,7 +243,7 @@ export function createCounter(meterName: string, counterName: string) {
const finalAttrs = sanitize(mergedAttrs) const finalAttrs = sanitize(mergedAttrs)
fail.add(1, finalAttrs) fail.add(1, finalAttrs)
console.error(`[${fullName}] validationError:`, finalAttrs) logger.error(`[${fullName}] validationError:`, finalAttrs)
}, },
/** /**
@@ -271,7 +273,7 @@ export function createCounter(meterName: string, counterName: string) {
const finalAttrs = sanitize(mergedAttrs) const finalAttrs = sanitize(mergedAttrs)
fail.add(1, finalAttrs) fail.add(1, finalAttrs)
console.error(`[${fullName}] httpError:`, finalAttrs) logger.error(`[${fullName}] httpError:`, finalAttrs)
}, },
/** /**
@@ -301,7 +303,7 @@ export function createCounter(meterName: string, counterName: string) {
const finalAttrs = sanitize(mergedAttrs) const finalAttrs = sanitize(mergedAttrs)
fail.add(1, finalAttrs) fail.add(1, finalAttrs)
console.error(`[${fullName}] fail:`, finalAttrs) logger.error(`[${fullName}] fail:`, finalAttrs)
}, },
} }
}, },

View File

@@ -1,4 +1,4 @@
import { describe, expect, test } from "vitest" import { beforeEach, describe, expect, test } from "vitest"
import { passwordValidator } from "./passwordValidator" import { passwordValidator } from "./passwordValidator"
@@ -88,7 +88,6 @@ describe("Should validate password the same way as Curity", () => {
curityPasswordRegex.lastIndex = 0 curityPasswordRegex.lastIndex = 0
}) })
test.each(testCases)("$description", ({ password }) => { test.each(testCases)("$description", ({ password }) => {
console.log(password)
const curityResult = curityPasswordRegex.test(password) const curityResult = curityPasswordRegex.test(password)
const zodResult = passwordValidator().safeParse(password) const zodResult = passwordValidator().safeParse(password)

View File

@@ -3,6 +3,7 @@ import "server-only"
import { ClientError, type GraphQLClient } from "graphql-request" import { ClientError, type GraphQLClient } from "graphql-request"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { env } from "../../env/server" import { env } from "../../env/server"
@@ -10,6 +11,7 @@ import type { DocumentNode } from "graphql"
import type { Data } from "../types/requestData" import type { Data } from "../types/requestData"
const requestLogger = createLogger("graphql-request")
export async function request<T>( export async function request<T>(
client: GraphQLClient, client: GraphQLClient,
query: string | DocumentNode, query: string | DocumentNode,
@@ -41,13 +43,9 @@ export async function request<T>(
/** /**
* TODO: Send to Monitoring (Logging and Metrics) * TODO: Send to Monitoring (Logging and Metrics)
*/ */
console.log({ requestLogger.debug("complexity", {
complexityLimit: rawResponse.headers.get("x-query-complexity"), complexityLimit: rawResponse.headers.get("x-query-complexity"),
})
console.log({
referenceDepth: rawResponse.headers.get("x-reference-depth"), referenceDepth: rawResponse.headers.get("x-reference-depth"),
})
console.log({
resolverCost: rawResponse.headers.get("x-resolver-cost"), resolverCost: rawResponse.headers.get("x-resolver-cost"),
}) })
@@ -63,17 +61,17 @@ export async function request<T>(
// @ts-expect-error: name does not exist (?) // @ts-expect-error: name does not exist (?)
).name.value ).name.value
console.log(`[gql] Sending graphql request to ${env.CMS_URL}`, { requestLogger.debug(`[gql] Sending graphql request to ${env.CMS_URL}`, {
operationName, operationName,
variables, variables,
}) })
} catch (e) { } catch (e) {
console.error(`[gql] Unable to extract operation name from query`, { requestLogger.error(`[gql] Unable to extract operation name from query`, {
query, query,
error: e, error: e,
}) })
console.log(`[gql] Sending graphql request to ${env.CMS_URL}`, { requestLogger.debug(`[gql] Sending graphql request to ${env.CMS_URL}`, {
operationName: "<Unable to extract>", operationName: "<Unable to extract>",
variables, variables,
}) })
@@ -94,18 +92,18 @@ export async function request<T>(
// @ts-expect-error: name does not exist (?) // @ts-expect-error: name does not exist (?)
).name.value ).name.value
console.log(`[gql] Response for ${env.CMS_URL}`, { requestLogger.debug(`[gql] Response for ${env.CMS_URL}`, {
response, response,
operationName, operationName,
variables, variables,
}) })
} catch (e) { } catch (e) {
console.error(`[gql] Unable to extract operation name from query`, { requestLogger.error(`[gql] Unable to extract operation name from query`, {
query, query,
error: e, error: e,
}) })
console.log(`[gql] Response for ${env.CMS_URL}`, { requestLogger.debug(`[gql] Response for ${env.CMS_URL}`, {
response, response,
operationName: "<Unable to extract>", operationName: "<Unable to extract>",
variables, variables,
@@ -138,7 +136,7 @@ export async function request<T>(
} }
} }
console.error( requestLogger.error(
`[gql] Error sending graphql request to ${env.CMS_URL}`, `[gql] Error sending graphql request to ${env.CMS_URL}`,
error error
) )

View File

@@ -3,6 +3,8 @@ import "server-only"
import deepmerge from "deepmerge" import deepmerge from "deepmerge"
import merge from "deepmerge" import merge from "deepmerge"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { request } from "./request" import { request } from "./request"
import type { CacheTime } from "@scandic-hotels/common/dataCache" import type { CacheTime } from "@scandic-hotels/common/dataCache"
@@ -18,6 +20,7 @@ export async function batchRequest<T>(
} }
})[] })[]
): Promise<Data<T>> { ): Promise<Data<T>> {
const batchLogger = createLogger("graphql-batch-request")
try { try {
const response = await Promise.allSettled( const response = await Promise.allSettled(
queries.map((query) => queries.map((query) =>
@@ -37,15 +40,14 @@ export async function batchRequest<T>(
if (reasons.length) { if (reasons.length) {
reasons.forEach((reason) => { reasons.forEach((reason) => {
console.error(`Batch request failed`, reason) batchLogger.error(`Batch request failed`, reason)
}) })
} }
return { data } return { data }
} catch (error) { } catch (error) {
console.error("Error in batched graphql request") batchLogger.error("Error in batched graphql request", error)
console.error(error) throw error
throw new Error("Something went wrong")
} }
} }

View File

@@ -7,6 +7,7 @@ import {
type CacheTime, type CacheTime,
getCacheClient, getCacheClient,
} from "@scandic-hotels/common/dataCache" } from "@scandic-hotels/common/dataCache"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { env } from "../../env/server" import { env } from "../../env/server"
import { getPreviewHash, isPreviewByUid } from "../previewContext" import { getPreviewHash, isPreviewByUid } from "../previewContext"
@@ -25,6 +26,8 @@ export async function request<T>(
ttl: CacheTime ttl: CacheTime
} }
): Promise<Data<T>> { ): Promise<Data<T>> {
const requestLogger = createLogger("graphql-request")
const shouldUsePreview = variables?.uid const shouldUsePreview = variables?.uid
? isPreviewByUid(variables.uid) ? isPreviewByUid(variables.uid)
: false : false
@@ -33,12 +36,12 @@ export async function request<T>(
internalRequest<T>(query, shouldUsePreview, variables, getPreviewHash()) internalRequest<T>(query, shouldUsePreview, variables, getPreviewHash())
if (!cacheOptions) { if (!cacheOptions) {
console.warn("[NO CACHE] for query", query) requestLogger.warn("[NO CACHE] for query", query)
return doCall() return doCall()
} }
if (shouldUsePreview) { if (shouldUsePreview) {
console.log("[NO CACHE] [PREVIEW] for query", query) requestLogger.debug("[NO CACHE] [PREVIEW] for query", query)
return doCall() return doCall()
} }

View File

@@ -1,5 +1,6 @@
import * as Sentry from "@sentry/nextjs" import * as Sentry from "@sentry/nextjs"
import { logger } from "@scandic-hotels/common/logger"
import { getServiceToken } from "@scandic-hotels/common/tokenManager" import { getServiceToken } from "@scandic-hotels/common/tokenManager"
import { env } from "../env/server" import { env } from "../env/server"
@@ -68,10 +69,10 @@ export const protectedProcedure = baseProcedure.use(async function (opts) {
const session = await opts.ctx.auth() const session = await opts.ctx.auth()
if (!authRequired && env.NODE_ENV === "development") { if (!authRequired && env.NODE_ENV === "development") {
console.info( logger.debug(
`❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌` `❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌`
) )
console.info(`path: ${opts.path} | type: ${opts.type}`) logger.debug(`path: ${opts.path} | type: ${opts.type}`)
} }
if (!session) { if (!session) {
@@ -93,10 +94,10 @@ export const safeProtectedProcedure = baseProcedure.use(async function (opts) {
const authRequired = opts.meta?.authRequired ?? true const authRequired = opts.meta?.authRequired ?? true
let session = await opts.ctx.auth() let session = await opts.ctx.auth()
if (!authRequired && env.NODE_ENV === "development") { if (!authRequired && env.NODE_ENV === "development") {
console.info( logger.debug(
`❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌` `❌❌❌❌ You are opting out of authorization, if its done on purpose maybe you should use the publicProcedure instead. ❌❌❌❌`
) )
console.info(`path: ${opts.path} | type: ${opts.type}`) logger.debug(`path: ${opts.path} | type: ${opts.type}`)
} }
if (!session || session.error === "RefreshAccessTokenError") { if (!session || session.error === "RefreshAccessTokenError") {

View File

@@ -2,6 +2,7 @@ import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { isDefined } from "@scandic-hotels/common/utils/isDefined" import { isDefined } from "@scandic-hotels/common/utils/isDefined"
import { safeTry } from "@scandic-hotels/common/utils/safeTry" import { safeTry } from "@scandic-hotels/common/utils/safeTry"
@@ -97,6 +98,7 @@ export async function getAutoCompleteDestinationsData({
serviceToken: string serviceToken: string
warmup?: boolean warmup?: boolean
}) { }) {
const autoCompleteLogger = createLogger("autocomplete-destinations")
const cacheClient = await getCacheClient() const cacheClient = await getCacheClient()
return await cacheClient.cacheOrGet( return await cacheClient.cacheOrGet(
`autocomplete:destinations:locations:${lang}`, `autocomplete:destinations:locations:${lang}`,
@@ -110,7 +112,7 @@ export async function getAutoCompleteDestinationsData({
}) })
if (!countries) { if (!countries) {
console.error("Unable to fetch countries") autoCompleteLogger.error("Unable to fetch countries")
throw new Error("Unable to fetch countries") throw new Error("Unable to fetch countries")
} }
@@ -146,7 +148,7 @@ export async function getAutoCompleteDestinationsData({
!cityUrls || !cityUrls ||
!countryUrls !countryUrls
) { ) {
console.error("Unable to fetch location URLs") autoCompleteLogger.error("Unable to fetch location URLs")
throw new Error("Unable to fetch location URLs") throw new Error("Unable to fetch location URLs")
} }

View File

@@ -1,3 +1,4 @@
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { createCounter } from "@scandic-hotels/common/telemetry" import { createCounter } from "@scandic-hotels/common/telemetry"
import { router } from "../.." import { router } from "../.."
@@ -19,6 +20,7 @@ import { bookingConfirmationSchema, createBookingSchema } from "./output"
import { cancelBooking } from "./utils" import { cancelBooking } from "./utils"
const refIdPlugin = createRefIdPlugin() const refIdPlugin = createRefIdPlugin()
const bookingLogger = createLogger("trpc.booking")
export const bookingMutationRouter = router({ export const bookingMutationRouter = router({
create: safeProtectedServiceProcedure create: safeProtectedServiceProcedure
@@ -173,10 +175,10 @@ export const bookingMutationRouter = router({
continue continue
} }
} else { } else {
console.info( bookingLogger.error(
`Cancelling booking failed for confirmationNumber: ${confirmationNumbers[idx]}` `Cancelling booking failed for confirmationNumber: ${confirmationNumbers[idx]}`,
response.reason
) )
console.error(response.reason)
} }
cancelledRoomsSuccessfully.push(null) cancelledRoomsSuccessfully.push(null)

View File

@@ -1,6 +1,7 @@
import { z, ZodError, ZodIssueCode } from "zod" import { z, ZodError, ZodIssueCode } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { logger } from "@scandic-hotels/common/logger"
import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url" import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url"
import { AlertTypeEnum } from "../../../types/alertType" import { AlertTypeEnum } from "../../../types/alertType"
@@ -541,7 +542,7 @@ export const headerRefsSchema = z
}) })
.transform((data) => { .transform((data) => {
if (!data.all_header.items.length) { if (!data.all_header.items.length) {
console.info(`Zod Error - No header returned in refs request`) logger.error(`Zod Error - No header returned in refs request`)
throw new ZodError([ throw new ZodError([
{ {
code: ZodIssueCode.custom, code: ZodIssueCode.custom,
@@ -688,7 +689,7 @@ export const headerSchema = z
}) })
.transform((data) => { .transform((data) => {
if (!data.all_header.items.length) { if (!data.all_header.items.length) {
console.info(`Zod Error - No header returned in request`) logger.error(`Zod Error - No header returned in request`)
throw new ZodError([ throw new ZodError([
{ {
code: ZodIssueCode.custom, code: ZodIssueCode.custom,

View File

@@ -1,4 +1,5 @@
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { createCounter } from "@scandic-hotels/common/telemetry" import { createCounter } from "@scandic-hotels/common/telemetry"
import { removeTrailingSlash } from "@scandic-hotels/common/utils/url" import { removeTrailingSlash } from "@scandic-hotels/common/utils/url"
@@ -68,6 +69,10 @@ export async function getUrlsOfAllLanguages(
uid: string, uid: string,
contentType: string contentType: string
) { ) {
const languageSwitcherLogger = createLogger(
"trpc.contentstack.languageSwitcher"
)
const getLanguageSwitcherCounter = createCounter( const getLanguageSwitcherCounter = createCounter(
"trpc.contentstack", "trpc.contentstack",
"languageSwitcher.get" "languageSwitcher.get"
@@ -143,8 +148,9 @@ export async function getUrlsOfAllLanguages(
fiNoSvDocument = GetFiNoSvUrlsStartPage fiNoSvDocument = GetFiNoSvUrlsStartPage
break break
default: default:
console.error(`type: [${contentType}]`) languageSwitcherLogger.error(
console.error(`Trying to get a content type that is not supported`) `Trying to get a content type that is not supported, ${contentType}`
)
throw internalServerError() throw internalServerError()
} }

View File

@@ -1,5 +1,6 @@
import { z } from "zod" import { z } from "zod"
import { logger } from "@scandic-hotels/common/logger"
import { toLang } from "@scandic-hotels/common/utils/languages" import { toLang } from "@scandic-hotels/common/utils/languages"
import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator" import { nullableStringValidator } from "@scandic-hotels/common/utils/zod/stringValidator"
@@ -115,7 +116,7 @@ function getRate(rate: RateDefinition) {
case "NotCancellable": case "NotCancellable":
return RateEnum.save return RateEnum.save
default: default:
console.info( logger.warn(
`Unknown cancellationRule [${rate.cancellationRule}]. This should never happen!` `Unknown cancellationRule [${rate.cancellationRule}]. This should never happen!`
) )
return null return null

View File

@@ -1,6 +1,7 @@
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { dt } from "@scandic-hotels/common/dt" import { dt } from "@scandic-hotels/common/dt"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { createCounter } from "@scandic-hotels/common/telemetry" import { createCounter } from "@scandic-hotels/common/telemetry"
import { env } from "../../../env/server" import { env } from "../../../env/server"
@@ -79,6 +80,8 @@ import type {
import type { CityLocation } from "../../types/locations" import type { CityLocation } from "../../types/locations"
import type { Room } from "../../types/room" import type { Room } from "../../types/room"
const hotelQueryLogger = createLogger("hotelQueryRouter")
export const hotelQueryRouter = router({ export const hotelQueryRouter = router({
availability: router({ availability: router({
hotelsByCity: safeProtectedServiceProcedure hotelsByCity: safeProtectedServiceProcedure
@@ -189,8 +192,10 @@ export const hotelQueryRouter = router({
const selectedRooms = [] const selectedRooms = []
for (const [idx, room] of availability.entries()) { for (const [idx, room] of availability.entries()) {
if (!room || "error" in room) { if (!room || "error" in room) {
console.info(`Availability failed: ${room.error}`) hotelQueryLogger.error(
console.error(room.details) `Availability failed: ${room.error}`,
room.details
)
selectedRooms.push(null) selectedRooms.push(null)
continue continue
} }
@@ -203,7 +208,7 @@ export const hotelQueryRouter = router({
ctx.userPoints ctx.userPoints
) )
if (!selected) { if (!selected) {
console.error("Unable to find selected room") hotelQueryLogger.error("Unable to find selected room")
selectedRooms.push(null) selectedRooms.push(null)
continue continue
} }
@@ -347,7 +352,7 @@ export const hotelQueryRouter = router({
) )
if (!selected) { if (!selected) {
console.error("Unable to find selected room") hotelQueryLogger.error("Unable to find selected room")
return null return null
} }
@@ -895,13 +900,13 @@ export const hotelQueryRouter = router({
const data = await response.json() const data = await response.json()
if (data.status !== "OK") { if (data.status !== "OK") {
console.error(`Geocode error: ${data.status}`) hotelQueryLogger.error(`Geocode error: ${data.status}`)
return null return null
} }
const location = data.results[0]?.geometry?.location const location = data.results[0]?.geometry?.location
if (!location) { if (!location) {
console.error("No location found in geocode response") hotelQueryLogger.error("No location found in geocode response")
return null return null
} }

View File

@@ -4,6 +4,7 @@ import stringify from "json-stable-stringify-without-jsonify"
import { Lang } from "@scandic-hotels/common/constants/language" import { Lang } from "@scandic-hotels/common/constants/language"
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation" import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { getCacheClient } from "@scandic-hotels/common/dataCache" import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { createCounter } from "@scandic-hotels/common/telemetry" import { createCounter } from "@scandic-hotels/common/telemetry"
import { chunk } from "@scandic-hotels/common/utils/chunk" import { chunk } from "@scandic-hotels/common/utils/chunk"
@@ -64,6 +65,8 @@ import type {
import type { Cities } from "./output" import type { Cities } from "./output"
export const locationsAffix = "locations" export const locationsAffix = "locations"
const hotelUtilsLogger = createLogger("hotelUtils")
export async function getCitiesByCountry({ export async function getCitiesByCountry({
countries, countries,
lang, lang,
@@ -101,8 +104,11 @@ export async function getCitiesByCountry({
const countryJson = await countryResponse.json() const countryJson = await countryResponse.json()
const citiesByCountry = citiesByCountrySchema.safeParse(countryJson) const citiesByCountry = citiesByCountrySchema.safeParse(countryJson)
if (!citiesByCountry.success) { if (!citiesByCountry.success) {
console.error(`Unable to parse cities by country ${country}`) hotelUtilsLogger.error(
console.error(citiesByCountry.error) `Unable to parse cities by country ${country}`,
citiesByCountry.error
)
throw new Error(`Unable to parse cities by country ${country}`) throw new Error(`Unable to parse cities by country ${country}`)
} }
return { ...citiesByCountry.data, country } return { ...citiesByCountry.data, country }
@@ -160,8 +166,10 @@ export async function getCountries({
const countriesJson = await countryResponse.json() const countriesJson = await countryResponse.json()
const countries = countriesSchema.safeParse(countriesJson) const countries = countriesSchema.safeParse(countriesJson)
if (!countries.success) { if (!countries.success) {
console.info(`Validation for countries failed`) hotelUtilsLogger.error(
console.error(countries.error) `Validation for countries failed`,
countries.error
)
return null return null
} }
@@ -447,8 +455,10 @@ export async function getLocations({
const apiJson = await apiResponse.json() const apiJson = await apiResponse.json()
const verifiedLocations = locationsSchema.safeParse(apiJson) const verifiedLocations = locationsSchema.safeParse(apiJson)
if (!verifiedLocations.success) { if (!verifiedLocations.success) {
console.info(`Locations Verification Failed`) hotelUtilsLogger.error(
console.error(verifiedLocations.error) `Locations Verification Failed`,
verifiedLocations.error
)
throw new Error("Unable to parse locations") throw new Error("Unable to parse locations")
} }
const chunkedLocations = chunk(verifiedLocations.data.data, 10) const chunkedLocations = chunk(verifiedLocations.data.data, 10)
@@ -471,10 +481,10 @@ export async function getLocations({
country, country,
} }
} else { } else {
console.info( hotelUtilsLogger.error(
`Location cannot be found in any of the countries cities` `Location cannot be found in any of the countries cities`,
location
) )
console.info(location)
} }
} }
} else if (location.type === "hotels") { } else if (location.type === "hotels") {
@@ -614,9 +624,10 @@ export async function getCity({
const cityJson = await cityResponse.json() const cityJson = await cityResponse.json()
const city = citiesSchema.safeParse(cityJson) const city = citiesSchema.safeParse(cityJson)
if (!city.success) { if (!city.success) {
console.info(`Validation of city failed`) hotelUtilsLogger.error(`Validation of city failed`, {
console.info(`cityUrl: ${cityUrl}`) error: city.error,
console.error(city.error) cityUrl,
})
return null return null
} }

View File

@@ -1,6 +1,8 @@
import * as Sentry from "@sentry/nextjs" import * as Sentry from "@sentry/nextjs"
import { z } from "zod" import { z } from "zod"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import * as api from "../../../api" import * as api from "../../../api"
import { protectedProcedure } from "../../../procedures" import { protectedProcedure } from "../../../procedures"
import { getOTPState } from "./otp/getOTPState" import { getOTPState } from "./otp/getOTPState"
@@ -18,13 +20,14 @@ const outputSchema = z.object({
]), ]),
}) })
const sasLogger = createLogger("SAS")
export const linkAccount = protectedProcedure export const linkAccount = protectedProcedure
.output(outputSchema) .output(outputSchema)
.mutation(async function ({ ctx }) { .mutation(async function ({ ctx }) {
const sasAuthToken = await getSasToken() const sasAuthToken = await getSasToken()
const { referenceId } = await getOTPState() const { referenceId } = await getOTPState()
console.log("[SAS] link account") sasLogger.debug("[SAS] link account")
const apiResponse = await api.post(api.endpoints.v1.Profile.link, { const apiResponse = await api.post(api.endpoints.v1.Profile.link, {
headers: { headers: {
@@ -47,7 +50,7 @@ export const linkAccount = protectedProcedure
linkedAndBoosted || linkedWithoutBoost || linkedWithUnknownBoost linkedAndBoosted || linkedWithoutBoost || linkedWithUnknownBoost
if (linked) { if (linked) {
console.log("[SAS] link account done") sasLogger.debug("[SAS] link account done")
return { linkingState: "linked" } return { linkingState: "linked" }
} }
@@ -56,12 +59,12 @@ export const linkAccount = protectedProcedure
const data = badRequestSchema.safeParse(result) const data = badRequestSchema.safeParse(result)
if (!data.success) { if (!data.success) {
const linkAccountBadRequestSchemaError = `[SAS] failed to parse link account bad request schema ${JSON.stringify(data.error)}` const linkAccountBadRequestSchemaError = `[SAS] failed to parse link account bad request schema ${JSON.stringify(data.error)}`
console.error(linkAccountBadRequestSchemaError) sasLogger.error(linkAccountBadRequestSchemaError)
Sentry.captureMessage(linkAccountBadRequestSchemaError) Sentry.captureMessage(linkAccountBadRequestSchemaError)
return { linkingState: "error" } return { linkingState: "error" }
} }
console.log("[SAS] link account error with response", result) sasLogger.error("[SAS] link account error with response", result)
const { errors } = data.data const { errors } = data.data
@@ -89,7 +92,7 @@ export const linkAccount = protectedProcedure
} }
const errorMessage = `[SAS] link account error with status code ${apiResponse.status} and response ${await apiResponse.text()}` const errorMessage = `[SAS] link account error with status code ${apiResponse.status} and response ${await apiResponse.text()}`
console.warn(errorMessage) sasLogger.error(errorMessage)
Sentry.captureMessage(errorMessage) Sentry.captureMessage(errorMessage)
return { linkingState: "error" } return { linkingState: "error" }
}) })

Some files were not shown because too many files have changed in this diff Show More