Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(in-4422): logger prettier #7360

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/poor-swans-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@vue-storefront/logger": minor
---

**[ADDED]** Alokai Logger Prettier. Now the output of the **Alokai Logger** will be formatted with readable and accessible way.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ describe("jsonReporter", () => {

jsonReporter(logObject);

expect(consoleSpy).toHaveBeenCalledWith({ message: "test message" });
expect(consoleSpy).toHaveBeenCalledWith(
'Alokai Log ($raw): {"message":"test message"}'
);
});

it("should log structuredLog directly in browser environment", () => {
Expand All @@ -36,7 +38,7 @@ describe("jsonReporter", () => {

jsonReporter(logObject);

expect(consoleSpy).toHaveBeenCalledWith({ message: "test message" });
expect(consoleSpy).toHaveBeenCalledWith('{"message":"test message"}');

delete (global as any).window;
});
Expand Down
168 changes: 168 additions & 0 deletions packages/logger/__tests__/unit/reporter/consola/prettier.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { jsonReporterPrettier } from "../../../../src/reporters/consola/prettier";

describe("jsonReporterPrettier", () => {
let consoleSpy: jest.SpyInstance;

beforeEach(() => {
consoleSpy = jest.spyOn(console, "log").mockImplementation(() => {});
delete (global as any).window;
});

afterEach(() => {
consoleSpy.mockRestore();
jest.resetModules();
});

it("should format middleware logs correctly in SSR mode", () => {
const mockLogFn = jest.fn();
const logObject = {
timestamp: "2024-03-14T12:00:00Z",
severity: "INFO",
message: "Test message",
alokai: {
context: "middleware",
},
metadata: { key: "value" },
troubleshooting: {
message: "Troubleshooting message",
steps: ["Step 1", "Step 2"],
},
};

jsonReporterPrettier(logObject, mockLogFn, "server");

expect(mockLogFn).toHaveBeenCalledTimes(5);
expect(mockLogFn.mock.calls[0][0]).toContain(
":: Alokai Log: Middleware ::"
);
expect(mockLogFn.mock.calls[0][0]).toContain("Test message");
expect(mockLogFn.mock.calls[1][0]).toContain("Troubleshooting message");
expect(mockLogFn.mock.calls[2][0]).toContain("Alokai Context: middleware");
});

it("should format storefront logs correctly in SSR mode", () => {
const logObject = {
timestamp: "2024-03-14T12:00:00Z",
severity: "ERROR",
message: "Test storefront message",
alokai: {
context: "storefront",
},
metadata: { key: "value" },
troubleshooting: {
message: "Troubleshooting message",
steps: ["Step 1", "Step 2"],
},
};

jsonReporterPrettier(logObject, console.log, "server");

expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining(":: Alokai Log: Storefront ::")
);
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining("Test storefront message")
);
});

it("should output raw JSON when context is neither middleware nor storefront", () => {
const logObject = {
timestamp: "2024-03-14T12:00:00Z",
severity: "WARNING",
message: "Test message",
alokai: {
context: "other",
},
};

jsonReporterPrettier(logObject, console.log, "server");

expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining("Alokai Log ($raw):")
);
expect(consoleSpy).toHaveBeenCalledWith(
expect.stringContaining(JSON.stringify(logObject))
);
});

it("should handle different severity levels with correct colors", () => {
const mockLogFn = jest.fn();
const severities = [
"INFO",
"ERROR",
"WARNING",
"DEBUG",
"NOTICE",
"EMERGENCY",
"ALERT",
"CRITICAL",
];

severities.forEach((severity) => {
const logObject = {
timestamp: "2024-03-14T12:00:00Z",
severity,
message: "Test message",
alokai: {
context: "middleware",
},
};

jsonReporterPrettier(logObject, mockLogFn, "server");
expect(mockLogFn).toHaveBeenCalledWith(expect.stringContaining(severity));
});
});

it("should not format logs when mode is not server", () => {
const mockLogFn = jest.fn();
const logObject = {
timestamp: "2024-03-14T12:00:00Z",
severity: "INFO",
message: "Test message",
};

jsonReporterPrettier(logObject, mockLogFn, "client");

expect(mockLogFn).toHaveBeenCalled();
const calls = mockLogFn.mock.calls.flat().join("\n");
expect(calls).toContain("Alokai Log unavailable in client mode");
});

it("should handle missing optional fields gracefully", () => {
const mockLogFn = jest.fn();
const logObject = {
timestamp: "2024-03-14T12:00:00Z",
severity: "INFO",
message: "Test message",
alokai: {
context: "middleware",
},
// No metadata or troubleshooting
};

jsonReporterPrettier(logObject, mockLogFn, "server");

expect(mockLogFn).toHaveBeenCalled();
const calls = mockLogFn.mock.calls.flat().join("\n");
expect(calls).not.toContain("Troubleshooting:");
expect(calls).not.toContain("Metadata:");
});

it("should include emojis in the log output for middleware context", () => {
const mockLogFn = jest.fn();
const logObject = {
timestamp: "2024-03-14T12:00:00Z",
severity: "INFO",
message: "Test message",
alokai: {
context: "middleware",
},
};

jsonReporterPrettier(logObject, mockLogFn, "server");

expect(mockLogFn).toHaveBeenCalled();
const calls = mockLogFn.mock.calls.flat().join("\n");
expect(calls).toContain("🔥 Severity");
});
});
5 changes: 3 additions & 2 deletions packages/logger/src/reporters/consola/jsonReporter.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { LogObject } from "consola";
import { jsonReporterPrettier } from "./prettier";

