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

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

View File

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

View File

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

View File

@@ -1,5 +1,6 @@
import * as Sentry from "@sentry/nextjs"
import { logger } from "@scandic-hotels/common/logger"
import { getServiceToken } from "@scandic-hotels/common/tokenManager"
import { env } from "../env/server"
@@ -68,10 +69,10 @@ export const protectedProcedure = baseProcedure.use(async function (opts) {
const session = await opts.ctx.auth()
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. ❌❌❌❌`
)
console.info(`path: ${opts.path} | type: ${opts.type}`)
logger.debug(`path: ${opts.path} | type: ${opts.type}`)
}
if (!session) {
@@ -93,10 +94,10 @@ export const safeProtectedProcedure = baseProcedure.use(async function (opts) {
const authRequired = opts.meta?.authRequired ?? true
let session = await opts.ctx.auth()
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. ❌❌❌❌`
)
console.info(`path: ${opts.path} | type: ${opts.type}`)
logger.debug(`path: ${opts.path} | type: ${opts.type}`)
}
if (!session || session.error === "RefreshAccessTokenError") {

View File

@@ -2,6 +2,7 @@ import { z } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { isDefined } from "@scandic-hotels/common/utils/isDefined"
import { safeTry } from "@scandic-hotels/common/utils/safeTry"
@@ -97,6 +98,7 @@ export async function getAutoCompleteDestinationsData({
serviceToken: string
warmup?: boolean
}) {
const autoCompleteLogger = createLogger("autocomplete-destinations")
const cacheClient = await getCacheClient()
return await cacheClient.cacheOrGet(
`autocomplete:destinations:locations:${lang}`,
@@ -110,7 +112,7 @@ export async function getAutoCompleteDestinationsData({
})
if (!countries) {
console.error("Unable to fetch countries")
autoCompleteLogger.error("Unable to fetch countries")
throw new Error("Unable to fetch countries")
}
@@ -146,7 +148,7 @@ export async function getAutoCompleteDestinationsData({
!cityUrls ||
!countryUrls
) {
console.error("Unable to fetch location URLs")
autoCompleteLogger.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 { router } from "../.."
@@ -19,6 +20,7 @@ import { bookingConfirmationSchema, createBookingSchema } from "./output"
import { cancelBooking } from "./utils"
const refIdPlugin = createRefIdPlugin()
const bookingLogger = createLogger("trpc.booking")
export const bookingMutationRouter = router({
create: safeProtectedServiceProcedure
@@ -173,10 +175,10 @@ export const bookingMutationRouter = router({
continue
}
} else {
console.info(
`Cancelling booking failed for confirmationNumber: ${confirmationNumbers[idx]}`
bookingLogger.error(
`Cancelling booking failed for confirmationNumber: ${confirmationNumbers[idx]}`,
response.reason
)
console.error(response.reason)
}
cancelledRoomsSuccessfully.push(null)

View File

@@ -1,6 +1,7 @@
import { z, ZodError, ZodIssueCode } from "zod"
import { Lang } from "@scandic-hotels/common/constants/language"
import { logger } from "@scandic-hotels/common/logger"
import { removeMultipleSlashes } from "@scandic-hotels/common/utils/url"
import { AlertTypeEnum } from "../../../types/alertType"
@@ -541,7 +542,7 @@ export const headerRefsSchema = z
})
.transform((data) => {
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([
{
code: ZodIssueCode.custom,
@@ -688,7 +689,7 @@ export const headerSchema = z
})
.transform((data) => {
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([
{
code: ZodIssueCode.custom,

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
import { Lang } from "@scandic-hotels/common/constants/language"
import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { dt } from "@scandic-hotels/common/dt"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { createCounter } from "@scandic-hotels/common/telemetry"
import { env } from "../../../env/server"
@@ -79,6 +80,8 @@ import type {
import type { CityLocation } from "../../types/locations"
import type { Room } from "../../types/room"
const hotelQueryLogger = createLogger("hotelQueryRouter")
export const hotelQueryRouter = router({
availability: router({
hotelsByCity: safeProtectedServiceProcedure
@@ -189,8 +192,10 @@ export const hotelQueryRouter = router({
const selectedRooms = []
for (const [idx, room] of availability.entries()) {
if (!room || "error" in room) {
console.info(`Availability failed: ${room.error}`)
console.error(room.details)
hotelQueryLogger.error(
`Availability failed: ${room.error}`,
room.details
)
selectedRooms.push(null)
continue
}
@@ -203,7 +208,7 @@ export const hotelQueryRouter = router({
ctx.userPoints
)
if (!selected) {
console.error("Unable to find selected room")
hotelQueryLogger.error("Unable to find selected room")
selectedRooms.push(null)
continue
}
@@ -347,7 +352,7 @@ export const hotelQueryRouter = router({
)
if (!selected) {
console.error("Unable to find selected room")
hotelQueryLogger.error("Unable to find selected room")
return null
}
@@ -895,13 +900,13 @@ export const hotelQueryRouter = router({
const data = await response.json()
if (data.status !== "OK") {
console.error(`Geocode error: ${data.status}`)
hotelQueryLogger.error(`Geocode error: ${data.status}`)
return null
}
const location = data.results[0]?.geometry?.location
if (!location) {
console.error("No location found in geocode response")
hotelQueryLogger.error("No location found in geocode response")
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 { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
import { getCacheClient } from "@scandic-hotels/common/dataCache"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { createCounter } from "@scandic-hotels/common/telemetry"
import { chunk } from "@scandic-hotels/common/utils/chunk"
@@ -64,6 +65,8 @@ import type {
import type { Cities } from "./output"
export const locationsAffix = "locations"
const hotelUtilsLogger = createLogger("hotelUtils")
export async function getCitiesByCountry({
countries,
lang,
@@ -101,8 +104,11 @@ export async function getCitiesByCountry({
const countryJson = await countryResponse.json()
const citiesByCountry = citiesByCountrySchema.safeParse(countryJson)
if (!citiesByCountry.success) {
console.error(`Unable to parse cities by country ${country}`)
console.error(citiesByCountry.error)
hotelUtilsLogger.error(
`Unable to parse cities by country ${country}`,
citiesByCountry.error
)
throw new Error(`Unable to parse cities by country ${country}`)
}
return { ...citiesByCountry.data, country }
@@ -160,8 +166,10 @@ export async function getCountries({
const countriesJson = await countryResponse.json()
const countries = countriesSchema.safeParse(countriesJson)
if (!countries.success) {
console.info(`Validation for countries failed`)
console.error(countries.error)
hotelUtilsLogger.error(
`Validation for countries failed`,
countries.error
)
return null
}
@@ -447,8 +455,10 @@ export async function getLocations({
const apiJson = await apiResponse.json()
const verifiedLocations = locationsSchema.safeParse(apiJson)
if (!verifiedLocations.success) {
console.info(`Locations Verification Failed`)
console.error(verifiedLocations.error)
hotelUtilsLogger.error(
`Locations Verification Failed`,
verifiedLocations.error
)
throw new Error("Unable to parse locations")
}
const chunkedLocations = chunk(verifiedLocations.data.data, 10)
@@ -471,10 +481,10 @@ export async function getLocations({
country,
}
} else {
console.info(
`Location cannot be found in any of the countries cities`
hotelUtilsLogger.error(
`Location cannot be found in any of the countries cities`,
location
)
console.info(location)
}
}
} else if (location.type === "hotels") {
@@ -614,9 +624,10 @@ export async function getCity({
const cityJson = await cityResponse.json()
const city = citiesSchema.safeParse(cityJson)
if (!city.success) {
console.info(`Validation of city failed`)
console.info(`cityUrl: ${cityUrl}`)
console.error(city.error)
hotelUtilsLogger.error(`Validation of city failed`, {
error: city.error,
cityUrl,
})
return null
}

View File

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

View File

@@ -4,6 +4,8 @@ import { cookies } from "next/headers"
import { v4 as uuidv4 } from "uuid"
import { z } from "zod"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { env } from "../../../../../../env/server"
import { protectedProcedure } from "../../../../../procedures"
import { getSasToken } from "../../getSasToken"
@@ -37,6 +39,7 @@ const failureSchema = z.object({
const outputSchema = z.union([successSchema, failureSchema])
const sasLogger = createLogger("SAS")
export const requestOtp = protectedProcedure
.output(outputSchema)
.mutation(async function () {
@@ -47,8 +50,8 @@ export const requestOtp = protectedProcedure
}
const tokenResponse = await fetchRequestOtp({ sasAuthToken })
console.log(
"[SAS] requestOtp",
sasLogger.debug(
"requestOtp",
tokenResponse.status,
tokenResponse.statusText
)
@@ -56,7 +59,7 @@ export const requestOtp = protectedProcedure
const body = await tokenResponse.json()
const parseResult = outputSchema.safeParse(body)
if (!parseResult.success) {
console.error("[SAS] requestOtp error", body)
sasLogger.error("requestOtp error", body)
if (!tokenResponse.ok) {
throw createError(body)
@@ -67,8 +70,8 @@ export const requestOtp = protectedProcedure
if (parseResult.data.status === "SENT") {
setSASOtpCookie(parseResult.data)
} else {
const sasRequestOtpErrorMessage = `[SAS] requestOtp did not return SENT status with body: ${JSON.stringify(body)}`
console.warn(sasRequestOtpErrorMessage)
const sasRequestOtpErrorMessage = `requestOtp did not return SENT status with body: ${JSON.stringify(body)}`
sasLogger.warn(sasRequestOtpErrorMessage)
Sentry.captureMessage(sasRequestOtpErrorMessage)
}
@@ -87,7 +90,7 @@ function createError(
| RequestOtpGeneralError
): TRPCError {
const errorInfo = parseSASRequestOtpError(errorBody)
console.error("[SAS] createError", errorInfo)
sasLogger.error("createError", errorInfo)
return new TRPCError({
code: "BAD_REQUEST",
cause: errorInfo,
@@ -97,7 +100,7 @@ function createError(
async function fetchRequestOtp({ sasAuthToken }: { sasAuthToken: string }) {
const endpoint = `${env.SAS_API_ENDPOINT}/api/scandic-partnership/customer/send-otp`
console.log("[SAS]: Requesting OTP")
sasLogger.debug("Requesting OTP")
return await fetch(endpoint, {
method: "POST",

View File

@@ -1,5 +1,7 @@
import { z } from "zod"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
export type RequestOtpResponseError = "TOO_MANY_REQUESTS" | "UNKNOWN"
const requestOtpGeneralError = z.enum([
@@ -39,14 +41,14 @@ const SAS_REQUEST_OTP_ERROR_CODES: {
} = {
TOO_MANY_REQUESTS: 10,
}
const sasLogger = createLogger("SAS")
const getErrorCodeByNumber = (number: number): RequestOtpResponseError => {
const v =
Object.entries(SAS_REQUEST_OTP_ERROR_CODES).find(
([_, value]) => value === number
)?.[0] ?? "UNKNOWN"
console.log("[SAS] getErrorCodeByNumber", number, v)
sasLogger.debug("getErrorCodeByNumber", number, v)
return v as RequestOtpResponseError
}

View File

@@ -1,6 +1,8 @@
import { TRPCError } from "@trpc/server"
import { z } from "zod"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { env } from "../../../../../../env/server"
import { protectedProcedure } from "../../../../../procedures"
import { getSasToken } from "../../getSasToken"
@@ -29,6 +31,7 @@ const outputSchema = z.object({
databaseUUID: z.string().uuid().optional(),
})
const sasLogger = createLogger("SAS")
export const verifyOtp = protectedProcedure
.input(inputSchema)
.output(outputSchema)
@@ -40,14 +43,14 @@ export const verifyOtp = protectedProcedure
}
const verifyResponse = await fetchVerifyOtp(input)
console.log(
"[SAS] verifyOTP",
sasLogger.debug(
"verifyOTP",
verifyResponse.status,
verifyResponse.statusText
)
if (verifyResponse.status > 499) {
console.error("[SAS] verifyOTP error", await verifyResponse.text())
sasLogger.error("verifyOTP error", await verifyResponse.text())
throw new TRPCError({
code: "SERVICE_UNAVAILABLE",
message: "Error from downstream SAS service",
@@ -55,15 +58,15 @@ export const verifyOtp = protectedProcedure
}
const data = await verifyResponse.json()
console.log("[SAS] verifyOTP data", data)
sasLogger.debug("verifyOTP data", data)
const result = outputSchema.safeParse(data)
if (!result.success) {
console.error("[SAS] verifyOTP error", result.error)
sasLogger.error("verifyOTP error", result.error)
throw createError(data)
}
console.log("[SAS] verifyOTP success")
console.log("[SAS] verifyOTP responding", result.data)
sasLogger.debug("verifyOTP success")
sasLogger.debug("verifyOTP responding", result.data)
return result.data
})

View File

@@ -3,6 +3,7 @@ import { cookies } from "next/headers"
import { z } from "zod"
import { FriendsMembershipLevels } from "@scandic-hotels/common/constants/membershipLevels"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import * as api from "../../../api"
import { protectedProcedure } from "../../../procedures"
@@ -20,7 +21,7 @@ const notMatchedSchema = z.object({
})
const outputSchema = z.union([matchedSchema, notMatchedSchema])
const sasLogger = createLogger("SAS")
export const performLevelUpgrade = protectedProcedure
.output(outputSchema)
.mutation(async function ({ ctx }) {
@@ -36,7 +37,7 @@ export const performLevelUpgrade = protectedProcedure
}
const currentLevel = profile.data.membership.membershipLevel
console.log("[SAS] tier match started")
sasLogger.debug("tier match started")
const apiResponse = await api.post(api.endpoints.v1.Profile.matchTier, {
headers: {
@@ -55,14 +56,14 @@ export const performLevelUpgrade = protectedProcedure
const updated = apiResponse.status === 200
if (updated) {
console.log("[SAS] tier match complete - boosted")
sasLogger.debug("tier match complete - boosted")
const result = await apiResponse.json()
const user = getUserSchema.parse(result)
if (!user.membership) {
const tierMatchErrorNoMembershipMessage =
"[SAS] tier match error - no membership"
console.log(tierMatchErrorNoMembershipMessage)
"tier match error - no membership"
sasLogger.debug(tierMatchErrorNoMembershipMessage)
Sentry.captureException(new Error(tierMatchErrorNoMembershipMessage))
return { tierMatchState: "error" }
}
@@ -77,21 +78,20 @@ export const performLevelUpgrade = protectedProcedure
const matchedNoChange = apiResponse.status === 204
if (matchedNoChange) {
console.log("[SAS] tier match complete - no change")
sasLogger.debug("tier match complete - no change")
return { tierMatchState: "alreadyMatched" }
}
const notLinked = apiResponse.status === 404
if (notLinked) {
const tierMatchErrorNotLinkedMessage =
"[SAS] tier match error - not linked"
console.warn(tierMatchErrorNotLinkedMessage)
const tierMatchErrorNotLinkedMessage = "tier match error - not linked"
sasLogger.error(tierMatchErrorNotLinkedMessage)
Sentry.captureMessage(tierMatchErrorNotLinkedMessage)
return { tierMatchState: "notLinked" }
}
const tierMatchErrorMessage = `[SAS] tier match error with status code ${apiResponse.status} and response ${await apiResponse.text()}`
console.error(tierMatchErrorMessage)
const tierMatchErrorMessage = `tier match error with status code ${apiResponse.status} and response ${await apiResponse.text()}`
sasLogger.error(tierMatchErrorMessage)
Sentry.captureException(new Error(tierMatchErrorMessage))
return { tierMatchState: "error" }
})

View File

@@ -1,6 +1,8 @@
import * as Sentry from "@sentry/nextjs"
import { z } from "zod"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import * as api from "../../../api"
import { protectedProcedure } from "../../../procedures"
import { getOTPState } from "./otp/getOTPState"
@@ -14,6 +16,7 @@ const transferPointsInputSchema = z.object({
points: z.number(),
})
const sasLogger = createLogger("SAS")
export const transferPoints = protectedProcedure
.output(outputSchema)
.input(transferPointsInputSchema)
@@ -43,20 +46,20 @@ export const transferPoints = protectedProcedure
const data = badRequestSchema.safeParse(result)
if (!data.success) {
const transferPointsBadRequestSchemaError = `[SAS] failed to parse transfer points bad request schema ${JSON.stringify(data.error)}`
console.error(transferPointsBadRequestSchemaError)
sasLogger.error(transferPointsBadRequestSchemaError)
Sentry.captureMessage(transferPointsBadRequestSchemaError)
return { transferState: "error" }
}
}
if (apiResponse.status === 404) {
const transferPointsNotFoundError = `[SAS] transfer points failed, no active partner link`
console.error(transferPointsNotFoundError)
sasLogger.error(transferPointsNotFoundError)
Sentry.captureMessage(transferPointsNotFoundError)
return { transferState: "notLinked" }
}
const errorMessage = `[SAS] transfer points error with status code ${apiResponse.status} and response ${await apiResponse.text()}`
console.warn(errorMessage)
sasLogger.error(errorMessage)
Sentry.captureMessage(errorMessage)
return { transferState: "error" }
})

View File

@@ -1,5 +1,7 @@
import { z } from "zod"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import * as api from "../../../api"
import { protectedProcedure } from "../../../procedures"
import { getOTPState } from "./otp/getOTPState"
@@ -8,7 +10,7 @@ import { getSasToken } from "./getSasToken"
const outputSchema = z.object({
linkingState: z.enum(["unlinked", "notLinked", "error"]),
})
const sasLogger = createLogger("SAS")
export const unlinkAccount = protectedProcedure
.output(outputSchema)
.mutation(async function ({ ctx }) {
@@ -29,24 +31,24 @@ export const unlinkAccount = protectedProcedure
})
if (apiResponse.status === 204 || apiResponse.status === 202) {
console.log("[SAS] unlink account success")
sasLogger.debug("unlink account success")
return { linkingState: "unlinked" }
}
if (apiResponse.status === 400) {
const result = await apiResponse.json()
console.log("[SAS] unlink account error with response", result)
sasLogger.debug("unlink account error with response", result)
return { linkingState: "error" }
}
if (apiResponse.status === 404) {
console.log("[SAS] tried unlinking an account that was not linked")
sasLogger.debug("tried unlinking an account that was not linked")
return { linkingState: "notLinked" }
}
console.log(
`[SAS] unlink account error with status code ${apiResponse.status} and response ${await apiResponse.text()}`
sasLogger.debug(
`unlink account error with status code ${apiResponse.status} and response ${await apiResponse.text()}`
)
return { linkingState: "error" }
})

View File

@@ -1,4 +1,5 @@
import { signupVerify } from "@scandic-hotels/common/constants/routes/signup"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { createCounter } from "@scandic-hotels/common/telemetry"
import { env } from "../../../env/server"
@@ -14,13 +15,14 @@ import {
} from "./input"
import { initiateSaveCardSchema, subscriberIdSchema } from "./output"
const userMutationLogger = createLogger("userMutationRouter")
export const userMutationRouter = router({
creditCard: router({
add: protectedProcedure.input(addCreditCardInput).mutation(async function ({
ctx,
input,
}) {
console.info(
userMutationLogger.info(
"api.user.creditCard.add start",
JSON.stringify({ query: { language: input.language } })
)
@@ -40,7 +42,7 @@ export const userMutationRouter = router({
if (!apiResponse.ok) {
const text = await apiResponse.text()
console.error(
userMutationLogger.error(
"api.user.creditCard.add error",
JSON.stringify({
query: { language: input.language },
@@ -57,7 +59,7 @@ export const userMutationRouter = router({
const apiJson = await apiResponse.json()
const verifiedData = initiateSaveCardSchema.safeParse(apiJson)
if (!verifiedData.success) {
console.error(
userMutationLogger.error(
"api.user.creditCard.add validation error",
JSON.stringify({
query: { language: input.language },
@@ -66,7 +68,7 @@ export const userMutationRouter = router({
)
return null
}
console.info(
userMutationLogger.info(
"api.user.creditCard.add success",
JSON.stringify({ query: { language: input.language } })
)
@@ -75,7 +77,10 @@ export const userMutationRouter = router({
save: protectedProcedure
.input(saveCreditCardInput)
.mutation(async function ({ ctx, input }) {
console.info("api.user.creditCard.save start", JSON.stringify({}))
userMutationLogger.info(
"api.user.creditCard.save start",
JSON.stringify({})
)
const apiResponse = await api.post(
api.endpoints.v1.Profile.CreditCards.transaction(input.transactionId),
{
@@ -87,7 +92,7 @@ export const userMutationRouter = router({
if (!apiResponse.ok) {
const text = await apiResponse.text()
console.error(
userMutationLogger.error(
"api.user.creditCard.save error",
JSON.stringify({
error: {
@@ -99,13 +104,16 @@ export const userMutationRouter = router({
)
return false
}
console.info("api.user.creditCard.save success", JSON.stringify({}))
userMutationLogger.info(
"api.user.creditCard.save success",
JSON.stringify({})
)
return true
}),
delete: protectedProcedure
.input(deleteCreditCardInput)
.mutation(async function ({ ctx, input }) {
console.info(
userMutationLogger.info(
"api.user.creditCard.delete start",
JSON.stringify({ query: {} })
)
@@ -122,7 +130,7 @@ export const userMutationRouter = router({
if (!apiResponse.ok) {
const text = await apiResponse.text()
console.error(
userMutationLogger.error(
"api.user.creditCard.delete error",
JSON.stringify({
error: {
@@ -135,7 +143,10 @@ export const userMutationRouter = router({
)
return false
}
console.info("api.user.creditCard.delete success", JSON.stringify({}))
userMutationLogger.info(
"api.user.creditCard.delete success",
JSON.stringify({})
)
return true
}),
}),

View File

@@ -1,5 +1,7 @@
import * as Sentry from "@sentry/nextjs"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { appRouter } from "./routers/appRouter"
import { createCallerFactory } from "."
@@ -22,12 +24,13 @@ export function appServerClient(
context: Context,
options: ServerClientOptions = {}
) {
const serverClientLogger = createLogger("serverClient")
return createCaller(context, {
onError: (args) => {
const { ctx, error, input, path, type } = args
console.error(`[serverClient] error for ${type}: ${path}`, error)
serverClientLogger.error(`error for ${type}: ${path}`, error)
if (input) {
console.error(`[serverClient] received input:`, input)
serverClientLogger.error(`received input:`, input)
}
options.onError?.(args)

View File

@@ -2,12 +2,15 @@ import "server-only"
import crypto from "crypto"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import { env } from "../../env/server"
const algorithm = "DES-ECB"
const encryptionKey = env.BOOKING_ENCRYPTION_KEY
const bufferKey = Buffer.from(encryptionKey, "utf8")
const encryptionLogger = createLogger("encryption")
export function encrypt(originalString: string) {
try {
const cipher = crypto.createCipheriv(algorithm, bufferKey, null)
@@ -25,7 +28,7 @@ export function encrypt(originalString: string) {
const result = Buffer.concat(buffers).toString("base64")
return result
} catch (e) {
console.log(e)
encryptionLogger.error("encryption error", e)
return ""
}
}
@@ -46,7 +49,7 @@ export function decrypt(encryptedString: string) {
.replace(/(\x00)*/g, "")
return result
} catch (e) {
console.log(e)
encryptionLogger.error("decryption error", e)
return ""
}
}

View File

@@ -1,24 +1,27 @@
import "server-only"
import { createLogger } from "@scandic-hotels/common/logger/createLogger"
import type { Session } from "next-auth"
export function isValidSession(session: Session | null): session is Session {
const sessionLogger = createLogger("session")
if (!session) {
return false
}
if (session.error) {
console.log(`Session error: ${session.error}`)
sessionLogger.error(`Session error: ${session.error}`)
return false
}
const token = session.token
if (token?.error) {
console.log(`Session token error: ${token.error}`)
sessionLogger.error(`Session token error: ${token.error}`)
return false
}
if (token?.expires_at && token.expires_at < Date.now()) {
console.log(`Session expired: ${session.token.expires_at}`)
sessionLogger.debug(`Session expired: ${session.token.expires_at}`)
return false
}