@appium/support
Version:
Support libs used across Appium packages
158 lines (140 loc) • 4.43 kB
text/typescript
import globalLog, {
markSensitive as _markSensitive,
type Logger,
} from '@appium/logger';
import type {
AppiumLogger,
AppiumLoggerContext,
AppiumLoggerLevel,
AppiumLoggerPrefix,
} from '@appium/types';
import _ from 'lodash';
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;
// 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,
}),
...(_.fromPairs(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 _.isError(args[0]) ? 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 = (_.isFunction(prefix) ? 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;