diff --git a/package-lock.json b/package-lock.json index 0042e0710f..9c918af487 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6169,84 +6169,6 @@ "chalk": "^2.0.1" } }, - "log-update": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-2.3.0.tgz", - "integrity": "sha1-iDKP19HOeTiykoN0bwsbwSayRwg=", - "requires": { - "ansi-escapes": "^3.0.0", - "cli-cursor": "^2.0.0", - "wrap-ansi": "^3.0.1" - }, - "dependencies": { - "ansi-escapes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.0.0.tgz", - "integrity": "sha512-O/klc27mWNUigtv0F8NJWbLF00OcegQalkqKURWdosW08YZKi4m6CnSUSvIZG1otNJbTWhN01Hhz389DW7mvDQ==" - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-3.0.1.tgz", - "integrity": "sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0" - } - } - } - }, "logform": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/logform/-/logform-1.5.0.tgz", diff --git a/package.json b/package.json index 76dce0d8be..adb7222491 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,6 @@ "klaw": "^2.1.1", "kubernetes-client": "^5.2.0", "log-symbols": "^2.2.0", - "log-update": "^2.3.0", "moment": "^2.22.1", "node-emoji": "^1.8.1", "node-pty": "^0.7.4", diff --git a/src/cli.ts b/src/cli.ts index 5b46778abd..9d6832033a 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -35,7 +35,7 @@ import { PluginError, } from "./exceptions" import { Garden } from "./garden" -import { FileWriter } from "./logger/writers" +import { FileWriter } from "./logger/writers/file-writer" import { getLogger, RootLogNode } from "./logger" import { resolve } from "path" import { BuildCommand } from "./commands/build" diff --git a/src/logger/index.ts b/src/logger/index.ts index 6472a25454..74dd0d2a0f 100644 --- a/src/logger/index.ts +++ b/src/logger/index.ts @@ -25,7 +25,9 @@ import { LogEntryOpts, LogSymbolType, } from "./types" -import { BasicTerminalWriter, FancyTerminalWriter, Writer } from "./writers" +import { BasicTerminalWriter } from "./writers/basic-terminal-writer" +import { FancyTerminalWriter } from "./writers/fancy-terminal-writer" +import { Writer } from "./writers/base" import { ParameterError } from "../exceptions" const ROOT_DEPTH = -1 diff --git a/src/logger/renderers.ts b/src/logger/renderers.ts index b56b93533c..a8249410f3 100644 --- a/src/logger/renderers.ts +++ b/src/logger/renderers.ts @@ -116,3 +116,28 @@ export function renderDuration(entry: LogEntry): string { ? msgStyle(` (finished in ${duration(entry.timestamp)}s)`) : "" } + +export function formatForTerminal(entry: LogEntry): string { + let renderers + if (entry.depth > 0) { + // Skip section on child entries. + renderers = [ + [leftPad, [entry]], + [renderSymbol, [entry]], + [renderEmoji, [entry]], + [renderMsg, [entry]], + [renderDuration, [entry]], + ["\n"], + ] + } else { + renderers = [ + [renderSymbol, [entry]], + [renderSection, [entry]], + [renderEmoji, [entry]], + [renderMsg, [entry]], + [renderDuration, [entry]], + ["\n"], + ] + } + return combine(renderers) +} diff --git a/src/logger/util.ts b/src/logger/util.ts index 463f625778..633a9b706b 100644 --- a/src/logger/util.ts +++ b/src/logger/util.ts @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { LogEntryOpts } from "./types" +import { LogEntryOpts, LogLevel } from "./types" import { LogEntry, LogNode } from "." export interface Node { @@ -107,7 +107,7 @@ export function interceptStream(stream: NodeJS.WriteStream, callback: Function) return restore } -export const getTerminalWidth = (stream: NodeJS.WriteStream = process.stdout) => { +export function getTerminalWidth(stream: NodeJS.WriteStream = process.stdout) { const columns = (stream || {}).columns if (!columns) { @@ -121,3 +121,7 @@ export const getTerminalWidth = (stream: NodeJS.WriteStream = process.stdout) => return columns } + +export function validate(level: LogLevel, entry: LogEntry): boolean { + return level >= entry.level && entry.opts.msg !== undefined +} diff --git a/src/logger/writers/base.ts b/src/logger/writers/base.ts new file mode 100644 index 0000000000..25fd8925db --- /dev/null +++ b/src/logger/writers/base.ts @@ -0,0 +1,21 @@ +import { + LogLevel, +} from "../types" + +import { LogEntry, RootLogNode } from "../index" + +export interface WriterConfig { + level?: LogLevel +} + +export abstract class Writer { + public level: LogLevel | undefined + + constructor({ level }: WriterConfig = {}) { + this.level = level + } + + abstract render(...args): string | string[] | null + abstract onGraphChange(entry: LogEntry, rootLogNode: RootLogNode): void + abstract stop(): void +} diff --git a/src/logger/writers/basic-terminal-writer.ts b/src/logger/writers/basic-terminal-writer.ts new file mode 100644 index 0000000000..fc0eab5299 --- /dev/null +++ b/src/logger/writers/basic-terminal-writer.ts @@ -0,0 +1,30 @@ +import { + LogLevel, +} from "../types" +import { + formatForTerminal, +} from "../renderers" +import { LogEntry, RootLogNode } from "../index" +import { validate } from "../util" +import { Writer } from "./base" + +export class BasicTerminalWriter extends Writer { + public level: LogLevel + + render(entry: LogEntry, rootLogNode: RootLogNode): string | null { + const level = this.level || rootLogNode.level + if (validate(level, entry)) { + return formatForTerminal(entry) + } + return null + } + + onGraphChange(entry: LogEntry, rootLogNode: RootLogNode) { + const out = this.render(entry, rootLogNode) + if (out) { + console.log(out) + } + } + + stop() { } +} diff --git a/src/logger/writers.ts b/src/logger/writers/fancy-terminal-writer.ts similarity index 64% rename from src/logger/writers.ts rename to src/logger/writers/fancy-terminal-writer.ts index edcf3bd369..099f5e59f4 100644 --- a/src/logger/writers.ts +++ b/src/logger/writers/fancy-terminal-writer.ts @@ -1,172 +1,27 @@ -/* - * Copyright (C) 2018 Garden Technologies, Inc. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - import * as ansiEscapes from "ansi-escapes" import * as cliCursor from "cli-cursor" import * as elegantSpinner from "elegant-spinner" -import * as path from "path" -import * as winston from "winston" import * as wrapAnsi from "wrap-ansi" import chalk from "chalk" -const stripAnsi = require("strip-ansi") -import { getChildEntries, getTerminalWidth, interceptStream } from "./util" import { EntryStatus, LogLevel, -} from "./types" - +} from "../types" import { - combine, + formatForTerminal, leftPad, - renderDuration, - renderEmoji, - renderError, renderMsg, - renderSection, - renderSymbol, -} from "./renderers" - -import { LogEntry, RootLogNode } from "./index" -import { sleep } from "../util" - -const { combine: winstonCombine, timestamp, printf } = winston.format +} from "../renderers" +import { LogEntry, RootLogNode } from "../index" +import { sleep } from "../../util" +import { getChildEntries, getTerminalWidth, interceptStream, validate } from "../util" +import { Writer, WriterConfig } from "./base" const FANCY_LOGGER_UPDATE_FREQUENCY_MS = 100 const FANCY_LOGGER_THROTTLE_MS = 600 -const DEFAULT_LOG_FILENAME = "development.log" -const DEFAULT_FILE_TRANSPORT_OPTIONS = { - format: winstonCombine( - timestamp(), - printf(info => `\n[${info.timestamp}] ${info.message}`), - ), - maxsize: 10000000, // 10 MB - maxFiles: 1, -} const spinnerStyle = chalk.cyan -const levelToStr = (lvl: LogLevel): string => LogLevel[lvl] -const validate = (level: LogLevel, entry: LogEntry): boolean => { - return level >= entry.level && entry.opts.msg !== undefined -} - -export interface WriterConfig { - level?: LogLevel -} - -export interface FileWriterConfig { - level: LogLevel - root: string - filename?: string - fileTransportOptions?: {} -} - -export abstract class Writer { - public level: LogLevel | undefined - - constructor({ level }: WriterConfig = {}) { - this.level = level - } - - abstract render(...args): string | string[] | null - abstract onGraphChange(entry: LogEntry, rootLogNode: RootLogNode): void - abstract stop(): void -} - -export class FileWriter extends Writer { - private winston: any // Types are still missing from Winston 3.x.x. - - public level: LogLevel - - constructor(config: FileWriterConfig) { - const { - fileTransportOptions = DEFAULT_FILE_TRANSPORT_OPTIONS, - filename = DEFAULT_LOG_FILENAME, - level, - root, - } = config - - super({ level }) - - this.winston = winston.createLogger({ - level: levelToStr(level), - transports: [ - new winston.transports.File({ - ...fileTransportOptions, - filename: path.join(root, filename), - }), - ], - }) - } - - render(entry: LogEntry): string | null { - const renderFn = entry.level === LogLevel.error ? renderError : renderMsg - if (validate(this.level, entry)) { - return stripAnsi(renderFn(entry)) - } - return null - } - - onGraphChange(entry: LogEntry) { - const out = this.render(entry) - if (out) { - this.winston.log(levelToStr(entry.level), out) - } - } - - stop() { } -} - -function formatForTerminal(entry: LogEntry): string { - let renderers - if (entry.depth > 0) { - // Skip section on child entries. - renderers = [ - [leftPad, [entry]], - [renderSymbol, [entry]], - [renderEmoji, [entry]], - [renderMsg, [entry]], - [renderDuration, [entry]], - ["\n"], - ] - } else { - renderers = [ - [renderSymbol, [entry]], - [renderSection, [entry]], - [renderEmoji, [entry]], - [renderMsg, [entry]], - [renderDuration, [entry]], - ["\n"], - ] - } - return combine(renderers) -} - -export class BasicTerminalWriter extends Writer { - public level: LogLevel - - render(entry: LogEntry, rootLogNode: RootLogNode): string | null { - const level = this.level || rootLogNode.level - if (validate(level, entry)) { - return formatForTerminal(entry) - } - return null - } - - onGraphChange(entry: LogEntry, rootLogNode: RootLogNode) { - const out = this.render(entry, rootLogNode) - if (out) { - console.log(out) - } - } - - stop() { } -} export type Coords = [number, number] diff --git a/src/logger/writers/file-writer.ts b/src/logger/writers/file-writer.ts new file mode 100644 index 0000000000..0b73c5289e --- /dev/null +++ b/src/logger/writers/file-writer.ts @@ -0,0 +1,79 @@ +import * as winston from "winston" +import * as path from "path" +import * as stripAnsi from "strip-ansi" + +import { + LogLevel, +} from "../types" +import { LogEntry } from "../index" +import { Writer } from "./base" +import { validate } from "../util" +import { + renderError, + renderMsg, +} from "../renderers" + +export interface FileWriterConfig { + level: LogLevel + root: string + filename?: string + fileTransportOptions?: {} +} + +const { combine: winstonCombine, timestamp, printf } = winston.format + +const DEFAULT_LOG_FILENAME = "development.log" +const DEFAULT_FILE_TRANSPORT_OPTIONS = { + format: winstonCombine( + timestamp(), + printf(info => `\n[${info.timestamp}] ${info.message}`), + ), + maxsize: 10000000, // 10 MB + maxFiles: 1, +} + +const levelToStr = (lvl: LogLevel): string => LogLevel[lvl] + +export class FileWriter extends Writer { + private winston: any // Types are still missing from Winston 3.x.x. + + public level: LogLevel + + constructor(config: FileWriterConfig) { + const { + fileTransportOptions = DEFAULT_FILE_TRANSPORT_OPTIONS, + filename = DEFAULT_LOG_FILENAME, + level, + root, + } = config + + super({ level }) + + this.winston = winston.createLogger({ + level: levelToStr(level), + transports: [ + new winston.transports.File({ + ...fileTransportOptions, + filename: path.join(root, filename), + }), + ], + }) + } + + render(entry: LogEntry): string | null { + const renderFn = entry.level === LogLevel.error ? renderError : renderMsg + if (validate(this.level, entry)) { + return stripAnsi(renderFn(entry)) + } + return null + } + + onGraphChange(entry: LogEntry) { + const out = this.render(entry) + if (out) { + this.winston.log(levelToStr(entry.level), out) + } + } + + stop() { } +} diff --git a/test/src/logger.ts b/test/src/logger.ts index 61fb825fe6..62052a9ab3 100644 --- a/test/src/logger.ts +++ b/test/src/logger.ts @@ -2,7 +2,8 @@ const stripAnsi = require("strip-ansi") import { expect } from "chai" import { LogLevel, EntryStatus, LogSymbolType, EntryStyle } from "../../src/logger/types" -import { BasicTerminalWriter, FancyTerminalWriter } from "../../src/logger/writers" +import { BasicTerminalWriter } from "../../src/logger/writers/basic-terminal-writer" +import { FancyTerminalWriter } from "../../src/logger/writers/fancy-terminal-writer" import { RootLogNode } from "../../src/logger" import { getChildNodes } from "../../src/logger/util"