From 0ed2657deb7532f94a4615dc51987cfae67748ac Mon Sep 17 00:00:00 2001 From: Ruslan Lopatin Date: Sat, 17 Jul 2021 11:06:43 +0700 Subject: [PATCH] Add `Logger` IoC context entry (#7) --- package.json | 4 +++ src/logger.spec.ts | 72 ++++++++++++++++++++++++++++++++++++++++++++++ src/logger.ts | 25 ++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 src/logger.spec.ts diff --git a/package.json b/package.json index 8623516..0045a23 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "exports": "./dist/logger.js", "devDependencies": { "@jest/globals": "^27.0.6", + "@proc7ts/context-builder": "^7.0.1", "@rollup/plugin-node-resolve": "^13.0.2", "@run-z/eslint-config": "^1.3.0", "@run-z/rollup-helpers": "^1.1.1", @@ -60,5 +61,8 @@ "lint:md": "run-z +z --then remark .", "test": "run-z +z env:NODE_OPTIONS='--experimental-vm-modules --no-warnings' --then jest", "z": "run-z +cmd:rollup,+cmd:typedoc,+cmd:eslint,+cmd:remark,+cmd:jest" + }, + "dependencies": { + "@proc7ts/context-values": "^7.0.0" } } diff --git a/src/logger.spec.ts b/src/logger.spec.ts new file mode 100644 index 0000000..2b13897 --- /dev/null +++ b/src/logger.spec.ts @@ -0,0 +1,72 @@ +import { afterEach, beforeEach, describe, expect, it, jest } from '@jest/globals'; +import { CxBuilder, cxConstAsset } from '@proc7ts/context-builder'; +import { CxGlobals } from '@proc7ts/context-values'; +import { SpyInstance, spyOn } from 'jest-mock'; +import { Logger } from './logger'; +import { consoleLogger } from './loggers'; + +describe('Logger', () => { + + let cxBuilder: CxBuilder; + + beforeEach(() => { + cxBuilder = new CxBuilder(get => ({ get })); + cxBuilder.provide(cxConstAsset(CxGlobals, cxBuilder.context)); + }); + + let logger: Logger; + + beforeEach(() => { + logger = cxBuilder.get(Logger); + }); + + it('logs to most recent logger', () => { + + const testLogger = { + error: jest.fn(), + } as Partial as Logger; + + cxBuilder.provide(cxConstAsset(Logger, testLogger)); + + const error = new Error('Test'); + + logger.error('Error', error); + + expect(testLogger.error).toHaveBeenCalledWith('Error', error); + expect(testLogger.error).toHaveBeenCalledTimes(1); + }); + it('is singleton', () => { + + const cxBuilder2 = new CxBuilder(get => ({ get }), cxBuilder); + + expect(cxBuilder2.get(Logger)).toBe(logger); + }); + + describe('by default', () => { + + let errorSpy: SpyInstance; + + beforeEach(() => { + errorSpy = spyOn(consoleLogger, 'error').mockImplementation(() => void 0); + }); + afterEach(() => { + errorSpy.mockRestore(); + }); + + it('logs to console', () => { + + const error = new Error('Test'); + + logger.error('Error', error); + + expect(errorSpy).toHaveBeenCalledWith('Error', error); + expect(errorSpy).toHaveBeenCalledTimes(1); + }); + }); + + describe('toString', () => { + it('provides string representation', () => { + expect(String(Logger)).toBe('[Logger]'); + }); + }); +}); diff --git a/src/logger.ts b/src/logger.ts index 9f0d733..c43b251 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -1,3 +1,6 @@ +import { CxEntry, CxGlobals, cxRecent, cxScoped } from '@proc7ts/context-values'; +import { consoleLogger, proxyLogger } from './loggers'; + /** * Basic logger interface. */ @@ -41,3 +44,25 @@ export interface Logger { trace(...args: unknown[]): void; } + +/** + * Global context entry containing logger to use. + * + * Logs to {@link consoleLogger console} by default. + */ +export const Logger: CxEntry = { + perContext: (/*#__PURE__*/ cxScoped( + CxGlobals, + (/*#__PURE__*/ cxRecent({ + create: (recent, _) => recent, + byDefault: _ => consoleLogger, + assign({ get, to }) { + + const logger = proxyLogger(get); + + return receiver => to((_, by) => receiver(logger, by)); + }, + })), + )), + toString: () => '[Logger]', +};