export const jsonReporter = (logObject: LogObject) => {
const defLogType = "log";
const logType = logObject?.type ?? defLogType;
const logFn = console[logType] ?? console[defLogType];

if (process.env.NODE_ENV === "development" || typeof window !== "undefined") {
logFn(logObject.args[0].structuredLog);
if (process.env.NODE_ENV === "development") {
jsonReporterPrettier(logObject.args[0].structuredLog, logFn);
} else {
logFn(JSON.stringify(logObject.args[0].structuredLog));
}
Expand Down
93 changes: 93 additions & 0 deletions packages/logger/src/reporters/consola/prettier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* Formats the severity level of a log message with ANSI escape codes for color.
*
* @param {String} severity - The severity level of the log message.
* @returns {String} - The formatted severity level with ANSI escape codes for color.
*/
const getSeverityLog = (severity: string): string => {
switch (severity) {
case "INFO":
case "info":
return "\x1b[30;104mINFO\x1b[0m"; // Blue background
case "ERROR":
case "error":
return "\x1b[41mERROR\x1b[0m"; // Red background
case "EMERGENCY":
case "emergency":
return "\x1b[45mEMERGENCY\x1b[0m"; // Magenta background
case "ALERT":
case "alert":
return "\x1b[45mALERT\x1b[0m"; // Magenta background
case "CRITICAL":
case "critical":
return "\x1b[45mCRITICAL\x1b[0m"; // Magenta background
case "WARNING":
case "warning":
return "\x1b[48;5;208mWARNING\x1b[0m"; // Yellow background
case "NOTICE":
case "notice":
return "\x1b[30;104mNOTICE\x1b[0m"; // Blue background
case "DEBUG":
case "debug":
return "\x1b[40mDEBUG\x1b[0m"; // Black background
default:
return severity; // Return the severity as is if it doesn't match any case
}
};

// Prettier and linter were disabled due to the way the code is formatted / printed within the console.
// eslint-disable-file no-console
// prettier-ignore
/**
* Formats and logs the logObject in a prettier way based on the mode.
*
* @param {Object} logObject - The log object to be formatted and logged.
* @param {Function} logFn - The logging function to use.
* @param {String} mode - The mode in which the log is being generated. Defaults to "server".
*/
export const jsonReporterPrettier = (logObject: Record<any, any>, logFn: Function, mode: string = "server") => {
if (!logObject || mode !== "server") {
logFn(`Alokai Log unavailable in ${mode} mode`);
return;
}

const { timestamp, severity, message, alokai, metadata, troubleshooting } = logObject;
const severityLog = getSeverityLog(severity);
const isSSR = typeof globalThis.window === "undefined";

if (isSSR && alokai?.context === "middleware") {
logFn(`\x1b[90m:: Alokai Log: Middleware ::\x1b[0m

🔥 Severity: ${severityLog}
🕓 Timestamp: \x1b[93m${new Date(timestamp).toLocaleString()}\x1b[0m (${timestamp})
💬 Message: ${message}
`);
if (troubleshooting) {
logFn(`🛠️ Troubleshooting: ${troubleshooting.message}
🚶 Steps to follow:
${troubleshooting.steps.map((step) => `➡️ ${step}`).join("\n")}
`);
}
if (alokai.context) {
logFn(`🛍️ Alokai Context: ${alokai.context}`);
}
if (metadata) {
logFn(`📁 Metadata: ${JSON.stringify(metadata)}`);
}
logFn(``);
} else if (isSSR && alokai?.context === "storefront") {
console.log(`\x1b[90m:: Alokai Log: Storefront ::\x1b[0m

🔥 Severity: ${severityLog}
🕓 Timestamp: \x1b[93m${new Date(timestamp).toLocaleString()}\x1b[0m (${timestamp})
💬 Message: ${message}

${troubleshooting ? `🛠️ Troubleshooting: ${troubleshooting.message}\n🚶 Steps to follow:\n${troubleshooting.steps.map((step) => `➡️ ${step}`).join("\n")}` : ""}

${alokai?.context ? `🛍️ Alokai Context: ${alokai.context}` : ""}
${metadata ? `📁 Metadata: ${JSON.stringify(metadata)}` : ""}
`);
} else {
console.log(`Alokai Log ($raw): ${JSON.stringify(logObject)}`);
}
};
37 changes: 9 additions & 28 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2648,6 +2648,12 @@
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==

"@vue-storefront/logger@./packages/logger":
version "1.0.0"
dependencies:
consola "^3"
dotenv "^16"

"@vue-storefront/rollup-config@^0.0.6":
version "0.0.6"
resolved "https://registry.yarnpkg.com/@vue-storefront/rollup-config/-/rollup-config-0.0.6.tgz#625f4e67790f11e2b9ff2843a95fea52849effa2"
Expand Down Expand Up @@ -10997,16 +11003,7 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"

"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -11146,7 +11143,7 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand All @@ -11160,13 +11157,6 @@ strip-ansi@^3.0.0:
dependencies:
ansi-regex "^2.0.0"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
Expand Down Expand Up @@ -12183,7 +12173,7 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand All @@ -12201,15 +12191,6 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.0.1, wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
Expand Down
Loading