From f9e5f9bf432c642416bf6bb2733a4c8493022f7b Mon Sep 17 00:00:00 2001 From: Iku-turso Date: Tue, 16 May 2023 15:15:31 +0300 Subject: [PATCH] feat: Implement automatic Feature-specific prefixing for logging Co-authored-by: Janne Savolainen Signed-off-by: Iku-turso --- packages/logger/src/logger.injectable.ts | 51 +++++++++++++++++++++--- packages/logger/src/logger.test.ts | 48 ++++++++++++++++++++-- 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/packages/logger/src/logger.injectable.ts b/packages/logger/src/logger.injectable.ts index 816d76fa39..d59bfe2ceb 100644 --- a/packages/logger/src/logger.injectable.ts +++ b/packages/logger/src/logger.injectable.ts @@ -2,9 +2,16 @@ * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ -import { getInjectable, getInjectionToken } from "@ogre-tools/injectable"; +import { kebabCase, toUpper } from "lodash/fp"; +import { + DiContainerForInjection, + getInjectable, + getInjectionToken, + lifecycleEnum, +} from "@ogre-tools/injectable"; import type { Logger } from "./logger"; import { winstonLoggerInjectable } from "./winston-logger.injectable"; +import { pipeline } from "@ogre-tools/fp"; export const loggerInjectionToken = getInjectionToken({ id: "logger-injection-token", @@ -46,32 +53,64 @@ export const logSillyInjectionToken = getInjectionToken({ id: "log-silly-injection-token", }); +const screamingKebabCase = (str: string) => pipeline(str, kebabCase, toUpper); + +const getLogFunctionFor = + (scenario: keyof Logger) => + (di: DiContainerForInjection): LogFunction => { + const winstonLogger = di.inject(winstonLoggerInjectable); + + return (message, ...data) => { + winstonLogger[scenario]( + di.sourceNamespace + ? `[${screamingKebabCase(di.sourceNamespace)}]: ${message}` + : message, + ...data + ); + }; + }; + export const logDebugInjectable = getInjectable({ id: "log-debug", - instantiate: (di): LogFunction => di.inject(winstonLoggerInjectable).debug, + instantiate: getLogFunctionFor("debug"), injectionToken: logDebugInjectionToken, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di) => di.sourceNamespace, + }), }); export const logInfoInjectable = getInjectable({ id: "log-info", - instantiate: (di): LogFunction => di.inject(winstonLoggerInjectable).info, + instantiate: getLogFunctionFor("info"), injectionToken: logInfoInjectionToken, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di) => di.sourceNamespace, + }), }); export const logWarningInjectable = getInjectable({ id: "log-warning", - instantiate: (di): LogFunction => di.inject(winstonLoggerInjectable).warn, + instantiate: getLogFunctionFor("warn"), injectionToken: logWarningInjectionToken, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di) => di.sourceNamespace, + }), }); export const logErrorInjectable = getInjectable({ id: "log-error", - instantiate: (di): LogFunction => di.inject(winstonLoggerInjectable).error, + instantiate: getLogFunctionFor("error"), injectionToken: logErrorInjectionToken, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di) => di.sourceNamespace, + }), }); export const logSillyInjectable = getInjectable({ id: "log-silly", - instantiate: (di): LogFunction => di.inject(winstonLoggerInjectable).silly, + instantiate: getLogFunctionFor("silly"), injectionToken: logSillyInjectionToken, + lifecycle: lifecycleEnum.keyedSingleton({ + getInstanceKey: (di) => di.sourceNamespace, + }), }); diff --git a/packages/logger/src/logger.test.ts b/packages/logger/src/logger.test.ts index 85399d0293..e8a689ff71 100644 --- a/packages/logger/src/logger.test.ts +++ b/packages/logger/src/logger.test.ts @@ -1,13 +1,18 @@ -import { createContainer } from "@ogre-tools/injectable"; +import { createContainer, getInjectable } from "@ogre-tools/injectable"; import { registerFeature } from "@k8slens/feature-core"; import { loggerFeature } from "./feature"; import { winstonLoggerInjectable } from "./winston-logger.injectable"; + import { - logDebugInjectionToken, logErrorInjectionToken, + logDebugInjectionToken, + logErrorInjectionToken, logInfoInjectionToken, - logSillyInjectionToken, logWarningInjectionToken, + logSillyInjectionToken, + logWarningInjectionToken, } from "./logger.injectable"; +import { getFeature } from "@k8slens/feature-core/src/feature"; + describe("logger", () => { [ { scenario: "debug", injectionToken: logDebugInjectionToken }, @@ -16,7 +21,7 @@ describe("logger", () => { { scenario: "error", injectionToken: logErrorInjectionToken }, { scenario: "silly", injectionToken: logSillyInjectionToken }, ].forEach(({ scenario, injectionToken }) => { - it(`when logging "${scenario}", does so`, () => { + it(`given not inside a Feature, when logging "${scenario}", does so without a prefix`, () => { const di = createContainer("irrelevant"); registerFeature(di, loggerFeature); @@ -34,5 +39,40 @@ describe("logger", () => { "some-data" ); }); + + it(`given inside a Feature, when logging "${scenario}", does so with Feature's id as prefix`, () => { + const di = createContainer("irrelevant"); + + const logScenarioInFeature = getInjectable({ + id: "some-functionality", + instantiate: (di) => di.inject(injectionToken), + }); + + + const someFeature = getFeature({ + id: "some-feature", + + register: (di) => { + di.register(logScenarioInFeature); + }, + + dependencies: [loggerFeature], + }); + + registerFeature(di, someFeature); + + const winstonLoggerStub = { [scenario]: jest.fn() } as any; + + di.override(winstonLoggerInjectable, () => winstonLoggerStub); + + const logScenario = di.inject(logScenarioInFeature); + + logScenario("some-message", "some-data"); + + expect(winstonLoggerStub[scenario]).toHaveBeenCalledWith( + "[SOME-FEATURE]: some-message", + "some-data" + ); + }); }); });