diff --git a/README.md b/README.md index 7b1d3f34f1..7b70c05c3d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ These libraries compose together to help you create performant modern JS apps th ## Usage -The Quilt repo is managed as a monorepo that is composed of 71 npm packages and one Ruby gem. +The Quilt repo is managed as a monorepo that is composed of 70 npm packages and one Ruby gem. Each package/gem has its own `README.md` and documentation describing usage. ### Package Index @@ -47,7 +47,6 @@ Each package/gem has its own `README.md` and documentation describing usage. | [@shopify/koa-performance](packages/koa-performance) | | Creating middleware that sends performance-related data through StatsD | | [@shopify/koa-shopify-graphql-proxy](packages/koa-shopify-graphql-proxy) | | A wrapper around `koa-better-http-proxy` which allows easy proxying of GraphQL requests from an embedded Shopify app | | [@shopify/koa-shopify-webhooks](packages/koa-shopify-webhooks) | | Receive webhooks from Shopify with ease | -| [@shopify/logger](packages/logger) | | Opinionated logger for production-scale applications | | [@shopify/mime-types](packages/mime-types) | | MIME type consistency | | [@shopify/name](packages/name) | | Name-related utilities | | [@shopify/network](packages/network) | | Common values related to dealing with the network | diff --git a/packages/logger/CHANGELOG.md b/packages/logger/CHANGELOG.md index 7eb7849b3d..ccadd050dd 100644 --- a/packages/logger/CHANGELOG.md +++ b/packages/logger/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +⚠️ DEPRECATED + +This package has been deprecated and should no longer be used. + +For legacy documentation, see [v2.1.0](https://github.com/Shopify/quilt/blob/%40shopify/logger%402.1.0/packages/logger/README.md). + ## 2.1.0 ### Minor Changes diff --git a/packages/logger/README.md b/packages/logger/README.md index 46332197e2..e51de342e3 100644 --- a/packages/logger/README.md +++ b/packages/logger/README.md @@ -1,86 +1,7 @@ # `@shopify/logger` -[![Build Status](https://github.com/Shopify/quilt/workflows/Node-CI/badge.svg?branch=main)](https://github.com/Shopify/quilt/actions?query=workflow%3ANode-CI) -[![Build Status](https://github.com/Shopify/quilt/workflows/Ruby-CI/badge.svg?branch=main)](https://github.com/Shopify/quilt/actions?query=workflow%3ARuby-CI) -[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE.md) -[![npm version](https://badge.fury.io/js/%40shopify%2Flogger.svg)](https://badge.fury.io/js/%40shopify%2Flogger.svg) +⚠️ DEPRECATED -Opinionated logger for production-scale applications. +This package has been deprecated and should no longer be used. -## Installation - -```bash -yarn add @shopify/logger -``` - -## Usage - -### Instantiation - -A basic logger can be created simply by calling the `Logger` constructor: - -```typescript -const logger = new Logger(); -``` - -The `Logger` constructor also takes an `options` object. For example: - -```typescript -const logger = new Logger({ - name: 'my-logger', - formatter: new ConsoleFormatter(), -}); -``` - -The `options` object adheres to the following interface: - -```typescript -interface LoggerOptions { - formatter?: Formatter; - name?: string; -} -``` - -The `name` of a logger will be used as it's root scope. A formatter may use this to provide context when outputting a log entry. For example, the `ConsoleFormatter` will preface each log with its scope, such as: - -``` -[my-logger] ℹ info - some log text -``` - -A `Formatter` is simply an object that implements the `Formatter` interface: - -```typescript -export interface Formatter { - format(entry: FormatEntry): any; -} -``` - -In addition to writing your own formatter, See [Formatters](#formatters) below for a list of provided formatters. - -### Logging - -Given the logger above, we can log information to the console using the built-in `info`, `warn`, and `error` functions. For example: - -```typescript -logger.info('Hello, world!'); -logger.warn('Something bad might happen'); -logger.error(new Error('Operation not permitted.')); -``` - -## Formatters - -This package provides the following formatters: - -- `ConsoleFormatter` - -### `ConsoleFormatter` - -Formats and prints logs via `console.log`, `console.warn`, and `console.error`. - -#### Screenshots - -![`ConsoleFormatter` sample error output](./docs/images/ConsoleFormatter__Error.png) - -![`ConsoleFormatter` sample log output on a dark background](./docs/images/ConsoleFormatter__DarkBG.png) - -![`ConsoleFormatter` sample log output on a light background](./docs/images/ConsoleFormatter__LightBG.png) +For legacy documentation, see [v2.1.0](https://github.com/Shopify/quilt/blob/%40shopify/logger%402.1.0/packages/logger/README.md). diff --git a/packages/logger/docs/images/ConsoleFormatter__DarkBG.png b/packages/logger/docs/images/ConsoleFormatter__DarkBG.png deleted file mode 100644 index 1f620b778d..0000000000 Binary files a/packages/logger/docs/images/ConsoleFormatter__DarkBG.png and /dev/null differ diff --git a/packages/logger/docs/images/ConsoleFormatter__Error.png b/packages/logger/docs/images/ConsoleFormatter__Error.png deleted file mode 100644 index 314719415f..0000000000 Binary files a/packages/logger/docs/images/ConsoleFormatter__Error.png and /dev/null differ diff --git a/packages/logger/docs/images/ConsoleFormatter__LightBG.png b/packages/logger/docs/images/ConsoleFormatter__LightBG.png deleted file mode 100644 index eb3ed8a695..0000000000 Binary files a/packages/logger/docs/images/ConsoleFormatter__LightBG.png and /dev/null differ diff --git a/packages/logger/package.json b/packages/logger/package.json deleted file mode 100644 index 87216892cb..0000000000 --- a/packages/logger/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "@shopify/logger", - "version": "2.1.0", - "license": "MIT", - "description": "Opinionated logger for production-scale applications", - "main": "index.js", - "types": "./build/ts/index.d.ts", - "publishConfig": { - "access": "public", - "@shopify:registry": "https://registry.npmjs.org" - }, - "author": "Shopify Inc.", - "repository": { - "type": "git", - "url": "git+https://github.com/Shopify/quilt.git", - "directory": "packages/logger" - }, - "bugs": { - "url": "https://github.com/Shopify/quilt/issues" - }, - "homepage": "https://github.com/Shopify/quilt/blob/main/packages/logger/README.md", - "engines": { - "node": ">=18.12.0" - }, - "dependencies": { - "chalk": "^2.4.1", - "log-symbols": "^2.2.0", - "pretty-ms": "^3.2.0" - }, - "sideEffects": false, - "files": [ - "build/", - "!build/*.tsbuildinfo", - "!build/ts/**/tests/", - "index.js", - "index.mjs", - "index.esnext" - ], - "module": "index.mjs", - "esnext": "index.esnext", - "exports": { - ".": { - "types": "./build/ts/index.d.ts", - "esnext": "./index.esnext", - "import": "./index.mjs", - "require": "./index.js" - } - } -} diff --git a/packages/logger/rollup.config.mjs b/packages/logger/rollup.config.mjs deleted file mode 100644 index 9368f1b17d..0000000000 --- a/packages/logger/rollup.config.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import {buildConfig} from '../../config/rollup.mjs'; - -export default buildConfig(import.meta.url, { - entries: ['./src/index.ts'], - entrypoints: {index: './src/index.ts'}, -}); diff --git a/packages/logger/src/Logger.ts b/packages/logger/src/Logger.ts deleted file mode 100644 index e64eba0056..0000000000 --- a/packages/logger/src/Logger.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type {LogEntry, Value, Formatter} from './types'; -import {LogLevel} from './types'; -import {ConsoleFormatter} from './formatters'; - -export interface LoggerOptions { - formatter?: Formatter; - name?: string; -} - -export class Logger { - private formatter: Formatter; - private scopes: string[]; - - constructor(options?: LoggerOptions) { - this.formatter = (options && options.formatter) || new ConsoleFormatter(); - this.scopes = options && options.name ? [options.name] : []; - } - - warn(entry: Value) { - this.log({level: LogLevel.Warn, payload: entry}); - } - - info(entry: Value) { - this.log({level: LogLevel.Info, payload: entry}); - } - - error(err: Error) { - this.log({level: LogLevel.Critical, payload: err}); - } - - private log(...entries: LogEntry[]) { - for (const entry of entries) { - this.formatter.format({...entry, scopes: this.scopes}); - } - } -} diff --git a/packages/logger/src/formatters/ConsoleFormatter.ts b/packages/logger/src/formatters/ConsoleFormatter.ts deleted file mode 100644 index 2a3629a9b2..0000000000 --- a/packages/logger/src/formatters/ConsoleFormatter.ts +++ /dev/null @@ -1,85 +0,0 @@ -import chalk from 'chalk'; -import {info, warning, error} from 'log-symbols'; -import prettyMs from 'pretty-ms'; - -import type {FormatEntry, Formatter} from '../types'; -import {LogLevel} from '../types'; - -const MAX_LEVEL_LENGTH = [LogLevel.Critical, LogLevel.Info, LogLevel.Warn] - .map((level) => level.length) - .reduce((lenA, lenB) => (lenA > lenB ? lenA : lenB)); - -export class ConsoleFormatter implements Formatter { - private startTime: [number, number]; - - constructor() { - this.startTime = process.hrtime(); - } - - private get timestamp() { - const duration = process.hrtime(this.startTime); - const [seconds, nanoseconds] = duration; - const milliseconds = seconds * 1000 + nanoseconds / 1e6; - return prettyMs(milliseconds); - } - - format(entry: FormatEntry) { - const timestamp = chalk.magenta(`+${this.timestamp}`); - - const {level, payload, scopes} = entry; - - let logMsg = ''; - - if (level === LogLevel.Critical) { - const {message, stack} = payload as Error; - logMsg = `${message} ${timestamp}`; - - if (stack != null) { - const [messageLine, ...otherLines] = stack.split('\n'); - logMsg = `${messageLine} ${timestamp}\n${otherLines - .map((line) => { - return chalk.gray(line); - }) - .join('\n')}`; - } - } else { - logMsg = `${payload} ${timestamp}`; - } - - const prefix = scopes.length ? `[${scopes.join(':')}] ` : ''; - const {logFn, icon, chalkColor} = configForLogLevel(level); - logFn( - prefix, - icon, - ' ', - chalkColor.bold.underline(level), - ' '.repeat(MAX_LEVEL_LENGTH - level.length), - logMsg, - ); - } -} - -function configForLogLevel(logLevel: LogLevel) { - if (logLevel === LogLevel.Warn) { - return { - // eslint-disable-next-line no-console - logFn: console.warn, - icon: warning, - chalkColor: chalk.yellow, - }; - } - if (logLevel === LogLevel.Critical) { - return { - // eslint-disable-next-line no-console - logFn: console.error, - icon: error, - chalkColor: chalk.red, - }; - } - return { - // eslint-disable-next-line no-console - logFn: console.log, - icon: info, - chalkColor: chalk.blue, - }; -} diff --git a/packages/logger/src/formatters/index.ts b/packages/logger/src/formatters/index.ts deleted file mode 100644 index 01aceee8bc..0000000000 --- a/packages/logger/src/formatters/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ConsoleFormatter'; diff --git a/packages/logger/src/formatters/tests/ConsoleFormatter.test.ts b/packages/logger/src/formatters/tests/ConsoleFormatter.test.ts deleted file mode 100644 index 874e701e14..0000000000 --- a/packages/logger/src/formatters/tests/ConsoleFormatter.test.ts +++ /dev/null @@ -1,135 +0,0 @@ -import {ConsoleFormatter} from '../ConsoleFormatter'; -import {LogLevel} from '../..'; - -describe('ConsoleFormatter', () => { - let consoleErrorSpy: jest.SpyInstance; - let consoleWarnSpy: jest.SpyInstance; - let consoleLogSpy: jest.SpyInstance; - - beforeEach(() => { - consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); - consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(); - consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(); - }); - - afterEach(() => { - consoleErrorSpy.mockRestore(); - consoleWarnSpy.mockRestore(); - consoleLogSpy.mockRestore(); - }); - - it('logs Critical entries to console.error', () => { - const formatter = new ConsoleFormatter(); - const errorSpy = jest.spyOn(console, 'error'); - const errorMsg = 'foo'; - const payload = new Error(errorMsg); - - formatter.format({ - level: LogLevel.Critical, - payload, - scopes: [], - }); - - expect(errorSpy).toHaveBeenCalledTimes(1); - const args = errorSpy.mock.calls[0]; - - expect(consoleCallIncludes(args, errorMsg)).toBe(true); - for (const stackLine of payload.stack!.split('\n')) { - expect(consoleCallIncludes(args, stackLine)).toBe(true); - } - - expect(consoleCallIncludes(args, LogLevel.Critical)).toBe(true); - - errorSpy.mockReset(); - errorSpy.mockRestore(); - }); - - it('logs Critical entries using Error.message when Error.stack is null', () => { - const formatter = new ConsoleFormatter(); - const errorSpy = jest.spyOn(console, 'error'); - const errorMsg = 'foo'; - const payload = new Error(errorMsg); - payload.stack = undefined; - - formatter.format({ - level: LogLevel.Critical, - payload, - scopes: [], - }); - - expect(errorSpy).toHaveBeenCalledTimes(1); - const args = errorSpy.mock.calls[0]; - - expect(consoleCallIncludes(args, errorMsg)).toBe(true); - - errorSpy.mockReset(); - errorSpy.mockRestore(); - }); - - it('logs Warn entries to console.warn', () => { - const formatter = new ConsoleFormatter(); - const warnSpy = jest.spyOn(console, 'warn'); - const message = 'foo'; - - formatter.format({ - level: LogLevel.Warn, - payload: message, - scopes: [], - }); - - expect(warnSpy).toHaveBeenCalledTimes(1); - const args = warnSpy.mock.calls[0]; - - expect(consoleCallIncludes(args, message)).toBe(true); - expect(consoleCallIncludes(args, LogLevel.Warn)).toBe(true); - - warnSpy.mockReset(); - warnSpy.mockRestore(); - }); - - it('logs Info entries to console.log', () => { - const formatter = new ConsoleFormatter(); - const logSpy = jest.spyOn(console, 'log'); - const message = 'foo'; - - formatter.format({ - level: LogLevel.Info, - payload: message, - scopes: [], - }); - - expect(logSpy).toHaveBeenCalledTimes(1); - const args = logSpy.mock.calls[0]; - - expect(consoleCallIncludes(args, message)).toBe(true); - expect(consoleCallIncludes(args, LogLevel.Info)).toBe(true); - - logSpy.mockReset(); - logSpy.mockRestore(); - }); - - it('logs with scopes', () => { - const formatter = new ConsoleFormatter(); - const logSpy = jest.spyOn(console, 'log'); - const message = 'foo'; - const scopes = ['a', 'b']; - - formatter.format({ - level: LogLevel.Info, - payload: message, - scopes, - }); - - expect(logSpy).toHaveBeenCalledTimes(1); - const args = logSpy.mock.calls[0]; - - expect(consoleCallIncludes(args, `[${scopes.join(':')}]`)).toBe(true); - - logSpy.mockReset(); - logSpy.mockRestore(); - }); -}); - -function consoleCallIncludes(args: string[], str: string) { - return args.findIndex((val) => val.includes(str)) !== -1; -} diff --git a/packages/logger/src/index.ts b/packages/logger/src/index.ts deleted file mode 100644 index 6c16c22267..0000000000 --- a/packages/logger/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './Logger'; -export * from './formatters'; -export * from './types'; diff --git a/packages/logger/src/tests/Logger.test.ts b/packages/logger/src/tests/Logger.test.ts deleted file mode 100644 index f9e2c56faa..0000000000 --- a/packages/logger/src/tests/Logger.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -import {Logger, LogLevel} from '..'; - -describe('Logger', () => { - describe('info()', () => { - it('passes the name as the root scope', () => { - const rootScope = 'fred'; - const formatSpy = jest.fn(({scopes}) => { - expect(scopes).toMatchObject([rootScope]); - }); - - const logger = new Logger({ - name: rootScope, - formatter: { - format: formatSpy, - }, - }); - - logger.info('hello'); - expect(formatSpy).toHaveBeenCalledTimes(1); - }); - - it('passes Info as the log level', () => { - const formatSpy = jest.fn(({level}) => { - expect(level).toBe(LogLevel.Info); - }); - - const logger = new Logger({ - name: 'fred', - formatter: { - format: formatSpy, - }, - }); - - logger.info('hello'); - expect(formatSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe('warn()', () => { - it('passes the name as the root scope', () => { - const rootScope = 'fred'; - const formatSpy = jest.fn(({scopes}) => { - expect(scopes).toMatchObject([rootScope]); - }); - - const logger = new Logger({ - name: rootScope, - formatter: { - format: formatSpy, - }, - }); - - logger.warn('hello'); - expect(formatSpy).toHaveBeenCalledTimes(1); - }); - - it('passes Warn as the log level', () => { - const formatSpy = jest.fn(({level}) => { - expect(level).toBe(LogLevel.Warn); - }); - - const logger = new Logger({ - name: 'fred', - formatter: { - format: formatSpy, - }, - }); - - logger.warn('hello'); - expect(formatSpy).toHaveBeenCalledTimes(1); - }); - }); - - describe('error()', () => { - it('passes the name as the root scope', () => { - const rootScope = 'fred'; - const formatSpy = jest.fn(({scopes}) => { - expect(scopes).toMatchObject([rootScope]); - }); - - const logger = new Logger({ - name: rootScope, - formatter: { - format: formatSpy, - }, - }); - - logger.error(new Error('hello')); - expect(formatSpy).toHaveBeenCalledTimes(1); - }); - - it('passes Critical as the log level', () => { - const formatSpy = jest.fn(({level}) => { - expect(level).toBe(LogLevel.Critical); - }); - - const logger = new Logger({ - name: 'fred', - formatter: { - format: formatSpy, - }, - }); - - logger.error(new Error('hello')); - expect(formatSpy).toHaveBeenCalledTimes(1); - }); - }); -}); diff --git a/packages/logger/src/types.ts b/packages/logger/src/types.ts deleted file mode 100644 index 4cbe1b1e96..0000000000 --- a/packages/logger/src/types.ts +++ /dev/null @@ -1,28 +0,0 @@ -export enum LogLevel { - Info = 'info', - Warn = 'warning', - Critical = 'critical', -} - -export type Value = string | number; - -export interface Data { - [key: string]: Value | Data; -} - -export type Loggable = any; - -export interface LogEntry { - level: LogLevel; - payload: Loggable; -} - -export interface FormatableLog { - scopes: string[]; -} - -export type FormatEntry = LogEntry & FormatableLog; - -export interface Formatter { - format(entry: FormatEntry): any; -} diff --git a/packages/logger/tsconfig.json b/packages/logger/tsconfig.json deleted file mode 100644 index 0cb905ae29..0000000000 --- a/packages/logger/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": "../../config/typescript/tsconfig.base.json", - "compilerOptions": { - "outDir": "build/ts", - "rootDir": "src" - }, - "include": [ - "../../config/typescript/*.d.ts", - "./src/**/*.ts", - "./src/**/*.tsx" - ] -} diff --git a/tsconfig.json b/tsconfig.json index ad34ca76e1..dd909687e7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -29,7 +29,6 @@ {"path": "./packages/koa-performance"}, {"path": "./packages/koa-shopify-graphql-proxy"}, {"path": "./packages/koa-shopify-webhooks"}, - {"path": "./packages/logger"}, {"path": "./packages/mime-types"}, {"path": "./packages/name"}, {"path": "./packages/network"},