UNPKG

appium-adb

Version:

Android Debug Bridge interface

216 lines 7.66 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Logcat = void 0; const support_1 = require("@appium/support"); const bluebird_1 = __importDefault(require("bluebird")); const lodash_1 = __importDefault(require("lodash")); const node_events_1 = require("node:events"); const teen_process_1 = require("teen_process"); const lru_cache_1 = require("lru-cache"); const log = support_1.logger.getLogger('Logcat'); const MAX_BUFFER_SIZE = 10000; const LOGCAT_PROC_STARTUP_TIMEOUT = 10000; const SUPPORTED_FORMATS = [ 'brief', 'process', 'tag', 'thread', 'raw', 'time', 'threadtime', 'long', ]; const SUPPORTED_PRIORITIES = ['v', 'd', 'i', 'w', 'e', 'f', 's']; const DEFAULT_PRIORITY = 'v'; const DEFAULT_TAG = '*'; const DEFAULT_FORMAT = 'threadtime'; const TRACE_PATTERN = /W\/Trace/; const EXECVP_ERR_PATTERN = /execvp\(\)/; class Logcat extends node_events_1.EventEmitter { adb; clearLogs; debug; debugTrace; maxBufferSize; logs; logIndexSinceLastRequest; proc; constructor(opts) { super(); this.adb = opts.adb; this.clearLogs = opts.clearDeviceLogsOnStart || false; this.debug = opts.debug; this.debugTrace = opts.debugTrace; this.maxBufferSize = opts.maxBufferSize || MAX_BUFFER_SIZE; this.logs = new lru_cache_1.LRUCache({ max: this.maxBufferSize, }); this.logIndexSinceLastRequest = null; this.proc = null; } async startCapture(opts = {}) { let started = false; return await new bluebird_1.default(async (_resolve, _reject) => { const resolve = function (...args) { started = true; _resolve(...args); }; const reject = function (...args) { started = true; _reject(...args); }; if (this.clearLogs) { await this.clear(); } const { format = DEFAULT_FORMAT, filterSpecs = [] } = opts; const cmd = [ ...this.adb.defaultArgs, 'logcat', '-v', requireFormat(format), ...formatFilterSpecs(filterSpecs), ]; log.debug(`Starting logs capture with command: ${support_1.util.quote([this.adb.path, ...cmd])}`); this.proc = new teen_process_1.SubProcess(this.adb.path, cmd); this.proc.on('exit', (code, signal) => { log.error(`Logcat terminated with code ${code}, signal ${signal}`); this.proc = null; if (!started) { log.warn('Logcat not started. Continuing'); resolve(); } }); this.proc.on('line-stderr', (line) => { if (!started && EXECVP_ERR_PATTERN.test(line)) { log.error('Logcat process failed to start'); return reject(new Error(`Logcat process failed to start. stderr: ${line}`)); } this.outputHandler(line, 'STDERR: '); resolve(); }); this.proc.on('line-stdout', (line) => { this.outputHandler(line); resolve(); }); await this.proc.start(0); // resolve after a timeout, even if no output was recorded setTimeout(resolve, LOGCAT_PROC_STARTUP_TIMEOUT); }); } async stopCapture() { log.debug('Stopping logcat capture'); if (!this.proc?.isRunning) { log.debug('Logcat already stopped'); this.proc = null; return; } this.proc.removeAllListeners('exit'); await this.proc.stop(); this.proc = null; } getLogs() { const result = []; let recentLogIndex = null; for (const entry of this.logs.rentries()) { const [index, value] = entry; if (typeof index !== 'number' || !Array.isArray(value)) { continue; } const [message, timestamp] = value; if ((this.logIndexSinceLastRequest && index > this.logIndexSinceLastRequest) || !this.logIndexSinceLastRequest) { recentLogIndex = index; result.push(toLogEntry(message, timestamp)); } } if (lodash_1.default.isInteger(recentLogIndex)) { this.logIndexSinceLastRequest = recentLogIndex; } return result; } getAllLogs() { const result = []; for (const value of this.logs.rvalues()) { if (!Array.isArray(value)) { continue; } const [message, timestamp] = value; result.push(toLogEntry(message, timestamp)); } return result; } async clear() { log.debug('Clearing logcat logs from device'); try { const args = [...this.adb.defaultArgs, 'logcat', '-c']; await (0, teen_process_1.exec)(this.adb.path, args); } catch (err) { const execErr = err; log.warn(`Failed to clear logcat logs: ${execErr.stderr || execErr.message}`); } } outputHandler(logLine, prefix = '') { const timestamp = Date.now(); let recentIndex = -1; for (const key of this.logs.keys()) { recentIndex = key; break; } this.logs.set(++recentIndex, [logLine, timestamp]); if (this.listenerCount('output')) { this.emit('output', toLogEntry(logLine, timestamp)); } if (this.debug && (this.debugTrace || !TRACE_PATTERN.test(logLine))) { log.debug(prefix + logLine); } } } exports.Logcat = Logcat; exports.default = Logcat; function requireFormat(format) { if (!SUPPORTED_FORMATS.includes(format)) { log.info(`The format value '${format}' is unknown. Supported values are: ${SUPPORTED_FORMATS}`); log.info(`Defaulting to '${DEFAULT_FORMAT}'`); return DEFAULT_FORMAT; } return format; } function toLogEntry(message, timestamp) { return { timestamp, level: 'ALL', message, }; } function requireSpec(spec) { const [tag, priority] = spec.split(':'); let resultTag = tag; if (!resultTag) { log.info(`The tag value in spec '${spec}' cannot be empty`); log.info(`Defaulting to '${DEFAULT_TAG}'`); resultTag = DEFAULT_TAG; } if (!priority) { log.info(`The priority value in spec '${spec}' is empty. Defaulting to Verbose (${DEFAULT_PRIORITY})`); return `${resultTag}:${DEFAULT_PRIORITY}`; } if (!SUPPORTED_PRIORITIES.some((p) => lodash_1.default.toLower(priority) === lodash_1.default.toLower(p))) { log.info(`The priority value in spec '${spec}' is unknown. Supported values are: ${SUPPORTED_PRIORITIES}`); log.info(`Defaulting to Verbose (${DEFAULT_PRIORITY})`); return `${resultTag}:${DEFAULT_PRIORITY}`; } return spec; } function formatFilterSpecs(filterSpecs) { if (!lodash_1.default.isArray(filterSpecs)) { filterSpecs = [filterSpecs]; } return filterSpecs .filter((spec) => spec && lodash_1.default.isString(spec) && !spec.startsWith('-')) .map((spec) => (spec.includes(':') ? requireSpec(spec) : spec)); } //# sourceMappingURL=logcat.js.map