feat: harmonize log and metrics

This commit is contained in:
Michael Zetterberg
2025-04-17 07:16:11 +02:00
parent 858a81b16f
commit 5323a8e46e
58 changed files with 2324 additions and 4726 deletions

View File

@@ -1,12 +1,8 @@
import { metrics } from "@opentelemetry/api"
import sjson from "secure-json-parse"
import * as api from "@/lib/api"
import { getVerifiedUser } from "@/server/routers/user/query"
import { getMembershipNumber } from "@/server/routers/user/utils"
import { createCounter } from "@/server/telemetry"
import { router, safeProtectedServiceProcedure } from "@/server/trpc"
import { isValidSession } from "@/utils/session"
import {
addPackageInput,
cancelBookingInput,
@@ -18,75 +14,6 @@ import {
} from "./input"
import { bookingConfirmationSchema, createBookingSchema } from "./output"
import type { Session } from "next-auth"
const meter = metrics.getMeter("trpc.bookings")
const createBookingCounter = meter.createCounter("trpc.bookings.create")
const createBookingSuccessCounter = meter.createCounter(
"trpc.bookings.create-success"
)
const createBookingFailCounter = meter.createCounter(
"trpc.bookings.create-fail"
)
const priceChangeCounter = meter.createCounter("trpc.bookings.price-change")
const priceChangeSuccessCounter = meter.createCounter(
"trpc.bookings.price-change-success"
)
const priceChangeFailCounter = meter.createCounter(
"trpc.bookings.price-change-fail"
)
const cancelBookingCounter = meter.createCounter("trpc.bookings.cancel")
const cancelBookingSuccessCounter = meter.createCounter(
"trpc.bookings.cancel-success"
)
const cancelBookingFailCounter = meter.createCounter(
"trpc.bookings.cancel-fail"
)
const addPackageCounter = meter.createCounter("trpc.bookings.add-package")
const addPackageSuccessCounter = meter.createCounter(
"trpc.bookings.add-package-success"
)
const addPackageFailCounter = meter.createCounter(
"trpc.bookings.add-package-fail"
)
const guaranteeBookingCounter = meter.createCounter("trpc.bookings.guarantee")
const guaranteeBookingSuccessCounter = meter.createCounter(
"trpc.bookings.guarantee-success"
)
const guaranteeBookingFailCounter = meter.createCounter(
"trpc.bookings.guarantee-fail"
)
const updateBookingCounter = meter.createCounter("trpc.bookings.update-booking")
const updateBookingSuccessCounter = meter.createCounter(
"trpc.bookings.update-booking-success"
)
const updateBookingFailCounter = meter.createCounter(
"trpc.bookings.update-booking-fail"
)
const removePackageCounter = meter.createCounter("trpc.bookings.remove-package")
const removePackageSuccessCounter = meter.createCounter(
"trpc.bookings.remove-package-success"
)
const removePackageFailCounter = meter.createCounter(
"trpc.bookings.remove-package-fail"
)
async function getMembershipNumber(
session: Session | null
): Promise<string | undefined> {
if (!isValidSession(session)) return undefined
const verifiedUser = await getVerifiedUser({ session })
if (!verifiedUser || "error" in verifiedUser) {
return undefined
}
return verifiedUser.data.membershipNumber
}
export const bookingMutationRouter = router({
create: safeProtectedServiceProcedure
.input(createBookingInput)
@@ -95,22 +22,17 @@ export const bookingMutationRouter = router({
const { language, ...inputWithoutLang } = input
const { hotelId, checkInDate, checkOutDate } = inputWithoutLang
const loggingAttributes = {
const createBookingCounter = createCounter("trpc.booking", "create")
const metricsCreateBooking = createBookingCounter.init({
membershipNumber: await getMembershipNumber(ctx.session),
checkInDate,
checkOutDate,
hotelId,
language,
}
})
createBookingCounter.add(1, loggingAttributes)
metricsCreateBooking.start()
console.info(
"api.booking.create start",
JSON.stringify({
query: loggingAttributes,
})
)
const headers = {
Authorization: `Bearer ${accessToken}`,
}
@@ -125,29 +47,9 @@ export const bookingMutationRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
createBookingFailCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.create error",
JSON.stringify({
query: loggingAttributes,
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
})
)
await metricsCreateBooking.httpError(apiResponse)
const apiJson = sjson.safeParse(text)
const apiJson = await apiResponse.json()
if ("errors" in apiJson && apiJson.errors.length) {
const error = apiJson.errors[0]
return { error: true, cause: error.code } as const
@@ -160,45 +62,25 @@ export const bookingMutationRouter = router({
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
createBookingFailCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
error_type: "validation_error",
})
console.error(
"api.booking.create validation error",
JSON.stringify({
query: loggingAttributes,
error: verifiedData.error,
})
)
metricsCreateBooking.validationError(verifiedData.error)
return null
}
createBookingSuccessCounter.add(1, {
hotelId,
checkInDate,
checkOutDate,
})
console.info(
"api.booking.create success",
JSON.stringify({
query: loggingAttributes,
})
)
metricsCreateBooking.success()
return verifiedData.data
}),
priceChange: safeProtectedServiceProcedure
.input(priceChangeInput)
.mutation(async function ({ ctx, input }) {
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber } = input
priceChangeCounter.add(1, { confirmationNumber })
const priceChangeCounter = createCounter("trpc.booking", "price-change")
const metricsPriceChange = priceChangeCounter.init({ confirmationNumber })
metricsPriceChange.start()
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const headers = {
Authorization: `Bearer ${accessToken}`,
@@ -213,46 +95,18 @@ export const bookingMutationRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
priceChangeFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.priceChange error",
JSON.stringify({
query: { confirmationNumber },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
await metricsPriceChange.httpError(apiResponse)
return null
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
priceChangeFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
})
console.error(
"api.booking.priceChange validation error",
JSON.stringify({
query: { confirmationNumber },
error: verifiedData.error,
})
)
metricsPriceChange.validationError(verifiedData.error)
return null
}
priceChangeSuccessCounter.add(1, { confirmationNumber })
metricsPriceChange.success()
return verifiedData.data
}),
@@ -262,6 +116,14 @@ export const bookingMutationRouter = router({
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber, language } = input
const cancelBookingCounter = createCounter("trpc.booking", "cancel")
const metricsCancelBooking = cancelBookingCounter.init({
confirmationNumber,
language,
})
metricsCancelBooking.start()
const headers = {
Authorization: `Bearer ${accessToken}`,
}
@@ -271,21 +133,6 @@ export const bookingMutationRouter = router({
reason: "WEB-CANCEL",
}
const loggingAttributes = {
confirmationNumber,
language,
}
cancelBookingCounter.add(1, loggingAttributes)
console.info(
"api.booking.cancel start",
JSON.stringify({
request: loggingAttributes,
headers,
})
)
const apiResponse = await api.remove(
api.endpoints.v1.Booking.cancel(confirmationNumber),
{
@@ -296,25 +143,7 @@ export const bookingMutationRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
cancelBookingFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.cancel error",
JSON.stringify({
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
query: loggingAttributes,
})
)
await metricsCancelBooking.httpError(apiResponse)
return false
}
@@ -323,29 +152,11 @@ export const bookingMutationRouter = router({
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
cancelBookingFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
})
console.error(
"api.booking.cancel validation error",
JSON.stringify({
query: loggingAttributes,
error: verifiedData.error,
})
)
metricsCancelBooking.validationError(verifiedData.error)
return null
}
cancelBookingSuccessCounter.add(1, loggingAttributes)
console.info(
"api.booking.cancel success",
JSON.stringify({
query: loggingAttributes,
})
)
metricsCancelBooking.success()
return verifiedData.data
}),
@@ -355,7 +166,10 @@ export const bookingMutationRouter = router({
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber, ...body } = input
addPackageCounter.add(1, { confirmationNumber })
const addPackageCounter = createCounter("trpc.booking", "package.add")
const metricsAddPackage = addPackageCounter.init({ confirmationNumber })
metricsAddPackage.start()
const headers = {
Authorization: `Bearer ${accessToken}`,
@@ -370,46 +184,18 @@ export const bookingMutationRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
addPackageFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.addPackage error",
JSON.stringify({
query: { confirmationNumber },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
await metricsAddPackage.httpError(apiResponse)
return null
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
addPackageFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
})
console.error(
"api.booking.addPackage validation error",
JSON.stringify({
query: { confirmationNumber },
error: verifiedData.error,
})
)
metricsAddPackage.validationError(verifiedData.error)
return null
}
addPackageSuccessCounter.add(1, { confirmationNumber })
metricsAddPackage.success()
return verifiedData.data
}),
@@ -419,7 +205,12 @@ export const bookingMutationRouter = router({
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber, language, ...body } = input
guaranteeBookingCounter.add(1, { confirmationNumber })
const guaranteeBookingCounter = createCounter("trpc.booking", "guarantee")
const metricsGuaranteeBooking = guaranteeBookingCounter.init({
confirmationNumber,
})
metricsGuaranteeBooking.start()
const headers = {
Authorization: `Bearer ${accessToken}`,
@@ -435,46 +226,18 @@ export const bookingMutationRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
guaranteeBookingFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.guarantee error",
JSON.stringify({
query: { confirmationNumber },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
await metricsGuaranteeBooking.httpError(apiResponse)
return null
}
const apiJson = await apiResponse.json()
const verifiedData = createBookingSchema.safeParse(apiJson)
if (!verifiedData.success) {
guaranteeBookingFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
})
console.error(
"api.booking.guarantee validation error",
JSON.stringify({
query: { confirmationNumber },
error: verifiedData.error,
})
)
metricsGuaranteeBooking.validationError(verifiedData.error)
return null
}
guaranteeBookingSuccessCounter.add(1, { confirmationNumber })
metricsGuaranteeBooking.success()
return verifiedData.data
}),
@@ -484,7 +247,12 @@ export const bookingMutationRouter = router({
const accessToken = ctx.session?.token.access_token || ctx.serviceToken
const { confirmationNumber, ...body } = input
updateBookingCounter.add(1, { confirmationNumber })
const updateBookingCounter = createCounter("trpc.booking", "update")
const metricsUpdateBooking = updateBookingCounter.init({
confirmationNumber,
})
metricsUpdateBooking.start()
const apiResponse = await api.put(
api.endpoints.v1.Booking.booking(confirmationNumber),
@@ -497,25 +265,7 @@ export const bookingMutationRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
updateBookingFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.updateBooking error",
JSON.stringify({
query: { confirmationNumber },
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
error: text,
},
})
)
await metricsUpdateBooking.httpError(apiResponse)
return null
}
@@ -523,21 +273,11 @@ export const bookingMutationRouter = router({
const verifiedData = bookingConfirmationSchema.safeParse(apiJson)
if (!verifiedData.success) {
updateBookingFailCounter.add(1, {
confirmationNumber,
error_type: "validation_error",
})
console.error(
"api.booking.updateBooking validation error",
JSON.stringify({
query: { confirmationNumber },
error: verifiedData.error,
})
)
metricsUpdateBooking.validationError(verifiedData.error)
return null
}
updateBookingSuccessCounter.add(1, { confirmationNumber })
metricsUpdateBooking.success()
return verifiedData.data
}),
@@ -547,26 +287,22 @@ export const bookingMutationRouter = router({
const accessToken = ctx.session?.token.access_token ?? ctx.serviceToken
const { confirmationNumber, codes, language } = input
const headers = {
Authorization: `Bearer ${accessToken}`,
}
const loggingAttributes = {
const removePackageCounter = createCounter(
"trpc.booking",
"package.remove"
)
const metricsRemovePackage = removePackageCounter.init({
confirmationNumber,
codes,
language,
})
metricsRemovePackage.start()
const headers = {
Authorization: `Bearer ${accessToken}`,
}
removePackageCounter.add(1, loggingAttributes)
console.info(
"api.booking.remove-package start",
JSON.stringify({
request: loggingAttributes,
headers,
})
)
const apiResponse = await api.remove(
api.endpoints.v1.Booking.packages(confirmationNumber),
{
@@ -576,36 +312,11 @@ export const bookingMutationRouter = router({
)
if (!apiResponse.ok) {
const text = await apiResponse.text()
removePackageFailCounter.add(1, {
confirmationNumber,
error_type: "http_error",
error: JSON.stringify({
status: apiResponse.status,
}),
})
console.error(
"api.booking.remove-package error",
JSON.stringify({
error: {
status: apiResponse.status,
statusText: apiResponse.statusText,
text,
},
query: loggingAttributes,
})
)
await metricsRemovePackage.httpError(apiResponse)
return false
}
removePackageSuccessCounter.add(1, loggingAttributes)
console.info(
"api.booking.remove-package success",
JSON.stringify({
query: loggingAttributes,
})
)
metricsRemovePackage.success()
return true
}),