diff --git a/src/platform/packages/shared/kbn-scout/src/common/services/index.ts b/src/platform/packages/shared/kbn-scout/src/common/services/index.ts index f4cb9311f056c..ad658458a0301 100644 --- a/src/platform/packages/shared/kbn-scout/src/common/services/index.ts +++ b/src/platform/packages/shared/kbn-scout/src/common/services/index.ts @@ -12,7 +12,6 @@ export { createScoutConfig } from './config'; export { getEsArchiver } from './es_archiver'; export { createKbnUrl } from './kibana_url'; export { createSamlSessionManager } from './saml_auth'; -export { getLogger } from './logger'; export type { KibanaUrl } from './kibana_url'; export type { SamlSessionManager } from '@kbn/test'; diff --git a/src/platform/packages/shared/kbn-scout/src/common/services/logger.ts b/src/platform/packages/shared/kbn-scout/src/common/services/logger.ts index 10ebc4ca231bd..d7038ac59affc 100644 --- a/src/platform/packages/shared/kbn-scout/src/common/services/logger.ts +++ b/src/platform/packages/shared/kbn-scout/src/common/services/logger.ts @@ -7,12 +7,44 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { ToolingLog } from '@kbn/tooling-log'; +import { LogLevel, ToolingLog, LOG_LEVEL_FLAGS, DEFAULT_LOG_LEVEL } from '@kbn/tooling-log'; export class ScoutLogger extends ToolingLog { - constructor(workerContext: string) { - super({ level: 'verbose', writeTo: process.stdout }, { context: workerContext }); - this.serviceLoaded('logger'); + /** + * Creates a ScoutLogger instance. + * + * Log level resolution priority: + * 1. The logLevel argument (if provided) + * 2. The SCOUT_LOG_LEVEL environment variable (if set) + * 3. The LOG_LEVEL environment variable (if set) + * 4. The default log level ('info') + * + * The log level string is normalized (case-insensitive), and 'quiet' is treated as 'error'. + * Only valid log levels from LOG_LEVEL_FLAGS are accepted. + * + * @param workerContext - Unique context string for the logger + * @param logLevel - Optional log level string (highest priority) + */ + constructor(workerContext: string, logLevel?: LogLevel) { + // Helper to normalize and resolve log level string + const resolveLogLevelFromEnv = (value: string | undefined): LogLevel | undefined => { + if (typeof value === 'string' && value) { + let normalized = value.toLowerCase(); + if (normalized === 'quiet') { + normalized = 'error'; + } + const found = LOG_LEVEL_FLAGS.find(({ name }) => name === normalized); + if (found) return found.name as LogLevel; + } + return undefined; + }; + + const level = + logLevel || + resolveLogLevelFromEnv(process.env.SCOUT_LOG_LEVEL) || + resolveLogLevelFromEnv(process.env.LOG_LEVEL) || + DEFAULT_LOG_LEVEL; + super({ level, writeTo: process.stdout }, { context: workerContext }); } /** @@ -30,18 +62,3 @@ export class ScoutLogger extends ToolingLog { this.debug(`[${name}] ${message}`); } } - -const loggerInstances = new Map(); - -/** - * Singleton logger instance for specific worker to share across the Scout components - * @param workerContext logger context, e.g. `scout-worker-1`; default is `scout` - * @returns {ScoutLogger} logger instance - */ -export function getLogger(workerContext: string = 'scout'): ScoutLogger { - if (!loggerInstances.has(workerContext)) { - loggerInstances.set(workerContext, new ScoutLogger(workerContext)); - } - - return loggerInstances.get(workerContext)!; -} diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/core_fixtures.ts b/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/core_fixtures.ts index 27917bfcbf606..7b523610ee962 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/core_fixtures.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/fixtures/worker/core_fixtures.ts @@ -17,13 +17,12 @@ import { createSamlSessionManager, createScoutConfig, KibanaUrl, - getLogger, - ScoutLogger, createElasticsearchCustomRole, createCustomRole, ElasticsearchRoleDescriptor, KibanaRole, } from '../../../common/services'; +import { ScoutLogger } from '../../../common/services/logger'; import type { ScoutTestOptions } from '../../types'; import type { ScoutTestConfig } from '.'; @@ -65,7 +64,9 @@ export const coreWorkerFixtures = base.extend< const workersCount = workerInfo.config.workers; const loggerContext = workersCount === 1 ? 'scout-worker' : `scout-worker-${workerInfo.parallelIndex + 1}`; - use(getLogger(loggerContext)); + // The log level is resolved inside the ScoutLogger constructor, which checks the argument, + // then SCOUT_LOG_LEVEL, then LOG_LEVEL, and finally defaults to 'info'. + use(new ScoutLogger(loggerContext)); }, { scope: 'worker' }, ], diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/global_hooks/data_ingestion.ts b/src/platform/packages/shared/kbn-scout/src/playwright/global_hooks/data_ingestion.ts index c861165e1b648..a5741e7398011 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/global_hooks/data_ingestion.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/global_hooks/data_ingestion.ts @@ -9,17 +9,17 @@ import { FullConfig } from 'playwright/test'; import { - getLogger, getEsArchiver, createScoutConfig, measurePerformanceAsync, getEsClient, getKbnClient, } from '../../common'; +import { ScoutLogger } from '../../common/services/logger'; import { ScoutTestOptions } from '../types'; export async function ingestTestDataHook(config: FullConfig, archives: string[]) { - const log = getLogger(); + const log = new ScoutLogger('scout: global hook'); if (archives.length === 0) { log.debug('[setup] no test data to ingest'); diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/global_hooks/synthtrace_ingestion.ts b/src/platform/packages/shared/kbn-scout/src/playwright/global_hooks/synthtrace_ingestion.ts index 2ac7c0e4c4650..20ccb47cfa529 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/global_hooks/synthtrace_ingestion.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/global_hooks/synthtrace_ingestion.ts @@ -18,7 +18,8 @@ import type { SynthtraceGenerator, } from '@kbn/apm-synthtrace-client'; import { SynthtraceClientTypes } from '@kbn/apm-synthtrace'; -import { getLogger, createScoutConfig, measurePerformanceAsync, getEsClient } from '../../common'; +import { createScoutConfig, measurePerformanceAsync, getEsClient } from '../../common'; +import { ScoutLogger } from '../../common/services/logger'; import { ScoutTestOptions } from '../types'; import { getSynthtraceClient } from '../../common/services/synthtrace'; @@ -38,7 +39,7 @@ const INGESTION_CLIENT_MAP: Record = { * @deprecated Use `globalSetupHook` and synthtrace fixtures instead */ export async function ingestSynthtraceDataHook(config: FullConfig, data: SynthtraceIngestionData) { - const log = getLogger(); + const log = new ScoutLogger('scout: global hook'); const { apm, infra } = data; const hasApmData = apm.length > 0; diff --git a/src/platform/packages/shared/kbn-scout/src/playwright/runner/run_tests.ts b/src/platform/packages/shared/kbn-scout/src/playwright/runner/run_tests.ts index cfb0a72f15672..e86c9581a40ee 100644 --- a/src/platform/packages/shared/kbn-scout/src/playwright/runner/run_tests.ts +++ b/src/platform/packages/shared/kbn-scout/src/playwright/runner/run_tests.ts @@ -8,10 +8,11 @@ */ import { resolve } from 'path'; -import { ToolingLog } from '@kbn/tooling-log'; +import { ToolingLog, pickLevelFromFlags } from '@kbn/tooling-log'; import { ProcRunner, withProcRunner } from '@kbn/dev-proc-runner'; import { getTimeReporter } from '@kbn/ci-stats-reporter'; import { REPO_ROOT } from '@kbn/repo-info'; +import { getFlags } from '@kbn/dev-cli-runner'; import { runElasticsearch, runKibanaServer } from '../../servers'; import { loadServersConfig } from '../../config'; import { silence } from '../../common'; @@ -31,13 +32,19 @@ export const getPlaywrightProject = ( return 'local'; }; -async function runPlaywrightTest(procs: ProcRunner, cmd: string, args: string[]) { +async function runPlaywrightTest( + procs: ProcRunner, + cmd: string, + args: string[], + env: Record = {} +) { return procs.run(`playwright`, { cmd, args, cwd: resolve(REPO_ROOT), env: { ...process.env, + ...env, }, wait: true, }); @@ -82,7 +89,8 @@ async function runLocalServersAndTests( log: ToolingLog, options: RunTestsOptions, cmd: string, - cmdArgs: string[] + cmdArgs: string[], + env: Record = {} ) { const config = await loadServersConfig(options.mode, log); const abortCtrl = new AbortController(); @@ -114,7 +122,7 @@ async function runLocalServersAndTests( // wait for 5 seconds await silence(log, 5000); - await runPlaywrightTest(procs, cmd, cmdArgs); + await runPlaywrightTest(procs, cmd, cmdArgs, env); } finally { try { await procs.stop('kibana'); @@ -133,6 +141,12 @@ export async function runTests(log: ToolingLog, options: RunTestsOptions) { const pwGrepTag = getPlaywrightGrepTag(options.mode); const pwConfigPath = options.configPath; const pwProject = getPlaywrightProject(options.testTarget, options.mode); + const globalFlags = getFlags(process.argv.slice(2), { + allowUnexpected: true, + }); + // Temporarily use `debug` log level for Playwright tests to better understand performance issues; + // We are going to change it to `info` in the future. This change doesn't affect Test Servers logging. + const logsLevel = pickLevelFromFlags(globalFlags, { default: 'debug' }); const pwBinPath = resolve(REPO_ROOT, './node_modules/.bin/playwright'); const pwCmdArgs = [ @@ -151,9 +165,13 @@ export async function runTests(log: ToolingLog, options: RunTestsOptions) { } if (pwProject === 'local') { - await runLocalServersAndTests(procs, log, options, pwBinPath, pwCmdArgs); + await runLocalServersAndTests(procs, log, options, pwBinPath, pwCmdArgs, { + SCOUT_LOG_LEVEL: logsLevel, + }); } else { - await runPlaywrightTest(procs, pwBinPath, pwCmdArgs); + await runPlaywrightTest(procs, pwBinPath, pwCmdArgs, { + SCOUT_LOG_LEVEL: logsLevel, + }); } reportTime(runStartTime, 'ready', {