aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
161 lines • 20.7 kB
JavaScript
;
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() {
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));
}
}
return Promise.all(promises);
}
/**
* Print out a cloudwatch event
*/
print(event) {
(0, logging_1.print)(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,
}).promise();
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 supressed.
// 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.code === 'ResourceNotFoundException') {
return [];
}
throw e;
}
logGroupsAccessSettings.logGroupsStartTimes[logGroupName] = endTime + 1;
return events;
}
}
exports.CloudWatchLogEventMonitor = CloudWatchLogEventMonitor;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibG9ncy1tb25pdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsibG9ncy1tb25pdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLDZCQUE2QjtBQUU3QiwrQkFBK0I7QUFDL0IsMkNBQTZDO0FBQzdDLDhDQUE0QztBQUc1Qzs7Ozs7O0dBTUc7QUFDSCxNQUFNLEtBQUssR0FBRyxJQUFLLENBQUM7QUEwQ3BCLE1BQWEseUJBQXlCO0lBYXBDLFlBQVksU0FBZ0I7UUFQNUI7O1dBRUc7UUFDYyxnQ0FBMkIsR0FBRyxJQUFJLEdBQUcsRUFBbUMsQ0FBQztRQUVsRixXQUFNLEdBQUcsS0FBSyxDQUFDO1FBR3JCLElBQUksQ0FBQyxTQUFTLEdBQUcsU0FBUyxFQUFFLE9BQU8sRUFBRSxJQUFJLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztJQUN0RCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxRQUFRO1FBQ2IsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7UUFDbkIsSUFBSSxDQUFDLGdCQUFnQixDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzNCLENBQUM7SUFFRDs7Ozs7Ozs7T0FRRztJQUNJLFVBQVU7UUFDZixJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztRQUNwQixJQUFJLENBQUMsU0FBUyxHQUFHLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUM1QixJQUFJLENBQUMsMkJBQTJCLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDM0MsQ0FBQztJQUVEOzs7Ozs7T0FNRztJQUNJLFlBQVksQ0FBQyxHQUFzQixFQUFFLEdBQVMsRUFBRSxhQUF1QjtRQUM1RSxNQUFNLE1BQU0sR0FBRyxHQUFHLEdBQUcsQ0FBQyxPQUFPLElBQUksR0FBRyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQzlDLE1BQU0sbUJBQW1CLEdBQUcsYUFBYSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsRUFBRSxTQUFTLEVBQUUsRUFBRTtZQUNsRSxHQUFHLENBQUMs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QUFPLE1BQU0sQ0FBQztJQUNoQixDQUFDO0NBQ0Y7QUF2S0QsOERBdUtDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0ICogYXMgdXRpbCBmcm9tICd1dGlsJztcbmltcG9ydCAqIGFzIGN4YXBpIGZyb20gJ0Bhd3MtY2RrL2N4LWFwaSc7XG5pbXBvcnQgKiBhcyBjaGFsayBmcm9tICdjaGFsayc7XG5pbXBvcnQgeyBwcmludCwgZXJyb3IgfSBmcm9tICcuLi8uLi9sb2dnaW5nJztcbmltcG9ydCB7IGZsYXR0ZW4gfSBmcm9tICcuLi8uLi91dGlsL2FycmF5cyc7XG5pbXBvcnQgeyBJU0RLIH0gZnJvbSAnLi4vYXdzLWF1dGgnO1xuXG4vKipcbiAqIEFmdGVyIHJlYWRpbmcgZXZlbnRzIGZyb20gYWxsIENsb3VkV2F0Y2ggbG9nIGdyb3Vwc1xuICogaG93IGxvbmcgc2hvdWxkIHdlIHdhaXQgdG8gcmVhZCBtb3JlIGV2ZW50cy5cbiAqXG4gKiBJZiB0aGVyZSBpcyBzb21lIGVycm9yIHdpdGggcmVhZGluZyBldmVudHMgKGkuZS4gVGhyb3R0bGUpXG4gKiB0aGVuIHRoaXMgaXMgYWxzbyBob3cgbG9uZyB3ZSB3YWl0IHVudGlsIHdlIHRyeSBhZ2FpblxuICovXG5jb25zdCBTTEVFUCA9IDJfMDAwO1xuXG4vKipcbiAqIFJlcHJlc2VudHMgYSBDbG91ZFdhdGNoIExvZyBFdmVudCB0aGF0IHdpbGwgYmVcbiAqIHByaW50ZWQgdG8gdGhlIHRlcm1pbmFsXG4gKi9cbmludGVyZmFjZSBDbG91ZFdhdGNoTG9nRXZlbnQge1xuICAvKipcbiAgICogVGhlIGxvZyBldmVudCBtZXNzYWdlXG4gICAqL1xuICByZWFkb25seSBtZXNzYWdlOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBuYW1lIG9mIHRoZSBsb2cgZ3JvdXBcbiAgICovXG4gIHJlYWRvbmx5IGxvZ0dyb3VwTmFtZTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgdGltZSBhdCB3aGljaCB0aGUgZXZlbnQgb2NjdXJyZWRcbiAgICovXG4gIHJlYWRvbmx5IHRpbWVzdGFtcDogRGF0ZTtcbn1cblxuLyoqXG4gKiBDb25maWd1cmF0aW9uIHRyYWNraW5nIGluZm9ybWF0aW9uIG9uIHRoZSBsb2cgZ3JvdXBzIHRoYXQgYXJlXG4gKiBiZWluZyBtb25pdG9yZWRcbiAqL1xuaW50ZXJmYWNlIExvZ0dyb3Vwc0FjY2Vzc1NldHRpbmdzIHtcbiAgLyoqXG4gICAqIFRoZSBTREsgZm9yIGEgZ2l2ZW4gZW52aXJvbm1lbnQgKGFjY291bnQvcmVnaW9uKVxuICAgKi9cbiAgcmVhZG9ubHkgc2RrOiBJU0RLO1xuXG4gIC8qKlxuICAgKiBBIG1hcCBvZiBsb2cgZ3JvdXBzIGFuZCBhc3NvY2lhdGVkIHN0YXJ0VGltZSBpbiBhIGdpdmVuIGFjY291bnQuXG4gICAqXG4gICAqIFRoZSBtb25pdG9yIHdpbGwgcmVhZCBldmVudHMgZnJvbSB0aGUgbG9nIGdyb3VwIHN0YXJ0aW5nIGF0IHRoZVxuICAgKiBhc3NvY2lhdGVkIHN0YXJ0VGltZVxuICAgKi9cbiAgcmVhZG9ubHkgbG9nR3JvdXBzU3RhcnRUaW1lczogeyBbbG9nR3JvdXBOYW1lOiBzdHJpbmddOiBudW1iZXIgfTtcbn1cblxuZXhwb3J0IGNsYXNzIENsb3VkV2F0Y2hMb2dFdmVudE1vbml0b3Ige1xuICAvKipcbiAgICogRGV0ZXJtaW5lcyB3aGljaCBldmVudHMgbm90IHRvIGRpc3BsYXlcbiAgICovXG4gIHByaXZhdGUgc3RhcnRUaW1lOiBudW1iZXI7XG5cbiAgLyoqXG4gICAqIE1hcCBvZiBlbnZpcm9ubWVudCAoYWNjb3VudDpyZWdpb24pIHRvIExvZ0dyb3Vwc0FjY2Vzc1NldHRpbmdzXG4gICAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGVudnNMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncyA9IG5ldyBNYXA8c3RyaW5nLCBMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncz4oKTtcblxuICBwcml2YXRlIGFjdGl2ZSA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKHN0YXJ0VGltZT86IERhdGUpIHtcbiAgICB0aGlzLnN0YXJ0VGltZSA9IHN0YXJ0VGltZT8uZ2V0VGltZSgpID8/IERhdGUubm93KCk7XG4gIH1cblxuICAvKipcbiAgICogcmVzdW1lIHJlYWRpbmcvcHJpbnRpbmcgZXZlbnRzXG4gICAqL1xuICBwdWJsaWMgYWN0aXZhdGUoKTogdm9pZCB7XG4gICAgdGhpcy5hY3RpdmUgPSB0cnVlO1xuICAgIHRoaXMuc2NoZWR1bGVOZXh0VGljaygwKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBkZWFjdGl2YXRlcyB0aGUgbW9uaXRvciBzbyBubyBuZXcgZXZlbnRzIGFyZSByZWFkXG4gICAqIHVzZSBjYXNlIGZvciB0aGlzIGlzIHdoZW4gd2UgYXJlIGluIHRoZSBtaWRkbGUgb2YgcGVyZm9ybWluZyBhIGRlcGxveW1lbnRcbiAgICogYW5kIGRvbid0IHdhbnQgdG8gaW50ZXJ3ZWF2ZSBhbGwgdGhlIGxvZ3MgdG9nZXRoZXIgd2l0aCB0aGUgQ0ZOXG4gICAqIGRlcGxveW1lbnQgbG9nc1xuICAgKlxuICAgKiBBbHNvIHJlc2V0cyB0aGUgc3RhcnQgdGltZSB0byBiZSB3aGVuIHRoZSBuZXcgZGVwbG95bWVudCB3YXMgdHJpZ2dlcmVkXG4gICAqIGFuZCBjbGVhcnMgdGhlIGxpc3Qgb2YgdHJhY2tlZCBsb2cgZ3JvdXBzXG4gICAqL1xuICBwdWJsaWMgZGVhY3RpdmF0ZSgpOiB2b2lkIHtcbiAgICB0aGlzLmFjdGl2ZSA9IGZhbHNlO1xuICAgIHRoaXMuc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICB0aGlzLmVudnNMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncy5jbGVhcigpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgQ2xvdWRXYXRjaCBsb2cgZ3JvdXBzIHRvIHJlYWQgbG9nIGV2ZW50cyBmcm9tLlxuICAgKiBTaW5jZSB3ZSBjb3VsZCBiZSB3YXRjaGluZyBtdWx0aXBsZSBzdGFja3MgdGhhdCBkZXBsb3kgdG9cbiAgICogbXVsdGlwbGUgZW52aXJvbm1lbnRzIChhY2NvdW50K3JlZ2lvbiksIHdlIG5lZWQgdG8gc3RvcmUgYSBsaXN0IG9mIGxvZyBncm91cHNcbiAgICogcGVyIGVudiBhbG9uZyB3aXRoIHRoZSBTREsgb2JqZWN0IHRoYXQgaGFzIGFjY2VzcyB0byByZWFkIGZyb21cbiAgICogdGhhdCBlbnZpcm9ubWVudC5cbiAgICovXG4gIHB1YmxpYyBhZGRMb2dHcm91cHMoZW52OiBjeGFwaS5FbnZpcm9ubWVudCwgc2RrOiBJU0RLLCBsb2dHcm91cE5hbWVzOiBzdHJpbmdbXSk6IHZvaWQge1xuICAgIGNvbnN0IGF3c0VudiA9IGAke2Vudi5hY2NvdW50fToke2Vudi5yZWdpb259YDtcbiAgICBjb25zdCBsb2dHcm91cHNTdGFydFRpbWVzID0gbG9nR3JvdXBOYW1lcy5yZWR1Y2UoKGFjYywgZ3JvdXBOYW1lKSA9PiB7XG4gICAgICBhY2NbZ3JvdXBOYW1lXSA9IHRoaXMuc3RhcnRUaW1lO1xuICAgICAgcmV0dXJuIGFjYztcbiAgICB9LCB7fSBhcyB7IFtsb2dHcm91cE5hbWU6IHN0cmluZ106IG51bWJlciB9KTtcbiAgICB0aGlzLmVudnNMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncy5zZXQoYXdzRW52LCB7XG4gICAgICBzZGssXG4gICAgICBsb2dHcm91cHNTdGFydFRpbWVzOiB7XG4gICAgICAgIC4uLnRoaXMuZW52c0xvZ0dyb3Vwc0FjY2Vzc1NldHRpbmdzLmdldChhd3NFbnYpPy5sb2dHcm91cHNTdGFydFRpbWVzLFxuICAgICAgICAuLi5sb2dHcm91cHNTdGFydFRpbWVzLFxuICAgICAgfSxcbiAgICB9KTtcbiAgfVxuXG4gIHByaXZhdGUgc2NoZWR1bGVOZXh0VGljayhzbGVlcDogbnVtYmVyKTogdm9pZCB7XG4gICAgc2V0VGltZW91dCgoKSA9PiB2b2lkKHRoaXMudGljaygpKSwgc2xlZXApO1xuICB9XG5cbiAgcHJpdmF0ZSBhc3luYyB0aWNrKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICghdGhpcy5hY3RpdmUpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGV2ZW50cyA9IGZsYXR0ZW4oYXdhaXQgdGhpcy5yZWFkTmV3RXZlbnRzKCkpO1xuICAgICAgZXZlbnRzLmZvckVhY2goZXZlbnQgPT4ge1xuICAgICAgICB0aGlzLnByaW50KGV2ZW50KTtcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIGVycm9yKCdFcnJvciBvY2N1cnJlZCB3aGlsZSBtb25pdG9yaW5nIGxvZ3M6ICVzJywgZSk7XG4gICAgfVxuXG4gICAgdGhpcy5zY2hlZHVsZU5leHRUaWNrKFNMRUVQKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWFkcyBhbGwgbmV3IGxvZyBldmVudHMgZnJvbSBhIHNldCBvZiBDbG91ZFdhdGNoIExvZyBHcm91cHNcbiAgICogaW4gcGFyYWxsZWxcbiAgICovXG4gIHByaXZhdGUgYXN5bmMgcmVhZE5ld0V2ZW50cygpOiBQcm9taXNlPEFycmF5PEFycmF5PENsb3VkV2F0Y2hMb2dFdmVudD4+PiB7XG4gICAgY29uc3QgcHJvbWlzZXM6IEFycmF5PFByb21pc2U8QXJyYXk8Q2xvdWRXYXRjaExvZ0V2ZW50Pj4+ID0gW107XG4gICAgZm9yIChjb25zdCBzZXR0aW5ncyBvZiB0aGlzLmVudnNMb2dHcm91cHNBY2Nlc3NTZXR0aW5ncy52YWx1ZXMoKSkge1xuICAgICAgZm9yIChjb25zdCBncm91cCBvZiBPYmplY3Qua2V5cyhzZXR0aW5ncy5sb2dHcm91cHNTdGFydFRpbWVzKSkge1xuICAgICAgICBwcm9taXNlcy5wdXNoKHRoaXMucmVhZEV2ZW50c0Zyb21Mb2dHcm91cChzZXR0aW5ncywgZ3JvdXApKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIFByb21pc2UuYWxsKHByb21pc2VzKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcmludCBvdXQgYSBjbG91ZHdhdGNoIGV2ZW50XG4gICAqL1xuICBwcml2YXRlIHByaW50KGV2ZW50OiBDbG91ZFdhdGNoTG9nRXZlbnQpOiB2b2lkIHtcbiAgICBwcmludCh1dGlsLmZvcm1hdCgnWyVzXSAlcyAlcycsXG4gICAgICBjaGFsay5ibHVlKGV2ZW50LmxvZ0dyb3VwTmFtZSksXG4gICAgICBjaGFsay55ZWxsb3coZXZlbnQudGltZXN0YW1wLnRvTG9jYWxlVGltZVN0cmluZygpKSxcbiAgICAgIGV2ZW50Lm1lc3NhZ2UudHJpbSgpKSk7XG4gIH1cblxuICAvKipcbiAgICogUmVhZHMgYWxsIG5ldyBsb2cgZXZlbnRzIGZyb20gYSBDbG91ZFdhdGNoIExvZyBHcm91cFxuICAgKiBzdGFydGluZyBhdCBlaXRoZXIgdGhlIHRpbWUgdGhlIGhvdHN3YXAgd2FzIHRyaWdnZXJlZCBvclxuICAgKiB3aGVuIHRoZSBsYXN0IGV2ZW50IHdhcyByZWFkIG9uIHRoZSBwcmV2aW91cyB0aWNrXG4gICAqL1xuICBwcml2YXRlIGFzeW5jIHJlYWRFdmVudHNGcm9tTG9nR3JvdXAoXG4gICAgbG9nR3JvdXBzQWNjZXNzU2V0dGluZ3M6IExvZ0dyb3Vwc0FjY2Vzc1NldHRpbmdzLFxuICAgIGxvZ0dyb3VwTmFtZTogc3RyaW5nLFxuICApOiBQcm9taXNlPEFycmF5PENsb3VkV2F0Y2hMb2dFdmVudD4+IHtcbiAgICBjb25zdCBldmVudHM6IENsb3VkV2F0Y2hMb2dFdmVudFtdID0gW107XG5cbiAgICAvLyBsb2cgZXZlbnRzIGZyb20gc29tZSBzZXJ2aWNlIGFyZSBpbmdlc3RlZCBmYXN0ZXIgdGhhbiBvdGhlcnNcbiAgICAvLyBzbyB3ZSBuZWVkIHRvIHRyYWNrIHRoZSBzdGFydC9lbmQgdGltZSBmb3IgZWFjaCBsb2cgZ3JvdXAgaW5kaXZpZHVhbGx5XG4gICAgLy8gdG8gbWFrZSBzdXJlIHRoYXQgd2UgcHJvY2VzcyBhbGwgZXZlbnRzIGZyb20gZWFjaCBsb2cgZ3JvdXBcbiAgICBjb25zdCBzdGFydFRpbWUgPSBsb2dHcm91cHNBY2Nlc3NTZXR0aW5ncy5sb2dHcm91cHNTdGFydFRpbWVzW2xvZ0dyb3VwTmFtZV0gPz8gdGhpcy5zdGFydFRpbWU7XG4gICAgbGV0IGVuZFRpbWUgPSBzdGFydFRpbWU7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgbG9nR3JvdXBzQWNjZXNzU2V0dGluZ3Muc2RrLmNsb3VkV2F0Y2hMb2dzKCkuZmlsdGVyTG9nRXZlbnRzKHtcbiAgICAgICAgbG9nR3JvdXBOYW1lOiBsb2dHcm91cE5hbWUsXG4gICAgICAgIGxpbWl0OiAxMDAsXG4gICAgICAgIHN0YXJ0VGltZTogc3RhcnRUaW1lLFxuICAgICAgfSkucHJvbWlzZSgpO1xuICAgICAgY29uc3QgZmlsdGVyZWRFdmVudHMgPSByZXNwb25zZS5ldmVudHMgPz8gW107XG5cbiAgICAgIGZvciAoY29uc3QgZXZlbnQgb2YgZmlsdGVyZWRFdmVudHMpIHtcbiAgICAgICAgaWYgKGV2ZW50Lm1lc3NhZ2UpIHtcbiAgICAgICAgICBldmVudHMucHVzaCh7XG4gICAgICAgICAgICBtZXNzYWdlOiBldmVudC5tZXNzYWdlLFxuICAgICAgICAgICAgbG9nR3JvdXBOYW1lLFxuICAgICAgICAgICAgdGltZXN0YW1wOiBldmVudC50aW1lc3RhbXAgPyBuZXcgRGF0ZShldmVudC50aW1lc3RhbXApIDogbmV3IERhdGUoKSxcbiAgICAgICAgICB9KTtcblxuICAgICAgICAgIGlmIChldmVudC50aW1lc3RhbXAgJiYgZW5kVGltZSA8IGV2ZW50LnRpbWVzdGFtcCkge1xuICAgICAgICAgICAgZW5kVGltZSA9IGV2ZW50LnRpbWVzdGFtcDtcbiAgICAgICAgICB9XG5cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgLy8gQXMgbG9uZyBhcyB0aGVyZSBhcmUgX2FueV8gZXZlbnRzIGluIHRoZSBsb2cgZ3JvdXAgYGZpbHRlckxvZ0V2ZW50c2Agd2lsbCByZXR1cm4gYSBuZXh0VG9rZW4uXG4gICAgICAvLyBUaGlzIGlzIHRydWUgZXZlbiBpZiB0aGVzZSBldmVudHMgYXJlIGJlZm9yZSBgc3RhcnRUaW1lYC4gU28gaWYgd2UgaGF2ZSAxMDAgZXZlbnRzIGFuZCBhIG5leHRUb2tlblxuICAgICAgLy8gdGhlbiBhc3N1bWUgdGhhdCB3ZSBoYXZlIGhpdCB0aGUgbGltaXQgYW5kIGxldCB0aGUgdXNlciBrbm93IHNvbWUgbWVzc2FnZXMgaGF2ZSBiZWVuIHN1cHJlc3NlZC5cbiAgICAgIC8vIFdlIGFyZSBlc3NlbnRpYWxseSBzaG93aW5nIHRoZW0gYSBzYW1wbGluZyAoMTAwMDAgZXZlbnRzIHByaW50ZWQgb3V0IGlzIG5vdCB2ZXJ5IHVzZWZ1bClcbiAgICAgIGlmIChmaWx0ZXJlZEV2ZW50cy5sZW5ndGggPT09IDEwMCAmJiByZXNwb25zZS5uZXh0VG9rZW4pIHtcbiAgICAgICAgZXZlbnRzLnB1c2goe1xuICAgICAgICAgIG1lc3NhZ2U6ICc+Pj4gYHdhdGNoYCBzaG93cyBvbmx5IHRoZSBmaXJzdCAxMDAgbG9nIG1lc3NhZ2VzIC0gdGhlIHJlc3QgaGF2ZSBiZWVuIHRydW5jYXRlZC4uLicsXG4gICAgICAgICAgbG9nR3JvdXBOYW1lLFxuICAgICAgICAgIHRpbWVzdGFtcDogbmV3IERhdGUoZW5kVGltZSksXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0gY2F0Y2ggKGU6IGFueSkge1xuICAgICAgLy8gd2l0aCBMYW1iZGEgZnVuY3Rpb25zIHRoZSBDbG91ZFdhdGNoIGlzIG5vdCBjcmVhdGVkXG4gICAgICAvLyB1bnRpbCBzb21ldGhpbmcgaXMgbG9nZ2VkLCBzbyBqdXN0IGtlZXAgcG9sbGluZyB1bnRpbFxuICAgICAgLy8gdGhlcmUgaXMgc29tdGhpbmcgdG8gZmluZFxuICAgICAgaWYgKGUuY29kZSA9PT0gJ1Jlc291cmNlTm90Rm91bmRFeGNlcHRpb24nKSB7XG4gICAgICAgIHJldHVybiBbXTtcbiAgICAgIH1cbiAgICAgIHRocm93IGU7XG4gICAgfVxuICAgIGxvZ0dyb3Vwc0FjY2Vzc1NldHRpbmdzLmxvZ0dyb3Vwc1N0YXJ0VGltZXNbbG9nR3JvdXBOYW1lXSA9IGVuZFRpbWUgKyAxO1xuICAgIHJldHVybiBldmVudHM7XG4gIH1cbn1cbiJdfQ==