[ftr] log every method call in services with --verbose (#28637)

* [ftr] log every method call in services with --verbose

* print args, wrap async wrappers
This commit is contained in:
Spencer 2019-01-14 10:11:33 -08:00 committed by GitHub
parent 579a738e98
commit a82def12e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 110 additions and 13 deletions

View file

@ -17,18 +17,23 @@
* under the License.
*/
const createdInstanceProxies = new WeakSet();
const INITIALIZING = Symbol('async instance initializing');
const asyncInitFns = new WeakSet();
export const isAsyncInstance = val =>(
createdInstanceProxies.has(val)
export const isAsyncInstance = val => (
val && asyncInitFns.has(val.init)
);
export const createAsyncInstance = (type, name, promiseForValue) => {
let instance = INITIALIZING;
const initPromise = promiseForValue.then(v => instance = v);
const initFn = () => initPromise;
const loadingTarget = {
init() {
return initPromise;
}
};
asyncInitFns.add(loadingTarget.init);
const assertReady = desc => {
if (instance === INITIALIZING) {
@ -46,7 +51,7 @@ export const createAsyncInstance = (type, name, promiseForValue) => {
}
};
const proxy = new Proxy({}, {
return new Proxy(loadingTarget, {
apply(target, context, args) {
assertReady(`${name}()`);
return Reflect.apply(instance, context, args);
@ -68,13 +73,19 @@ export const createAsyncInstance = (type, name, promiseForValue) => {
},
get(target, prop, receiver) {
if (prop === 'init') return initFn;
if (loadingTarget.hasOwnProperty(prop)) {
return Reflect.get(loadingTarget, prop, receiver);
}
assertReady(`${name}.${prop}`);
return Reflect.get(instance, prop, receiver);
},
getOwnPropertyDescriptor(target, prop) {
if (loadingTarget.hasOwnProperty(prop)) {
return Reflect.getOwnPropertyDescriptor(loadingTarget, prop);
}
assertReady(`${name}.${prop}`);
return Reflect.getOwnPropertyDescriptor(instance, prop);
},
@ -85,7 +96,9 @@ export const createAsyncInstance = (type, name, promiseForValue) => {
},
has(target, prop) {
if (prop === 'init') return true;
if (!loadingTarget.hasOwnProperty(prop)) {
return Reflect.has(loadingTarget, prop);
}
assertReady(`${name}.${prop}`);
return Reflect.has(instance, prop);
@ -116,10 +129,4 @@ export const createAsyncInstance = (type, name, promiseForValue) => {
return Reflect.setPrototypeOf(instance, prototype);
}
});
// add the created provider to the WeakMap so we can
// check for it later in `isAsyncProvider()`
createdInstanceProxies.add(proxy);
return proxy;
};

View file

@ -19,6 +19,7 @@
import { loadTracer } from '../load_tracer';
import { createAsyncInstance, isAsyncInstance } from './async_instance';
import { createVerboseInstance } from './verbose_instance';
export class ProviderCollection {
constructor(log, providers) {
@ -104,6 +105,14 @@ export class ProviderCollection {
instance = createAsyncInstance(type, name, instance);
}
if (name !== '__leadfoot__' && name !== 'log' && name !== 'config' && instance && typeof instance === 'object') {
instance = createVerboseInstance(
this._log,
type === 'PageObject' ? `PageObjects.${name}` : name,
instance
);
}
instances.set(provider, instance);
}

View file

@ -0,0 +1,81 @@
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { inspect } from 'util';
function printArgs(args) {
return args.map((arg) => {
if (typeof arg === 'string' || typeof arg === 'number' || arg instanceof Date) {
return inspect(arg);
}
if (Array.isArray(arg)) {
return `[${printArgs(arg)}]`;
}
return Object.prototype.toString.call(arg);
}).join(', ');
}
export function createVerboseInstance(log, name, instance) {
if (!log.getWriters().some(l => l.level.flags.verbose)) {
return instance;
}
return new Proxy(instance, {
get(_, prop) {
const value = instance[prop];
if (typeof value !== 'function' || prop === 'init') {
return value;
}
return function (...args) {
log.verbose(`${name}.${prop}(${printArgs(args)})`);
log.indent(2);
let result;
try {
result = {
returned: value.apply(this, args)
};
} catch (error) {
result = {
thrown: error
};
}
if (result.hasOwnProperty('thrown')) {
log.indent(-2);
throw result.thrown;
}
const { returned } = result;
if (returned && typeof returned.then === 'function') {
return returned.finally(() => {
log.indent(-2);
});
}
log.indent(-2);
return returned;
};
},
});
}