mono/packages/core/dist/observableInternal/logging.js
2025-01-28 13:42:22 +01:00

336 lines
10 KiB
JavaScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
let globalObservableLogger;
export function setLogger(logger) {
globalObservableLogger = logger;
}
export function getLogger() {
return globalObservableLogger;
}
export function logObservable(obs) {
if (!globalObservableLogger) {
const l = new ConsoleObservableLogger();
l.addFilteredObj(obs);
setLogger(l);
}
else {
if (globalObservableLogger instanceof ConsoleObservableLogger) {
globalObservableLogger.addFilteredObj(obs);
}
}
}
export class ConsoleObservableLogger {
indentation = 0;
_filteredObjects;
addFilteredObj(obj) {
if (!this._filteredObjects) {
this._filteredObjects = new Set();
}
this._filteredObjects.add(obj);
}
_isIncluded(obj) {
return this._filteredObjects?.has(obj) ?? true;
}
textToConsoleArgs(text) {
return consoleTextToArgs([
normalText(repeat('| ', this.indentation)),
text,
]);
}
formatInfo(info) {
if (!info.hadValue) {
return [
normalText(` `),
styled(formatValue(info.newValue, 60), {
color: 'green',
}),
normalText(` (initial)`),
];
}
return info.didChange
? [
normalText(` `),
styled(formatValue(info.oldValue, 70), {
color: 'red',
strikeThrough: true,
}),
normalText(` `),
styled(formatValue(info.newValue, 60), {
color: 'green',
}),
]
: [normalText(` (unchanged)`)];
}
handleObservableChanged(observable, info) {
if (!this._isIncluded(observable)) {
return;
}
console.log(...this.textToConsoleArgs([
formatKind('observable value changed'),
styled(observable.debugName, { color: 'BlueViolet' }),
...this.formatInfo(info),
]));
}
changedObservablesSets = new WeakMap();
formatChanges(changes) {
if (changes.size === 0) {
return undefined;
}
return styled(' (changed deps: ' +
[...changes].map((o) => o.debugName).join(', ') +
')', { color: 'gray' });
}
handleDerivedCreated(derived) {
const existingHandleChange = derived.handleChange;
this.changedObservablesSets.set(derived, new Set());
derived.handleChange = (observable, change) => {
this.changedObservablesSets.get(derived).add(observable);
return existingHandleChange.apply(derived, [observable, change]);
};
const debugTrackUpdating = false;
if (debugTrackUpdating) {
const updating = [];
derived.__debugUpdating = updating;
const existingBeginUpdate = derived.beginUpdate;
derived.beginUpdate = (obs) => {
updating.push(obs);
return existingBeginUpdate.apply(derived, [obs]);
};
const existingEndUpdate = derived.endUpdate;
derived.endUpdate = (obs) => {
const idx = updating.indexOf(obs);
if (idx === -1) {
console.error('endUpdate called without beginUpdate', derived.debugName, obs.debugName);
}
updating.splice(idx, 1);
return existingEndUpdate.apply(derived, [obs]);
};
}
}
handleDerivedRecomputed(derived, info) {
if (!this._isIncluded(derived)) {
return;
}
const changedObservables = this.changedObservablesSets.get(derived);
if (!changedObservables) {
return;
}
console.log(...this.textToConsoleArgs([
formatKind('derived recomputed'),
styled(derived.debugName, { color: 'BlueViolet' }),
...this.formatInfo(info),
this.formatChanges(changedObservables),
{ data: [{ fn: derived._debugNameData.referenceFn ?? derived._computeFn }] }
]));
changedObservables.clear();
}
handleDerivedCleared(derived) {
if (!this._isIncluded(derived)) {
return;
}
console.log(...this.textToConsoleArgs([
formatKind('derived cleared'),
styled(derived.debugName, { color: 'BlueViolet' }),
]));
}
handleFromEventObservableTriggered(observable, info) {
if (!this._isIncluded(observable)) {
return;
}
console.log(...this.textToConsoleArgs([
formatKind('observable from event triggered'),
styled(observable.debugName, { color: 'BlueViolet' }),
...this.formatInfo(info),
{ data: [{ fn: observable._getValue }] }
]));
}
handleAutorunCreated(autorun) {
if (!this._isIncluded(autorun)) {
return;
}
const existingHandleChange = autorun.handleChange;
this.changedObservablesSets.set(autorun, new Set());
autorun.handleChange = (observable, change) => {
this.changedObservablesSets.get(autorun).add(observable);
return existingHandleChange.apply(autorun, [observable, change]);
};
}
handleAutorunTriggered(autorun) {
const changedObservables = this.changedObservablesSets.get(autorun);
if (!changedObservables) {
return;
}
if (this._isIncluded(autorun)) {
console.log(...this.textToConsoleArgs([
formatKind('autorun'),
styled(autorun.debugName, { color: 'BlueViolet' }),
this.formatChanges(changedObservables),
{ data: [{ fn: autorun._debugNameData.referenceFn ?? autorun._runFn }] }
]));
}
changedObservables.clear();
this.indentation++;
}
handleAutorunFinished(autorun) {
this.indentation--;
}
handleBeginTransaction(transaction) {
let transactionName = transaction.getDebugName();
if (transactionName === undefined) {
transactionName = '';
}
if (this._isIncluded(transaction)) {
console.log(...this.textToConsoleArgs([
formatKind('transaction'),
styled(transactionName, { color: 'BlueViolet' }),
{ data: [{ fn: transaction._fn }] }
]));
}
this.indentation++;
}
handleEndTransaction() {
this.indentation--;
}
}
function consoleTextToArgs(text) {
const styles = new Array();
const data = [];
let firstArg = '';
function process(t) {
if ('length' in t) {
for (const item of t) {
if (item) {
process(item);
}
}
}
else if ('text' in t) {
firstArg += `%c${t.text}`;
styles.push(t.style);
if (t.data) {
data.push(...t.data);
}
}
else if ('data' in t) {
data.push(...t.data);
}
}
process(text);
const result = [firstArg, ...styles];
result.push(...data);
return result;
}
function normalText(text) {
return styled(text, { color: 'black' });
}
function formatKind(kind) {
return styled(padStr(`${kind}: `, 10), { color: 'black', bold: true });
}
function styled(text, options = {
color: 'black',
}) {
function objToCss(styleObj) {
return Object.entries(styleObj).reduce((styleString, [propName, propValue]) => {
return `${styleString}${propName}:${propValue};`;
}, '');
}
const style = {
color: options.color,
};
if (options.strikeThrough) {
style['text-decoration'] = 'line-through';
}
if (options.bold) {
style['font-weight'] = 'bold';
}
return {
text,
style: objToCss(style),
};
}
function formatValue(value, availableLen) {
switch (typeof value) {
case 'number':
return '' + value;
case 'string':
if (value.length + 2 <= availableLen) {
return `"${value}"`;
}
return `"${value.substr(0, availableLen - 7)}"+...`;
case 'boolean':
return value ? 'true' : 'false';
case 'undefined':
return 'undefined';
case 'object':
if (value === null) {
return 'null';
}
if (Array.isArray(value)) {
return formatArray(value, availableLen);
}
return formatObject(value, availableLen);
case 'symbol':
return value.toString();
case 'function':
return `[[Function${value.name ? ' ' + value.name : ''}]]`;
default:
return '' + value;
}
}
function formatArray(value, availableLen) {
let result = '[ ';
let first = true;
for (const val of value) {
if (!first) {
result += ', ';
}
if (result.length - 5 > availableLen) {
result += '...';
break;
}
first = false;
result += `${formatValue(val, availableLen - result.length)}`;
}
result += ' ]';
return result;
}
function formatObject(value, availableLen) {
if (typeof value.toString === 'function' && value.toString !== Object.prototype.toString) {
const val = value.toString();
if (val.length <= availableLen) {
return val;
}
return val.substring(0, availableLen - 3) + '...';
}
let result = '{ ';
let first = true;
for (const [key, val] of Object.entries(value)) {
if (!first) {
result += ', ';
}
if (result.length - 5 > availableLen) {
result += '...';
break;
}
first = false;
result += `${key}: ${formatValue(val, availableLen - result.length)}`;
}
result += ' }';
return result;
}
function repeat(str, count) {
let result = '';
for (let i = 1; i <= count; i++) {
result += str;
}
return result;
}
function padStr(str, length) {
while (str.length < length) {
str += ' ';
}
return str;
}
//# sourceMappingURL=logging.js.map