From a3850dc106ec6053b1202e97bbbb6f4daebd9b3f Mon Sep 17 00:00:00 2001 From: Rosen Vladimirov Date: Wed, 1 May 2019 14:10:54 +0300 Subject: [PATCH 01/13] chore: delete unused progress-indicator --- lib/common/bootstrap.ts | 1 - lib/common/declarations.d.ts | 14 ------------- lib/common/progress-indicator.ts | 36 -------------------------------- test/plugins-service.ts | 7 ------- 4 files changed, 58 deletions(-) delete mode 100644 lib/common/progress-indicator.ts diff --git a/lib/common/bootstrap.ts b/lib/common/bootstrap.ts index 2aa2c255c8..d5276f5ae4 100644 --- a/lib/common/bootstrap.ts +++ b/lib/common/bootstrap.ts @@ -27,7 +27,6 @@ $injector.require("childProcess", "./child-process"); $injector.require("prompter", "./prompter"); $injector.require("projectHelper", "./project-helper"); $injector.require("pluginVariablesHelper", "./plugin-variables-helper"); -$injector.require("progressIndicator", "./progress-indicator"); $injector.requireCommand(["help", "/?"], "./commands/help"); $injector.requireCommand("usage-reporting", "./commands/analytics"); diff --git a/lib/common/declarations.d.ts b/lib/common/declarations.d.ts index 823adfe9fc..7959c0ad8f 100644 --- a/lib/common/declarations.d.ts +++ b/lib/common/declarations.d.ts @@ -1281,20 +1281,6 @@ interface IServiceContractGenerator { generate(definitionsPath?: string): Promise; } -/** - * Used to show indication that a process is running - */ -interface IProgressIndicator { - /** - * Prints indication that a process is running - * @param {Promise} promise process - * @param {number} timeout time interval for printing indication - * @param {boolean} options whether to surpress the trailing new line printed after the process ends - * @return {Promise} - */ - showProgressIndicator(promise: Promise, timeout: number, options?: { surpressTrailingNewLine?: boolean }): Promise; -} - /** * Describes project file that should be livesynced */ diff --git a/lib/common/progress-indicator.ts b/lib/common/progress-indicator.ts deleted file mode 100644 index ee48daa699..0000000000 --- a/lib/common/progress-indicator.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { isInteractive } from './helpers'; - -export class ProgressIndicator implements IProgressIndicator { - constructor(private $logger: ILogger) { } - - public async showProgressIndicator(promise: Promise, timeout: number, options?: { surpressTrailingNewLine?: boolean }): Promise { - const surpressTrailingNewLine = options && options.surpressTrailingNewLine; - - let isFulfilled = false; - - const tempPromise = new Promise((resolve, reject) => { - promise - .then(res => { - isFulfilled = true; - resolve(res); - }) - .catch(err => { - isFulfilled = true; - reject(err); - }); - }); - - if (!isInteractive()) { - while (!isFulfilled) { - await this.$logger.printMsgWithTimeout(".", timeout); - } - } - - if (!surpressTrailingNewLine) { - this.$logger.out(); - } - - return tempPromise; - } -} -$injector.register("progressIndicator", ProgressIndicator); diff --git a/test/plugins-service.ts b/test/plugins-service.ts index bb2b011cd1..630a577b5f 100644 --- a/test/plugins-service.ts +++ b/test/plugins-service.ts @@ -107,13 +107,6 @@ function createTestInjector() { showCommandLineHelp: async (): Promise => (undefined) }); testInjector.register("settingsService", SettingsService); - testInjector.register("progressIndicator", { - getSpinner: (msg: string) => ({ - start: (): void => undefined, - stop: (): void => undefined, - message: (): void => undefined - }) - }); testInjector.register("httpClient", {}); testInjector.register("extensibilityService", {}); testInjector.register("androidPluginBuildService", stubs.AndroidPluginBuildServiceStub); From 02d6dfbfa385e49b261b0e5858c65f24a6a71ff2 Mon Sep 17 00:00:00 2001 From: Rosen Vladimirov Date: Wed, 1 May 2019 14:12:18 +0300 Subject: [PATCH 02/13] chore: remove `printMsgWithTimeout` from logger --- lib/common/definitions/logger.d.ts | 1 - lib/common/logger.ts | 10 ---------- lib/common/test/unit-tests/stubs.ts | 3 --- test/stubs.ts | 3 --- 4 files changed, 17 deletions(-) diff --git a/lib/common/definitions/logger.d.ts b/lib/common/definitions/logger.d.ts index a719108d6c..3a3a1e44b6 100644 --- a/lib/common/definitions/logger.d.ts +++ b/lib/common/definitions/logger.d.ts @@ -14,6 +14,5 @@ interface ILogger { prepare(item: any): string; printInfoMessageOnSameLine(message: string): void; - printMsgWithTimeout(message: string, timeout: number): Promise; printOnStderr(formatStr?: any, ...args: any[]): void; } diff --git a/lib/common/logger.ts b/lib/common/logger.ts index aebd47b2a0..d3228d34f3 100644 --- a/lib/common/logger.ts +++ b/lib/common/logger.ts @@ -113,16 +113,6 @@ export class Logger implements ILogger { } } - public printMsgWithTimeout(message: string, timeout: number): Promise { - return new Promise((resolve, reject) => { - setTimeout(() => { - this.printInfoMessageOnSameLine(message); - resolve(); - }, timeout); - - }); - } - public printMarkdown(...args: string[]): void { const opts = { unescape: true, diff --git a/lib/common/test/unit-tests/stubs.ts b/lib/common/test/unit-tests/stubs.ts index ba0609a2b8..3e97787eb8 100644 --- a/lib/common/test/unit-tests/stubs.ts +++ b/lib/common/test/unit-tests/stubs.ts @@ -47,9 +47,6 @@ export class CommonLoggerStub implements ILogger { } printInfoMessageOnSameLine(message: string): void { } - async printMsgWithTimeout(message: string, timeout: number): Promise { - return null; - } printMarkdown(message: string): void { this.output += message; diff --git a/test/stubs.ts b/test/stubs.ts index 7556081a47..eecca957b6 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -36,9 +36,6 @@ export class LoggerStub implements ILogger { } printInfoMessageOnSameLine(message: string): void { } - async printMsgWithTimeout(message: string, timeout: number): Promise { - return null; - } printMarkdown(message: string): void { } From 822173a5bf0ded08b947905b463012254dd18d74 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Fri, 3 May 2019 10:32:59 +0300 Subject: [PATCH 03/13] chore: rename initService to projectInitService Rename `initService` to `projectInitService` as the new name has more sense. --- lib/bootstrap.ts | 2 +- lib/commands/init.ts | 8 ++++---- lib/declarations.d.ts | 2 +- lib/services/{init-service.ts => project-init-service.ts} | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) rename lib/services/{init-service.ts => project-init-service.ts} (95%) diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index d99ca32d44..76de985005 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -109,7 +109,7 @@ $injector.require("xcprojService", "./services/xcproj-service"); $injector.require("versionsService", "./services/versions-service"); $injector.requireCommand("install", "./commands/install"); -$injector.require("initService", "./services/init-service"); +$injector.require("projectInitService", "./services/project-init-service"); $injector.requireCommand("init", "./commands/init"); $injector.require("infoService", "./services/info-service"); diff --git a/lib/commands/init.ts b/lib/commands/init.ts index a704c25022..36984c0dd6 100644 --- a/lib/commands/init.ts +++ b/lib/commands/init.ts @@ -1,12 +1,12 @@ -export class InitCommand implements ICommand { +export class ProjectInitCommand implements ICommand { public allowedParameters: ICommandParameter[] = []; public enableHooks = false; - constructor(private $initService: IInitService) { } + constructor(private $projectInitService: IProjectInitService) { } public async execute(args: string[]): Promise { - return this.$initService.initialize(); + return this.$projectInitService.initialize(); } } -$injector.registerCommand("init", InitCommand); +$injector.registerCommand("init", ProjectInitCommand); diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index e8dfe938e0..883943db2a 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -603,7 +603,7 @@ interface IUpdatePlatformOptions extends IPlatformTemplate { canUpdate: boolean; } -interface IInitService { +interface IProjectInitService { initialize(): Promise; } diff --git a/lib/services/init-service.ts b/lib/services/project-init-service.ts similarity index 95% rename from lib/services/init-service.ts rename to lib/services/project-init-service.ts index 8a35b0af0f..edb8bea6f9 100644 --- a/lib/services/init-service.ts +++ b/lib/services/project-init-service.ts @@ -3,7 +3,7 @@ import * as helpers from "../common/helpers"; import * as path from "path"; import * as semver from "semver"; -export class InitService implements IInitService { +export class ProjectInitService implements IProjectInitService { private static MIN_SUPPORTED_FRAMEWORK_VERSIONS: IStringDictionary = { "tns-ios": "1.1.0", "tns-android": "1.1.0", @@ -107,7 +107,7 @@ export class InitService implements IInitService { } const allVersions: any = await this.$packageManager.view(packageName, { "versions": true }); - const versions = _.filter(allVersions, (v: string) => semver.gte(v, InitService.MIN_SUPPORTED_FRAMEWORK_VERSIONS[packageName])); + const versions = _.filter(allVersions, (v: string) => semver.gte(v, ProjectInitService.MIN_SUPPORTED_FRAMEWORK_VERSIONS[packageName])); if (versions.length === 1) { this.$logger.info(`Only ${versions[0]} version is available for ${packageName}.`); return this.buildVersionData(versions[0]); @@ -121,7 +121,7 @@ export class InitService implements IInitService { private buildVersionData(version: string): IStringDictionary { const result: IStringDictionary = {}; - result[InitService.VERSION_KEY_NAME] = version; + result[ProjectInitService.VERSION_KEY_NAME] = version; return result; } @@ -130,4 +130,4 @@ export class InitService implements IInitService { return !helpers.isInteractive() || this.$options.force; } } -$injector.register("initService", InitService); +$injector.register("projectInitService", ProjectInitService); From 516982913d460a2fc366eb5eb44d0a7716b1bb2b Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Fri, 3 May 2019 14:37:54 +0300 Subject: [PATCH 04/13] feat: expose logger and enable configuring it Expose `$logger` for using when CLI is required as a library. Also introduce `initialize` method in it, that allows full configuration of the log4js instance. Implement a new `emit-appender` logger, that emits `logData` event with information for the logging instead of printing it to the stdout. This emitter can be used with the following code: ```JavaScript const tns = require("nativescript"); const { LoggerAppenders } = tns.constants; const { EventEmitter } = require("events"); const emitter = new EventEmitter(); // IMPORTANT: Due to current log4js behavior, you must set the event handler BEFORE calling initialize of the logger. emitter.on("logData", logData => { // logData contains two properties: loggingEvent and formattedMessage // loggingEvent contains information about the level of the message and the raw message itself // formattedMessage is the message with specified layout (i.e. it is string). Default layout is `messagePassThrough`. }); tns.logger.initialize({ appenderOptions: { type: LoggerAppenders.emitAppender, emitter }); ``` This is the easiest way to use the new appender. You can also use LoggerLevel from constants and specify different logging levels. You can pass whatever layout you need for the message. --- lib/bootstrap.ts | 2 +- lib/common/definitions/logger.d.ts | 52 +++++++++++----- lib/common/logger-appenders/emit-appender.ts | 38 ++++++++++++ lib/common/logger.ts | 52 ++++++++++++---- lib/common/test/unit-tests/logger.ts | 2 + lib/common/test/unit-tests/stubs.ts | 1 + lib/constants.ts | 63 ++++++++++++++++++++ test/nativescript-cli-lib.ts | 7 ++- test/stubs.ts | 1 + 9 files changed, 188 insertions(+), 30 deletions(-) create mode 100644 lib/common/logger-appenders/emit-appender.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 76de985005..cafc649a96 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -1,5 +1,5 @@ require("./common/bootstrap"); -$injector.require("logger", "./common/logger"); +$injector.requirePublicClass("logger", "./common/logger"); $injector.require("config", "./config"); $injector.require("options", "./options"); // note: order above is important! diff --git a/lib/common/definitions/logger.d.ts b/lib/common/definitions/logger.d.ts index 3a3a1e44b6..6d203e3a0e 100644 --- a/lib/common/definitions/logger.d.ts +++ b/lib/common/definitions/logger.d.ts @@ -1,18 +1,40 @@ -interface ILogger { - getLevel(): string; - fatal(formatStr?: any, ...args: any[]): void; - error(formatStr?: any, ...args: any[]): void; - warn(formatStr?: any, ...args: any[]): void; - warnWithLabel(formatStr?: any, ...args: any[]): void; - info(formatStr?: any, ...args: any[]): void; - debug(formatStr?: any, ...args: any[]): void; - trace(formatStr?: any, ...args: any[]): void; - printMarkdown(...args: any[]): void; +import { Layout, LoggingEvent, Configuration, Level } from "log4js"; +import { EventEmitter } from "events"; +import { LoggerLevel } from "../../constants"; - out(formatStr?: any, ...args: any[]): void; - write(...args: any[]): void; +declare global { + interface IAppenderOptions extends IDictionary { + type: string; + } - prepare(item: any): string; - printInfoMessageOnSameLine(message: string): void; - printOnStderr(formatStr?: any, ...args: any[]): void; + interface ILoggerOptions { + level?: LoggerLevel; + appenderOptions?: IAppenderOptions; + } + + interface ILogger { + initialize(opts?: ILoggerOptions): void; + getLevel(): string; + fatal(formatStr?: any, ...args: any[]): void; + error(formatStr?: any, ...args: any[]): void; + warn(formatStr?: any, ...args: any[]): void; + warnWithLabel(formatStr?: any, ...args: any[]): void; + info(formatStr?: any, ...args: any[]): void; + debug(formatStr?: any, ...args: any[]): void; + trace(formatStr?: any, ...args: any[]): void; + printMarkdown(...args: any[]): void; + + out(formatStr?: any, ...args: any[]): void; + write(...args: any[]): void; + + prepare(item: any): string; + printInfoMessageOnSameLine(message: string): void; + printOnStderr(formatStr?: any, ...args: any[]): void; + } + + + interface Log4JSEmitAppenderConfiguration extends Configuration { + layout: Layout; + emitter: EventEmitter; + } } diff --git a/lib/common/logger-appenders/emit-appender.ts b/lib/common/logger-appenders/emit-appender.ts new file mode 100644 index 0000000000..23939ceae2 --- /dev/null +++ b/lib/common/logger-appenders/emit-appender.ts @@ -0,0 +1,38 @@ +import { LoggingEvent } from "log4js"; +import { EventEmitter } from "events"; + +const logDataEventName = "logData"; +function emitAppender(layout: Function, emitter: EventEmitter) { + const appender = (loggingEvent: LoggingEvent) => { + emitter.emit(logDataEventName, { loggingEvent, formattedMessage: layout(loggingEvent) }); + }; + + appender.shutdown = () => { + emitter.removeAllListeners(logDataEventName); + }; + + return appender; +} + +function configure(config: Log4JSEmitAppenderConfiguration, layouts: any) { + // the default layout for the appender + let layout = layouts.messagePassThroughLayout; + + // check if there is another layout specified + if (config.layout) { + layout = layouts.layout(config.layout.type, config.layout); + } + + if (!config.emitter) { + throw new Error("Emitter must be passed to emit-appender"); + } + + if (!config.emitter.emit || typeof config.emitter.emit !== "function") { + throw new Error("The passed emitter must be instance of EventEmitter"); + } + + // create a new appender instance + return emitAppender(layout, config.emitter); +} + +exports.configure = configure; diff --git a/lib/common/logger.ts b/lib/common/logger.ts index d3228d34f3..17cb539a9c 100644 --- a/lib/common/logger.ts +++ b/lib/common/logger.ts @@ -2,6 +2,7 @@ import * as log4js from "log4js"; import * as util from "util"; import * as stream from "stream"; import * as marked from "marked"; +import { cache } from "./decorators"; const TerminalRenderer = require("marked-terminal"); const chalk = require("chalk"); @@ -11,26 +12,35 @@ export class Logger implements ILogger { private passwordReplacement = "$1$3*******$2$4"; private static LABEL = "[WARNING]:"; - constructor($config: Config.IConfig, + constructor(private $config: Config.IConfig, private $options: IOptions) { - const appenders: IDictionary = {}; - const categories: IDictionary<{ appenders: string[]; level: string; }> = {}; - let level: string = null; - if (this.$options.log) { - level = this.$options.log; - } else { - level = $config.DEBUG ? "TRACE" : "INFO"; - } + } + + @cache() + public initialize(opts?: ILoggerOptions): void { + opts = opts || {}; + const { appenderOptions: appenderOpts, level } = opts; - appenders["out"] = { + const appender: any = { type: "console", layout: { type: "messagePassThrough" } }; - categories["default"] = { - appenders: ['out'], - level + + if (appenderOpts) { + _.merge(appender, appenderOpts); + } + + const appenders: IDictionary = { + out: appender + }; + + const categories: IDictionary<{ appenders: string[]; level: string; }> = { + default: { + appenders: ['out'], + level: level || (this.$config.DEBUG ? "TRACE" : "INFO") + } }; log4js.configure({ appenders, categories }); @@ -39,14 +49,20 @@ export class Logger implements ILogger { } getLevel(): string { + this.initialize(); + return this.log4jsLogger.level.toString(); } fatal(...args: string[]): void { + this.initialize(); + this.log4jsLogger.fatal.apply(this.log4jsLogger, args); } error(...args: string[]): void { + this.initialize(); + const message = util.format.apply(null, args); const colorizedMessage = message.red; @@ -54,6 +70,8 @@ export class Logger implements ILogger { } warn(...args: string[]): void { + this.initialize(); + const message = util.format.apply(null, args); const colorizedMessage = message.yellow; @@ -61,20 +79,28 @@ export class Logger implements ILogger { } warnWithLabel(...args: string[]): void { + this.initialize(); + const message = util.format.apply(null, args); this.warn(`${Logger.LABEL} ${message}`); } info(...args: string[]): void { + this.initialize(); + this.log4jsLogger.info.apply(this.log4jsLogger, args); } debug(...args: string[]): void { + this.initialize(); + const encodedArgs: string[] = this.getPasswordEncodedArguments(args); this.log4jsLogger.debug.apply(this.log4jsLogger, encodedArgs); } trace(...args: string[]): void { + this.initialize(); + const encodedArgs: string[] = this.getPasswordEncodedArguments(args); this.log4jsLogger.trace.apply(this.log4jsLogger, encodedArgs); } diff --git a/lib/common/test/unit-tests/logger.ts b/lib/common/test/unit-tests/logger.ts index a9b8c0f7fa..6ddbef0685 100644 --- a/lib/common/test/unit-tests/logger.ts +++ b/lib/common/test/unit-tests/logger.ts @@ -45,6 +45,8 @@ describe("logger", () => { } }; + // Initialize the logger manually, so we can overwrite the log4jsLogger property + logger.initialize(); logger.log4jsLogger = log4jsLogger; }); diff --git a/lib/common/test/unit-tests/stubs.ts b/lib/common/test/unit-tests/stubs.ts index 3e97787eb8..567d637684 100644 --- a/lib/common/test/unit-tests/stubs.ts +++ b/lib/common/test/unit-tests/stubs.ts @@ -18,6 +18,7 @@ export class LockServiceStub implements ILockService { } export class CommonLoggerStub implements ILogger { + initialize(opts?: ILoggerOptions): void { } getLevel(): string { return undefined; } fatal(...args: string[]): void { } error(...args: string[]): void { } diff --git a/lib/constants.ts b/lib/constants.ts index 7443a4632a..bcf374bdd7 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -1,5 +1,6 @@ require("colors"); import { PreviewAppLiveSyncEvents } from "./services/livesync/playground/preview-app-constants"; +import { join } from "path"; export const APP_FOLDER_NAME = "app"; export const APP_RESOURCES_FOLDER_NAME = "App_Resources"; @@ -299,3 +300,65 @@ export enum IOSNativeTargetTypes { watchExtension = "watch_extension", appExtension = "app_extension" } + +export const LoggerAppenders = { + emitAppender: join(__dirname, "common", "logger-appenders", "emit-appender"), +}; + +export enum LoggerLevel { + /** + * Show all log messages. + * Log levels are used to assign importance to log messages, with the integer value being used to sort them. + * If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF) + */ + ALL = "ALL", + + /** + * Log levels are used to assign importance to log messages, with the integer value being used to sort them. + * If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF) + */ + TRACE = "TRACE", + + /** + * Log levels are used to assign importance to log messages, with the integer value being used to sort them. + * If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF) + */ + DEBUG = "DEBUG", + + /** + * Log levels are used to assign importance to log messages, with the integer value being used to sort them. + * If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF) + */ + INFO = "INFO", + + /** + * Log levels are used to assign importance to log messages, with the integer value being used to sort them. + * If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF) + */ + WARN = "WARN", + + /** + * Log levels are used to assign importance to log messages, with the integer value being used to sort them. + * If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF) + */ + ERROR = "ERROR", + + /** + * Log levels are used to assign importance to log messages, with the integer value being used to sort them. + * If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF) + */ + FATAL = "FATAL", + + /** + * Log levels are used to assign importance to log messages, with the integer value being used to sort them. + * If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF) + */ + MARK = "MARK", + + /** + * Disable all logging. + * Log levels are used to assign importance to log messages, with the integer value being used to sort them. + * If you do not specify anything in your configuration, the default values are used (ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < MARK < OFF) + */ + OFF = "OFF" +} diff --git a/test/nativescript-cli-lib.ts b/test/nativescript-cli-lib.ts index f32f35f779..70f55bdc2a 100644 --- a/test/nativescript-cli-lib.ts +++ b/test/nativescript-cli-lib.ts @@ -23,7 +23,7 @@ describe("nativescript-cli-lib", () => { "getIOSAssetsStructure", "getAndroidAssetsStructure" ], - constants: ["CONFIG_NS_APP_RESOURCES_ENTRY", "CONFIG_NS_APP_ENTRY", "CONFIG_NS_FILE_NAME"], + constants: ["CONFIG_NS_APP_RESOURCES_ENTRY", "CONFIG_NS_APP_ENTRY", "CONFIG_NS_FILE_NAME", "LoggerLevel", "LoggerAppenders"], localBuildService: ["build"], deviceLogProvider: null, packageManager: ["install", "uninstall", "view", "search"], @@ -62,6 +62,11 @@ describe("nativescript-cli-lib", () => { ], cleanupService: [ "setCleanupLogFile" + ], + logger: [ + "initialize", + "getLevel", + "info" ] }; diff --git a/test/stubs.ts b/test/stubs.ts index eecca957b6..bfdd8efda5 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -11,6 +11,7 @@ import { HostInfo } from "./../lib/common/host-info"; import { DevicePlatformsConstants } from "./../lib/common/mobile/device-platforms-constants"; export class LoggerStub implements ILogger { + initialize(opts?: ILoggerOptions): void { } getLevel(): string { return undefined; } fatal(...args: string[]): void { } error(...args: string[]): void { } From 86dd22a8ad64d7284e05487a691a91423b43f859 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Fri, 3 May 2019 15:13:53 +0300 Subject: [PATCH 05/13] feat: introduce initializeService Introduce initializeService which can be used to setup some specifics of CLI (currently the logger) and to print all required warnings. At the moment, whenever we want to introduce new warnings, we need to introduce new methods that we call from CLI and when it is used as a library. With the new solution all such warnings can be placed in the initialize method and it will automatically show those warnings. --- lib/bootstrap.ts | 2 ++ lib/definitions/initialize-service.d.ts | 3 +++ lib/nativescript-cli.ts | 17 +++++--------- lib/services/initialize-service.ts | 30 +++++++++++++++++++++++++ test/nativescript-cli-lib.ts | 3 +++ 5 files changed, 43 insertions(+), 12 deletions(-) create mode 100644 lib/definitions/initialize-service.d.ts create mode 100644 lib/services/initialize-service.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index cafc649a96..090c7c01ae 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -199,3 +199,5 @@ $injector.requirePublic("cleanupService", "./services/cleanup-service"); $injector.require("applePortalSessionService", "./services/apple-portal/apple-portal-session-service"); $injector.require("applePortalCookieService", "./services/apple-portal/apple-portal-cookie-service"); $injector.require("applePortalApplicationService", "./services/apple-portal/apple-portal-application-service"); + +$injector.requirePublicClass("initializeService", "./services/initialize-service"); diff --git a/lib/definitions/initialize-service.d.ts b/lib/definitions/initialize-service.d.ts new file mode 100644 index 0000000000..e4cab0db93 --- /dev/null +++ b/lib/definitions/initialize-service.d.ts @@ -0,0 +1,3 @@ +interface IInitializeService { + initialize(initOpts?: { loggerOptions?: ILoggerOptions }): Promise; +} diff --git a/lib/nativescript-cli.ts b/lib/nativescript-cli.ts index 431413ca28..2620b33018 100644 --- a/lib/nativescript-cli.ts +++ b/lib/nativescript-cli.ts @@ -1,6 +1,5 @@ require("./bootstrap"); -import { EOL } from "os"; import * as shelljs from "shelljs"; shelljs.config.silent = true; shelljs.config.fatal = true; @@ -25,6 +24,11 @@ process.on = (event: string, listener: any): any => { const err: IErrors = $injector.resolve("$errors"); err.printCallStack = config.DEBUG; + const $options = $injector.resolve("options"); + + const $initializeService = $injector.resolve("initializeService"); + await $initializeService.initialize({ loggerOptions: { level: $options.log } }); + const extensibilityService: IExtensibilityService = $injector.resolve("extensibilityService"); try { await settlePromises(extensibilityService.loadExtensions()); @@ -32,17 +36,6 @@ process.on = (event: string, listener: any): any => { logger.trace("Unable to load extensions. Error is: ", err); } - const $sysInfo = $injector.resolve("sysInfo"); - const macOSWarning = await $sysInfo.getMacOSWarningMessage(); - if (macOSWarning) { - const message = `${EOL}${macOSWarning.message}${EOL}`; - if (macOSWarning.severity === SystemWarningsSeverity.high) { - logger.printOnStderr(message.red.bold); - } else { - logger.warn(message); - } - } - const commandDispatcher: ICommandDispatcher = $injector.resolve("commandDispatcher"); const messages: IMessagesService = $injector.resolve("$messagesService"); diff --git a/lib/services/initialize-service.ts b/lib/services/initialize-service.ts new file mode 100644 index 0000000000..55deb76228 --- /dev/null +++ b/lib/services/initialize-service.ts @@ -0,0 +1,30 @@ +import { EOL } from "os"; + +export class InitializeService implements IInitializeService { + // NOTE: Do not inject anything here, use $injector.resolve in the code + // Injecting something may lead to logger initialization, but we want to initialize it from here. + constructor(private $injector: IInjector) { } + + public async initialize(initOpts?: { loggerOptions?: ILoggerOptions }): Promise { + initOpts = initOpts || {}; + const $logger = this.$injector.resolve("logger"); + $logger.initialize(initOpts.loggerOptions); + + await this.showWarnings($logger); + } + + private async showWarnings($logger: ILogger): Promise { + const $sysInfo = $injector.resolve("sysInfo"); + const macOSWarning = await $sysInfo.getMacOSWarningMessage(); + if (macOSWarning) { + const message = `${EOL}${macOSWarning.message}${EOL}`; + if (macOSWarning.severity === SystemWarningsSeverity.high) { + $logger.printOnStderr(message.red.bold); + } else { + $logger.warn(message); + } + } + } +} + +$injector.register("initializeService", InitializeService); diff --git a/test/nativescript-cli-lib.ts b/test/nativescript-cli-lib.ts index 70f55bdc2a..33b08b031c 100644 --- a/test/nativescript-cli-lib.ts +++ b/test/nativescript-cli-lib.ts @@ -67,6 +67,9 @@ describe("nativescript-cli-lib", () => { "initialize", "getLevel", "info" + ], + initializeService: [ + "initialize" ] }; From 449e8a7341a08484b048e5d0c9cb9ac67b4b4038 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Fri, 3 May 2019 15:39:02 +0300 Subject: [PATCH 06/13] fix: use correct method for showing all warnings on initialize --- lib/common/verify-node-version.ts | 57 ++++++++++++++++-------------- lib/services/initialize-service.ts | 10 +++--- 2 files changed, 36 insertions(+), 31 deletions(-) diff --git a/lib/common/verify-node-version.ts b/lib/common/verify-node-version.ts index f95db5578a..7e14722c55 100644 --- a/lib/common/verify-node-version.ts +++ b/lib/common/verify-node-version.ts @@ -53,38 +53,43 @@ export function verifyNodeVersion(): void { } } +var isGetNodeWarningCalled = false; export function getNodeWarning(): ISystemWarning { - var verificationOpts = getNodeVersionOpts(); - var cliName = verificationOpts.cliName; - var supportedVersionsRange = verificationOpts.supportedVersionsRange; - var deprecatedVersions = verificationOpts.deprecatedVersions; - var nodeVer = verificationOpts.nodeVer; + if (!isGetNodeWarningCalled) { + isGetNodeWarningCalled = true; + + var verificationOpts = getNodeVersionOpts(); + var cliName = verificationOpts.cliName; + var supportedVersionsRange = verificationOpts.supportedVersionsRange; + var deprecatedVersions = verificationOpts.deprecatedVersions; + var nodeVer = verificationOpts.nodeVer; - var warningMessage = ""; - if (deprecatedVersions) { - deprecatedVersions.forEach(function (version) { - if (semver.satisfies(nodeVer, version)) { - warningMessage = "Support for Node.js " + version + " is deprecated and will be removed in one of the next releases of " + cliName + - ". Please, upgrade to the latest Node.js LTS version. "; - return warningMessage; + var warningMessage = ""; + if (deprecatedVersions) { + deprecatedVersions.forEach(function (version) { + if (semver.satisfies(nodeVer, version)) { + warningMessage = "Support for Node.js " + version + " is deprecated and will be removed in one of the next releases of " + cliName + + ". Please, upgrade to the latest Node.js LTS version. "; + return warningMessage; + } + }); + } + + if (!warningMessage) { + var checkSatisfied = semver.satisfies(nodeVer, supportedVersionsRange); + if (!checkSatisfied) { + warningMessage = "Support for Node.js " + nodeVer + " is not verified. " + cliName + " CLI might not install or run properly."; } - }); - } + } - if (!warningMessage) { - var checkSatisfied = semver.satisfies(nodeVer, supportedVersionsRange); - if (!checkSatisfied) { - warningMessage = "Support for Node.js " + nodeVer + " is not verified. " + cliName + " CLI might not install or run properly."; + if (warningMessage) { + return { + message: warningMessage, + severity: SystemWarningsSeverity.medium + }; } - } - if (warningMessage) { - return { - message: warningMessage, - severity: SystemWarningsSeverity.medium - }; + return null; } - - return null; } /* tslint:enable */ diff --git a/lib/services/initialize-service.ts b/lib/services/initialize-service.ts index 55deb76228..83fd10913a 100644 --- a/lib/services/initialize-service.ts +++ b/lib/services/initialize-service.ts @@ -15,15 +15,15 @@ export class InitializeService implements IInitializeService { private async showWarnings($logger: ILogger): Promise { const $sysInfo = $injector.resolve("sysInfo"); - const macOSWarning = await $sysInfo.getMacOSWarningMessage(); - if (macOSWarning) { - const message = `${EOL}${macOSWarning.message}${EOL}`; - if (macOSWarning.severity === SystemWarningsSeverity.high) { + const systemWarnings = await $sysInfo.getSystemWarnings(); + _.each(systemWarnings, systemWarning => { + const message = `${EOL}${systemWarning.message}${EOL}`; + if (systemWarning.severity === SystemWarningsSeverity.high) { $logger.printOnStderr(message.red.bold); } else { $logger.warn(message); } - } + }); } } From b28f5be0ac332a43f2e019badb8a255c01ce8c4c Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Tue, 7 May 2019 21:15:57 +0300 Subject: [PATCH 07/13] feat: use only log4js methods in CLI logger Refactor the logic in CLI's logger to use log4js methods only - currently we have some methods that use `console.log`, others use `process.stdout`, `process.stderr`, etc. Also, there are some methods that apply console colorization for the message, but in fact CLI may be used as a library or in CI, where these colorization does not make sense. To resolve this, introduce a new appender and new layout that CLI will pass to `log4js`. The appender (called `cli-appender`) will control if the message will be shown on `stdout` or `stderr`. The layout (called `cli-layout`) will control the presentation of the message on the terminal, i.e. if we should add new line at the end of the message, if we should wrap it in borders (with `*` symbols, etc.) and the colorization of the message. The layout will be applied on all methods, so you can use it with any log4js method, i.e. you can do: ```TypeScript this.$logger.warn("This is my message", { useStderr: true }); this.$logger.info("This is my message", { useStderr: true }); ``` Before passing the data to `log4js`, CLI will set required properties in the context of the logging event, so the appender and the layout can read them. This way, in case CLI is used as a library, the custom properties and CLI specific layout, will not be shown. Also delete some methods from logger API, so it is streamlined. Also change logger.error to print on stderr instead of stdout. Move logger.ts to logger directory, so all logger logic will be on a single place. --- lib/bootstrap.ts | 2 +- lib/common/definitions/logger.d.ts | 8 +- lib/common/logger/appenders/cli-appender.ts | 29 +++++ .../appenders}/emit-appender.ts | 6 +- lib/common/logger/layouts/cli-layout.ts | 33 +++++ lib/common/{ => logger}/logger.ts | 122 ++++++++++-------- lib/common/test/unit-tests/logger.ts | 2 +- lib/common/test/unit-tests/stubs.ts | 33 ++--- lib/constants.ts | 12 +- lib/nativescript-cli.ts | 4 +- lib/services/initialize-service.ts | 8 +- test/stubs.ts | 19 +-- 12 files changed, 171 insertions(+), 107 deletions(-) create mode 100644 lib/common/logger/appenders/cli-appender.ts rename lib/common/{logger-appenders => logger/appenders}/emit-appender.ts (81%) create mode 100644 lib/common/logger/layouts/cli-layout.ts rename lib/common/{ => logger}/logger.ts (57%) diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 090c7c01ae..3b77b4214b 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -1,5 +1,5 @@ require("./common/bootstrap"); -$injector.requirePublicClass("logger", "./common/logger"); +$injector.requirePublicClass("logger", "./common/logger/logger"); $injector.require("config", "./config"); $injector.require("options", "./options"); // note: order above is important! diff --git a/lib/common/definitions/logger.d.ts b/lib/common/definitions/logger.d.ts index 6d203e3a0e..fcbb21053d 100644 --- a/lib/common/definitions/logger.d.ts +++ b/lib/common/definitions/logger.d.ts @@ -14,22 +14,16 @@ declare global { interface ILogger { initialize(opts?: ILoggerOptions): void; + initializeCliLogger(): void; getLevel(): string; fatal(formatStr?: any, ...args: any[]): void; error(formatStr?: any, ...args: any[]): void; warn(formatStr?: any, ...args: any[]): void; - warnWithLabel(formatStr?: any, ...args: any[]): void; info(formatStr?: any, ...args: any[]): void; debug(formatStr?: any, ...args: any[]): void; trace(formatStr?: any, ...args: any[]): void; printMarkdown(...args: any[]): void; - - out(formatStr?: any, ...args: any[]): void; - write(...args: any[]): void; - prepare(item: any): string; - printInfoMessageOnSameLine(message: string): void; - printOnStderr(formatStr?: any, ...args: any[]): void; } diff --git a/lib/common/logger/appenders/cli-appender.ts b/lib/common/logger/appenders/cli-appender.ts new file mode 100644 index 0000000000..19f5cbee0d --- /dev/null +++ b/lib/common/logger/appenders/cli-appender.ts @@ -0,0 +1,29 @@ +import { LoggingEvent } from "log4js"; +import { LoggerConfigData } from "../../../constants"; + +function cliAppender(layout: Function) { + const appender = (loggingEvent: LoggingEvent) => { + if (loggingEvent && loggingEvent.data) { + const stream = loggingEvent.context[LoggerConfigData.useStderr] ? process.stderr : process.stdout; + const preparedData = layout(loggingEvent); + stream.write(preparedData); + } + }; + + return appender; +} + +function configure(config: Log4JSEmitAppenderConfiguration, layouts: any) { + // the default layout for the appender + let layout = layouts.messagePassThroughLayout; + + // check if there is another layout specified + if (config.layout) { + layout = layouts.layout(config.layout.type, config.layout); + } + + // create a new appender instance + return cliAppender(layout); +} + +exports.configure = configure; diff --git a/lib/common/logger-appenders/emit-appender.ts b/lib/common/logger/appenders/emit-appender.ts similarity index 81% rename from lib/common/logger-appenders/emit-appender.ts rename to lib/common/logger/appenders/emit-appender.ts index 23939ceae2..f0b907174e 100644 --- a/lib/common/logger-appenders/emit-appender.ts +++ b/lib/common/logger/appenders/emit-appender.ts @@ -1,14 +1,14 @@ import { LoggingEvent } from "log4js"; import { EventEmitter } from "events"; +import { EmitAppenderEventName } from "../../../constants"; -const logDataEventName = "logData"; function emitAppender(layout: Function, emitter: EventEmitter) { const appender = (loggingEvent: LoggingEvent) => { - emitter.emit(logDataEventName, { loggingEvent, formattedMessage: layout(loggingEvent) }); + emitter.emit(EmitAppenderEventName, { loggingEvent, formattedMessage: layout(loggingEvent) }); }; appender.shutdown = () => { - emitter.removeAllListeners(logDataEventName); + emitter.removeAllListeners(EmitAppenderEventName); }; return appender; diff --git a/lib/common/logger/layouts/cli-layout.ts b/lib/common/logger/layouts/cli-layout.ts new file mode 100644 index 0000000000..60595a3aeb --- /dev/null +++ b/lib/common/logger/layouts/cli-layout.ts @@ -0,0 +1,33 @@ +import { format } from "util"; +import { getMessageWithBorders } from "../../helpers"; +import { LoggingEvent } from "log4js"; +import { LoggerConfigData, LoggerLevel } from "../../../constants"; +import { EOL } from "os"; + +export function layout(config: any) { + return function (logEvent: LoggingEvent): string { + let msg = format.apply(null, logEvent.data); + + if (logEvent.context[LoggerConfigData.wrapMessageWithBorders]) { + msg = getMessageWithBorders(msg); + } + + if (!logEvent.context[LoggerConfigData.skipNewLine]) { + msg += EOL; + } + + if (logEvent.level.isEqualTo(LoggerLevel.INFO)) { + return msg; + } + + if (logEvent.level.isEqualTo(LoggerLevel.ERROR)) { + return msg.red.bold; + } + + if (logEvent.level.isEqualTo(LoggerLevel.WARN)) { + return msg.yellow; + } + + return msg; + }; +} diff --git a/lib/common/logger.ts b/lib/common/logger/logger.ts similarity index 57% rename from lib/common/logger.ts rename to lib/common/logger/logger.ts index 17cb539a9c..dcd0418b0c 100644 --- a/lib/common/logger.ts +++ b/lib/common/logger/logger.ts @@ -2,7 +2,9 @@ import * as log4js from "log4js"; import * as util from "util"; import * as stream from "stream"; import * as marked from "marked"; -import { cache } from "./decorators"; +import { cache } from "../decorators"; +import { layout } from "./layouts/cli-layout"; +import { LoggerConfigData, LoggerLevel, LoggerAppenders } from "../../constants"; const TerminalRenderer = require("marked-terminal"); const chalk = require("chalk"); @@ -10,7 +12,6 @@ export class Logger implements ILogger { private log4jsLogger: log4js.Logger = null; private passwordRegex = /(password=).*?(['&,]|$)|(password["']?\s*:\s*["']).*?(["'])/i; private passwordReplacement = "$1$3*******$2$4"; - private static LABEL = "[WARNING]:"; constructor(private $config: Config.IConfig, private $options: IOptions) { @@ -48,68 +49,49 @@ export class Logger implements ILogger { this.log4jsLogger = log4js.getLogger(); } - getLevel(): string { - this.initialize(); + public initializeCliLogger(): void { + log4js.addLayout("cli", layout); - return this.log4jsLogger.level.toString(); + this.initialize({ + appenderOptions: { type: LoggerAppenders.cliAppender, layout: { type: "cli" } }, + level: this.$options.log + }); } - fatal(...args: string[]): void { + getLevel(): string { this.initialize(); - this.log4jsLogger.fatal.apply(this.log4jsLogger, args); + return this.log4jsLogger.level.toString(); } - error(...args: string[]): void { - this.initialize(); - - const message = util.format.apply(null, args); - const colorizedMessage = message.red; - - this.log4jsLogger.error.apply(this.log4jsLogger, [colorizedMessage]); + fatal(...args: any[]): void { + this.logMessage(args, LoggerLevel.FATAL); } - warn(...args: string[]): void { - this.initialize(); - - const message = util.format.apply(null, args); - const colorizedMessage = message.yellow; - - this.log4jsLogger.warn.apply(this.log4jsLogger, [colorizedMessage]); + error(...args: any[]): void { + args.push({ [LoggerConfigData.useStderr]: true }); + this.logMessage(args, LoggerLevel.ERROR); } - warnWithLabel(...args: string[]): void { - this.initialize(); - - const message = util.format.apply(null, args); - this.warn(`${Logger.LABEL} ${message}`); + warn(...args: any[]): void { + this.logMessage(args, LoggerLevel.WARN); } - info(...args: string[]): void { - this.initialize(); - - this.log4jsLogger.info.apply(this.log4jsLogger, args); + info(...args: any[]): void { + this.logMessage(args, LoggerLevel.INFO); } - debug(...args: string[]): void { - this.initialize(); - + debug(...args: any[]): void { const encodedArgs: string[] = this.getPasswordEncodedArguments(args); - this.log4jsLogger.debug.apply(this.log4jsLogger, encodedArgs); + this.logMessage(encodedArgs, LoggerLevel.DEBUG); } - trace(...args: string[]): void { - this.initialize(); - + trace(...args: any[]): void { const encodedArgs: string[] = this.getPasswordEncodedArguments(args); - this.log4jsLogger.trace.apply(this.log4jsLogger, encodedArgs); - } - - out(...args: string[]): void { - console.log(util.format.apply(null, args)); + this.logMessage(encodedArgs, LoggerLevel.TRACE); } - write(...args: string[]): void { + write(...args: any[]): void { process.stdout.write(util.format.apply(null, args)); } @@ -133,12 +115,6 @@ export class Logger implements ILogger { return JSON.stringify(item); } - public printInfoMessageOnSameLine(message: string): void { - if (!this.$options.log || this.$options.log === "info") { - this.write(message); - } - } - public printMarkdown(...args: string[]): void { const opts = { unescape: true, @@ -160,13 +136,53 @@ export class Logger implements ILogger { marked.setOptions({ renderer: new TerminalRenderer(opts) }); const formattedMessage = marked(util.format.apply(null, args)); - this.write(formattedMessage); + this.info(formattedMessage, { [LoggerConfigData.skipNewLine]: true }); } - public printOnStderr(...args: string[]): void { - if (process.stderr) { - process.stderr.write(util.format.apply(null, args)); + private logMessage(inputData: any[], logMethod: string): void { + this.initialize(); + + const logOpts = this.getLogOptionsForMessage(inputData); + const data = logOpts.data; + delete logOpts.data; + + for (const prop in logOpts) { + this.log4jsLogger.addContext(prop, logOpts[prop]); } + + (>this.log4jsLogger)[logMethod.toLowerCase()].apply(this.log4jsLogger, data); + + for (const prop in logOpts) { + this.log4jsLogger.removeContext(prop); + } + } + + private getLogOptionsForMessage(data: any[]): { data: any[], [key: string]: any } { + const opts = _.keys(LoggerConfigData); + + const result: any = {}; + const cleanedData = _.cloneDeep(data); + + const dataToCheck = data.filter(el => typeof el === "object"); + + for (const element of dataToCheck) { + if (opts.length === 0) { + break; + } + + const remainingOpts = _.cloneDeep(opts); + for (const prop of remainingOpts) { + const hasProp = element && element.hasOwnProperty(prop); + if (hasProp) { + opts.splice(opts.indexOf(prop), 1); + result[prop] = element[prop]; + cleanedData.splice(cleanedData.indexOf(element), 1); + } + } + } + + result.data = cleanedData; + return result; } private getPasswordEncodedArguments(args: string[]): string[] { diff --git a/lib/common/test/unit-tests/logger.ts b/lib/common/test/unit-tests/logger.ts index 6ddbef0685..058329fb68 100644 --- a/lib/common/test/unit-tests/logger.ts +++ b/lib/common/test/unit-tests/logger.ts @@ -1,5 +1,5 @@ import { Yok } from "../../yok"; -import { Logger } from "../../logger"; +import { Logger } from "../../logger/logger"; import * as path from "path"; import { assert } from "chai"; import * as fileSystemFile from "../../file-system"; diff --git a/lib/common/test/unit-tests/stubs.ts b/lib/common/test/unit-tests/stubs.ts index 567d637684..b0fb842f02 100644 --- a/lib/common/test/unit-tests/stubs.ts +++ b/lib/common/test/unit-tests/stubs.ts @@ -2,6 +2,7 @@ import * as util from "util"; import { EventEmitter } from "events"; +import { LoggerConfigData } from "../../../constants"; export class LockServiceStub implements ILockService { public async lock(lockFilePath?: string, lockOpts?: ILockOptions): Promise<() => void> { @@ -19,43 +20,31 @@ export class LockServiceStub implements ILockService { export class CommonLoggerStub implements ILogger { initialize(opts?: ILoggerOptions): void { } + initializeCliLogger(): void { } getLevel(): string { return undefined; } - fatal(...args: string[]): void { } - error(...args: string[]): void { } - warn(...args: string[]): void { - this.out.apply(this, args); + fatal(...args: any[]): void { } + error(...args: any[]): void { } + warn(...args: any[]): void { + this.output += util.format.apply(null, args) + "\n"; } - warnWithLabel(...args: string[]): void { } - info(...args: string[]): void { - this.out.apply(this, args); + info(...args: any[]): void { + this.output += util.format.apply(null, args) + "\n"; } - debug(...args: string[]): void { } - trace(...args: string[]): void { + debug(...args: any[]): void { } + trace(...args: any[]): void { this.traceOutput += util.format.apply(null, args) + "\n"; } public output = ""; public traceOutput = ""; - out(...args: string[]): void { - this.output += util.format.apply(null, args) + "\n"; - } - - write(...args: string[]): void { } - prepare(item: any): string { return ""; } - printInfoMessageOnSameLine(message: string): void { } - printMarkdown(message: string): void { this.output += message; } - - printOnStderr(...args: string[]): void { - // nothing to do here - } } export class ErrorsStub implements IErrors { @@ -168,7 +157,7 @@ export class DeviceLogProviderStub extends EventEmitter implements Mobile.IDevic public currentDeviceProjectNames: IStringDictionary = {}; logData(line: string, platform: string, deviceIdentifier: string): void { - this.logger.write(line, platform, deviceIdentifier); + this.logger.info(line, platform, deviceIdentifier, { [LoggerConfigData.skipNewLine]: true }); } setLogLevel(level: string, deviceIdentifier?: string): void { diff --git a/lib/constants.ts b/lib/constants.ts index bcf374bdd7..e70a951c17 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -301,8 +301,10 @@ export enum IOSNativeTargetTypes { appExtension = "app_extension" } +const pathToLoggerAppendersDir = join(__dirname, "common", "logger", "appenders"); export const LoggerAppenders = { - emitAppender: join(__dirname, "common", "logger-appenders", "emit-appender"), + emitAppender: join(pathToLoggerAppendersDir, "emit-appender"), + cliAppender: join(pathToLoggerAppendersDir, "cli-appender") }; export enum LoggerLevel { @@ -362,3 +364,11 @@ export enum LoggerLevel { */ OFF = "OFF" } + +export enum LoggerConfigData { + useStderr = "useStderr", + wrapMessageWithBorders = "wrapMessageWithBorders", + skipNewLine = "skipNewLine" +} + +export const EMIT_APPENDER_EVENT_NAME = "logData"; diff --git a/lib/nativescript-cli.ts b/lib/nativescript-cli.ts index 2620b33018..39b6479e60 100644 --- a/lib/nativescript-cli.ts +++ b/lib/nativescript-cli.ts @@ -24,10 +24,8 @@ process.on = (event: string, listener: any): any => { const err: IErrors = $injector.resolve("$errors"); err.printCallStack = config.DEBUG; - const $options = $injector.resolve("options"); - const $initializeService = $injector.resolve("initializeService"); - await $initializeService.initialize({ loggerOptions: { level: $options.log } }); + await $initializeService.initialize(); const extensibilityService: IExtensibilityService = $injector.resolve("extensibilityService"); try { diff --git a/lib/services/initialize-service.ts b/lib/services/initialize-service.ts index 83fd10913a..d57e2969b2 100644 --- a/lib/services/initialize-service.ts +++ b/lib/services/initialize-service.ts @@ -8,7 +8,11 @@ export class InitializeService implements IInitializeService { public async initialize(initOpts?: { loggerOptions?: ILoggerOptions }): Promise { initOpts = initOpts || {}; const $logger = this.$injector.resolve("logger"); - $logger.initialize(initOpts.loggerOptions); + if (initOpts.loggerOptions) { + $logger.initialize(initOpts.loggerOptions); + } else { + $logger.initializeCliLogger(); + } await this.showWarnings($logger); } @@ -19,7 +23,7 @@ export class InitializeService implements IInitializeService { _.each(systemWarnings, systemWarning => { const message = `${EOL}${systemWarning.message}${EOL}`; if (systemWarning.severity === SystemWarningsSeverity.high) { - $logger.printOnStderr(message.red.bold); + $logger.error(message); } else { $logger.warn(message); } diff --git a/test/stubs.ts b/test/stubs.ts index bfdd8efda5..5763c9d84c 100644 --- a/test/stubs.ts +++ b/test/stubs.ts @@ -12,12 +12,15 @@ import { DevicePlatformsConstants } from "./../lib/common/mobile/device-platform export class LoggerStub implements ILogger { initialize(opts?: ILoggerOptions): void { } + initializeCliLogger(): void { } getLevel(): string { return undefined; } fatal(...args: string[]): void { } error(...args: string[]): void { } warn(...args: string[]): void { } - warnWithLabel(...args: string[]): void { } - info(...args: string[]): void { } + info(...args: string[]): void { + this.output += util.format.apply(null, args) + "\n"; + } + debug(...args: string[]): void { } trace(...args: string[]): void { this.traceOutput += util.format.apply(null, args) + "\n"; @@ -26,23 +29,11 @@ export class LoggerStub implements ILogger { public output = ""; public traceOutput = ""; - out(...args: string[]): void { - this.output += util.format.apply(null, args) + "\n"; - } - - write(...args: string[]): void { } - prepare(item: any): string { return ""; } - printInfoMessageOnSameLine(message: string): void { } - printMarkdown(message: string): void { } - - printOnStderr(...args: string[]): void { - // nothing to do here - } } export class FileSystemStub implements IFileSystem { From e874d335f2ee654842e7953d6a11734f1f8a0571 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Tue, 7 May 2019 21:24:22 +0300 Subject: [PATCH 08/13] chore: handle changes in logger API There are several changes in logger API that need to be changed in the code - they are mostly renaming of methods. --- lib/commands/appstore-list.ts | 4 +-- lib/commands/extensibility/list-extensions.ts | 2 +- lib/commands/list-platforms.ts | 10 +++--- lib/commands/plugin/list-plugins.ts | 10 +++--- lib/commands/post-install.ts | 2 +- lib/common/commands/analytics.ts | 2 +- lib/common/commands/autocompletion.ts | 2 +- .../commands/device/list-applications.ts | 2 +- lib/common/commands/device/list-devices.ts | 10 +++--- lib/common/commands/proxy/proxy-clear.ts | 2 +- lib/common/commands/proxy/proxy-get.ts | 2 +- lib/common/commands/proxy/proxy-set.ts | 4 +-- lib/common/dispatchers.ts | 2 +- lib/common/logger/appenders/emit-appender.ts | 6 ++-- .../android/android-emulator-services.ts | 11 ++++--- lib/common/mobile/device-log-provider.ts | 3 +- .../ios/device/ios-device-file-system.ts | 4 +-- .../mobile/mobile-core/devices-service.ts | 2 +- .../services/auto-completion-service.ts | 14 ++++---- .../mobile/android-device-file-system.ts | 2 +- .../mobile/project-files-manager.ts | 2 +- lib/package-installation-manager.ts | 4 +-- lib/project-data.ts | 2 +- lib/services/analytics/analytics-service.ts | 2 +- .../android-resources-migration-service.ts | 6 ++-- lib/services/doctor-service.ts | 10 +++--- lib/services/ios-project-service.ts | 2 +- lib/services/ios-provision-service.ts | 10 +++--- .../nativescript-cloud-extension-service.ts | 2 +- lib/services/platform-service.ts | 32 +++++++++---------- lib/services/plugins-service.ts | 8 ++--- lib/services/project-init-service.ts | 2 +- lib/services/versions-service.ts | 2 +- lib/services/workflow-service.ts | 7 ++-- .../node-modules/node-modules-dest-copy.ts | 2 +- lib/xml-validator.ts | 2 +- test/commands/post-install.ts | 2 +- test/ios-project-service.ts | 2 +- test/package-installation-manager.ts | 2 +- test/services/subscription-service.ts | 4 +-- 40 files changed, 101 insertions(+), 100 deletions(-) diff --git a/lib/commands/appstore-list.ts b/lib/commands/appstore-list.ts index 40391c7047..aadc7e665b 100644 --- a/lib/commands/appstore-list.ts +++ b/lib/commands/appstore-list.ts @@ -34,14 +34,14 @@ export class ListiOSApps implements ICommand { const applications = await this.$applePortalApplicationService.getApplications({ username, password }); if (!applications || !applications.length) { - this.$logger.out("Seems you don't have any applications yet."); + this.$logger.info("Seems you don't have any applications yet."); } else { const table: any = createTable(["Application Name", "Bundle Identifier", "In Flight Version"], applications.map(application => { const version = (application && application.versionSets && application.versionSets.length && application.versionSets[0].inFlightVersion && application.versionSets[0].inFlightVersion.version) || ""; return [application.name, application.bundleId, version]; })); - this.$logger.out(table.toString()); + this.$logger.info(table.toString()); } } } diff --git a/lib/commands/extensibility/list-extensions.ts b/lib/commands/extensibility/list-extensions.ts index aabc3f9c13..d3a1761bc7 100644 --- a/lib/commands/extensibility/list-extensions.ts +++ b/lib/commands/extensibility/list-extensions.ts @@ -13,7 +13,7 @@ export class ListExtensionsCommand implements ICommand { }); const table = helpers.createTable(["Name", "Version"], data); - this.$logger.out(table.toString()); + this.$logger.info(table.toString()); } else { this.$logger.info("No extensions installed."); } diff --git a/lib/commands/list-platforms.ts b/lib/commands/list-platforms.ts index 7210609be8..bd450f52b9 100644 --- a/lib/commands/list-platforms.ts +++ b/lib/commands/list-platforms.ts @@ -15,16 +15,16 @@ export class ListPlatformsCommand implements ICommand { if (installedPlatforms.length > 0) { const preparedPlatforms = this.$platformService.getPreparedPlatforms(this.$projectData); if (preparedPlatforms.length > 0) { - this.$logger.out("The project is prepared for: ", helpers.formatListOfNames(preparedPlatforms, "and")); + this.$logger.info("The project is prepared for: ", helpers.formatListOfNames(preparedPlatforms, "and")); } else { - this.$logger.out("The project is not prepared for any platform"); + this.$logger.info("The project is not prepared for any platform"); } - this.$logger.out("Installed platforms: ", helpers.formatListOfNames(installedPlatforms, "and")); + this.$logger.info("Installed platforms: ", helpers.formatListOfNames(installedPlatforms, "and")); } else { const formattedPlatformsList = helpers.formatListOfNames(this.$platformService.getAvailablePlatforms(this.$projectData), "and"); - this.$logger.out("Available platforms for this OS: ", formattedPlatformsList); - this.$logger.out("No installed platforms found. Use $ tns platform add"); + this.$logger.info("Available platforms for this OS: ", formattedPlatformsList); + this.$logger.info("No installed platforms found. Use $ tns platform add"); } } } diff --git a/lib/commands/plugin/list-plugins.ts b/lib/commands/plugin/list-plugins.ts index f9afa9c316..ac91549e79 100644 --- a/lib/commands/plugin/list-plugins.ts +++ b/lib/commands/plugin/list-plugins.ts @@ -16,18 +16,18 @@ export class ListPluginsCommand implements ICommand { const dependenciesData: string[][] = this.createTableCells(installedPlugins.dependencies); const dependenciesTable: any = createTable(headers, dependenciesData); - this.$logger.out("Dependencies:"); - this.$logger.out(dependenciesTable.toString()); + this.$logger.info("Dependencies:"); + this.$logger.info(dependenciesTable.toString()); if (installedPlugins.devDependencies && installedPlugins.devDependencies.length) { const devDependenciesData: string[][] = this.createTableCells(installedPlugins.devDependencies); const devDependenciesTable: any = createTable(headers, devDependenciesData); - this.$logger.out("Dev Dependencies:"); - this.$logger.out(devDependenciesTable.toString()); + this.$logger.info("Dev Dependencies:"); + this.$logger.info(devDependenciesTable.toString()); } else { - this.$logger.out("There are no dev dependencies."); + this.$logger.info("There are no dev dependencies."); } const viewDependenciesCommand: string = "npm view grep dependencies".cyan.toString(); diff --git a/lib/commands/post-install.ts b/lib/commands/post-install.ts index f9539d84f3..aa07aee4f0 100644 --- a/lib/commands/post-install.ts +++ b/lib/commands/post-install.ts @@ -38,7 +38,7 @@ export class PostInstallCliCommand implements ICommand { } // Make sure the success message is separated with at least one line from all other messages. - this.$logger.out(); + this.$logger.info(); this.$logger.printMarkdown("Installation successful. You are good to go. Connect with us on `http://twitter.com/NativeScript`."); if (canExecutePostInstallTask) { diff --git a/lib/common/commands/analytics.ts b/lib/common/commands/analytics.ts index 52e9d10871..bc35003138 100644 --- a/lib/common/commands/analytics.ts +++ b/lib/common/commands/analytics.ts @@ -41,7 +41,7 @@ class AnalyticsCommand implements ICommand { break; case "status": case "": - this.$logger.out(await this.$analyticsService.getStatusMessage(this.settingName, this.$options.json, this.humanReadableSettingName)); + this.$logger.info(await this.$analyticsService.getStatusMessage(this.settingName, this.$options.json, this.humanReadableSettingName)); break; } } diff --git a/lib/common/commands/autocompletion.ts b/lib/common/commands/autocompletion.ts index 1bc11bb526..40c1efb0cd 100644 --- a/lib/common/commands/autocompletion.ts +++ b/lib/common/commands/autocompletion.ts @@ -19,7 +19,7 @@ export class AutoCompleteCommand implements ICommand { this.$logger.info("Autocompletion is already enabled"); } } else { - this.$logger.out("If you are using bash or zsh, you can enable command-line completion."); + this.$logger.info("If you are using bash or zsh, you can enable command-line completion."); const message = "Do you want to enable it now?"; const autoCompetionStatus = await this.$prompter.confirm(message, () => true); diff --git a/lib/common/commands/device/list-applications.ts b/lib/common/commands/device/list-applications.ts index 5c9fe66513..f90fa36125 100644 --- a/lib/common/commands/device/list-applications.ts +++ b/lib/common/commands/device/list-applications.ts @@ -19,7 +19,7 @@ export class ListApplicationsCommand implements ICommand { }; await this.$devicesService.execute(action); - this.$logger.out(output.join(EOL)); + this.$logger.info(output.join(EOL)); } } $injector.registerCommand(["device|list-applications", "devices|list-applications"], ListApplicationsCommand); diff --git a/lib/common/commands/device/list-devices.ts b/lib/common/commands/device/list-devices.ts index 3601650592..1e49785fac 100644 --- a/lib/common/commands/device/list-devices.ts +++ b/lib/common/commands/device/list-devices.ts @@ -23,7 +23,7 @@ export class ListDevicesCommand implements ICommand { this.printEmulators("\nAvailable emulators", emulators); } - this.$logger.out("\nConnected devices & emulators"); + this.$logger.info("\nConnected devices & emulators"); let index = 1; await this.$devicesService.initialize({ platform: args[0], deviceId: null, skipInferPlatform: true, skipDeviceDetectionInterval: true, skipEmulatorStart: true }); @@ -31,7 +31,7 @@ export class ListDevicesCommand implements ICommand { let action: (_device: Mobile.IDevice) => Promise; if (this.$options.json) { action = async (device) => { - this.$logger.out(JSON.stringify(device.deviceInfo)); + this.$logger.info(JSON.stringify(device.deviceInfo)); }; } else { action = async (device) => { @@ -44,18 +44,18 @@ export class ListDevicesCommand implements ICommand { await this.$devicesService.execute(action, undefined, { allowNoDevices: true }); if (!this.$options.json && table.length) { - this.$logger.out(table.toString()); + this.$logger.info(table.toString()); } } private printEmulators(title: string, emulators: Mobile.IDeviceInfo[]) { - this.$logger.out(title); + this.$logger.info(title); const table: any = createTable(["Device Name", "Platform", "Version", "Device Identifier", "Image Identifier", "Error Help"], []); for (const info of emulators) { table.push([info.displayName, info.platform, info.version, info.identifier || "", info.imageIdentifier || "", info.errorHelp || ""]); } - this.$logger.out(table.toString()); + this.$logger.info(table.toString()); } } diff --git a/lib/common/commands/proxy/proxy-clear.ts b/lib/common/commands/proxy/proxy-clear.ts index 859e491ab0..78e0a8523d 100644 --- a/lib/common/commands/proxy/proxy-clear.ts +++ b/lib/common/commands/proxy/proxy-clear.ts @@ -10,7 +10,7 @@ export class ProxyClearCommand extends ProxyCommandBase { public async execute(args: string[]): Promise { await this.$proxyService.clearCache(); - this.$logger.out("Successfully cleared proxy."); + this.$logger.info("Successfully cleared proxy."); await this.tryTrackUsage(); } } diff --git a/lib/common/commands/proxy/proxy-get.ts b/lib/common/commands/proxy/proxy-get.ts index 8b376dc8ac..4f49e891e8 100644 --- a/lib/common/commands/proxy/proxy-get.ts +++ b/lib/common/commands/proxy/proxy-get.ts @@ -10,7 +10,7 @@ export class ProxyGetCommand extends ProxyCommandBase { } public async execute(args: string[]): Promise { - this.$logger.out(await this.$proxyService.getInfo()); + this.$logger.info(await this.$proxyService.getInfo()); await this.tryTrackUsage(); } } diff --git a/lib/common/commands/proxy/proxy-set.ts b/lib/common/commands/proxy/proxy-set.ts index 8bbfaab1d0..461d26c8ff 100644 --- a/lib/common/commands/proxy/proxy-set.ts +++ b/lib/common/commands/proxy/proxy-set.ts @@ -107,8 +107,8 @@ export class ProxySetCommand extends ProxyCommandBase { this.$logger.warn(`${messageNote}Run '${clientName} proxy set --help' for more information.`); await this.$proxyService.setCache(settings); - this.$logger.out(`Successfully setup proxy.${EOL}`); - this.$logger.out(await this.$proxyService.getInfo()); + this.$logger.info(`Successfully setup proxy.${EOL}`); + this.$logger.info(await this.$proxyService.getInfo()); await this.tryTrackUsage(); } diff --git a/lib/common/dispatchers.ts b/lib/common/dispatchers.ts index 4a33a299ba..db126ca6bc 100644 --- a/lib/common/dispatchers.ts +++ b/lib/common/dispatchers.ts @@ -61,7 +61,7 @@ export class CommandDispatcher implements ICommandDispatcher { if (json && json.buildVersion) { version = `${version}-${json.buildVersion}`; } - this.$logger.out(version); + this.$logger.info(version); } } $injector.register("commandDispatcher", CommandDispatcher); diff --git a/lib/common/logger/appenders/emit-appender.ts b/lib/common/logger/appenders/emit-appender.ts index f0b907174e..d64c0d9294 100644 --- a/lib/common/logger/appenders/emit-appender.ts +++ b/lib/common/logger/appenders/emit-appender.ts @@ -1,14 +1,14 @@ import { LoggingEvent } from "log4js"; import { EventEmitter } from "events"; -import { EmitAppenderEventName } from "../../../constants"; +import { EMIT_APPENDER_EVENT_NAME } from "../../../constants"; function emitAppender(layout: Function, emitter: EventEmitter) { const appender = (loggingEvent: LoggingEvent) => { - emitter.emit(EmitAppenderEventName, { loggingEvent, formattedMessage: layout(loggingEvent) }); + emitter.emit(EMIT_APPENDER_EVENT_NAME, { loggingEvent, formattedMessage: layout(loggingEvent) }); }; appender.shutdown = () => { - emitter.removeAllListeners(EmitAppenderEventName); + emitter.removeAllListeners(EMIT_APPENDER_EVENT_NAME); }; return appender; diff --git a/lib/common/mobile/android/android-emulator-services.ts b/lib/common/mobile/android/android-emulator-services.ts index 0226a03a73..c9f2f20c07 100644 --- a/lib/common/mobile/android/android-emulator-services.ts +++ b/lib/common/mobile/android/android-emulator-services.ts @@ -1,6 +1,7 @@ import { AndroidVirtualDevice } from "../../constants"; import { getCurrentEpochTime, sleep } from "../../helpers"; import { EOL } from "os"; +import { LoggerConfigData } from "../../../constants"; export class AndroidEmulatorServices implements Mobile.IEmulatorPlatformService { constructor(private $androidGenymotionService: Mobile.IAndroidVirtualDeviceService, @@ -65,7 +66,7 @@ export class AndroidEmulatorServices implements Mobile.IEmulatorPlatformService this.$androidVirtualDeviceService.detach(deviceInfo); } - private async startEmulatorCore(options: Mobile.IAndroidStartEmulatorOptions): Promise<{runningEmulator: Mobile.IDeviceInfo, errors: string[], endTimeEpoch: number}> { + private async startEmulatorCore(options: Mobile.IAndroidStartEmulatorOptions): Promise<{ runningEmulator: Mobile.IDeviceInfo, errors: string[], endTimeEpoch: number }> { const timeout = options.timeout || AndroidVirtualDevice.TIMEOUT_SECONDS; const endTimeEpoch = getCurrentEpochTime() + this.$utils.getMilliSecondsTimeout(timeout); @@ -146,21 +147,21 @@ export class AndroidEmulatorServices implements Mobile.IEmulatorPlatformService return (best && best.version >= AndroidVirtualDevice.MIN_ANDROID_VERSION) ? best : null; } - private async waitForEmulatorBootToComplete(emulator: Mobile.IDeviceInfo, endTimeEpoch: number, timeout: number): Promise<{runningEmulator: Mobile.IDeviceInfo, errors: string[]}> { - this.$logger.printInfoMessageOnSameLine("Waiting for emulator device initialization..."); + private async waitForEmulatorBootToComplete(emulator: Mobile.IDeviceInfo, endTimeEpoch: number, timeout: number): Promise<{ runningEmulator: Mobile.IDeviceInfo, errors: string[] }> { + this.$logger.info("Waiting for emulator device initialization...", { [LoggerConfigData.skipNewLine]: true }); const isInfiniteWait = this.$utils.getMilliSecondsTimeout(timeout || AndroidVirtualDevice.TIMEOUT_SECONDS) === 0; while (getCurrentEpochTime() < endTimeEpoch || isInfiniteWait) { const isEmulatorBootCompleted = await this.isEmulatorBootCompleted(emulator.identifier); if (isEmulatorBootCompleted) { - this.$logger.printInfoMessageOnSameLine(EOL); + this.$logger.info(EOL, { [LoggerConfigData.skipNewLine]: true }); return { runningEmulator: emulator, errors: [] }; } - this.$logger.printInfoMessageOnSameLine("."); + this.$logger.info(".", { [LoggerConfigData.skipNewLine]: true }); await sleep(10000); } diff --git a/lib/common/mobile/device-log-provider.ts b/lib/common/mobile/device-log-provider.ts index 18261a35b8..ab2da4345a 100644 --- a/lib/common/mobile/device-log-provider.ts +++ b/lib/common/mobile/device-log-provider.ts @@ -1,5 +1,6 @@ import { DeviceLogProviderBase } from "./device-log-provider-base"; import { DEVICE_LOG_EVENT_NAME } from "../constants"; +import { LoggerConfigData } from "../../constants"; export class DeviceLogProvider extends DeviceLogProviderBase { constructor(protected $logFilter: Mobile.ILogFilter, @@ -21,7 +22,7 @@ export class DeviceLogProvider extends DeviceLogProviderBase { } private logDataCore(data: string): void { - this.$logger.write(data); + this.$logger.info(data, { [LoggerConfigData.skipNewLine]: true }); } } $injector.register("deviceLogProvider", DeviceLogProvider); diff --git a/lib/common/mobile/ios/device/ios-device-file-system.ts b/lib/common/mobile/ios/device/ios-device-file-system.ts index d5637751cf..ffc9a0b238 100644 --- a/lib/common/mobile/ios/device/ios-device-file-system.ts +++ b/lib/common/mobile/ios/device/ios-device-file-system.ts @@ -19,7 +19,7 @@ export class IOSDeviceFileSystem implements Mobile.IDeviceFileSystem { let children: string[] = []; const result = await this.$iosDeviceOperations.listDirectory([{ deviceId: deviceIdentifier, path: devicePath, appId: appIdentifier }]); children = result[deviceIdentifier][0].response; - this.$logger.out(children.join(EOL)); + this.$logger.info(children.join(EOL)); } public async getFile(deviceFilePath: string, appIdentifier: string, outputFilePath?: string): Promise { @@ -29,7 +29,7 @@ export class IOSDeviceFileSystem implements Mobile.IDeviceFileSystem { } const fileContent = await this.getFileContent(deviceFilePath, appIdentifier); - this.$logger.out(fileContent); + this.$logger.info(fileContent); } public async getFileContent(deviceFilePath: string, appIdentifier: string): Promise { diff --git a/lib/common/mobile/mobile-core/devices-service.ts b/lib/common/mobile/mobile-core/devices-service.ts index 72ebf1b517..00858d4510 100644 --- a/lib/common/mobile/mobile-core/devices-service.ts +++ b/lib/common/mobile/mobile-core/devices-service.ts @@ -606,7 +606,7 @@ export class DevicesService extends EventEmitter implements Mobile.IDevicesServi return; } - this.$logger.out("Searching for devices..."); + this.$logger.info("Searching for devices..."); deviceInitOpts = deviceInitOpts || {}; this._data = deviceInitOpts; diff --git a/lib/common/services/auto-completion-service.ts b/lib/common/services/auto-completion-service.ts index 8cf5b56b84..46a5e322de 100644 --- a/lib/common/services/auto-completion-service.ts +++ b/lib/common/services/auto-completion-service.ts @@ -98,7 +98,7 @@ export class AutoCompletionService implements IAutoCompletionService { this.removeObsoleteAutoCompletion(); if (this.scriptsOk && this.scriptsUpdated) { - this.$logger.out("Restart your shell to disable command auto-completion."); + this.$logger.info("Restart your shell to disable command auto-completion."); } } @@ -108,7 +108,7 @@ export class AutoCompletionService implements IAutoCompletionService { this.removeObsoleteAutoCompletion(); if (this.scriptsOk && this.scriptsUpdated) { - this.$logger.out("Restart your shell to enable command auto-completion."); + this.$logger.info("Restart your shell to enable command auto-completion."); } } @@ -155,11 +155,11 @@ export class AutoCompletionService implements IAutoCompletionService { this.scriptsUpdated = true; } } catch (err) { - this.$logger.out("Unable to update %s. Command-line completion might not work.", fileName); + this.$logger.info("Unable to update %s. Command-line completion might not work.", fileName); // When npm is installed with sudo, in some cases the installation cannot write to shell profiles // Advise the user how to enable autocompletion after the installation is completed. if ((err.code === "EPERM" || err.code === "EACCES") && !this.$hostInfo.isWindows && process.env.SUDO_USER) { - this.$logger.out("To enable command-line completion, run '$ %s autocomplete enable'.", this.$staticConfig.CLIENT_NAME); + this.$logger.info("To enable command-line completion, run '$ %s autocomplete enable'.", this.$staticConfig.CLIENT_NAME); } this.$logger.trace(err); @@ -179,8 +179,8 @@ export class AutoCompletionService implements IAutoCompletionService { } catch (err) { // If file does not exist, autocompletion was not working for it, so ignore this error. if (err.code !== "ENOENT") { - this.$logger.out("Failed to update %s. Auto-completion may still work or work incorrectly. ", fileName); - this.$logger.out(err); + this.$logger.info("Failed to update %s. Auto-completion may still work or work incorrectly. ", fileName); + this.$logger.info(err); this.scriptsOk = false; } } @@ -211,7 +211,7 @@ export class AutoCompletionService implements IAutoCompletionService { this.$fs.chmod(filePath, "0644"); } } catch (err) { - this.$logger.out("Failed to update %s. Auto-completion may not work. ", filePath); + this.$logger.info("Failed to update %s. Auto-completion may not work. ", filePath); this.$logger.trace(err); this.scriptsOk = false; } diff --git a/lib/common/test/unit-tests/mobile/android-device-file-system.ts b/lib/common/test/unit-tests/mobile/android-device-file-system.ts index afe7fbd854..20e8d0d70d 100644 --- a/lib/common/test/unit-tests/mobile/android-device-file-system.ts +++ b/lib/common/test/unit-tests/mobile/android-device-file-system.ts @@ -1,7 +1,7 @@ import { AndroidDeviceFileSystem } from "../../../mobile/android/android-device-file-system"; import { Yok } from "../../../yok"; import { Errors } from "../../../errors"; -import { Logger } from "../../../logger"; +import { Logger } from "../../../logger/logger"; import { MobileHelper } from "../../../mobile/mobile-helper"; import { DevicePlatformsConstants } from "../../../mobile/device-platforms-constants"; diff --git a/lib/common/test/unit-tests/mobile/project-files-manager.ts b/lib/common/test/unit-tests/mobile/project-files-manager.ts index 4df6e5d759..b94972c78d 100644 --- a/lib/common/test/unit-tests/mobile/project-files-manager.ts +++ b/lib/common/test/unit-tests/mobile/project-files-manager.ts @@ -7,7 +7,7 @@ import { HostInfo } from "../../../host-info"; import { LocalToDevicePathDataFactory } from "../../../mobile/local-to-device-path-data-factory"; import { MobileHelper } from "../../../mobile/mobile-helper"; import { ProjectFilesManager } from "../../../services/project-files-manager"; -import { Logger } from "../../../logger"; +import { Logger } from "../../../logger/logger"; import * as path from "path"; import { Yok } from "../../../yok"; import { ProjectFilesProviderBase } from "../../../services/project-files-provider-base"; diff --git a/lib/package-installation-manager.ts b/lib/package-installation-manager.ts index b2e62e66c4..62ebf0a5f1 100644 --- a/lib/package-installation-manager.ts +++ b/lib/package-installation-manager.ts @@ -91,7 +91,7 @@ export class PackageInstallationManager implements IPackageInstallationManager { await this.$childProcess.exec(`npm install ${inspectorNpmPackageName}@${version} --prefix ${cachePath}`, { maxBuffer: 250 * 1024 }); } - this.$logger.out("Using inspector from cache."); + this.$logger.info("Using inspector from cache."); return pathToPackageInCache; } @@ -139,7 +139,7 @@ export class PackageInstallationManager implements IPackageInstallationManager { } private async npmInstall(packageName: string, pathToSave: string, version: string, dependencyType: string): Promise { - this.$logger.out(`Installing ${packageName}`); + this.$logger.info(`Installing ${packageName}`); packageName = packageName + (version ? `@${version}` : ""); diff --git a/lib/project-data.ts b/lib/project-data.ts index 0e3106b7e7..538651f0db 100644 --- a/lib/project-data.ts +++ b/lib/project-data.ts @@ -260,7 +260,7 @@ export class ProjectData implements IProjectData { @cache() private warnProjectId(): void { - this.$logger.warnWithLabel("IProjectData.projectId is deprecated. Please use IProjectData.projectIdentifiers[platform]."); + this.$logger.warn("[WARNING]: IProjectData.projectId is deprecated. Please use IProjectData.projectIdentifiers[platform]."); } } $injector.register("projectData", ProjectData, true); diff --git a/lib/services/analytics/analytics-service.ts b/lib/services/analytics/analytics-service.ts index f160359193..a620f2c209 100644 --- a/lib/services/analytics/analytics-service.ts +++ b/lib/services/analytics/analytics-service.ts @@ -33,7 +33,7 @@ export class AnalyticsService implements IAnalyticsService, IDisposable { let trackFeatureUsage = initialTrackFeatureUsageStatus === AnalyticsStatus.enabled; if (await this.isNotConfirmed(this.$staticConfig.TRACK_FEATURE_USAGE_SETTING_NAME) && isInteractive()) { - this.$logger.out("Do you want to help us improve " + this.$logger.info("Do you want to help us improve " + this.$analyticsSettingsService.getClientName() + " by automatically sending anonymous usage statistics? We will not use this information to identify or contact you." + " You can read our official Privacy Policy at"); diff --git a/lib/services/android-resources-migration-service.ts b/lib/services/android-resources-migration-service.ts index 2aac57b327..a565bacc22 100644 --- a/lib/services/android-resources-migration-service.ts +++ b/lib/services/android-resources-migration-service.ts @@ -27,14 +27,14 @@ export class AndroidResourcesMigrationService implements IAndroidResourcesMigrat try { await this.tryMigrate(originalAppResources, appResourcesDestination, appResourcesBackup); - this.$logger.out(`Successfully updated your project's application resources '/Android' directory structure.${EOL}The previous version of your Android application resources has been renamed to '/${AndroidResourcesMigrationService.ANDROID_DIR_OLD}'`); + this.$logger.info(`Successfully updated your project's application resources '/Android' directory structure.${EOL}The previous version of your Android application resources has been renamed to '/${AndroidResourcesMigrationService.ANDROID_DIR_OLD}'`); } catch (error) { try { this.recover(originalAppResources, appResourcesDestination, appResourcesBackup); - this.$logger.out("Failed to update resources. They should be in their initial state."); + this.$logger.info("Failed to update resources. They should be in their initial state."); } catch (err) { this.$logger.trace(err); - this.$logger.out(`Failed to update resources.${EOL} Backup of original content is inside "${appResourcesBackup}".${EOL}If "${originalAppResources} is missing copy from backup folder."`); + this.$logger.info(`Failed to update resources.${EOL} Backup of original content is inside "${appResourcesBackup}".${EOL}If "${originalAppResources} is missing copy from backup folder."`); } finally { this.$errors.failWithoutHelp(error.message); } diff --git a/lib/services/doctor-service.ts b/lib/services/doctor-service.ts index e408876713..feda360ebf 100644 --- a/lib/services/doctor-service.ts +++ b/lib/services/doctor-service.ts @@ -40,7 +40,7 @@ export class DoctorService implements IDoctorService { if (hasWarnings) { this.$logger.info("There seem to be issues with your configuration."); } else { - this.$logger.out("No issues were detected.".bold); + this.$logger.info("No issues were detected.".bold); this.printInfosCore(infos); } @@ -74,7 +74,7 @@ export class DoctorService implements IDoctorService { return; } - this.$logger.out("Running the setup script to try and automatically configure your environment."); + this.$logger.info("Running the setup script to try and automatically configure your environment."); if (this.$hostInfo.isDarwin) { await this.runSetupScriptCore(DoctorService.DarwinSetupScriptLocation, []); @@ -186,9 +186,9 @@ export class DoctorService implements IDoctorService { private printPackageManagerTip() { if (this.$hostInfo.isWindows) { - this.$logger.out("TIP: To avoid setting up the necessary environment variables, you can use the chocolatey package manager to install the Android SDK and its dependencies." + EOL); + this.$logger.info("TIP: To avoid setting up the necessary environment variables, you can use the chocolatey package manager to install the Android SDK and its dependencies." + EOL); } else if (this.$hostInfo.isDarwin) { - this.$logger.out("TIP: To avoid setting up the necessary environment variables, you can use the Homebrew package manager to install the Android SDK and its dependencies." + EOL); + this.$logger.info("TIP: To avoid setting up the necessary environment variables, you can use the Homebrew package manager to install the Android SDK and its dependencies." + EOL); } } @@ -199,7 +199,7 @@ export class DoctorService implements IDoctorService { if (info.type === constants.WARNING_TYPE_NAME) { message = `WARNING: ${info.message.yellow} ${EOL} ${info.additionalInformation} ${EOL}`; } - this.$logger.out(message); + this.$logger.info(message); }); } diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 7aca88f31e..2fcf6e9144 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -1424,7 +1424,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f const mergedPlist = plist.parse(this.$fs.readText(mergedPlistPath)); if (infoPlist.CFBundleIdentifier && infoPlist.CFBundleIdentifier !== mergedPlist.CFBundleIdentifier) { - this.$logger.warnWithLabel("The CFBundleIdentifier key inside the 'Info.plist' will be overriden by the 'id' inside 'package.json'."); + this.$logger.warn("[WARNING]: The CFBundleIdentifier key inside the 'Info.plist' will be overriden by the 'id' inside 'package.json'."); } } diff --git a/lib/services/ios-provision-service.ts b/lib/services/ios-provision-service.ts index 19c0ba380c..1df007ac6a 100644 --- a/lib/services/ios-provision-service.ts +++ b/lib/services/ios-provision-service.ts @@ -55,16 +55,16 @@ export class IOSProvisionService { } match.eligable.forEach(prov => pushProvision(prov)); - this.$logger.out(table.toString()); - this.$logger.out(); - this.$logger.out("There are also " + match.nonEligable.length + " non-eligable provisioning profiles."); - this.$logger.out(); + this.$logger.info(table.toString()); + this.$logger.info(); + this.$logger.info("There are also " + match.nonEligable.length + " non-eligable provisioning profiles."); + this.$logger.info(); } public async listTeams(): Promise { const teams = await this.getDevelopmentTeams(); const table = createTable(["Team Name", "Team ID"], teams.map(team => [quoteString(team.name), team.id])); - this.$logger.out(table.toString()); + this.$logger.info(table.toString()); } private async queryProvisioningProfilesAndDevices(projectId: string): Promise<{ devices: string[], match: mobileprovision.provision.Result }> { diff --git a/lib/services/nativescript-cloud-extension-service.ts b/lib/services/nativescript-cloud-extension-service.ts index 3ff6c90a24..04c99c8bdc 100644 --- a/lib/services/nativescript-cloud-extension-service.ts +++ b/lib/services/nativescript-cloud-extension-service.ts @@ -11,7 +11,7 @@ export class NativeScriptCloudExtensionService implements INativeScriptCloudExte return this.$extensibilityService.installExtension(constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME); } - this.$logger.out(`Extension ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} is already installed.`); + this.$logger.info(`Extension ${constants.NATIVESCRIPT_CLOUD_EXTENSION_NAME} is already installed.`); } public isInstalled(): boolean { diff --git a/lib/services/platform-service.ts b/lib/services/platform-service.ts index 4589674cbd..500ca4df7a 100644 --- a/lib/services/platform-service.ts +++ b/lib/services/platform-service.ts @@ -97,7 +97,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { this.$logger.trace("Package: %s", projectData.projectIdentifiers[platform]); this.$logger.trace("Name: %s", projectData.projectName); - this.$logger.out("Copying template files..."); + this.$logger.info("Copying template files..."); let packageToInstall = ""; if (frameworkPath) { @@ -136,7 +136,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { } this.$fs.ensureDirectoryExists(platformPath); - this.$logger.out(`Platform ${platform} successfully added. v${installedPlatformVersion}`); + this.$logger.info(`Platform ${platform} successfully added. v${installedPlatformVersion}`); } private async addPlatformCore(platformData: IPlatformData, frameworkDir: string, platformTemplate: string, projectData: IProjectData, config: IPlatformOptions, nativePrepare?: INativePrepare): Promise { @@ -249,7 +249,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { ); this.$projectChangesService.savePrepareInfo(platformInfo.platform, platformInfo.projectData); } else { - this.$logger.out("Skipping prepare."); + this.$logger.info("Skipping prepare."); } return true; @@ -316,7 +316,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { filesToRemove?: string[], nativePrepare?: INativePrepare): Promise { - this.$logger.out("Preparing project..."); + this.$logger.info("Preparing project..."); const platformData = this.$platformsData.getPlatformData(platform, projectData); const frameworkVersion = this.getCurrentPlatformVersion(platform, projectData); @@ -361,7 +361,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { this.$projectFilesManager.processPlatformSpecificFiles(directoryPath, platform, projectFilesConfig, excludedDirs); - this.$logger.out(`Project successfully prepared (${platform})`); + this.$logger.info(`Project successfully prepared (${platform})`); } public async shouldBuild(platform: string, projectData: IProjectData, buildConfig: IBuildConfig, outputPath?: string): Promise { @@ -404,7 +404,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { @performanceLog() public async buildPlatform(platform: string, buildConfig: IBuildConfig, projectData: IProjectData): Promise { - this.$logger.out("Building project..."); + this.$logger.info("Building project..."); const action = constants.TrackActionNames.Build; const isForDevice = this.$mobileHelper.isAndroidPlatform(platform) ? null : buildConfig && buildConfig.buildForDevice; @@ -424,7 +424,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { const handler = (data: any) => { this.emit(constants.BUILD_OUTPUT_EVENT_NAME, data); - this.$logger.printInfoMessageOnSameLine(data.data.toString()); + this.$logger.info(data.data.toString(), { [constants.LoggerConfigData.skipNewLine]: true }); }; await attachAwaitDetach(constants.BUILD_OUTPUT_EVENT_NAME, platformData.platformProjectService, handler, platformData.platformProjectService.buildProject(platformData.projectRoot, projectData, buildConfig)); @@ -432,7 +432,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { const buildInfoFilePath = this.getBuildOutputPath(platform, platformData, buildConfig); this.saveBuildInfoFile(platform, projectData.projectDir, buildInfoFilePath); - this.$logger.out("Project successfully built."); + this.$logger.info("Project successfully built."); return this.lastOutputPath(platform, buildConfig, projectData); } @@ -480,7 +480,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { } public async installApplication(device: Mobile.IDevice, buildConfig: IBuildConfig, projectData: IProjectData, packageFile?: string, outputFilePath?: string): Promise { - this.$logger.out(`Installing on device ${device.deviceInfo.identifier}...`); + this.$logger.info(`Installing on device ${device.deviceInfo.identifier}...`); await this.$analyticsService.trackEventActionInGoogleAnalytics({ action: constants.TrackActionNames.Deploy, @@ -519,7 +519,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { await device.fileSystem.putFile(path.join(buildInfoFilePath, buildInfoFileName), deviceFilePath, appIdentifier); } - this.$logger.out(`Successfully installed on device with identifier '${device.deviceInfo.identifier}'.`); + this.$logger.info(`Successfully installed on device with identifier '${device.deviceInfo.identifier}'.`); } private async updateHashesOnDevice(data: { device: Mobile.IDevice, appIdentifier: string, outputFilePath: string, platformData: IPlatformData }): Promise { @@ -573,13 +573,13 @@ export class PlatformService extends EventEmitter implements IPlatformService { if (shouldBuild) { installPackageFile = await deployInfo.buildPlatform(deployInfo.platform, buildConfig, deployInfo.projectData); } else { - this.$logger.out("Skipping package build. No changes detected on the native side. This will be fast!"); + this.$logger.info("Skipping package build. No changes detected on the native side. This will be fast!"); } if (deployInfo.deployOptions.forceInstall || shouldBuild || (await this.shouldInstall(device, deployInfo.projectData, buildConfig))) { await this.installApplication(device, buildConfig, deployInfo.projectData, installPackageFile, deployInfo.outputPath); } else { - this.$logger.out("Skipping install."); + this.$logger.info("Skipping install."); } }; @@ -593,11 +593,11 @@ export class PlatformService extends EventEmitter implements IPlatformService { } public async startApplication(platform: string, runOptions: IRunPlatformOptions, appData: Mobile.IStartApplicationData): Promise { - this.$logger.out("Starting..."); + this.$logger.info("Starting..."); const action = async (device: Mobile.IDevice) => { await device.applicationManager.startApplication(appData); - this.$logger.out(`Successfully started on device with identifier '${device.deviceInfo.identifier}'.`); + this.$logger.info(`Successfully started on device with identifier '${device.deviceInfo.identifier}'.`); }; await this.$devicesService.initialize({ platform: platform, deviceId: runOptions.device }); @@ -713,7 +713,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { this.$fs.deleteDirectory(platformDir); this.$projectDataService.removeNSProperty(projectData.projectDir, platformData.frameworkPackageName); - this.$logger.out(`Platform ${platform} successfully removed.`); + this.$logger.info(`Platform ${platform} successfully removed.`); } catch (err) { this.$logger.error(`Failed to remove ${platform} platform with errors:`); if (gradleErrorMessage) { @@ -948,7 +948,7 @@ export class PlatformService extends EventEmitter implements IPlatformService { await this.removePlatforms([packageName], projectData); packageName = updateOptions.newVersion ? `${packageName}@${updateOptions.newVersion}` : packageName; await this.addPlatform(packageName, updateOptions.platformTemplate, projectData, config); - this.$logger.out("Successfully updated to version ", updateOptions.newVersion); + this.$logger.info("Successfully updated to version ", updateOptions.newVersion); } // TODO: Remove this method from here. It has nothing to do with platform diff --git a/lib/services/plugins-service.ts b/lib/services/plugins-service.ts index f18a9e8e7f..64dc3c38cf 100644 --- a/lib/services/plugins-service.ts +++ b/lib/services/plugins-service.ts @@ -70,7 +70,7 @@ export class PluginsService implements IPluginsService { throw err; } - this.$logger.out(`Successfully installed plugin ${realNpmPackageJson.name}.`); + this.$logger.info(`Successfully installed plugin ${realNpmPackageJson.name}.`); } else { await this.$packageManager.uninstall(realNpmPackageJson.name, { save: true }, projectData.projectDir); this.$errors.failWithoutHelp(`${plugin} is not a valid NativeScript plugin. Verify that the plugin package.json file contains a nativescript key and try again.`); @@ -93,14 +93,14 @@ export class PluginsService implements IPluginsService { const action = async (modulesDestinationPath: string, platform: string, platformData: IPlatformData): Promise => { shelljs.rm("-rf", path.join(modulesDestinationPath, pluginName)); - this.$logger.out(`Successfully removed plugin ${pluginName} for ${platform}.`); + this.$logger.info(`Successfully removed plugin ${pluginName} for ${platform}.`); showMessage = false; }; await this.executeForAllInstalledPlatforms(action, projectData); if (showMessage) { - this.$logger.out(`Successfully removed plugin ${pluginName}`); + this.$logger.info(`Successfully removed plugin ${pluginName}`); } } @@ -119,7 +119,7 @@ export class PluginsService implements IPluginsService { await this.preparePluginNativeCode(pluginData, platform, projectData); // Show message - this.$logger.out(`Successfully prepared plugin ${pluginData.name} for ${platform}.`); + this.$logger.info(`Successfully prepared plugin ${pluginData.name} for ${platform}.`); } } diff --git a/lib/services/project-init-service.ts b/lib/services/project-init-service.ts index edb8bea6f9..24937a1f5c 100644 --- a/lib/services/project-init-service.ts +++ b/lib/services/project-init-service.ts @@ -74,7 +74,7 @@ export class ProjectInitService implements IProjectInitService { throw err; } - this.$logger.out("Project successfully initialized."); + this.$logger.info("Project successfully initialized."); } private get projectFilePath(): string { diff --git a/lib/services/versions-service.ts b/lib/services/versions-service.ts index e5d566e2cd..9f5ef644a2 100644 --- a/lib/services/versions-service.ts +++ b/lib/services/versions-service.ts @@ -135,7 +135,7 @@ class VersionsService implements IVersionsService { }, () => this.getAllComponentsVersions()); if (!helpers.isInteractive()) { - versionsInformation.map(componentInformation => this.$logger.out(componentInformation.message)); + versionsInformation.map(componentInformation => this.$logger.info(componentInformation.message)); } _.forEach(versionsInformation, componentInformation => { diff --git a/lib/services/workflow-service.ts b/lib/services/workflow-service.ts index 210dcbea35..3cbba6a5e8 100644 --- a/lib/services/workflow-service.ts +++ b/lib/services/workflow-service.ts @@ -2,6 +2,7 @@ import * as helpers from "../common/helpers"; import * as path from "path"; import * as semver from "semver"; import { EOL } from "os"; +import { LoggerConfigData } from "../constants"; export class WorkflowService implements IWorkflowService { private legacyWorkflowDeprecationMessage = `With the upcoming NativeScript 6.0 the Webpack workflow will become the only way of building apps. @@ -70,16 +71,14 @@ __Improve your project by switching to the Webpack workflow.__ private showLegacyWorkflowWarning() { const legacyWorkflowWarning = `You are using the Legacy Workflow.${EOL}${EOL}${this.legacyWorkflowDeprecationMessage}`; - const warningWithBorders = helpers.getMessageWithBorders(legacyWorkflowWarning); - this.$logger.warn(warningWithBorders); + this.$logger.warn(legacyWorkflowWarning, { [LoggerConfigData.wrapMessageWithBorders]: true }); } private showNoBundleWarning() { const legacyWorkflowWarning = `You are using the '--no-bundle' flag which is switching to the Legacy Workflow.${EOL}${EOL}${this.legacyWorkflowDeprecationMessage}`; - const warningWithBorders = helpers.getMessageWithBorders(legacyWorkflowWarning); - this.$logger.warn(warningWithBorders); + this.$logger.warn(legacyWorkflowWarning, { [LoggerConfigData.wrapMessageWithBorders]: true }); } private async ensureWebpackPluginInstalled(projectData: IProjectData) { diff --git a/lib/tools/node-modules/node-modules-dest-copy.ts b/lib/tools/node-modules/node-modules-dest-copy.ts index 55bcaaedc4..afcd8ccbf4 100644 --- a/lib/tools/node-modules/node-modules-dest-copy.ts +++ b/lib/tools/node-modules/node-modules-dest-copy.ts @@ -184,7 +184,7 @@ export class NpmPluginPrepare { if (appFolderExists) { this.$pluginsService.preparePluginScripts(pluginData, platform, projectData, projectFilesConfig); // Show message - this.$logger.out(`Successfully prepared plugin ${pluginData.name} for ${platform}.`); + this.$logger.info(`Successfully prepared plugin ${pluginData.name} for ${platform}.`); } } } diff --git a/lib/xml-validator.ts b/lib/xml-validator.ts index 6d33e1ca28..08970a745e 100644 --- a/lib/xml-validator.ts +++ b/lib/xml-validator.ts @@ -15,7 +15,7 @@ export class XmlValidator implements IXmlValidator { xmlHasErrors = xmlHasErrors || hasErrors; if (hasErrors) { this.$logger.info(`${file} has syntax errors.`.red.bold); - this.$logger.out(errorOutput.yellow); + this.$logger.info(errorOutput.yellow); } }); return !xmlHasErrors; diff --git a/test/commands/post-install.ts b/test/commands/post-install.ts index 6be24a78bc..39640112ea 100644 --- a/test/commands/post-install.ts +++ b/test/commands/post-install.ts @@ -35,7 +35,7 @@ const createTestInjector = (): IInjector => { }); testInjector.register("logger", { - out: (formatStr?: any, ...args: any[]): void => undefined, + info: (formatStr?: any, ...args: any[]): void => undefined, printMarkdown: (...args: any[]): void => undefined }); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 28fdcf890e..0fad626fd0 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -9,7 +9,7 @@ import * as iOSProjectServiceLib from "../lib/services/ios-project-service"; import { IOSProjectService } from "../lib/services/ios-project-service"; import { IOSEntitlementsService } from "../lib/services/ios-entitlements-service"; import { XcconfigService } from "../lib/services/xcconfig-service"; -import * as LoggerLib from "../lib/common/logger"; +import * as LoggerLib from "../lib/common/logger/logger"; import * as OptionsLib from "../lib/options"; import * as yok from "../lib/common/yok"; import { DevicesService } from "../lib/common/mobile/mobile-core/devices-service"; diff --git a/test/package-installation-manager.ts b/test/package-installation-manager.ts index bec00a5ff7..242324f26e 100644 --- a/test/package-installation-manager.ts +++ b/test/package-installation-manager.ts @@ -3,7 +3,7 @@ import * as ConfigLib from "../lib/config"; import * as ErrorsLib from "../lib/common/errors"; import * as FsLib from "../lib/common/file-system"; import * as HostInfoLib from "../lib/common/host-info"; -import * as LoggerLib from "../lib/common/logger"; +import * as LoggerLib from "../lib/common/logger/logger"; import * as NpmLib from "../lib/node-package-manager"; import * as YarnLib from "../lib/yarn-package-manager"; import * as PackageManagerLib from "../lib/package-manager"; diff --git a/test/services/subscription-service.ts b/test/services/subscription-service.ts index c4cbb9b529..30effd389c 100644 --- a/test/services/subscription-service.ts +++ b/test/services/subscription-service.ts @@ -135,7 +135,7 @@ describe("subscriptionService", () => { subscriptionService.shouldAskForEmailResult = false; const logger = testInjector.resolve("logger"); let loggerOutput = ""; - logger.out = (...args: string[]): void => { + logger.info = (...args: string[]): void => { loggerOutput += args.join(" "); }; @@ -151,7 +151,7 @@ describe("subscriptionService", () => { const logger = testInjector.resolve("logger"); let loggerOutput = ""; - logger.out = (...args: string[]): void => { + logger.info = (...args: string[]): void => { loggerOutput += args.join(" "); }; From 4f1d7a80b8f9304e00c9c72ebed871873a5c7b09 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Tue, 7 May 2019 21:31:28 +0300 Subject: [PATCH 09/13] chore: remove write method from logger --- lib/common/logger/logger.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/common/logger/logger.ts b/lib/common/logger/logger.ts index dcd0418b0c..9047bbbbb0 100644 --- a/lib/common/logger/logger.ts +++ b/lib/common/logger/logger.ts @@ -91,10 +91,6 @@ export class Logger implements ILogger { this.logMessage(encodedArgs, LoggerLevel.TRACE); } - write(...args: any[]): void { - process.stdout.write(util.format.apply(null, args)); - } - prepare(item: any): string { if (typeof item === "undefined" || item === null) { return "[no content]"; From 8b6527215c4922a34d575ae6c600ff63c43aca94 Mon Sep 17 00:00:00 2001 From: Rosen Vladimirov Date: Wed, 8 May 2019 02:33:41 +0300 Subject: [PATCH 10/13] chore: use correct interfaces for appenders --- lib/common/definitions/logger.d.ts | 7 +++++-- lib/common/logger/appenders/cli-appender.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/common/definitions/logger.d.ts b/lib/common/definitions/logger.d.ts index fcbb21053d..6077c31d7d 100644 --- a/lib/common/definitions/logger.d.ts +++ b/lib/common/definitions/logger.d.ts @@ -5,6 +5,7 @@ import { LoggerLevel } from "../../constants"; declare global { interface IAppenderOptions extends IDictionary { type: string; + layout?: Layout; } interface ILoggerOptions { @@ -26,9 +27,11 @@ declare global { prepare(item: any): string; } - - interface Log4JSEmitAppenderConfiguration extends Configuration { + interface Log4JSAppenderConfiguration extends Configuration { layout: Layout; + } + + interface Log4JSEmitAppenderConfiguration extends Log4JSAppenderConfiguration { emitter: EventEmitter; } } diff --git a/lib/common/logger/appenders/cli-appender.ts b/lib/common/logger/appenders/cli-appender.ts index 19f5cbee0d..bf8c2355c6 100644 --- a/lib/common/logger/appenders/cli-appender.ts +++ b/lib/common/logger/appenders/cli-appender.ts @@ -13,7 +13,7 @@ function cliAppender(layout: Function) { return appender; } -function configure(config: Log4JSEmitAppenderConfiguration, layouts: any) { +function configure(config: Log4JSAppenderConfiguration, layouts: any) { // the default layout for the appender let layout = layouts.messagePassThroughLayout; From 9509b7435e9bd3630a5822bd983d0ccd58446a2d Mon Sep 17 00:00:00 2001 From: Rosen Vladimirov Date: Wed, 8 May 2019 02:34:13 +0300 Subject: [PATCH 11/13] docs: add information for logger in Public API --- PublicAPI.md | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/PublicAPI.md b/PublicAPI.md index 066640eb0d..4dcc34fc07 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -72,6 +72,16 @@ const tns = require("nativescript"); * [getPlaygroundAppQrCode](#getplaygroundappqrcode) * [cleanupService](#cleanupservice) * [setCleanupLogFile](#setcleanuplogfile) +* [initializeService](#initializeService) + * [initialize](#initialize) +* [logger](#logger) + * [initialize](#initialize) + * [getLevel](#getlevel) + * [appenders](#appenders) + * [emit-appender](#emit-appender) + * [cli-appender](#cli-appender) + * [custom layouts](#custom-layouts) + ## Module projectService @@ -1514,6 +1524,155 @@ const tns = require("nativescript"); tns.cleanupService.setCleanupLogFile("/Users/username/cleanup-logs.txt"); ``` +## initializeService +The `initializeService` is used to initialize CLI's configuration and the beginning and print all warnings related to current environment. + +### initialize +This method inits CLI's logger and prints all system warnings. + +* Definition +```TypeScript +initialize(initOpts?: { loggerOptions?: ILoggerOptions }): Promise +``` + +> NOTE: For more information about loggerOptions, you can check `logger`. + +* Usage +```JavaScript +const tns = require("nativescript"); +tns.initializeService.initialize(); +``` + +## logger + +`logger` module is used to show any kind of information to the user. The `logger` uses `log4js` internally, which allows setting different levels for the messages. +The levels are available in `tns.constants.LoggerLevel` enum. Only messages from the current log level (or higher) are shown to the user, i.e. in case the log level is set to `INFO`, `DEBUG` and `TRACE` messages will not be shown to the user, but `WARN` and `ERROR` messages will be shown.
+`logger` module can be configured how to show the messages by using different appenders and layouts.
+* `appenders` are responsible for output of log events. They may write events to files, send emails, store them in a database, or anything. Most appenders use layouts to serialise the events to strings for output. +* `layout` is a function for converting a LogEvent into a string representation. + +`log4js` has predefined appenders and layouts that can be used. In case you do not pass any options to logger's initialization, CLI will default to [console appender](https://log4js-node.github.io/log4js-node/console.html) with [messagePassThrough layout](https://log4js-node.github.io/log4js-node/layouts.html#message-pass-through) with `INFO` log level.
+You can override only the properties you want, i.e. only the log level, the layout or the appender.
+`nativescript` itself has additional appenders that you can use. More information about them can be found below. You can get a full list of the available appenders by checking the `tns.constants.LoggerAppenders` object.
+ +> NOTE: When CLI is used as a command-line tool, it uses a custom appender and layout in order to write coloured messages to stdout or stderr. + +### initialize +The `initialize` method initializes the log4js settings - level, appender and layout. Once called, the settings cannot be changed anymore for the current process. + +* Definition +```TypeScript +interface IAppenderOptions extends IDictionary { + type: string; + layout?: Layout; +} + +interface ILoggerOptions { + level?: LoggerLevel; + appenderOptions?: IAppenderOptions; +} + +initialize(opts?: ILoggerOptions): void; +``` + +* Usage + * Initialize with default settings: + ```JavaScript + tns.logger.initialize(); + ``` + * Initialize with DEBUG log level: + ```JavaScript + tns.logger.initialize({ level: tns.constants.LoggerLevel.DEBUG }); + ``` + * Initialize with different appender, for example [fileSync](https://log4js-node.github.io/log4js-node/fileSync.html) appender: + ```JavaScript + tns.logger.initialize({ appenderOptions: { type: "fileSync" } }); + ``` + * Initialize with different layout, for example [Pattern](https://log4js-node.github.io/log4js-node/layouts.html#pattern) layout: + ```JavaScript + tns.logger.initialize({ appenderOptions: { layout: { type: "pattern" } } }); + ``` + * Initialize with custom appender, layout and level: + ```JavaScript + tns.logger.initialize({ appenderOptions: { type: "fileSync", layout: { type: "pattern" } }, level: tns.constants.LoggerLevel.DEBUG }); + ``` + +### getLevel +This method returns information for the current log level. + +* Definition +```TypeScript +getLevel(): string; +``` + +* Usage +```JavaScript +console.log(`Current log level is: ${tns.logger.getLevel()}`); +``` + +### appenders +The `appenders` are log4js concept. `appenders` are responsible for output of log events. You can use all predefined [log4js appenders](https://log4js-node.github.io/log4js-node/appenders.html) and also several predefined CLI appenders + +#### emit-appender +The `emit-appender` is used to emit the log events through a passed emitter instead of writing the messages. Whenever a message should be shown, the `emit-appender` emits `logData` event with an object containing the `loggingEvent` and the message passed through the specified layout stored in `formattedMessage` property. + +* Usage: +```JavaScript +const tns = require("nativescript"); +const { EventEmitter } = require("events"); +const { EMIT_APPENDER_EVENT_NAME, LoggerAppenders } = tns.constants; +const emitter = new EventEmitter(); +// IMPORTANT: Add the event handler before calling logger's initialize method. +// This is required as log4js makes a copy of the appenderOptions, where the emitter is passed +// NOTE: In case you want to debug the event handler, place `debugger` in it. +emitter.on(EMIT_APPENDER_EVENT_NAME, (logData) => { + if (logData.loggingEvent.level.levelStr === LoggerLevel.WARN) { + console.log(`WARNING: ${logData.formattedMessage}`); + } +}); + +const logger = tns.logger; +logger.initialize({ + appenderOptions: { + type: LoggerAppenders.emitAppender, + emitter + } +}); +``` + +> NOTE: In several cases CLI passes additional configuration properties in the `context` of the `loggingEvent`. Full list is available in the `tns.constants.LoggerConfigData` object. These properties are used by CLI's layout and appender to change the way the message is printed on the terminal and if it should be on stderr or stdout. + +#### cli-appender +`cli-appender` prints messages to stdout or stderr based on the passed options for the message. + +* Usage +```JavaScript +const tns = require("nativescript"); +const { EventEmitter } = require("events"); +const { EMIT_APPENDER_EVENT_NAME, LoggerAppenders } = tns.constants; + +const logger = tns.logger; +logger.initialize({ + appenderOptions: { + type: LoggerAppenders.cliAppender, + } +}); +``` + +### custom layouts +You can define your own layout function in the following way: +```JavaScript +const log4js = require("nativescript/node_modules/log4js"); +const util = require("util"); +log4js.addLayout("myCustomLayout", (config) => { + return (loggingEvent) => { + return util.format.apply(null, loggingEvent.data); + } +}); + +tns.logger.initialize({ appenderOptions: { layout: { type: "myCustomLayout" } } }); +``` + ## How to add a new method to Public API CLI is designed as command line tool and when it is used as a library, it does not give you access to all of the methods. This is mainly implementation detail. Most of the CLI's code is created to work in command line, not as a library, so before adding method to public API, most probably it will require some modification. For example the `$options` injected module contains information about all `--` options passed on the terminal. When the CLI is used as a library, the options are not populated. Before adding method to public API, make sure its implementation does not rely on `$options`. From 9bff5a02654463b4534e9a9fadde331aae5e2745 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Wed, 8 May 2019 19:17:55 +0300 Subject: [PATCH 12/13] chore: handle PR comments --- PublicAPI.md | 2 +- lib/common/logger/appenders/emit-appender.ts | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/PublicAPI.md b/PublicAPI.md index 4dcc34fc07..6246904fb6 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -1525,7 +1525,7 @@ tns.cleanupService.setCleanupLogFile("/Users/username/cleanup-logs.txt"); ``` ## initializeService -The `initializeService` is used to initialize CLI's configuration and the beginning and print all warnings related to current environment. +The `initializeService` is used to initialize CLI's configuration at the beginning and print all warnings related to current environment. ### initialize This method inits CLI's logger and prints all system warnings. diff --git a/lib/common/logger/appenders/emit-appender.ts b/lib/common/logger/appenders/emit-appender.ts index d64c0d9294..9902579a67 100644 --- a/lib/common/logger/appenders/emit-appender.ts +++ b/lib/common/logger/appenders/emit-appender.ts @@ -15,14 +15,6 @@ function emitAppender(layout: Function, emitter: EventEmitter) { } function configure(config: Log4JSEmitAppenderConfiguration, layouts: any) { - // the default layout for the appender - let layout = layouts.messagePassThroughLayout; - - // check if there is another layout specified - if (config.layout) { - layout = layouts.layout(config.layout.type, config.layout); - } - if (!config.emitter) { throw new Error("Emitter must be passed to emit-appender"); } @@ -31,6 +23,14 @@ function configure(config: Log4JSEmitAppenderConfiguration, layouts: any) { throw new Error("The passed emitter must be instance of EventEmitter"); } + // the default layout for the appender + let layout = layouts.messagePassThroughLayout; + + // check if there is another layout specified + if (config.layout) { + layout = layouts.layout(config.layout.type, config.layout); + } + // create a new appender instance return emitAppender(layout, config.emitter); } From 194106c47906bf9942071ffd377eba6eda79a4a4 Mon Sep 17 00:00:00 2001 From: rosen-vladimirov Date: Wed, 8 May 2019 19:18:53 +0300 Subject: [PATCH 13/13] feat: improve initialization service Improve initialization service, so it can be used to initialize all settings required at the beginning of CLI process. --- PublicAPI.md | 33 ++++++++++++++++++----- lib/common/definitions/extensibility.d.ts | 5 ++++ lib/definitions/initialize-service.d.ts | 8 +++++- lib/services/initialize-service.ts | 14 +++++++++- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/PublicAPI.md b/PublicAPI.md index 6246904fb6..3fdbba0e30 100644 --- a/PublicAPI.md +++ b/PublicAPI.md @@ -1528,20 +1528,41 @@ tns.cleanupService.setCleanupLogFile("/Users/username/cleanup-logs.txt"); The `initializeService` is used to initialize CLI's configuration at the beginning and print all warnings related to current environment. ### initialize -This method inits CLI's logger and prints all system warnings. +This method executes initialization actions based on the passed parameters. In case `loggerOptions` are not passed, the default CLI logger will be used. +After initialization, the method will print all system warnings. * Definition ```TypeScript -initialize(initOpts?: { loggerOptions?: ILoggerOptions }): Promise +interface IInitializeOptions { + loggerOptions?: ILoggerOptions; + settingsServiceOptions?: IConfigurationSettings; + extensibilityOptions?: { pathToExtensions: string }; +} + +interface IInitializeService { + initialize(initOpts?: IInitializeOptions): Promise; +} + ``` > NOTE: For more information about loggerOptions, you can check `logger`. * Usage -```JavaScript -const tns = require("nativescript"); -tns.initializeService.initialize(); -``` + * Initialization without passing any data - `logger` will be initialized with default CLI settings. Warnings will be printed if there are any. + ```JavaScript + const tns = require("nativescript"); + tns.initializeService.initialize(); + ``` + * Initialize with custom settings service options: + ```JavaScript + const tns = require("nativescript"); + tns.initializeService.initialize({ settingsServiceOptions: { profileDir: "/Users/username/customDir", userAgentName: "MyApp" } }); + ``` + * Initialize with custom extensibility path: + ```JavaScript + const tns = require("nativescript"); + tns.initializeService.initialize({ extensibilityOptions: { pathToExtensions: "/Users/username/customDir/extensions" } }); + ``` ## logger diff --git a/lib/common/definitions/extensibility.d.ts b/lib/common/definitions/extensibility.d.ts index 778b36d24d..cf7c261f40 100644 --- a/lib/common/definitions/extensibility.d.ts +++ b/lib/common/definitions/extensibility.d.ts @@ -130,6 +130,11 @@ interface IExtensibilityService { * @returns {IExtensionCommandInfo} Information about the extension and the registered command. */ getExtensionNameWhereCommandIsRegistered(inputOpts: IGetExtensionCommandInfoParams): Promise; + + /** + * Defines the path where CLI will search for extensions. + */ + pathToExtensions: string; } /** diff --git a/lib/definitions/initialize-service.d.ts b/lib/definitions/initialize-service.d.ts index e4cab0db93..c2b7b22c09 100644 --- a/lib/definitions/initialize-service.d.ts +++ b/lib/definitions/initialize-service.d.ts @@ -1,3 +1,9 @@ +interface IInitializeOptions { + loggerOptions?: ILoggerOptions; + settingsServiceOptions?: IConfigurationSettings; + extensibilityOptions?: { pathToExtensions: string }; +} + interface IInitializeService { - initialize(initOpts?: { loggerOptions?: ILoggerOptions }): Promise; + initialize(initOpts?: IInitializeOptions): Promise; } diff --git a/lib/services/initialize-service.ts b/lib/services/initialize-service.ts index d57e2969b2..117da1b4c1 100644 --- a/lib/services/initialize-service.ts +++ b/lib/services/initialize-service.ts @@ -5,7 +5,7 @@ export class InitializeService implements IInitializeService { // Injecting something may lead to logger initialization, but we want to initialize it from here. constructor(private $injector: IInjector) { } - public async initialize(initOpts?: { loggerOptions?: ILoggerOptions }): Promise { + public async initialize(initOpts?: IInitializeOptions): Promise { initOpts = initOpts || {}; const $logger = this.$injector.resolve("logger"); if (initOpts.loggerOptions) { @@ -14,6 +14,18 @@ export class InitializeService implements IInitializeService { $logger.initializeCliLogger(); } + if (initOpts.settingsServiceOptions) { + const $settingsService = this.$injector.resolve("settingsService"); + $settingsService.setSettings(initOpts.settingsServiceOptions); + } + + if (initOpts.extensibilityOptions) { + if (initOpts.extensibilityOptions.pathToExtensions) { + const $extensibilityService = this.$injector.resolve("extensibilityService"); + $extensibilityService.pathToExtensions = initOpts.extensibilityOptions.pathToExtensions; + } + } + await this.showWarnings($logger); }