Merge pull request #583 from vector-im/ts-conversion-logging

Convert src/logging to typescript
This commit is contained in:
Bruno Windels 2021-11-17 14:37:42 +01:00 committed by GitHub
commit efccc1e19e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 464 additions and 316 deletions

View file

@ -184,7 +184,7 @@ import {Clock as MockClock} from "../../../../mocks/Clock.js";
import {createMockStorage} from "../../../../mocks/Storage";
import {ListObserver} from "../../../../mocks/ListObserver.js";
import {createEvent, withTextBody, withContent} from "../../../../mocks/event.js";
import {NullLogItem, NullLogger} from "../../../../logging/NullLogger.js";
import {NullLogItem, NullLogger} from "../../../../logging/NullLogger";
import {HomeServer as MockHomeServer} from "../../../../mocks/HomeServer.js";
// other imports
import {BaseMessageTile} from "./tiles/BaseMessageTile.js";

View file

@ -15,23 +15,27 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {LogItem} from "./LogItem.js";
import {LogLevel, LogFilter} from "./LogFilter.js";
import {LogItem} from "./LogItem";
import {LogLevel, LogFilter} from "./LogFilter";
import type {ILogger, ILogExport, FilterCreator, LabelOrValues, LogCallback, ILogItem} from "./types";
import type {Platform} from "../platform/web/Platform.js";
export abstract class BaseLogger implements ILogger {
protected _openItems: Set<LogItem> = new Set();
protected _platform: Platform;
export class BaseLogger {
constructor({platform}) {
this._openItems = new Set();
this._platform = platform;
}
log(labelOrValues, logLevel = LogLevel.Info) {
const item = new LogItem(labelOrValues, logLevel, null, this);
item._end = item._start;
this._persistItem(item, null, false);
log(labelOrValues: LabelOrValues, logLevel: LogLevel = LogLevel.Info): void {
const item = new LogItem(labelOrValues, logLevel, this);
item.end = item.start;
this._persistItem(item, undefined, false);
}
/** if item is a log item, wrap the callback in a child of it, otherwise start a new root log item. */
wrapOrRun(item, labelOrValues, callback, logLevel = null, filterCreator = null) {
wrapOrRun<T>(item: ILogItem | undefined, labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T {
if (item) {
return item.wrap(labelOrValues, callback, logLevel, filterCreator);
} else {
@ -43,28 +47,31 @@ export class BaseLogger {
where the (async) result or errors are not propagated but still logged.
Useful to pair with LogItem.refDetached.
@return {LogItem} the log item added, useful to pass to LogItem.refDetached */
runDetached(labelOrValues, callback, logLevel = null, filterCreator = null) {
if (logLevel === null) {
@return {ILogItem} the log item added, useful to pass to LogItem.refDetached */
runDetached<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): ILogItem {
if (!logLevel) {
logLevel = LogLevel.Info;
}
const item = new LogItem(labelOrValues, logLevel, null, this);
this._run(item, callback, logLevel, filterCreator, false /* don't throw, nobody is awaiting */);
const item = new LogItem(labelOrValues, logLevel, this);
this._run(item, callback, logLevel, false /* don't throw, nobody is awaiting */, filterCreator);
return item;
}
/** run a callback wrapped in a log operation.
Errors and duration are transparently logged, also for async operations.
Whatever the callback returns is returned here. */
run(labelOrValues, callback, logLevel = null, filterCreator = null) {
if (logLevel === null) {
run<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T {
if (logLevel === undefined) {
logLevel = LogLevel.Info;
}
const item = new LogItem(labelOrValues, logLevel, null, this);
return this._run(item, callback, logLevel, filterCreator, true);
const item = new LogItem(labelOrValues, logLevel, this);
return this._run(item, callback, logLevel, true, filterCreator);
}
_run(item, callback, logLevel, filterCreator, shouldThrow) {
_run<T>(item: LogItem, callback: LogCallback<T>, logLevel: LogLevel, wantResult: true, filterCreator?: FilterCreator): T;
// we don't return if we don't throw, as we don't have anything to return when an error is caught but swallowed for the fire-and-forget case.
_run<T>(item: LogItem, callback: LogCallback<T>, logLevel: LogLevel, wantResult: false, filterCreator?: FilterCreator): void;
_run<T>(item: LogItem, callback: LogCallback<T>, logLevel: LogLevel, wantResult: boolean, filterCreator?: FilterCreator): T | void {
this._openItems.add(item);
const finishItem = () => {
@ -88,24 +95,29 @@ export class BaseLogger {
};
try {
const result = item.run(callback);
let result = item.run(callback);
if (result instanceof Promise) {
return result.then(promiseResult => {
result = result.then(promiseResult => {
finishItem();
return promiseResult;
}, err => {
finishItem();
if (shouldThrow) {
if (wantResult) {
throw err;
}
});
}) as unknown as T;
if (wantResult) {
return result;
}
} else {
finishItem();
return result;
if(wantResult) {
return result;
}
}
} catch (err) {
finishItem();
if (shouldThrow) {
if (wantResult) {
throw err;
}
}
@ -127,24 +139,20 @@ export class BaseLogger {
this._openItems.clear();
}
_persistItem() {
throw new Error("not implemented");
}
abstract _persistItem(item: LogItem, filter?: LogFilter, forced?: boolean): void;
async export() {
throw new Error("not implemented");
}
abstract export(): Promise<ILogExport | undefined>;
// expose log level without needing
get level() {
get level(): typeof LogLevel {
return LogLevel;
}
_now() {
_now(): number {
return this._platform.clock.now();
}
_createRefId() {
_createRefId(): number {
return Math.round(this._platform.random() * Number.MAX_SAFE_INTEGER);
}
}

View file

@ -13,32 +13,35 @@ 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 {BaseLogger} from "./BaseLogger.js";
import {BaseLogger} from "./BaseLogger";
import {LogItem} from "./LogItem";
import type {ILogItem, LogItemValues, ILogExport} from "./types";
export class ConsoleLogger extends BaseLogger {
_persistItem(item) {
_persistItem(item: LogItem): void {
printToConsole(item);
}
async export(): Promise<ILogExport | undefined> {
return undefined;
}
}
const excludedKeysFromTable = ["l", "id"];
function filterValues(values) {
if (!values) {
return null;
}
function filterValues(values: LogItemValues): LogItemValues | null {
return Object.entries(values)
.filter(([key]) => !excludedKeysFromTable.includes(key))
.reduce((obj, [key, value]) => {
.reduce((obj: LogItemValues, [key, value]) => {
obj = obj || {};
obj[key] = value;
return obj;
}, null);
}
function printToConsole(item) {
function printToConsole(item: LogItem): void {
const label = `${itemCaption(item)} (${item.duration}ms)`;
const filteredValues = filterValues(item._values);
const shouldGroup = item._children || filteredValues;
const filteredValues = filterValues(item.values);
const shouldGroup = item.children || filteredValues;
if (shouldGroup) {
if (item.error) {
console.group(label);
@ -58,8 +61,8 @@ function printToConsole(item) {
if (filteredValues) {
console.table(filteredValues);
}
if (item._children) {
for(const c of item._children) {
if (item.children) {
for(const c of item.children) {
printToConsole(c);
}
}
@ -68,18 +71,18 @@ function printToConsole(item) {
}
}
function itemCaption(item) {
if (item._values.t === "network") {
return `${item._values.method} ${item._values.url}`;
} else if (item._values.l && typeof item._values.id !== "undefined") {
return `${item._values.l} ${item._values.id}`;
} else if (item._values.l && typeof item._values.status !== "undefined") {
return `${item._values.l} (${item._values.status})`;
} else if (item._values.l && item.error) {
return `${item._values.l} failed`;
} else if (typeof item._values.ref !== "undefined") {
return `ref ${item._values.ref}`;
function itemCaption(item: ILogItem): string {
if (item.values.t === "network") {
return `${item.values.method} ${item.values.url}`;
} else if (item.values.l && typeof item.values.id !== "undefined") {
return `${item.values.l} ${item.values.id}`;
} else if (item.values.l && typeof item.values.status !== "undefined") {
return `${item.values.l} (${item.values.status})`;
} else if (item.values.l && item.error) {
return `${item.values.l} failed`;
} else if (typeof item.values.ref !== "undefined") {
return `ref ${item.values.ref}`;
} else {
return item._values.l || item._values.type;
return item.values.l || item.values.type;
}
}

View file

@ -22,10 +22,25 @@ import {
iterateCursor,
fetchResults,
} from "../matrix/storage/idb/utils";
import {BaseLogger} from "./BaseLogger.js";
import {BaseLogger} from "./BaseLogger";
import type {Interval} from "../platform/web/dom/Clock";
import type {Platform} from "../platform/web/Platform.js";
import type {BlobHandle} from "../platform/web/dom/BlobHandle.js";
import type {ILogItem, ILogExport} from "./types";
import type {LogFilter} from "./LogFilter";
type QueuedItem = {
json: string;
id?: number;
}
export class IDBLogger extends BaseLogger {
constructor(options) {
private readonly _name: string;
private readonly _limit: number;
private readonly _flushInterval: Interval;
private _queuedItems: QueuedItem[];
constructor(options: {name: string, flushInterval?: number, limit?: number, platform: Platform}) {
super(options);
const {name, flushInterval = 60 * 1000, limit = 3000} = options;
this._name = name;
@ -36,18 +51,19 @@ export class IDBLogger extends BaseLogger {
this._flushInterval = this._platform.clock.createInterval(() => this._tryFlush(), flushInterval);
}
dispose() {
// TODO: move dispose to ILogger, listen to pagehide elsewhere and call dispose from there, which calls _finishAllAndFlush
dispose(): void {
window.removeEventListener("pagehide", this, false);
this._flushInterval.dispose();
}
handleEvent(evt) {
handleEvent(evt: Event): void {
if (evt.type === "pagehide") {
this._finishAllAndFlush();
}
}
async _tryFlush() {
async _tryFlush(): Promise<void> {
const db = await this._openDB();
try {
const txn = db.transaction(["logs"], "readwrite");
@ -77,13 +93,13 @@ export class IDBLogger extends BaseLogger {
}
}
_finishAllAndFlush() {
_finishAllAndFlush(): void {
this._finishOpenItems();
this.log({l: "pagehide, closing logs", t: "navigation"});
this._persistQueuedItems(this._queuedItems);
}
_loadQueuedItems() {
_loadQueuedItems(): QueuedItem[] {
const key = `${this._name}_queuedItems`;
try {
const json = window.localStorage.getItem(key);
@ -97,18 +113,18 @@ export class IDBLogger extends BaseLogger {
return [];
}
_openDB() {
_openDB(): Promise<IDBDatabase> {
return openDatabase(this._name, db => db.createObjectStore("logs", {keyPath: "id", autoIncrement: true}), 1);
}
_persistItem(logItem, filter, forced) {
const serializedItem = logItem.serialize(filter, forced);
_persistItem(logItem: ILogItem, filter: LogFilter, forced: boolean): void {
const serializedItem = logItem.serialize(filter, undefined, forced);
this._queuedItems.push({
json: JSON.stringify(serializedItem)
});
}
_persistQueuedItems(items) {
_persistQueuedItems(items: QueuedItem[]): void {
try {
window.localStorage.setItem(`${this._name}_queuedItems`, JSON.stringify(items));
} catch (e) {
@ -116,12 +132,12 @@ export class IDBLogger extends BaseLogger {
}
}
async export() {
async export(): Promise<ILogExport> {
const db = await this._openDB();
try {
const txn = db.transaction(["logs"], "readonly");
const logs = txn.objectStore("logs");
const storedItems = await fetchResults(logs.openCursor(), () => false);
const storedItems: QueuedItem[] = await fetchResults(logs.openCursor(), () => false);
const allItems = storedItems.concat(this._queuedItems);
return new IDBLogExport(allItems, this, this._platform);
} finally {
@ -131,17 +147,20 @@ export class IDBLogger extends BaseLogger {
}
}
async _removeItems(items) {
async _removeItems(items: QueuedItem[]): Promise<void> {
const db = await this._openDB();
try {
const txn = db.transaction(["logs"], "readwrite");
const logs = txn.objectStore("logs");
for (const item of items) {
const queuedIdx = this._queuedItems.findIndex(i => i.id === item.id);
if (queuedIdx === -1) {
if (typeof item.id === "number") {
logs.delete(item.id);
} else {
this._queuedItems.splice(queuedIdx, 1);
// assume the (non-persisted) object in each array will be the same
const queuedIdx = this._queuedItems.indexOf(item);
if (queuedIdx === -1) {
this._queuedItems.splice(queuedIdx, 1);
}
}
}
await txnAsPromise(txn);
@ -153,33 +172,37 @@ export class IDBLogger extends BaseLogger {
}
}
class IDBLogExport {
constructor(items, logger, platform) {
class IDBLogExport implements ILogExport {
private readonly _items: QueuedItem[];
private readonly _logger: IDBLogger;
private readonly _platform: Platform;
constructor(items: QueuedItem[], logger: IDBLogger, platform: Platform) {
this._items = items;
this._logger = logger;
this._platform = platform;
}
get count() {
get count(): number {
return this._items.length;
}
/**
* @return {Promise}
*/
removeFromStore() {
removeFromStore(): Promise<void> {
return this._logger._removeItems(this._items);
}
asBlob() {
asBlob(): BlobHandle {
const log = {
formatVersion: 1,
appVersion: this._platform.updateService?.version,
items: this._items.map(i => JSON.parse(i.json))
};
const json = JSON.stringify(log);
const buffer = this._platform.encoding.utf8.encode(json);
const blob = this._platform.createBlob(buffer, "application/json");
const buffer: Uint8Array = this._platform.encoding.utf8.encode(json);
const blob: BlobHandle = this._platform.createBlob(buffer, "application/json");
return blob;
}
}

View file

@ -14,31 +14,35 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
export const LogLevel = {
All: 1,
Debug: 2,
Detail: 3,
Info: 4,
Warn: 5,
Error: 6,
Fatal: 7,
Off: 8,
import type {ILogItem, ISerializedItem} from "./types";
export enum LogLevel {
All = 1,
Debug,
Detail,
Info,
Warn,
Error,
Fatal,
Off
}
export class LogFilter {
constructor(parentFilter) {
private _min?: LogLevel;
private _parentFilter?: LogFilter;
constructor(parentFilter?: LogFilter) {
this._parentFilter = parentFilter;
this._min = null;
}
filter(item, children) {
filter(item: ILogItem, children: ISerializedItem[] | null): boolean {
if (this._parentFilter) {
if (!this._parentFilter.filter(item, children)) {
return false;
}
}
// neither our children or us have a loglevel high enough, filter out.
if (this._min !== null && !Array.isArray(children) && item.logLevel < this._min) {
if (this._min !== undefined && !Array.isArray(children) && item.logLevel < this._min) {
return false;
} else {
return true;
@ -46,7 +50,7 @@ export class LogFilter {
}
/* methods to build the filter */
minLevel(logLevel) {
minLevel(logLevel: LogLevel): LogFilter {
this._min = logLevel;
return this;
}

View file

@ -15,39 +15,47 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {LogLevel, LogFilter} from "./LogFilter.js";
import {LogLevel, LogFilter} from "./LogFilter";
import type {BaseLogger} from "./BaseLogger";
import type {ISerializedItem, ILogItem, LogItemValues, LabelOrValues, FilterCreator, LogCallback} from "./types";
export class LogItem {
constructor(labelOrValues, logLevel, filterCreator, logger) {
export class LogItem implements ILogItem {
public readonly start: number;
public logLevel: LogLevel;
public error?: Error;
public end?: number;
private _values: LogItemValues;
private _logger: BaseLogger;
private _filterCreator?: FilterCreator;
private _children?: Array<LogItem>;
constructor(labelOrValues: LabelOrValues, logLevel: LogLevel, logger: BaseLogger, filterCreator?: FilterCreator) {
this._logger = logger;
this._start = logger._now();
this._end = null;
this.start = logger._now();
// (l)abel
this._values = typeof labelOrValues === "string" ? {l: labelOrValues} : labelOrValues;
this.error = null;
this.logLevel = logLevel;
this._children = null;
this._filterCreator = filterCreator;
}
/** start a new root log item and run it detached mode, see BaseLogger.runDetached */
runDetached(labelOrValues, callback, logLevel, filterCreator) {
runDetached(labelOrValues: LabelOrValues, callback: LogCallback<unknown>, logLevel?: LogLevel, filterCreator?: FilterCreator): ILogItem {
return this._logger.runDetached(labelOrValues, callback, logLevel, filterCreator);
}
/** start a new detached root log item and log a reference to it from this item */
wrapDetached(labelOrValues, callback, logLevel, filterCreator) {
wrapDetached(labelOrValues: LabelOrValues, callback: LogCallback<unknown>, logLevel?: LogLevel, filterCreator?: FilterCreator): void {
this.refDetached(this.runDetached(labelOrValues, callback, logLevel, filterCreator));
}
/** logs a reference to a different log item, usually obtained from runDetached.
This is useful if the referenced operation can't be awaited. */
refDetached(logItem, logLevel = null) {
refDetached(logItem: ILogItem, logLevel?: LogLevel): void {
logItem.ensureRefId();
return this.log({ref: logItem._values.refId}, logLevel);
this.log({ref: logItem.values.refId}, logLevel);
}
ensureRefId() {
ensureRefId(): void {
if (!this._values.refId) {
this.set("refId", this._logger._createRefId());
}
@ -56,29 +64,33 @@ export class LogItem {
/**
* Creates a new child item and runs it in `callback`.
*/
wrap(labelOrValues, callback, logLevel = null, filterCreator = null) {
wrap<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T {
const item = this.child(labelOrValues, logLevel, filterCreator);
return item.run(callback);
}
get duration() {
if (this._end) {
return this._end - this._start;
get duration(): number | undefined {
if (this.end) {
return this.end - this.start;
} else {
return null;
return undefined;
}
}
durationWithoutType(type) {
return this.duration - this.durationOfType(type);
durationWithoutType(type: string): number | undefined {
const durationOfType = this.durationOfType(type);
if (this.duration && durationOfType) {
return this.duration - durationOfType;
}
}
durationOfType(type) {
durationOfType(type: string): number | undefined {
if (this._values.t === type) {
return this.duration;
} else if (this._children) {
return this._children.reduce((sum, c) => {
return sum + c.durationOfType(type);
const duration = c.durationOfType(type);
return sum + (duration ?? 0);
}, 0);
} else {
return 0;
@ -91,12 +103,12 @@ export class LogItem {
*
* Hence, the child item is not returned.
*/
log(labelOrValues, logLevel = null) {
const item = this.child(labelOrValues, logLevel, null);
item._end = item._start;
log(labelOrValues: LabelOrValues, logLevel?: LogLevel): void {
const item = this.child(labelOrValues, logLevel);
item.end = item.start;
}
set(key, value) {
set(key: string | object, value?: unknown): void {
if(typeof key === "object") {
const values = key;
Object.assign(this._values, values);
@ -105,7 +117,7 @@ export class LogItem {
}
}
serialize(filter, parentStartTime = null, forced) {
serialize(filter: LogFilter, parentStartTime: number | undefined, forced: boolean): ISerializedItem | undefined {
if (this._filterCreator) {
try {
filter = this._filterCreator(new LogFilter(filter), this);
@ -113,10 +125,10 @@ export class LogItem {
console.error("Error creating log filter", err);
}
}
let children;
if (this._children !== null) {
children = this._children.reduce((array, c) => {
const s = c.serialize(filter, this._start, false);
let children: Array<ISerializedItem> | null = null;
if (this._children) {
children = this._children.reduce((array: Array<ISerializedItem>, c) => {
const s = c.serialize(filter, this.start, false);
if (s) {
if (array === null) {
array = [];
@ -127,12 +139,12 @@ export class LogItem {
}, null);
}
if (filter && !filter.filter(this, children)) {
return null;
return;
}
// in (v)alues, (l)abel and (t)ype are also reserved.
const item = {
const item: ISerializedItem = {
// (s)tart
s: parentStartTime === null ? this._start : this._start - parentStartTime,
s: typeof parentStartTime === "number" ? this.start - parentStartTime : this.start,
// (d)uration
d: this.duration,
// (v)alues
@ -171,20 +183,19 @@ export class LogItem {
* @param {Function} callback [description]
* @return {[type]} [description]
*/
run(callback) {
if (this._end !== null) {
run<T>(callback: LogCallback<T>): T {
if (this.end !== undefined) {
console.trace("log item is finished, additional logs will likely not be recorded");
}
let result;
try {
result = callback(this);
const result = callback(this);
if (result instanceof Promise) {
return result.then(promiseResult => {
this.finish();
return promiseResult;
}, err => {
throw this.catch(err);
});
}) as unknown as T;
} else {
this.finish();
return result;
@ -198,45 +209,53 @@ export class LogItem {
* finished the item, recording the end time. After finishing, an item can't be modified anymore as it will be persisted.
* @internal shouldn't typically be called by hand. allows to force finish if a promise is still running when closing the app
*/
finish() {
if (this._end === null) {
if (this._children !== null) {
finish(): void {
if (this.end === undefined) {
if (this._children) {
for(const c of this._children) {
c.finish();
}
}
this._end = this._logger._now();
this.end = this._logger._now();
}
}
// expose log level without needing import everywhere
get level() {
get level(): typeof LogLevel {
return LogLevel;
}
catch(err) {
catch(err: Error): Error {
this.error = err;
this.logLevel = LogLevel.Error;
this.finish();
return err;
}
child(labelOrValues, logLevel, filterCreator) {
if (this._end !== null) {
child(labelOrValues: LabelOrValues, logLevel?: LogLevel, filterCreator?: FilterCreator): LogItem {
if (this.end) {
console.trace("log item is finished, additional logs will likely not be recorded");
}
if (!logLevel) {
logLevel = this.logLevel || LogLevel.Info;
}
const item = new LogItem(labelOrValues, logLevel, filterCreator, this._logger);
if (this._children === null) {
const item = new LogItem(labelOrValues, logLevel, this._logger, filterCreator);
if (!this._children) {
this._children = [];
}
this._children.push(item);
return item;
}
get logger() {
get logger(): BaseLogger {
return this._logger;
}
get values(): LogItemValues {
return this._values;
}
get children(): Array<LogItem> | undefined {
return this._children;
}
}

View file

@ -1,99 +0,0 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed 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 {LogLevel} from "./LogFilter.js";
function noop () {}
export class NullLogger {
constructor() {
this.item = new NullLogItem(this);
}
log() {}
run(_, callback) {
return callback(this.item);
}
wrapOrRun(item, _, callback) {
if (item) {
return item.wrap(null, callback);
} else {
return this.run(null, callback);
}
}
runDetached(_, callback) {
new Promise(r => r(callback(this.item))).then(noop, noop);
}
async export() {
return null;
}
get level() {
return LogLevel;
}
}
export class NullLogItem {
constructor(logger) {
this.logger = logger;
}
wrap(_, callback) {
return callback(this);
}
log() {}
set() {}
runDetached(_, callback) {
new Promise(r => r(callback(this))).then(noop, noop);
}
wrapDetached(_, callback) {
return this.refDetached(null, callback);
}
run(callback) {
return callback(this);
}
refDetached() {}
ensureRefId() {}
get level() {
return LogLevel;
}
get duration() {
return 0;
}
catch(err) {
return err;
}
child() {
return this;
}
finish() {}
}
export const Instance = new NullLogger();

106
src/logging/NullLogger.ts Normal file
View file

@ -0,0 +1,106 @@
/*
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed 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 {LogLevel} from "./LogFilter";
import type {ILogger, ILogExport, ILogItem, LabelOrValues, LogCallback, LogItemValues} from "./types";
function noop (): void {}
export class NullLogger implements ILogger {
public readonly item: ILogItem = new NullLogItem(this);
log(): void {}
run<T>(_, callback: LogCallback<T>): T {
return callback(this.item);
}
wrapOrRun<T>(item: ILogItem | undefined, _, callback: LogCallback<T>): T {
if (item) {
return item.wrap(_, callback);
} else {
return this.run(_, callback);
}
}
runDetached(_, callback): ILogItem {
new Promise(r => r(callback(this.item))).then(noop, noop);
return this.item;
}
async export(): Promise<ILogExport | undefined> {
return undefined;
}
get level(): typeof LogLevel {
return LogLevel;
}
}
export class NullLogItem implements ILogItem {
public readonly logger: NullLogger;
public readonly logLevel: LogLevel;
public children?: Array<ILogItem>;
public values: LogItemValues;
public error?: Error;
constructor(logger: NullLogger) {
this.logger = logger;
}
wrap<T>(_: LabelOrValues, callback: LogCallback<T>): T {
return callback(this);
}
log(): void {}
set(): void {}
runDetached(_: LabelOrValues, callback: LogCallback<unknown>): ILogItem {
new Promise(r => r(callback(this))).then(noop, noop);
return this;
}
wrapDetached(_: LabelOrValues, _callback: LogCallback<unknown>): void {
return this.refDetached();
}
refDetached(): void {}
ensureRefId(): void {}
get level(): typeof LogLevel {
return LogLevel;
}
get duration(): 0 {
return 0;
}
catch(err: Error): Error {
return err;
}
child(): ILogItem {
return this;
}
finish(): void {}
serialize(): undefined {
return undefined;
}
}
export const Instance = new NullLogger();

82
src/logging/types.ts Normal file
View file

@ -0,0 +1,82 @@
/*
Copyright 2020 Bruno Windels <bruno@windels.cloud>
Copyright 2021 The Matrix.org Foundation C.I.C.
Licensed 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 {LogLevel, LogFilter} from "./LogFilter";
import type {BaseLogger} from "./BaseLogger";
import type {BlobHandle} from "../platform/web/dom/BlobHandle.js";
export interface ISerializedItem {
s: number;
d?: number;
v: LogItemValues;
l: LogLevel;
e?: {
stack?: string;
name: string;
message: string;
};
f?: boolean;
c?: Array<ISerializedItem>;
};
export interface ILogItem {
logLevel: LogLevel;
error?: Error;
readonly logger: ILogger;
readonly level: typeof LogLevel;
readonly end?: number;
readonly start?: number;
readonly values: LogItemValues;
wrap<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T;
log(labelOrValues: LabelOrValues, logLevel?: LogLevel): void;
set(key: string | object, value: unknown): void;
runDetached(labelOrValues: LabelOrValues, callback: LogCallback<unknown>, logLevel?: LogLevel, filterCreator?: FilterCreator): ILogItem;
wrapDetached(labelOrValues: LabelOrValues, callback: LogCallback<unknown>, logLevel?: LogLevel, filterCreator?: FilterCreator): void;
refDetached(logItem: ILogItem, logLevel?: LogLevel): void;
ensureRefId(): void;
catch(err: Error): Error;
serialize(filter: LogFilter, parentStartTime: number | undefined, forced: boolean): ISerializedItem | undefined;
}
export interface ILogger {
log(labelOrValues: LabelOrValues, logLevel?: LogLevel): void;
wrapOrRun<T>(item: ILogItem | undefined, labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T;
runDetached<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): ILogItem;
run<T>(labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T;
export(): Promise<ILogExport | undefined>;
get level(): typeof LogLevel;
}
export interface ILogExport {
get count(): number;
removeFromStore(): Promise<void>;
asBlob(): BlobHandle;
}
export type LogItemValues = {
l?: string;
t?: string;
id?: unknown;
status?: string | number;
refId?: number;
ref?: number;
[key: string]: any
}
export type LabelOrValues = string | LogItemValues;
export type FilterCreator = ((filter: LogFilter, item: ILogItem) => LogFilter);
export type LogCallback<T> = (item: ILogItem) => T;

View file

@ -1,16 +0,0 @@
// these are helper functions if you can't assume you always have a log item (e.g. some code paths call with one set, others don't)
// if you know you always have a log item, better to use the methods on the log item than these utility functions.
import {Instance as NullLoggerInstance} from "./NullLogger.js";
export function wrapOrRunNullLogger(logItem, labelOrValues, callback, logLevel = null, filterCreator = null) {
if (logItem) {
return logItem.wrap(logItem, labelOrValues, callback, logLevel, filterCreator);
} else {
return NullLoggerInstance.run(null, callback);
}
}
export function ensureLogItem(logItem) {
return logItem || NullLoggerInstance.item;
}

18
src/logging/utils.ts Normal file
View file

@ -0,0 +1,18 @@
// these are helper functions if you can't assume you always have a log item (e.g. some code paths call with one set, others don't)
// if you know you always have a log item, better to use the methods on the log item than these utility functions.
import {Instance as NullLoggerInstance} from "./NullLogger";
import type {FilterCreator, ILogItem, LabelOrValues, LogCallback} from "./types";
import {LogLevel} from "./LogFilter";
export function wrapOrRunNullLogger<T>(logItem: ILogItem | undefined, labelOrValues: LabelOrValues, callback: LogCallback<T>, logLevel?: LogLevel, filterCreator?: FilterCreator): T | Promise<T> {
if (logItem) {
return logItem.wrap(labelOrValues, callback, logLevel, filterCreator);
} else {
return NullLoggerInstance.run(null, callback);
}
}
export function ensureLogItem(logItem: ILogItem): ILogItem {
return logItem || NullLoggerInstance.item;
}

View file

@ -26,7 +26,7 @@ import type {OlmWorker} from "../OlmWorker";
import type {Transaction} from "../../storage/idb/Transaction";
import type {TimelineEvent} from "../../storage/types";
import type {DecryptionResult} from "../DecryptionResult";
import type {LogItem} from "../../../logging/LogItem";
import type {ILogItem} from "../../../logging/types";
export class Decryption {
private keyLoader: KeyLoader;
@ -136,7 +136,7 @@ export class Decryption {
* Extracts room keys from decrypted device messages.
* The key won't be persisted yet, you need to call RoomKey.write for that.
*/
roomKeysFromDeviceMessages(decryptionResults: DecryptionResult[], log: LogItem): IncomingRoomKey[] {
roomKeysFromDeviceMessages(decryptionResults: DecryptionResult[], log: ILogItem): IncomingRoomKey[] {
const keys: IncomingRoomKey[] = [];
for (const dr of decryptionResults) {
if (dr.event?.type !== "m.room_key" || dr.event.content?.algorithm !== MEGOLM_ALGORITHM) {

View file

@ -27,7 +27,7 @@ import {Heroes} from "./members/Heroes.js";
import {EventEntry} from "./timeline/entries/EventEntry.js";
import {ObservedEventMap} from "./ObservedEventMap.js";
import {DecryptionSource} from "../e2ee/common.js";
import {ensureLogItem} from "../../logging/utils.js";
import {ensureLogItem} from "../../logging/utils";
import {PowerLevels} from "./PowerLevels.js";
import {RetainedObservableValue} from "../../observable/ObservableValue";

View file

@ -244,7 +244,7 @@ export class Invite extends EventEmitter {
}
}
import {NullLogItem} from "../../logging/NullLogger.js";
import {NullLogItem} from "../../logging/NullLogger";
import {Clock as MockClock} from "../../mocks/Clock.js";
import {default as roomInviteFixture} from "../../fixtures/matrix/invites/room.js";
import {default as dmInviteFixture} from "../../fixtures/matrix/invites/dm.js";

View file

@ -353,7 +353,7 @@ export class SendQueue {
import {HomeServer as MockHomeServer} from "../../../mocks/HomeServer.js";
import {createMockStorage} from "../../../mocks/Storage";
import {ListObserver} from "../../../mocks/ListObserver.js";
import {NullLogger, NullLogItem} from "../../../logging/NullLogger.js";
import {NullLogger, NullLogItem} from "../../../logging/NullLogger";
import {createEvent, withTextBody, withTxnId} from "../../../mocks/event.js";
import {poll} from "../../../mocks/poll.js";
import {createAnnotation} from "../timeline/relations.js";

View file

@ -346,7 +346,7 @@ import {Clock as MockClock} from "../../../mocks/Clock.js";
import {createMockStorage} from "../../../mocks/Storage";
import {ListObserver} from "../../../mocks/ListObserver.js";
import {createEvent, withTextBody, withContent, withSender} from "../../../mocks/event.js";
import {NullLogItem} from "../../../logging/NullLogger.js";
import {NullLogItem} from "../../../logging/NullLogger";
import {EventEntry} from "./entries/EventEntry.js";
import {User} from "../../User.js";
import {PendingEvent} from "../sending/PendingEvent.js";

View file

@ -205,7 +205,7 @@ import {FragmentIdComparer} from "../FragmentIdComparer.js";
import {RelationWriter} from "./RelationWriter.js";
import {createMockStorage} from "../../../../mocks/Storage";
import {FragmentBoundaryEntry} from "../entries/FragmentBoundaryEntry.js";
import {NullLogItem} from "../../../../logging/NullLogger.js";
import {NullLogItem} from "../../../../logging/NullLogger";
import {TimelineMock, eventIds, eventId} from "../../../../mocks/TimelineMock.ts";
import {SyncWriter} from "./SyncWriter.js";
import {MemberWriter} from "./MemberWriter.js";

View file

@ -257,7 +257,7 @@ import {createMockStorage} from "../../../../mocks/Storage";
import {createEvent, withTextBody, withRedacts, withContent} from "../../../../mocks/event.js";
import {createAnnotation} from "../relations.js";
import {FragmentIdComparer} from "../FragmentIdComparer.js";
import {NullLogItem} from "../../../../logging/NullLogger.js";
import {NullLogItem} from "../../../../logging/NullLogger";
export function tests() {
const fragmentIdComparer = new FragmentIdComparer([]);

View file

@ -258,7 +258,7 @@ export class SyncWriter {
import {createMockStorage} from "../../../../mocks/Storage";
import {createEvent, withTextBody} from "../../../../mocks/event.js";
import {Instance as nullLogger} from "../../../../logging/NullLogger.js";
import {Instance as nullLogger} from "../../../../logging/NullLogger";
export function tests() {
const roomId = "!abc:hs.tld";
return {

View file

@ -16,7 +16,7 @@ limitations under the License.
import {iterateCursor, DONE, NOT_DONE, reqAsPromise} from "./utils";
import {StorageError} from "../common";
import {LogItem} from "../../../logging/LogItem.js";
import {ILogItem} from "../../../logging/types";
import {IDBKey} from "./Transaction";
// this is the part of the Transaction class API that is used here and in the Store subclass,
@ -25,7 +25,7 @@ export interface ITransaction {
idbFactory: IDBFactory;
IDBKeyRange: typeof IDBKeyRange;
databaseName: string;
addWriteError(error: StorageError, refItem: LogItem | undefined, operationName: string, keys: IDBKey[] | undefined);
addWriteError(error: StorageError, refItem: ILogItem | undefined, operationName: string, keys: IDBKey[] | undefined);
}
type Reducer<A,B> = (acc: B, val: A) => B
@ -277,7 +277,7 @@ export function tests() {
class MockTransaction extends MockIDBImpl {
get databaseName(): string { return "mockdb"; }
addWriteError(error: StorageError, refItem: LogItem | undefined, operationName: string, keys: IDBKey[] | undefined) {}
addWriteError(error: StorageError, refItem: ILogItem | undefined, operationName: string, keys: IDBKey[] | undefined) {}
}
interface TestEntry {

View file

@ -18,7 +18,7 @@ import {IDOMStorage} from "./types";
import {Transaction} from "./Transaction";
import { STORE_NAMES, StoreNames, StorageError } from "../common";
import { reqAsPromise } from "./utils";
import { BaseLogger } from "../../../logging/BaseLogger.js";
import { ILogger } from "../../../logging/types";
const WEBKITEARLYCLOSETXNBUG_BOGUS_KEY = "782rh281re38-boguskey";
@ -26,13 +26,13 @@ export class Storage {
private _db: IDBDatabase;
private _hasWebkitEarlyCloseTxnBug: boolean;
readonly logger: BaseLogger;
readonly logger: ILogger;
readonly idbFactory: IDBFactory
readonly IDBKeyRange: typeof IDBKeyRange;
readonly storeNames: typeof StoreNames;
readonly localStorage: IDOMStorage;
constructor(idbDatabase: IDBDatabase, idbFactory: IDBFactory, _IDBKeyRange: typeof IDBKeyRange, hasWebkitEarlyCloseTxnBug: boolean, localStorage: IDOMStorage, logger: BaseLogger) {
constructor(idbDatabase: IDBDatabase, idbFactory: IDBFactory, _IDBKeyRange: typeof IDBKeyRange, hasWebkitEarlyCloseTxnBug: boolean, localStorage: IDOMStorage, logger: ILogger) {
this._db = idbDatabase;
this.idbFactory = idbFactory;
this.IDBKeyRange = _IDBKeyRange;

View file

@ -20,11 +20,10 @@ import { openDatabase, reqAsPromise } from "./utils";
import { exportSession, importSession, Export } from "./export";
import { schema } from "./schema";
import { detectWebkitEarlyCloseTxnBug } from "./quirks";
import { BaseLogger } from "../../../logging/BaseLogger.js";
import { LogItem } from "../../../logging/LogItem.js";
import { ILogItem } from "../../../logging/types";
const sessionName = (sessionId: string) => `hydrogen_session_${sessionId}`;
const openDatabaseWithSessionId = function(sessionId: string, idbFactory: IDBFactory, localStorage: IDOMStorage, log: LogItem) {
const openDatabaseWithSessionId = function(sessionId: string, idbFactory: IDBFactory, localStorage: IDOMStorage, log: ILogItem) {
const create = (db, txn, oldVersion, version) => createStores(db, txn, oldVersion, version, localStorage, log);
return openDatabase(sessionName(sessionId), create, schema.length, idbFactory);
}
@ -63,7 +62,7 @@ export class StorageFactory {
this._localStorage = localStorage;
}
async create(sessionId: string, log: LogItem): Promise<Storage> {
async create(sessionId: string, log: ILogItem): Promise<Storage> {
await this._serviceWorkerHandler?.preventConcurrentSessionAccess(sessionId);
requestPersistedStorage().then(persisted => {
// Firefox lies here though, and returns true even if the user denied the request
@ -83,23 +82,25 @@ export class StorageFactory {
return reqAsPromise(req);
}
async export(sessionId: string, log: LogItem): Promise<Export> {
async export(sessionId: string, log: ILogItem): Promise<Export> {
const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, this._localStorage, log);
return await exportSession(db);
}
async import(sessionId: string, data: Export, log: LogItem): Promise<void> {
async import(sessionId: string, data: Export, log: ILogItem): Promise<void> {
const db = await openDatabaseWithSessionId(sessionId, this._idbFactory, this._localStorage, log);
return await importSession(db, data);
}
}
async function createStores(db: IDBDatabase, txn: IDBTransaction, oldVersion: number | null, version: number, localStorage: IDOMStorage, log: LogItem): Promise<void> {
async function createStores(db: IDBDatabase, txn: IDBTransaction, oldVersion: number | null, version: number, localStorage: IDOMStorage, log: ILogItem): Promise<void> {
const startIdx = oldVersion || 0;
return log.wrap({l: "storage migration", oldVersion, version}, async log => {
for(let i = startIdx; i < version; ++i) {
const migrationFunc = schema[i];
await log.wrap(`v${i + 1}`, log => migrationFunc(db, txn, localStorage, log));
}
});
return log.wrap(
{ l: "storage migration", oldVersion, version },
async (log) => {
for (let i = startIdx; i < version; ++i) {
const migrationFunc = schema[i];
await log.wrap(`v${i + 1}`, (log) => migrationFunc(db, txn, localStorage, log));
}
});
}

View file

@ -18,7 +18,7 @@ import {QueryTarget, IDBQuery, ITransaction} from "./QueryTarget";
import {IDBRequestError, IDBRequestAttemptError} from "./error";
import {reqAsPromise} from "./utils";
import {Transaction, IDBKey} from "./Transaction";
import {LogItem} from "../../../logging/LogItem.js";
import {ILogItem} from "../../../logging/types";
const LOG_REQUESTS = false;
@ -145,7 +145,7 @@ export class Store<T> extends QueryTarget<T> {
return new QueryTarget<T>(new QueryTargetWrapper<T>(this._idbStore.index(indexName)), this._transaction);
}
put(value: T, log?: LogItem): void {
put(value: T, log?: ILogItem): void {
// If this request fails, the error will bubble up to the transaction and abort it,
// which is the behaviour we want. Therefore, it is ok to not create a promise for this
// request and await it.
@ -160,13 +160,13 @@ export class Store<T> extends QueryTarget<T> {
this._prepareErrorLog(request, log, "put", undefined, value);
}
add(value: T, log?: LogItem): void {
add(value: T, log?: ILogItem): void {
// ok to not monitor result of request, see comment in `put`.
const request = this._idbStore.add(value);
this._prepareErrorLog(request, log, "add", undefined, value);
}
async tryAdd(value: T, log: LogItem): Promise<boolean> {
async tryAdd(value: T, log: ILogItem): Promise<boolean> {
try {
await reqAsPromise(this._idbStore.add(value));
return true;
@ -181,13 +181,13 @@ export class Store<T> extends QueryTarget<T> {
}
}
delete(keyOrKeyRange: IDBValidKey | IDBKeyRange, log?: LogItem): void {
delete(keyOrKeyRange: IDBValidKey | IDBKeyRange, log?: ILogItem): void {
// ok to not monitor result of request, see comment in `put`.
const request = this._idbStore.delete(keyOrKeyRange);
this._prepareErrorLog(request, log, "delete", keyOrKeyRange, undefined);
}
private _prepareErrorLog(request: IDBRequest, log: LogItem | undefined, operationName: string, key: IDBKey | undefined, value: T | undefined) {
private _prepareErrorLog(request: IDBRequest, log: ILogItem | undefined, operationName: string, key: IDBKey | undefined, value: T | undefined) {
if (log) {
log.ensureRefId();
}

View file

@ -36,15 +36,14 @@ import {OutboundGroupSessionStore} from "./stores/OutboundGroupSessionStore";
import {GroupSessionDecryptionStore} from "./stores/GroupSessionDecryptionStore";
import {OperationStore} from "./stores/OperationStore";
import {AccountDataStore} from "./stores/AccountDataStore";
import {LogItem} from "../../../logging/LogItem.js";
import {BaseLogger} from "../../../logging/BaseLogger.js";
import type {ILogger, ILogItem} from "../../../logging/types";
export type IDBKey = IDBValidKey | IDBKeyRange;
class WriteErrorInfo {
constructor(
public readonly error: StorageError,
public readonly refItem: LogItem | undefined,
public readonly refItem: ILogItem | undefined,
public readonly operationName: string,
public readonly keys: IDBKey[] | undefined,
) {}
@ -77,7 +76,7 @@ export class Transaction {
return this._storage.databaseName;
}
get logger(): BaseLogger {
get logger(): ILogger {
return this._storage.logger;
}
@ -169,7 +168,7 @@ export class Transaction {
return this._store(StoreNames.accountData, idbStore => new AccountDataStore(idbStore));
}
async complete(log?: LogItem): Promise<void> {
async complete(log?: ILogItem): Promise<void> {
try {
await txnAsPromise(this._txn);
} catch (err) {
@ -190,7 +189,7 @@ export class Transaction {
return error;
}
abort(log?: LogItem): void {
abort(log?: ILogItem): void {
// TODO: should we wrap the exception in a StorageError?
try {
this._txn.abort();
@ -202,14 +201,14 @@ export class Transaction {
}
}
addWriteError(error: StorageError, refItem: LogItem | undefined, operationName: string, keys: IDBKey[] | undefined) {
addWriteError(error: StorageError, refItem: ILogItem | undefined, operationName: string, keys: IDBKey[] | undefined) {
// don't log subsequent `AbortError`s
if (error.errcode !== "AbortError" || this._writeErrors.length === 0) {
this._writeErrors.push(new WriteErrorInfo(error, refItem, operationName, keys));
}
}
private _logWriteErrors(parentItem: LogItem | undefined) {
private _logWriteErrors(parentItem: ILogItem | undefined) {
const callback = errorGroupItem => {
// we don't have context when there is no parentItem, so at least log stores
if (!parentItem) {

View file

@ -11,10 +11,10 @@ import {SessionStore} from "./stores/SessionStore";
import {Store} from "./Store";
import {encodeScopeTypeKey} from "./stores/OperationStore";
import {MAX_UNICODE} from "./stores/common";
import {LogItem} from "../../../logging/LogItem.js";
import {ILogItem} from "../../../logging/types";
export type MigrationFunc = (db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: LogItem) => Promise<void> | void;
export type MigrationFunc = (db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem) => Promise<void> | void;
// FUNCTIONS SHOULD ONLY BE APPENDED!!
// the index in the array is the database version
export const schema: MigrationFunc[] = [
@ -166,7 +166,7 @@ function createTimelineRelationsStore(db: IDBDatabase) : void {
}
//v11 doesn't change the schema, but ensures all userIdentities have all the roomIds they should (see #470)
async function fixMissingRoomsInUserIdentities(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: LogItem) {
async function fixMissingRoomsInUserIdentities(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem) {
const roomSummaryStore = txn.objectStore("roomSummary");
const trackedRoomIds: string[] = [];
await iterateCursor<SummaryData>(roomSummaryStore.openCursor(), roomSummary => {
@ -220,7 +220,7 @@ async function changeSSSSKeyPrefix(db: IDBDatabase, txn: IDBTransaction) {
}
}
// v13
async function backupAndRestoreE2EEAccountToLocalStorage(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: LogItem) {
async function backupAndRestoreE2EEAccountToLocalStorage(db: IDBDatabase, txn: IDBTransaction, localStorage: IDOMStorage, log: ILogItem) {
const session = txn.objectStore("session");
// the Store object gets passed in several things through the Transaction class (a wrapper around IDBTransaction),
// the only thing we should need here is the databaseName though, so we mock it out.

View file

@ -16,8 +16,8 @@ limitations under the License.
import {Store} from "../Store";
import {IDOMStorage} from "../types";
import {SESSION_E2EE_KEY_PREFIX} from "../../../e2ee/common.js";
import {LogItem} from "../../../../logging/LogItem.js";
import {parse, stringify} from "../../../../utils/typedJSON";
import type {ILogItem} from "../../../../logging/types";
export interface SessionEntry {
key: string;
@ -64,7 +64,7 @@ export class SessionStore {
});
}
async tryRestoreE2EEIdentityFromLocalStorage(log: LogItem): Promise<boolean> {
async tryRestoreE2EEIdentityFromLocalStorage(log: ILogItem): Promise<boolean> {
let success = false;
const lsPrefix = this._localStorageKeyPrefix;
const prefix = lsPrefix + SESSION_E2EE_KEY_PREFIX;

View file

@ -20,7 +20,7 @@ import { encodeUint32, decodeUint32 } from "../utils";
import {KeyLimits} from "../../common";
import {Store} from "../Store";
import {TimelineEvent, StateEvent} from "../../types";
import {LogItem} from "../../../../logging/LogItem.js";
import {ILogItem} from "../../../../logging/types";
interface Annotation {
count: number;
@ -286,7 +286,7 @@ export class TimelineEventStore {
*
* Returns if the event was not yet known and the entry was written.
*/
tryInsert(entry: TimelineEventEntry, log: LogItem): Promise<boolean> {
tryInsert(entry: TimelineEventEntry, log: ILogItem): Promise<boolean> {
(entry as TimelineEventStorageEntry).key = encodeKey(entry.roomId, entry.fragmentId, entry.eventIndex);
(entry as TimelineEventStorageEntry).eventIdKey = encodeEventIdKey(entry.roomId, entry.event.event_id);
return this._timelineStore.tryAdd(entry as TimelineEventStorageEntry, log);
@ -320,7 +320,7 @@ export class TimelineEventStore {
import {createMockStorage} from "../../../../mocks/Storage";
import {createEvent, withTextBody} from "../../../../mocks/event.js";
import {createEventEntry} from "../../../room/timeline/persistence/common.js";
import {Instance as logItem} from "../../../../logging/NullLogger.js";
import {Instance as nullLogger} from "../../../../logging/NullLogger";
export function tests() {
@ -368,7 +368,7 @@ export function tests() {
let eventKey = EventKey.defaultFragmentKey(109);
for (const insertedId of insertedIds) {
const entry = createEventEntry(eventKey.nextKey(), roomId, createEventWithId(insertedId));
assert(await txn.timelineEvents.tryInsert(entry, logItem));
assert(await txn.timelineEvents.tryInsert(entry, nullLogger.item));
eventKey = eventKey.nextKey();
}
const eventKeyMap = await txn.timelineEvents.getEventKeysForIds(roomId, checkedIds);

View file

@ -18,7 +18,7 @@ import {FDBFactory, FDBKeyRange} from "../../lib/fake-indexeddb/index.js";
import {StorageFactory} from "../matrix/storage/idb/StorageFactory";
import {IDOMStorage} from "../matrix/storage/idb/types";
import {Storage} from "../matrix/storage/idb/Storage";
import {Instance as nullLogger} from "../logging/NullLogger.js";
import {Instance as nullLogger} from "../logging/NullLogger";
import {openDatabase, CreateObjectStore} from "../matrix/storage/idb/utils";
export function createMockStorage(): Promise<Storage> {

View file

@ -21,8 +21,8 @@ import {SessionInfoStorage} from "../../matrix/sessioninfo/localstorage/SessionI
import {SettingsStorage} from "./dom/SettingsStorage.js";
import {Encoding} from "./utils/Encoding.js";
import {OlmWorker} from "../../matrix/e2ee/OlmWorker.js";
import {IDBLogger} from "../../logging/IDBLogger.js";
import {ConsoleLogger} from "../../logging/ConsoleLogger.js";
import {IDBLogger} from "../../logging/IDBLogger";
import {ConsoleLogger} from "../../logging/ConsoleLogger";
import {RootView} from "./ui/RootView.js";
import {Clock} from "./dom/Clock.js";
import {ServiceWorkerHandler} from "./dom/ServiceWorkerHandler.js";