diff --git a/.changeset/empty-candles-tap.md b/.changeset/empty-candles-tap.md new file mode 100644 index 0000000000..1938018aa4 --- /dev/null +++ b/.changeset/empty-candles-tap.md @@ -0,0 +1,15 @@ +--- +"@saleor/apps-logger": patch +--- + +Added new transport `LoggerVercelTransport`. It is currently in experimental stage but it can be used to send logs directly to Vercel log drain. This transport has optional argument of `loggerContext` - if used you need to make sure that function is executed only on the server. + +Usage: + +```ts +import { logger } from "@saleor/apps-logger"; +import { attachLoggerVercelTransport } from "@saleor/apps-logger/node"; +import { loggerContext } from "./logger-context"; + +attachLoggerVercelTransport(logger, loggerContext); +``` diff --git a/.changeset/forty-tools-beg.md b/.changeset/forty-tools-beg.md new file mode 100644 index 0000000000..d3b62bcb8c --- /dev/null +++ b/.changeset/forty-tools-beg.md @@ -0,0 +1,5 @@ +--- +"app-avatax": patch +--- + +Added new `LoggerVercelTransport` support. It will help us send logs to our infrastructure without need of OTEL unstable logs API. diff --git a/apps/avatax/src/lib/app-configuration-logger.test.ts b/apps/avatax/src/lib/app-configuration-logger.test.ts index a139344f9b..1c61c64415 100644 --- a/apps/avatax/src/lib/app-configuration-logger.test.ts +++ b/apps/avatax/src/lib/app-configuration-logger.test.ts @@ -81,13 +81,13 @@ describe("AppConfigurationLogger", () => { ); expect(mockInfo).toHaveBeenCalledWith("Received configuration", { - address: JSON.stringify({ + address: { city: "test", country: "test", zip: "10111", state: "NY", street: "test", - }), + }, appConfigName: "config", channelSlug: "default-channel", companyCode: "test", diff --git a/apps/avatax/src/lib/app-configuration-logger.ts b/apps/avatax/src/lib/app-configuration-logger.ts index 29c64c8620..4f590750b7 100644 --- a/apps/avatax/src/lib/app-configuration-logger.ts +++ b/apps/avatax/src/lib/app-configuration-logger.ts @@ -29,7 +29,7 @@ export class AppConfigurationLogger { appConfigName: resolvedAvataxConfig.config.name, shippingTaxCode: resolvedAvataxConfig.config.shippingTaxCode, companyCode: resolvedAvataxConfig.config.companyCode, - address: JSON.stringify(resolvedAvataxConfig.config.address), + address: resolvedAvataxConfig.config.address, isSandbox: resolvedAvataxConfig.config.isSandbox, isAutocommit: resolvedAvataxConfig.config.isAutocommit, isDocumentRecordingEnabled: resolvedAvataxConfig.config.isDocumentRecordingEnabled, diff --git a/apps/avatax/src/logger.ts b/apps/avatax/src/logger.ts index 39f700a19c..cf4ffee51a 100644 --- a/apps/avatax/src/logger.ts +++ b/apps/avatax/src/logger.ts @@ -1,7 +1,5 @@ import { attachLoggerConsoleTransport, createLogger, logger } from "@saleor/apps-logger"; -import packageJson from "../package.json"; - logger.settings.maskValuesOfKeys = ["metadata", "username", "password", "apiKey"]; if (process.env.NODE_ENV !== "production") { @@ -9,14 +7,17 @@ if (process.env.NODE_ENV !== "production") { } if (typeof window === "undefined") { - import("@saleor/apps-logger/node").then( - async ({ attachLoggerOtelTransport, attachLoggerSentryTransport, LoggerContext }) => { - const loggerContext = await import("./logger-context").then((m) => m.loggerContext); + // Don't remove require - it's necessary for proper logger initialization + const { + attachLoggerSentryTransport, + attachLoggerVercelTransport, + } = require("@saleor/apps-logger/node"); + + attachLoggerSentryTransport(logger); - attachLoggerSentryTransport(logger); - attachLoggerOtelTransport(logger, packageJson.version, loggerContext); - }, - ); + if (process.env.NODE_ENV === "production") { + attachLoggerVercelTransport(logger, require("./logger-context").loggerContext); + } } export { createLogger, logger }; diff --git a/apps/avatax/src/modules/calculate-taxes/use-case/calculate-taxes.use-case.ts b/apps/avatax/src/modules/calculate-taxes/use-case/calculate-taxes.use-case.ts index de7bb718b5..d5054765c8 100644 --- a/apps/avatax/src/modules/calculate-taxes/use-case/calculate-taxes.use-case.ts +++ b/apps/avatax/src/modules/calculate-taxes/use-case/calculate-taxes.use-case.ts @@ -221,7 +221,7 @@ export class CalculateTaxesUseCase { }); }, ).map((results) => { - this.logger.info("Taxes calculated", { calculatedTaxes: JSON.stringify(results) }); + this.logger.info("Taxes calculated", { calculatedTaxes: results }); ClientLogStoreRequest.create({ level: "info", diff --git a/apps/avatax/src/modules/crud-settings/crud-settings.service.ts b/apps/avatax/src/modules/crud-settings/crud-settings.service.ts index 16042aa3f9..b3d0676d36 100644 --- a/apps/avatax/src/modules/crud-settings/crud-settings.service.ts +++ b/apps/avatax/src/modules/crud-settings/crud-settings.service.ts @@ -46,7 +46,7 @@ export class CrudSettingsManager { if (!validation.success) { this.logger.error("Error while validating metadata", { - error: JSON.stringify(validation.error), + error: validation.error, metadataKey: this.params.metadataKey, }); throw new Error("Error while validating metadata"); diff --git a/apps/avatax/src/pages/api/webhooks/checkout-calculate-taxes.ts b/apps/avatax/src/pages/api/webhooks/checkout-calculate-taxes.ts index ae05c0173d..f6f68ef6ba 100644 --- a/apps/avatax/src/pages/api/webhooks/checkout-calculate-taxes.ts +++ b/apps/avatax/src/pages/api/webhooks/checkout-calculate-taxes.ts @@ -45,7 +45,7 @@ const handler = checkoutCalculateTaxesSyncWebhook.createHandler(async (req, res, subscriptionErrorChecker.checkPayload(payload); logger.info("Tax base payload for checkout calculate taxes", { - payload: JSON.stringify(payload.taxBase), + payload: payload.taxBase, }); loggerContext.set(ObservabilityAttributes.CHANNEL_SLUG, ctx.payload.taxBase.channel.slug); diff --git a/packages/logger/index.ts b/packages/logger/index.ts index 87a90322b9..5d46d7a678 100644 --- a/packages/logger/index.ts +++ b/packages/logger/index.ts @@ -1,2 +1,2 @@ -export { logger, createLogger } from "./src/logger"; +export { createLogger, logger } from "./src/logger"; export { attachLoggerConsoleTransport } from "./src/logger-console-transport"; diff --git a/packages/logger/node.ts b/packages/logger/node.ts index 570867bcd2..b000b5e590 100644 --- a/packages/logger/node.ts +++ b/packages/logger/node.ts @@ -1,3 +1,4 @@ export { LoggerContext, wrapWithLoggerContext } from "./src/logger-context"; export * from "./src/logger-otel-transport"; export * from "./src/logger-sentry-transport"; +export * from "./src/logger-vercel-transport"; diff --git a/packages/logger/src/logger-context.test.ts b/packages/logger/src/logger-context.test.ts index cc214ccf0e..3aa3fcc41f 100644 --- a/packages/logger/src/logger-context.test.ts +++ b/packages/logger/src/logger-context.test.ts @@ -1,4 +1,5 @@ -import { describe, it, expect } from "vitest"; +import { describe, expect, it } from "vitest"; + import { LoggerContext } from "./logger-context"; describe("LoggerContext", () => { diff --git a/packages/logger/src/logger-sentry-transport.ts b/packages/logger/src/logger-sentry-transport.ts index ecbb0f9b76..f1dd8e9952 100644 --- a/packages/logger/src/logger-sentry-transport.ts +++ b/packages/logger/src/logger-sentry-transport.ts @@ -1,6 +1,6 @@ -import { ILogObj, Logger } from "tslog"; import * as Sentry from "@sentry/nextjs"; import { SeverityLevel } from "@sentry/nextjs"; +import { ILogObj, Logger } from "tslog"; const loggerLevelToSentryLevel = (level: string): SeverityLevel => { switch (level) { diff --git a/packages/logger/src/logger-vercel-transport.ts b/packages/logger/src/logger-vercel-transport.ts new file mode 100644 index 0000000000..eaab7f161b --- /dev/null +++ b/packages/logger/src/logger-vercel-transport.ts @@ -0,0 +1,38 @@ +import { ILogObj, Logger } from "tslog"; + +import { LoggerContext } from "./logger-context"; + +export const attachLoggerVercelTransport = ( + logger: Logger, + loggerContext?: LoggerContext, +) => { + logger.attachTransport((log) => { + const { message, attributes, _meta } = log; + + const bodyMessage = log._meta.name ? `[${log._meta.name}] ${message}` : message; + + const stringifiedMessage = JSON.stringify({ + message: bodyMessage, + ...attributes, + ...loggerContext?.getRawContext(), + _meta: { + ..._meta, + // used to filter out log in log drain + source: "saleor-app", + }, + }); + + // Prints Vercel log in proper level https://vercel.com/docs/observability/runtime-logs#level + if (_meta.logLevelName === "ERROR") { + console.error(stringifiedMessage); + return; + } + + if (_meta.logLevelName === "WARN") { + console.warn(stringifiedMessage); + return; + } + + console.log(stringifiedMessage); + }); +};