UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

166 lines 21.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CloudWatchLogEventMonitor = void 0; const util = require("util"); const chalk = require("chalk"); const logging_1 = require("../../logging"); const arrays_1 = require("../../util/arrays"); /** * After reading events from all CloudWatch log groups * how long should we wait to read more events. * * If there is some error with reading events (i.e. Throttle) * then this is also how long we wait until we try again */ const SLEEP = 2000; class CloudWatchLogEventMonitor { constructor(startTime) { /** * Map of environment (account:region) to LogGroupsAccessSettings */ this.envsLogGroupsAccessSettings = new Map(); this.active = false; this.startTime = startTime?.getTime() ?? Date.now(); } /** * resume reading/printing events */ activate() { this.active = true; this.scheduleNextTick(0); } /** * deactivates the monitor so no new events are read * use case for this is when we are in the middle of performing a deployment * and don't want to interweave all the logs together with the CFN * deployment logs * * Also resets the start time to be when the new deployment was triggered * and clears the list of tracked log groups */ deactivate() { this.active = false; this.startTime = Date.now(); this.envsLogGroupsAccessSettings.clear(); } /** * Adds CloudWatch log groups to read log events from. * Since we could be watching multiple stacks that deploy to * multiple environments (account+region), we need to store a list of log groups * per env along with the SDK object that has access to read from * that environment. */ addLogGroups(env, sdk, logGroupNames) { const awsEnv = `${env.account}:${env.region}`; const logGroupsStartTimes = logGroupNames.reduce((acc, groupName) => { acc[groupName] = this.startTime; return acc; }, {}); this.envsLogGroupsAccessSettings.set(awsEnv, { sdk, logGroupsStartTimes: { ...this.envsLogGroupsAccessSettings.get(awsEnv)?.logGroupsStartTimes, ...logGroupsStartTimes, }, }); } scheduleNextTick(sleep) { setTimeout(() => void this.tick(), sleep); } async tick() { // excluding from codecoverage because this // doesn't always run (depends on timing) /* istanbul ignore next */ if (!this.active) { return; } try { const events = (0, arrays_1.flatten)(await this.readNewEvents()); events.forEach((event) => { this.print(event); }); } catch (e) { (0, logging_1.error)('Error occurred while monitoring logs: %s', e); } this.scheduleNextTick(SLEEP); } /** * Reads all new log events from a set of CloudWatch Log Groups * in parallel */ async readNewEvents() { const promises = []; for (const settings of this.envsLogGroupsAccessSettings.values()) { for (const group of Object.keys(settings.logGroupsStartTimes)) { promises.push(this.readEventsFromLogGroup(settings, group)); } } // Limited set of log groups // eslint-disable-next-line @cdklabs/promiseall-no-unbounded-parallelism return Promise.all(promises); } /** * Print out a cloudwatch event */ print(event) { (0, logging_1.info)(util.format('[%s] %s %s', chalk.blue(event.logGroupName), chalk.yellow(event.timestamp.toLocaleTimeString()), event.message.trim())); } /** * Reads all new log events from a CloudWatch Log Group * starting at either the time the hotswap was triggered or * when the last event was read on the previous tick */ async readEventsFromLogGroup(logGroupsAccessSettings, logGroupName) { const events = []; // log events from some service are ingested faster than others // so we need to track the start/end time for each log group individually // to make sure that we process all events from each log group const startTime = logGroupsAccessSettings.logGroupsStartTimes[logGroupName] ?? this.startTime; let endTime = startTime; try { const response = await logGroupsAccessSettings.sdk.cloudWatchLogs().filterLogEvents({ logGroupName: logGroupName, limit: 100, startTime: startTime, }); const filteredEvents = response.events ?? []; for (const event of filteredEvents) { if (event.message) { events.push({ message: event.message, logGroupName, timestamp: event.timestamp ? new Date(event.timestamp) : new Date(), }); if (event.timestamp && endTime < event.timestamp) { endTime = event.timestamp; } } } // As long as there are _any_ events in the log group `filterLogEvents` will return a nextToken. // This is true even if these events are before `startTime`. So if we have 100 events and a nextToken // then assume that we have hit the limit and let the user know some messages have been suppressed. // We are essentially showing them a sampling (10000 events printed out is not very useful) if (filteredEvents.length === 100 && response.nextToken) { events.push({ message: '>>> `watch` shows only the first 100 log messages - the rest have been truncated...', logGroupName, timestamp: new Date(endTime), }); } } catch (e) { // with Lambda functions the CloudWatch is not created // until something is logged, so just keep polling until // there is somthing to find if (e.name === 'ResourceNotFoundException') { return []; } throw e; } logGroupsAccessSettings.logGroupsStartTimes[logGroupName] = endTime + 1; return events; } } exports.CloudWatchLogEventMonitor = CloudWatchLogEventMonitor; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9ncy1tb25pdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsibG9ncy1tb25pdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDZCQUE2QjtBQUU3QiwrQkFBK0I7QUFDL0IsMkNBQTRDO0FBQzVDLDhDQUE0QztBQUc1Qzs7Ozs7O0dBTUc7QUFDSCxNQUFNLEtBQUssR0FBRyxJQUFLLENBQUM7QUEwQ3BCLE1BQWEseUJBQXlCO0lBYXBDLFlBQVksU0FBZ0I7UUFQNUI7O1dBRUc7UUFDYyxnQ0FBMkIsR0FBRyxJQUFJLEdBQUcsRUFBbUMsQ0FBQztRQUVsRixXQUFNLEdBQUcsS0FBSyxDQUFDO1FBR3JCLElBQUksQ0FBQyxTQUFTLEdBQUcsU0FBUyxFQUFFLE9BQU8sRUFBRSxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN0RCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxRQUFRO1FBQ2IsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7UUFDbkIsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNCLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNJLFVBQVU7UUFDZixJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztRQUNwQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUM1QixJQUFJLENBQUMsMkJBQTJCLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDM0MsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLFlBQVksQ0FBQyxHQUFzQixFQUFFLEdBQVEsRUFBRSxhQUF1QjtRQUMzRSxNQUFNLE1BQU0sR0FBRyxHQUFHLEdBQUcsQ0FBQyxPQUFPLElBQUksR0FBRyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQzlDLE1BQU0sbUJBQW1CLEdBQUcsYUFBYSxDQUFDLE1BQU0sQ0FDOUMsQ0FBQyxHQUFHLEVBQUUsU0FBUyxFQUFFLEVBQUU7WUFDakIsR0FBRyxDQUFD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LENBQUMsSUFBSSxLQUFLLDJCQUEyQixFQUFFLENBQUM7Z0JBQzNDLE9BQU8sRUFBRSxDQUFDO1lBQ1osQ0FBQztZQUNELE1BQU0sQ0FBQyxDQUFDO1FBQ1YsQ0FBQztRQUNELHVCQUF1QixDQUFDLG1CQUFtQixDQUFDLFlBQVksQ0FBQyxHQUFHLE9BQU8sR0FBRyxDQUFDLENBQUM7UUFDeEUsT0FBTyxNQUFNLENBQUM7SUFDaEIsQ0FBQztDQUNGO0FBbExELDhEQWtMQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIHV0aWwgZnJvbSAndXRpbCc7XG5pbXBvcnQgKiBhcyBjeGFwaSBmcm9tICdAYXdzLWNkay9jeC1hcGknO1xuaW1wb3J0ICogYXMgY2hhbGsgZnJvbSAnY2hhbGsnO1xuaW1wb3J0IHsgaW5mbywgZXJyb3IgfSBmcm9tICcuLi8uLi9sb2dnaW5nJztcbmltcG9ydCB7IGZsYXR0ZW4gfSBmcm9tICcuLi8uLi91dGlsL2FycmF5cyc7XG5pbXBvcnQgdHlwZSB7IFNESyB9IGZyb20gJy4uL2F3cy1hdXRoJztcblxuLyoqXG4gKiBBZnRlciByZWFkaW5nIGV2ZW50cyBmcm9tIGFsbCBDbG91ZFdhdGNoIGxvZyBncm91cHNcbiAqIGhvdyBsb25nIHNob3VsZCB3ZSB3YWl0IHRvIHJlYWQgbW9yZSBldmVudHMuXG4gKlxuICogSWYgdGhlcmUgaXMgc29tZSBlcnJvciB3aXRoIHJlYWRpbmcgZXZlbnRzIChpLmUuIFRocm90dGxlKVxuICogdGhlbiB0aGlzIGlzIGFsc28gaG93IGxvbmcgd2Ugd2FpdCB1bnRpbCB3ZSB0cnkgYWdhaW5cbiAqL1xuY29uc3QgU0xFRVAgPSAyXzAwMDtcblxuLyoqXG4gKiBSZXByZXNlbnRzIGEgQ2xvdWRXYXRjaCBMb2cgRXZlbnQgdGhhdCB3aWxsIGJlXG4gKiBwcmludGVkIHRvIHRoZSB0ZXJtaW5hbFxuICovXG5pbnRlcmZhY2UgQ2xvdWRXYXRjaExvZ0V2ZW50IHtcbiAgLyoqXG4gICAqIFRoZSBsb2cgZXZlbnQgbWVzc2FnZVxuICAgKi9cbiAgcmVhZG9ubHkgbWVzc2FnZTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgbmFtZSBvZiB0aGUgbG9nIGdyb3VwXG4gICAqL1xuICByZWFkb25seSBsb2dHcm91cE5hbWU6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIHRpbWUgYXQgd2hpY2ggdGhlIGV2ZW50IG9jY3VycmVkXG4gICAqL1xuICByZWFkb25seSB0aW1lc3RhbXA6IERhdGU7XG59XG5cbi8qKlxuICogQ29uZmlndXJhdGlvbiB0cmFja2luZyBpbmZvcm1hdGlvbiBvbiB0aGUgbG9nIGdyb3VwcyB0aGF0IGFyZVxuICogYmVpbmcgbW9uaXRvcmVkXG4gKi9cbmludGVyZmFjZSBMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncyB7XG4gIC8qKlxuICAgKiBUaGUgU0RLIGZvciBhIGdpdmVuIGVudmlyb25tZW50IChhY2NvdW50L3JlZ2lvbilcbiAgICovXG4gIHJlYWRvbmx5IHNkazogU0RLO1xuXG4gIC8qKlxuICAgKiBBIG1hcCBvZiBsb2cgZ3JvdXBzIGFuZCBhc3NvY2lhdGVkIHN0YXJ0VGltZSBpbiBhIGdpdmVuIGFjY291bnQuXG4gICAqXG4gICAqIFRoZSBtb25pdG9yIHdpbGwgcmVhZCBldmVudHMgZnJvbSB0aGUgbG9nIGdyb3VwIHN0YXJ0aW5nIGF0IHRoZVxuICAgKiBhc3NvY2lhdGVkIHN0YXJ0VGltZVxuICAgKi9cbiAgcmVhZG9ubHkgbG9nR3JvdXBzU3RhcnRUaW1lczogeyBbbG9nR3JvdXBOYW1lOiBzdHJpbmddOiBudW1iZXIgfTtcbn1cblxuZXhwb3J0IGNsYXNzIENsb3VkV2F0Y2hMb2dFdmVudE1vbml0b3Ige1xuICAvKipcbiAgICogRGV0ZXJtaW5lcyB3aGljaCBldmVudHMgbm90IHRvIGRpc3BsYXlcbiAgICovXG4gIHByaXZhdGUgc3RhcnRUaW1lOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIE1hcCBvZiBlbnZpcm9ubWVudCAoYWNjb3VudDpyZWdpb24pIHRvIExvZ0dyb3Vwc0FjY2Vzc1NldHRpbmdzXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGVudnNMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncyA9IG5ldyBNYXA8c3RyaW5nLCBMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncz4oKTtcblxuICBwcml2YXRlIGFjdGl2ZSA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKHN0YXJ0VGltZT86IERhdGUpIHtcbiAgICB0aGlzLnN0YXJ0VGltZSA9IHN0YXJ0VGltZT8uZ2V0VGltZSgpID8/IERhdGUubm93KCk7XG4gIH1cblxuICAvKipcbiAgICogcmVzdW1lIHJlYWRpbmcvcHJpbnRpbmcgZXZlbnRzXG4gICAqL1xuICBwdWJsaWMgYWN0aXZhdGUoKTogdm9pZCB7XG4gICAgdGhpcy5hY3RpdmUgPSB0cnVlO1xuICAgIHRoaXMuc2NoZWR1bGVOZXh0VGljaygwKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBkZWFjdGl2YXRlcyB0aGUgbW9uaXRvciBzbyBubyBuZXcgZXZlbnRzIGFyZSByZWFkXG4gICAqIHVzZSBjYXNlIGZvciB0aGlzIGlzIHdoZW4gd2UgYXJlIGluIHRoZSBtaWRkbGUgb2YgcGVyZm9ybWluZyBhIGRlcGxveW1lbnRcbiAgICogYW5kIGRvbid0IHdhbnQgdG8gaW50ZXJ3ZWF2ZSBhbGwgdGhlIGxvZ3MgdG9nZXRoZXIgd2l0aCB0aGUgQ0ZOXG4gICAqIGRlcGxveW1lbnQgbG9nc1xuICAgKlxuICAgKiBBbHNvIHJlc2V0cyB0aGUgc3RhcnQgdGltZSB0byBiZSB3aGVuIHRoZSBuZXcgZGVwbG95bWVudCB3YXMgdHJpZ2dlcmVkXG4gICAqIGFuZCBjbGVhcnMgdGhlIGxpc3Qgb2YgdHJhY2tlZCBsb2cgZ3JvdXBzXG4gICAqL1xuICBwdWJsaWMgZGVhY3RpdmF0ZSgpOiB2b2lkIHtcbiAgICB0aGlzLmFjdGl2ZSA9IGZhbHNlO1xuICAgIHRoaXMuc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICB0aGlzLmVudnNMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncy5jbGVhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgQ2xvdWRXYXRjaCBsb2cgZ3JvdXBzIHRvIHJlYWQgbG9nIGV2ZW50cyBmcm9tLlxuICAgKiBTaW5jZSB3ZSBjb3VsZCBiZSB3YXRjaGluZyBtdWx0aXBsZSBzdGFja3MgdGhhdCBkZXBsb3kgdG9cbiAgICogbXVsdGlwbGUgZW52aXJvbm1lbnRzIChhY2NvdW50K3JlZ2lvbiksIHdlIG5lZWQgdG8gc3RvcmUgYSBsaXN0IG9mIGxvZyBncm91cHNcbiAgICogcGVyIGVudiBhbG9uZyB3aXRoIHRoZSBTREsgb2JqZWN0IHRoYXQgaGFzIGFjY2VzcyB0byByZWFkIGZyb21cbiAgICogdGhhdCBlbnZpcm9ubWVudC5cbiAgICovXG4gIHB1YmxpYyBhZGRMb2dHcm91cHMoZW52OiBjeGFwaS5FbnZpcm9ubWVudCwgc2RrOiBTREssIGxvZ0dyb3VwTmFtZXM6IHN0cmluZ1tdKTogdm9pZCB7XG4gICAgY29uc3QgYXdzRW52ID0gYCR7ZW52LmFjY291bnR9OiR7ZW52LnJlZ2lvbn1gO1xuICAgIGNvbnN0IGxvZ0dyb3Vwc1N0YXJ0VGltZXMgPSBsb2dHcm91cE5hbWVzLnJlZHVjZShcbiAgICAgIChhY2MsIGdyb3VwTmFtZSkgPT4ge1xuICAgICAgICBhY2NbZ3JvdXBOYW1lXSA9IHRoaXMuc3RhcnRUaW1lO1xuICAgICAgICByZXR1cm4gYWNjO1xuICAgICAgfSxcbiAgICAgIHt9IGFzIHsgW2xvZ0dyb3VwTmFtZTogc3RyaW5nXTogbnVtYmVyIH0sXG4gICAgKTtcbiAgICB0aGlzLmVudnNMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncy5zZXQoYXdzRW52LCB7XG4gICAgICBzZGssXG4gICAgICBsb2dHcm91cHNTdGFydFRpbWVzOiB7XG4gICAgICAgIC4uLnRoaXMuZW52c0xvZ0dyb3Vwc0FjY2Vzc1NldHRpbmdzLmdldChhd3NFbnYpPy5sb2dHcm91cHNTdGFydFRpbWVzLFxuICAgICAgICAuLi5sb2dHcm91cHNTdGFydFRpbWVzLFxuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgc2NoZWR1bGVOZXh0VGljayhzbGVlcDogbnVtYmVyKTogdm9pZCB7XG4gICAgc2V0VGltZW91dCgoKSA9PiB2b2lkIHRoaXMudGljaygpLCBzbGVlcCk7XG4gIH1cblxuICBwcml2YXRlIGFzeW5jIHRpY2soKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gZXhjbHVkaW5nIGZyb20gY29kZWNvdmVyYWdlIGJlY2F1c2UgdGhpc1xuICAgIC8vIGRvZXNuJ3QgYWx3YXlzIHJ1biAoZGVwZW5kcyBvbiB0aW1pbmcpXG4gICAgLyogaXN0YW5idWwgaWdub3JlIG5leHQgKi9cbiAgICBpZiAoIXRoaXMuYWN0aXZlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBjb25zdCBldmVudHMgPSBmbGF0dGVuKGF3YWl0IHRoaXMucmVhZE5ld0V2ZW50cygpKTtcbiAgICAgIGV2ZW50cy5mb3JFYWNoKChldmVudCkgPT4ge1xuICAgICAgICB0aGlzLnByaW50KGV2ZW50KTtcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGVycm9yKCdFcnJvciBvY2N1cnJlZCB3aGlsZSBtb25pdG9yaW5nIGxvZ3M6ICVzJywgZSk7XG4gICAgfVxuXG4gICAgdGhpcy5zY2hlZHVsZU5leHRUaWNrKFNMRUVQKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWFkcyBhbGwgbmV3IGxvZyBldmVudHMgZnJvbSBhIHNldCBvZiBDbG91ZFdhdGNoIExvZyBHcm91cHNcbiAgICogaW4gcGFyYWxsZWxcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVhZE5ld0V2ZW50cygpOiBQcm9taXNlPEFycmF5PEFycmF5PENsb3VkV2F0Y2hMb2dFdmVudD4+PiB7XG4gICAgY29uc3QgcHJvbWlzZXM6IEFycmF5PFByb21pc2U8QXJyYXk8Q2xvdWRXYXRjaExvZ0V2ZW50Pj4+ID0gW107XG4gICAgZm9yIChjb25zdCBzZXR0aW5ncyBvZiB0aGlzLmVudnNMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncy52YWx1ZXMoKSkge1xuICAgICAgZm9yIChjb25zdCBncm91cCBvZiBPYmplY3Qua2V5cyhzZXR0aW5ncy5sb2dHcm91cHNTdGFydFRpbWVzKSkge1xuICAgICAgICBwcm9taXNlcy5wdXNoKHRoaXMucmVhZEV2ZW50c0Zyb21Mb2dHcm91cChzZXR0aW5ncywgZ3JvdXApKTtcbiAgICAgIH1cbiAgICB9XG4gICAgLy8gTGltaXRlZCBzZXQgb2YgbG9nIGdyb3Vwc1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAY2RrbGFicy9wcm9taXNlYWxsLW5vLXVuYm91bmRlZC1wYXJhbGxlbGlzbVxuICAgIHJldHVybiBQcm9taXNlLmFsbChwcm9taXNlcyk7XG4gIH1cblxuICAvKipcbiAgICogUHJpbnQgb3V0IGEgY2xvdWR3YXRjaCBldmVudFxuICAgKi9cbiAgcHJpdmF0ZSBwcmludChldmVudDogQ2xvdWRXYXRjaExvZ0V2ZW50KTogdm9pZCB7XG4gICAgaW5mbyhcbiAgICAgIHV0aWwuZm9ybWF0KFxuICAgICAgICAnWyVzXSAlcyAlcycsXG4gICAgICAgIGNoYWxrLmJsdWUoZXZlbnQubG9nR3JvdXBOYW1lKSxcbiAgICAgICAgY2hhbGsueWVsbG93KGV2ZW50LnRpbWVzdGFtcC50b0xvY2FsZVRpbWVTdHJpbmcoKSksXG4gICAgICAgIGV2ZW50Lm1lc3NhZ2UudHJpbSgpLFxuICAgICAgKSxcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlYWRzIGFsbCBuZXcgbG9nIGV2ZW50cyBmcm9tIGEgQ2xvdWRXYXRjaCBMb2cgR3JvdXBcbiAgICogc3RhcnRpbmcgYXQgZWl0aGVyIHRoZSB0aW1lIHRoZSBob3Rzd2FwIHdhcyB0cmlnZ2VyZWQgb3JcbiAgICogd2hlbiB0aGUgbGFzdCBldmVudCB3YXMgcmVhZCBvbiB0aGUgcHJldmlvdXMgdGlja1xuICAgKi9cbiAgcHJpdmF0ZSBhc3luYyByZWFkRXZlbnRzRnJvbUxvZ0dyb3VwKFxuICAgIGxvZ0dyb3Vwc0FjY2Vzc1NldHRpbmdzOiBMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncyxcbiAgICBsb2dHcm91cE5hbWU6IHN0cmluZyxcbiAgKTogUHJvbWlzZTxBcnJheTxDbG91ZFdhdGNoTG9nRXZlbnQ+PiB7XG4gICAgY29uc3QgZXZlbnRzOiBDbG91ZFdhdGNoTG9nRXZlbnRbXSA9IFtdO1xuXG4gICAgLy8gbG9nIGV2ZW50cyBmcm9tIHNvbWUgc2VydmljZSBhcmUgaW5nZXN0ZWQgZmFzdGVyIHRoYW4gb3RoZXJzXG4gICAgLy8gc28gd2UgbmVlZCB0byB0cmFjayB0aGUgc3RhcnQvZW5kIHRpbWUgZm9yIGVhY2ggbG9nIGdyb3VwIGluZGl2aWR1YWxseVxuICAgIC8vIHRvIG1ha2Ugc3VyZSB0aGF0IHdlIHByb2Nlc3MgYWxsIGV2ZW50cyBmcm9tIGVhY2ggbG9nIGdyb3VwXG4gICAgY29uc3Qgc3RhcnRUaW1lID0gbG9nR3JvdXBzQWNjZXNzU2V0dGluZ3MubG9nR3JvdXBzU3RhcnRUaW1lc1tsb2dHcm91cE5hbWVdID8/IHRoaXMuc3RhcnRUaW1lO1xuICAgIGxldCBlbmRUaW1lID0gc3RhcnRUaW1lO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXNwb25zZSA9IGF3YWl0IGxvZ0dyb3Vwc0FjY2Vzc1NldHRpbmdzLnNkay5jbG91ZFdhdGNoTG9ncygpLmZpbHRlckxvZ0V2ZW50cyh7XG4gICAgICAgIGxvZ0dyb3VwTmFtZTogbG9nR3JvdXBOYW1lLFxuICAgICAgICBsaW1pdDogMTAwLFxuICAgICAgICBzdGFydFRpbWU6IHN0YXJ0VGltZSxcbiAgICAgIH0pO1xuICAgICAgY29uc3QgZmlsdGVyZWRFdmVudHMgPSByZXNwb25zZS5ldmVudHMgPz8gW107XG5cbiAgICAgIGZvciAoY29uc3QgZXZlbnQgb2YgZmlsdGVyZWRFdmVudHMpIHtcbiAgICAgICAgaWYgKGV2ZW50Lm1lc3NhZ2UpIHtcbiAgICAgICAgICBldmVudHMucHVzaCh7XG4gICAgICAgICAgICBtZXNzYWdlOiBldmVudC5tZXNzYWdlLFxuICAgICAgICAgICAgbG9nR3JvdXBOYW1lLFxuICAgICAgICAgICAgdGltZXN0YW1wOiBldmVudC50aW1lc3RhbXAgPyBuZXcgRGF0ZShldmVudC50aW1lc3RhbXApIDogbmV3IERhdGUoKSxcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIGlmIChldmVudC50aW1lc3RhbXAgJiYgZW5kVGltZSA8IGV2ZW50LnRpbWVzdGFtcCkge1xuICAgICAgICAgICAgZW5kVGltZSA9IGV2ZW50LnRpbWVzdGFtcDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICAgIC8vIEFzIGxvbmcgYXMgdGhlcmUgYXJlIF9hbnlfIGV2ZW50cyBpbiB0aGUgbG9nIGdyb3VwIGBmaWx0ZXJMb2dFdmVudHNgIHdpbGwgcmV0dXJuIGEgbmV4dFRva2VuLlxuICAgICAgLy8gVGhpcyBpcyB0cnVlIGV2ZW4gaWYgdGhlc2UgZXZlbnRzIGFyZSBiZWZvcmUgYHN0YXJ0VGltZWAuIFNvIGlmIHdlIGhhdmUgMTAwIGV2ZW50cyBhbmQgYSBuZXh0VG9rZW5cbiAgICAgIC8vIHRoZW4gYXNzdW1lIHRoYXQgd2UgaGF2ZSBoaXQgdGhlIGxpbWl0IGFuZCBsZXQgdGhlIHVzZXIga25vdyBzb21lIG1lc3NhZ2VzIGhhdmUgYmVlbiBzdXBwcmVzc2VkLlxuICAgICAgLy8gV2UgYXJlIGVzc2VudGlhbGx5IHNob3dpbmcgdGhlbSBhIHNhbXBsaW5nICgxMDAwMCBldmVudHMgcHJpbnRlZCBvdXQgaXMgbm90IHZlcnkgdXNlZnVsKVxuICAgICAgaWYgKGZpbHRlcmVkRXZlbnRzLmxlbmd0aCA9PT0gMTAwICYmIHJlc3BvbnNlLm5leHRUb2tlbikge1xuICAgICAgICBldmVudHMucHVzaCh7XG4gICAgICAgICAgbWVzc2FnZTogJz4+PiBgd2F0Y2hgIHNob3dzIG9ubHkgdGhlIGZpcnN0IDEwMCBsb2cgbWVzc2FnZXMgLSB0aGUgcmVzdCBoYXZlIGJlZW4gdHJ1bmNhdGVkLi4uJyxcbiAgICAgICAgICBsb2dHcm91cE5hbWUsXG4gICAgICAgICAgdGltZXN0YW1wOiBuZXcgRGF0ZShlbmRUaW1lKSxcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfSBjYXRjaCAoZTogYW55KSB7XG4gICAgICAvLyB3aXRoIExhbWJkYSBmdW5jdGlvbnMgdGhlIENsb3VkV2F0Y2ggaXMgbm90IGNyZWF0ZWRcbiAgICAgIC8vIHVudGlsIHNvbWV0aGluZyBpcyBsb2dnZWQsIHNvIGp1c3Qga2VlcCBwb2xsaW5nIHVudGlsXG4gICAgICAvLyB0aGVyZSBpcyBzb210aGluZyB0byBmaW5kXG4gICAgICBpZiAoZS5uYW1lID09PSAnUmVzb3VyY2VOb3RGb3VuZEV4Y2VwdGlvbicpIHtcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgfVxuICAgICAgdGhyb3cgZTtcbiAgICB9XG4gICAgbG9nR3JvdXBzQWNjZXNzU2V0dGluZ3MubG9nR3JvdXBzU3RhcnRUaW1lc1tsb2dHcm91cE5hbWVdID0gZW5kVGltZSArIDE7XG4gICAgcmV0dXJuIGV2ZW50cztcbiAgfVxufVxuIl19