From e76a5d97827b7211e5be4951ae8fec285a33d4da Mon Sep 17 00:00:00 2001 From: Eythor Magnusson Date: Thu, 23 Nov 2023 11:22:16 +0100 Subject: [PATCH] improvement(logger): some minor fixes Particularly: - Setting logs for some built-in providers to debug because that's just more fitting - Use the "correct" log context when rendering new lines for graph and provider logs. If we use this.log the newlines are visible in the dev console where they shouldn't be because that's in the context of "internal" commands --- core/src/cli/cli.ts | 9 +++++++-- core/src/commands/login.ts | 4 ++-- core/src/garden.ts | 25 +++++++++++++++++++------ core/src/graph/actions.ts | 2 +- core/src/logger/renderers.ts | 19 +++++++++++++++---- core/src/monitors/port-forward.ts | 2 +- core/src/mutagen.ts | 2 +- core/src/tasks/resolve-provider.ts | 4 ++-- 8 files changed, 48 insertions(+), 19 deletions(-) diff --git a/core/src/cli/cli.ts b/core/src/cli/cli.ts index 79573fa135..51934c3aeb 100644 --- a/core/src/cli/cli.ts +++ b/core/src/cli/cli.ts @@ -224,7 +224,8 @@ ${renderCommands(commands)} } = parsedOpts const parsedCliVars = parseCliVarFlags(cliVars) - const gardenLog = log.createLog({ name: "garden" }) + const gardenLog = log.createLog({ name: "garden", showDuration: true }) + const initLog = command.getFullName() !== "dev" ? log.createLog({ name: "garden", showDuration: true }) : null // Some commands may set their own logger type so we update the logger config here, // once we've resolved the command. @@ -242,7 +243,10 @@ ${renderCommands(commands)} // Init Cloud API (if applicable) let cloudApi: CloudApi | undefined - gardenLog.info("Initializing...") + if (initLog) { + // Print a Garden log before the Cloud logs, just for setting context (skipped in dev console) + initLog.info("Initializing...") + } if (!command.noProject) { const config = await this.getProjectConfig(log, workingDir) const cloudDomain = getGardenCloudDomain(config?.domain) @@ -280,6 +284,7 @@ ${renderCommands(commands)} commandInfo, environmentString: environmentName, log, + initLog: initLog || undefined, forceRefresh, variableOverrides: parsedCliVars, plugins: this.plugins, diff --git a/core/src/commands/login.ts b/core/src/commands/login.ts index 29fac6de55..1faaa7bd00 100644 --- a/core/src/commands/login.ts +++ b/core/src/commands/login.ts @@ -92,7 +92,7 @@ export class LoginCommand extends Command<{}, Opts> { const cloudApi = await CloudApi.factory({ log, cloudDomain, skipLogging: true, globalConfigStore }) if (cloudApi) { - log.info({ msg: `You're already logged in to ${cloudDomain}.` }) + log.success({ msg: `You're already logged in to ${cloudDomain}.` }) cloudApi.close() return {} } @@ -114,7 +114,7 @@ export class LoginCommand extends Command<{}, Opts> { log.info({ msg: `Logging in to ${cloudDomain}...` }) const tokenResponse = await login(log, cloudDomain, garden.events) await CloudApi.saveAuthToken(log, globalConfigStore, tokenResponse, cloudDomain) - log.info({ msg: `Successfully logged in to ${cloudDomain}.` }) + log.success({ msg: `Successfully logged in to ${cloudDomain}.`, showDuration: false }) return {} } diff --git a/core/src/garden.ts b/core/src/garden.ts index e701423b84..f804573c01 100644 --- a/core/src/garden.ts +++ b/core/src/garden.ts @@ -167,6 +167,7 @@ import { configureNoOpExporter } from "./util/open-telemetry/tracing.js" import { detectModuleOverlap, makeOverlapErrors } from "./util/module-overlap.js" import { GotHttpError } from "./util/http.js" import { styles } from "./logger/styles.js" +import { renderDuration } from "./logger/util.js" const defaultLocalAddress = "localhost" @@ -179,6 +180,7 @@ export interface GardenOpts { globalConfigStore?: GlobalConfigStore legacyBuildSync?: boolean log?: Log + initLog?: Log monitors?: MonitorManager noEnterprise?: boolean persistent?: boolean @@ -203,6 +205,7 @@ export interface GardenParams { globalConfigStore?: GlobalConfigStore localConfigStore?: LocalConfigStore log: Log + initLog?: Log moduleIncludePatterns?: string[] moduleExcludePatterns?: string[] monitors?: MonitorManager @@ -234,6 +237,7 @@ interface GardenInstanceState { @Profile() export class Garden { public log: Log + private initLog?: Log private loadedPlugins?: GardenPluginSpec[] protected actionConfigs: ActionConfigMap protected moduleConfigs: ModuleConfigMap @@ -311,6 +315,7 @@ export class Garden { this.namespace = params.namespace this.gardenDirPath = params.gardenDirPath this.log = params.log + this.initLog = params.initLog this.artifactsPath = params.artifactsPath this.vcsInfo = params.vcsInfo this.opts = params.opts @@ -849,7 +854,7 @@ export class Garden { this.resolvedProviders[provider.name] = provider } - providerLog.success("Finished initializing providers") + providerLog.success("Finished resolving providers") if (someCached || allCached) { const msg = allCached ? "All" : "Some" providerLog.info( @@ -860,7 +865,7 @@ export class Garden { providerLog.silly(() => `Resolved providers: ${providers.map((p) => p.name).join(", ")}`) // Print a new line after resolving providers - this.log.info("") + providerLog.info("") }) return keyBy(providers, "name") @@ -1162,9 +1167,14 @@ export class Garden { // This event is internal only, not to be streamed this.events.emit("configGraph", { graph }) - graphLog.success("Done") - // Print a new line after resolving graph - this.log.info("") + graphLog.success("Finished resolving graph") + + // Log the initial Garden class initialization with duration (only once) + if (this.initLog) { + // We set the duration "manually" instead of using `initLog.success()` so we can add a new line at the end. + this.initLog.info(styles.success(`Finished initializing Garden ${renderDuration(this.initLog.getDuration(1))}\n`)) + this.initLog = undefined + } return graph.toConfigGraph() } @@ -1172,12 +1182,14 @@ export class Garden { async getResolvedConfigGraph(params: GetConfigGraphParams): Promise { const graph = await this.getConfigGraph(params) const resolved = await this.resolveActions({ graph, actions: graph.getActions(), log: params.log }) - return new ResolvedConfigGraph({ + const resolvedGraph = new ResolvedConfigGraph({ actions: Object.values(resolved), moduleGraph: graph.moduleGraph, // TODO: perhaps groups should be resolved here groups: graph.getGroups(), }) + + return resolvedGraph } @OtelTraced({ @@ -1972,6 +1984,7 @@ export const resolveGardenParams = profileAsync(async function _resolveGardenPar dotIgnoreFile: config.dotIgnoreFile, proxy, log, + initLog: opts.initLog, moduleIncludePatterns: (config.scan || {}).include, username: _username, forceRefresh: opts.forceRefresh, diff --git a/core/src/graph/actions.ts b/core/src/graph/actions.ts index 6a4f2d86f7..c7fc0463e5 100644 --- a/core/src/graph/actions.ts +++ b/core/src/graph/actions.ts @@ -467,7 +467,7 @@ export async function resolveAction({ const results = await garden.processTasks({ tasks: [task], log, throwOnError: true }) - log.info(`Done!`) + log.success({ msg: `Done`, showDuration: false }) return >(results.results.getResult(task)!.result!.outputs.resolvedAction) } diff --git a/core/src/logger/renderers.ts b/core/src/logger/renderers.ts index 7c2851749b..3261b2d678 100644 --- a/core/src/logger/renderers.ts +++ b/core/src/logger/renderers.ts @@ -26,6 +26,13 @@ type RenderFn = (entry: LogEntry, logger: Logger) => string export const SECTION_PADDING = 25 +const symbols = process.env.GARDEN_THEME === "tokyonight" ? { + info: styles.accent('ℹ'), + success: styles.success('✔'), + warning: styles.warning('⚠'), + error: styles.error('✖'), +} : logSymbols + export function padSection(section: string, width: number = SECTION_PADDING) { const diff = width - stringWidth(section) return diff <= 0 ? section : section + repeat(" ", diff) @@ -76,7 +83,7 @@ export function renderSymbol(entry: LogEntry): string { symbol = "info" } - return symbol ? `${logSymbols[symbol]} ` : "" + return symbol ? `${symbols[symbol]} ` : "" } export function renderTimestamp(entry: LogEntry, logger: Logger): string { @@ -136,9 +143,13 @@ export function renderData(entry: LogEntry): string { } export function renderSection(entry: LogEntry): string { - const { msg } = entry + const { msg, data, error } = entry const section = getSection(entry) + if (!msg) { + return "" + } + if (section && msg) { return `${padSection(styles.section(section))} ${styles.accent.bold("→")} ` } else if (section) { @@ -151,8 +162,8 @@ export function renderSection(entry: LogEntry): string { * Formats entries for the terminal writer. */ export function formatForTerminal(entry: LogEntry, logger: Logger): string { - const { msg: msg, symbol, data, error } = entry - const empty = [msg, symbol, data, error].every((val) => val === undefined) + const { msg: msg, data, error } = entry + const empty = [msg, data, error].every((val) => val === undefined) if (empty) { return "" diff --git a/core/src/monitors/port-forward.ts b/core/src/monitors/port-forward.ts index 1891533cd2..cf60999563 100644 --- a/core/src/monitors/port-forward.ts +++ b/core/src/monitors/port-forward.ts @@ -86,7 +86,7 @@ export class PortForwardMonitor extends Monitor { this.log.info( styles.primary( `Port forward: ` + - styles.underline(proxy.localUrl) + + styles.link(proxy.localUrl) + ` → ${targetHost}:${proxy.spec.targetPort}` + (proxy.spec.name ? ` (${proxy.spec.name})` : "") ) diff --git a/core/src/mutagen.ts b/core/src/mutagen.ts index 6b193905a5..11655e93a1 100644 --- a/core/src/mutagen.ts +++ b/core/src/mutagen.ts @@ -388,7 +388,7 @@ export class Mutagen { } const syncCount = session.successfulCycles || 0 - const description = `from ${sourceDescription} to ${targetDescription}` + const description = `from ${styles.highlight(sourceDescription)} to ${styles.highlight(targetDescription)}` const isInitialSync = activeSync.lastSyncCount === 0 // Mutagen resets the sync count to zero after resuming from a sync paused diff --git a/core/src/tasks/resolve-provider.ts b/core/src/tasks/resolve-provider.ts index 612183ae70..8174ed3bb0 100644 --- a/core/src/tasks/resolve-provider.ts +++ b/core/src/tasks/resolve-provider.ts @@ -41,8 +41,8 @@ import type { Log } from "../logger/log-entry.js" * resolved per se. A bit hacky but this is just a cosmetic change. */ function getProviderLog(providerName: string, log: Log) { - const verboseLogProviders = ["templated", "container"] - const fixLevel = verboseLogProviders.includes(providerName) ? LogLevel.verbose : undefined + const debugLogProviders = ["templated", "container"] + const fixLevel = debugLogProviders.includes(providerName) ? LogLevel.silly : undefined return log.createLog({ name: providerName, fixLevel }) }