simplify log filtering, while also adding depth based filtering

This commit is contained in:
Bruno Windels 2021-02-16 15:07:17 +01:00
parent 78805f0a65
commit 4defbe1322
4 changed files with 97 additions and 117 deletions

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import {LogItem} from "./LogItem.js"; import {LogItem} from "./LogItem.js";
import {LogLevel} from "./LogFilter.js"; import {LogLevel, LogFilter} from "./LogFilter.js";
export class BaseLogger { export class BaseLogger {
constructor({platform}) { constructor({platform}) {
@ -33,13 +33,19 @@ export class BaseLogger {
this._platform.settingsStorage.setBool("anonymize", this._anonymize); this._platform.settingsStorage.setBool("anonymize", this._anonymize);
} }
run(labelOrValues, callback, logFilterDef) { run(labelOrValues, callback, logLevel = LogLevel.Info, filterCreator = null) {
const item = new LogItem(labelOrValues, logFilterDef, this._platform, this._anonymize); const item = new LogItem(labelOrValues, logLevel, null, this._platform, this._anonymize);
this._openItems.add(item); this._openItems.add(item);
const finishItem = () => { const finishItem = () => {
const serialized = item.serialize(null); let filter = new LogFilter();
console.log("serialized log item", item, serialized); if (filterCreator) {
filter = filterCreator(filter, this);
} else {
// if not filter is specified, filter out anything lower than the initial log level
filter = filter.minLevel(logLevel);
}
const serialized = item.serialize(filter, 0);
if (serialized) { if (serialized) {
this._persistItem(serialized); this._persistItem(serialized);
} }

View file

@ -24,74 +24,37 @@ export const LogLevel = {
Off: 7, Off: 7,
} }
export function wrapLogFilterSource(logFilterDef) { export class LogFilter {
if (typeof logFilterDef === "function") {
return new DeferredFilterCreator(logFilterDef);
} else if (typeof logFilterDef === "number") {
return new SimpleFilterCreator(logFilterDef);
}
return null;
}
class LogFilter {
constructor(parentFilter) { constructor(parentFilter) {
this._default = parentFilter ? parentFilter._default : null; this._parentFilter = parentFilter;
this._min = parentFilter ? parentFilter._min : null; this._min = null;
this._maxDepth = null;
}
filter(item, children, depth) {
if (this._parentFilter) {
if (!this._parentFilter.filter(item, children, depth)) {
return false;
}
}
// neither our children or us have a loglevel high enough, filter out.
if (this._min !== null && children === null && item.logLevel < this._min) {
return false;
} if (this._maxDepth !== null && depth > this._maxDepth) {
return false;
} else {
return true;
}
} }
/* methods to build the filter */ /* methods to build the filter */
min(logLevel) { minLevel(logLevel) {
this._min = logLevel; this._min = logLevel;
if (this._default === null) {
this._default = logLevel;
}
return this; return this;
} }
default(logLevel) { maxDepth(depth) {
this._default = logLevel; this._maxDepth = depth;
if (this._min === null) {
this._min = logLevel;
}
return this; return this;
} }
/* methods to use the filter */
/** determine log level for item */
itemLevel(item) {
if (item._error) {
return LogLevel.Error;
}
return this._default;
}
/** determines whether an item should be persisted */
includeItem(item, logLevel, children) {
// neither our children or us have a loglevel high enough, bail out.
return logLevel >= this._min || children;
}
}
/**
* Allows to determine the log level of an item after it has finished.
* So we can set the log level on the item duration for example.
*/
class DeferredFilterCreator {
constructor(fn) {
this._fn = fn;
}
createFilter(item, parentFilter) {
return this._fn(new LogFilter(parentFilter), item);
}
}
class SimpleFilterCreator {
constructor(logLevel) {
this._logLevel = logLevel;
}
createFilter(item, parentFilter) {
return new LogFilter(parentFilter).default(this._logLevel);
}
} }

View file

@ -14,29 +14,30 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {LogLevel, wrapLogFilterSource} from "./LogFilter.js"; import {LogLevel, LogFilter} from "./LogFilter.js";
export class LogItem { export class LogItem {
constructor(labelOrValues, logFilterDef, platform, anonymize) { constructor(labelOrValues, logLevel, filterCreator, platform, anonymize) {
this._platform = platform; this._platform = platform;
this._anonymize = anonymize; this._anonymize = anonymize;
this._start = platform.clock.now(); this._start = platform.clock.now();
this._end = null; this._end = null;
this._values = typeof labelOrValues === "string" ? {label: labelOrValues} : labelOrValues; this._values = typeof labelOrValues === "string" ? {l: labelOrValues} : labelOrValues;
this._error = null; this.error = null;
this._children = []; this.logLevel = logLevel;
this._logFilterSource = wrapLogFilterSource(logFilterDef); this._children = null;
this._filterCreator = filterCreator;
} }
/** /**
* Creates a new child item and runs it in `callback`. * Creates a new child item and runs it in `callback`.
*/ */
wrap(labelOrValues, callback, logFilterDef = null) { wrap(labelOrValues, callback, logLevel = LogLevel.Info, filterCreator = null) {
const item = this.child(labelOrValues, logFilterDef); const item = this.child(labelOrValues, logLevel, filterCreator);
return item.run(callback); return item.run(callback);
} }
duration() { get duration() {
if (this._end) { if (this._end) {
return this._end - this._start; return this._end - this._start;
} else { } else {
@ -50,8 +51,8 @@ export class LogItem {
* *
* Hence, the child item is not returned. * Hence, the child item is not returned.
*/ */
log(labelOrValues, logFilterDef = null) { log(labelOrValues, logLevel = LogLevel.Info) {
const item = this.child(labelOrValues, logFilterDef); const item = this.child(labelOrValues, logLevel, null);
item.end = item.start; item.end = item.start;
} }
@ -73,41 +74,45 @@ export class LogItem {
} }
} }
serialize(parentFilter) { serialize(filter, depth) {
const filter = this._logFilterSource ? this._logFilterSource.createFilter(this, parentFilter) : parentFilter; if (this._filterCreator) {
const logLevel = filter.itemLevel(this); filter = this._filterCreator(new LogFilter(filter), this);
console.log("logLevel for item", logLevel); }
const children = this._children.reduce((array, c) => { let children;
const s = c.serialize(filter); if (this._children !== null) {
if (s) { children = this._children.reduce((array, c) => {
array = array || []; const s = c.serialize(filter, depth + 1);
array.push(s); if (s) {
} if (array === null) {
return array; array = [];
}, null); }
array.push(s);
if (!filter.includeItem(this, logLevel, children)) { }
console.log("excluding log item", logLevel, children, this); return array;
}, null);
}
if (!filter.filter(this, children, depth)) {
return null; return null;
} }
const item = {
let error = null; // (s)tart
if (this._error) { s: this._start,
error = { // (d)uration
stack: this._error.stack, d: this.duration,
name: this._error.name // (v)alues
v: this._values,
// (l)evel
l: this.logLevel
};
if (this.error) {
// (e)rror
item.e = {
stack: this.error.stack,
name: this.error.name
}; };
} }
const item = {
s: this._start,
e: this._end,
v: this._values,
l: logLevel
};
if (error) {
item.err = error;
}
if (children) { if (children) {
// (c)hildren
item.c = children; item.c = children;
} }
return item; return item;
@ -155,8 +160,10 @@ export class LogItem {
*/ */
finish() { finish() {
if (this._end === null) { if (this._end === null) {
for(const c of this._children) { if (this._children !== null) {
c.finish(); for(const c of this._children) {
c.finish();
}
} }
this._end = this._platform.clock.now(); this._end = this._platform.clock.now();
} }
@ -168,16 +175,20 @@ export class LogItem {
} }
catch(err) { catch(err) {
this._error = err; this.error = err;
this.logLevel = LogLevel.Error;
this.finish(); this.finish();
return err; return err;
} }
child(labelOrValues, logFilterDef = null) { child(labelOrValues, logLevel, filterCreator) {
if (this._end !== null) { if (this._end !== null) {
console.trace("log item is finished, additional logs will likely not be recorded"); console.trace("log item is finished, additional logs will likely not be recorded");
} }
const item = new LogItem(labelOrValues, logFilterDef, this._platform, this._anonymize); const item = new LogItem(labelOrValues, logLevel, filterCreator, this._platform, this._anonymize);
if (this._children === null) {
this._children = [];
}
this._children.push(item); this._children.push(item);
return item; return item;
} }

View file

@ -111,13 +111,14 @@ export class Sync {
const timeout = this._status.get() === SyncStatus.Syncing ? INCREMENTAL_TIMEOUT : 0; const timeout = this._status.get() === SyncStatus.Syncing ? INCREMENTAL_TIMEOUT : 0;
const syncResult = await this._logger.run("sync", const syncResult = await this._logger.run("sync",
log => this._syncRequest(syncToken, timeout, log), log => this._syncRequest(syncToken, timeout, log),
this._logger.level.Info,
(filter, log) => { (filter, log) => {
if (log.duration >= 2000) { if (log.duration >= 2000 || this._status.get() === SyncStatus.CatchupSync) {
return filter.min(log.level.Info).default(log.level.Warn); return filter.minLevel(log.level.Info);
} if (this._status.get() === SyncStatus.CatchupSync) { } else if (log.error) {
return filter.min(log.level.Info).default(log.level.Info); return filter.minLevel(log.level.Error);
} else { } else {
return filter.min(log.level.Error); return filter.maxDepth(0);
} }
}); });
syncToken = syncResult.syncToken; syncToken = syncResult.syncToken;
@ -192,7 +193,6 @@ export class Sync {
const isInitialSync = !syncToken; const isInitialSync = !syncToken;
syncToken = response.next_batch; syncToken = response.next_batch;
log.set("isInitialSync", isInitialSync);
log.set("syncToken", log.anonymize(syncToken)); log.set("syncToken", log.anonymize(syncToken));
log.set("status", this._status.get()); log.set("status", this._status.get());