mirror of
https://github.com/appium/appium.git
synced 2026-10-27 03:25:54 +08:00
124 lines
4.2 KiB
TypeScript
124 lines
4.2 KiB
TypeScript
import globalLog, {type Logger, markSensitive as _markSensitive} from '@appium/logger';
|
|
import type {AppiumLogger, AppiumLoggerContext, AppiumLoggerLevel, AppiumLoggerPrefix} from '@appium/types';
|
|
|
|
export const LEVELS: readonly AppiumLoggerLevel[] = ['silly', 'verbose', 'debug', 'info', 'http', 'warn', 'error'];
|
|
|
|
const MAX_LOG_RECORDS_COUNT = 3000;
|
|
|
|
interface GlobalWithNpmlog {
|
|
_global_npmlog?: Logger;
|
|
}
|
|
|
|
const globalWithNpmlog = globalThis as typeof globalThis & GlobalWithNpmlog;
|
|
const noop = () => {};
|
|
|
|
// mock log object is used in testing mode to silence the output
|
|
const MOCK_LOG = {
|
|
unwrap: () => ({
|
|
loadSecureValuesPreprocessingRules: () => Promise.resolve({issues: [], rules: []}),
|
|
level: 'verbose',
|
|
prefix: '',
|
|
log: noop,
|
|
}),
|
|
...(Object.fromEntries(LEVELS.map((l) => [l, noop])) as Record<AppiumLoggerLevel, (...args: any[]) => void>),
|
|
} as unknown as Logger;
|
|
|
|
export const log = getLogger();
|
|
|
|
/**
|
|
* @param prefix - Optional log prefix
|
|
* @returns A wrapped Appium logger instance
|
|
*/
|
|
export function getLogger(prefix: AppiumLoggerPrefix | null = null): AppiumLogger {
|
|
const {logger, defaultToVerbose} = _getLogger();
|
|
|
|
const wrappedLogger = {
|
|
unwrap: () => logger,
|
|
levels: [...LEVELS],
|
|
prefix: prefix ?? undefined,
|
|
errorWithException(...args: any[]) {
|
|
this.error(...args);
|
|
return args[0] instanceof Error ? args[0] : new Error(args.join('\n'));
|
|
},
|
|
errorAndThrow(...args: any[]) {
|
|
throw this.errorWithException(...args);
|
|
},
|
|
updateAsyncContext(contextInfo: AppiumLoggerContext, replace = false) {
|
|
this.unwrap().updateAsyncStorage?.(contextInfo, replace);
|
|
},
|
|
} as AppiumLogger;
|
|
|
|
Object.defineProperty(wrappedLogger, 'level', {
|
|
get() {
|
|
return logger.level;
|
|
},
|
|
set(newValue: string) {
|
|
logger.level = newValue;
|
|
},
|
|
enumerable: true,
|
|
configurable: true,
|
|
});
|
|
|
|
const isDebugTimestampLoggingEnabled = process.env._LOG_TIMESTAMP === '1';
|
|
|
|
for (const level of LEVELS) {
|
|
wrappedLogger[level] = function (this: typeof wrappedLogger, ...args: any[]) {
|
|
const finalPrefix = getFinalPrefix(this.prefix, isDebugTimestampLoggingEnabled);
|
|
if (args.length) {
|
|
(logger as Record<string, (...a: any[]) => void>)[level](finalPrefix, ...args);
|
|
} else {
|
|
(logger as Record<string, (...a: any[]) => void>)[level](finalPrefix, '');
|
|
}
|
|
};
|
|
}
|
|
|
|
// Default to verbose when the global was not already set (first use, or standalone);
|
|
// main server will override later.
|
|
if (defaultToVerbose) {
|
|
wrappedLogger.level = 'verbose';
|
|
}
|
|
|
|
return wrappedLogger;
|
|
}
|
|
|
|
/**
|
|
* Marks arbitrary log message as sensitive.
|
|
* This message will then be replaced with the default replacer
|
|
* while being logged by any `info`, `debug`, etc. methods if the
|
|
* asyncStorage has `isSensitive` flag enabled in its async context.
|
|
* The latter is enabled by the corresponding HTTP middleware
|
|
* in response to the `X-Appium-Is-Sensitive` request header
|
|
* being set to 'true'.
|
|
*/
|
|
export function markSensitive<T>(logMessage: T): {[k: string]: T} {
|
|
return _markSensitive(logMessage);
|
|
}
|
|
|
|
function _getLogger(): {logger: Logger; defaultToVerbose: boolean} {
|
|
const testingMode = process.env._TESTING === '1';
|
|
const forceLogMode = process.env._FORCE_LOGS === '1';
|
|
const defaultToVerbose = !globalWithNpmlog._global_npmlog;
|
|
const logger: Logger = testingMode && !forceLogMode ? MOCK_LOG : (globalWithNpmlog._global_npmlog ?? globalLog);
|
|
if (!testingMode && !globalWithNpmlog._global_npmlog && logger === globalLog) {
|
|
globalWithNpmlog._global_npmlog = globalLog;
|
|
logger.maxRecordSize = MAX_LOG_RECORDS_COUNT;
|
|
}
|
|
return {logger, defaultToVerbose};
|
|
}
|
|
|
|
function getFinalPrefix(prefix: AppiumLoggerPrefix | null | undefined, shouldLogTimestamp = false): string {
|
|
const result = (typeof prefix === 'function' ? prefix() : prefix) ?? '';
|
|
if (!shouldLogTimestamp) {
|
|
return result;
|
|
}
|
|
const now = new Date();
|
|
const pad = (n: number, z = 2) => String(n).padStart(z, '0');
|
|
const formattedTimestamp = `[${pad(now.getHours())}-${pad(now.getMinutes())}-${pad(now.getSeconds())}:${pad(
|
|
now.getMilliseconds(),
|
|
3,
|
|
)}]`;
|
|
return result ? `${formattedTimestamp} ${result}` : formattedTimestamp;
|
|
}
|
|
|
|
export default log;
|