refactor: removing static references to TsLogger
This commit is contained in:
committed by
Piotr Pędziwiatr
parent
a020359a27
commit
b20278e82b
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,3 +15,7 @@ yarn-error.log
|
||||
cache/
|
||||
|
||||
.experiments/
|
||||
|
||||
yalc.lock
|
||||
|
||||
.yalc/
|
||||
|
||||
9
src/cache/impl/BsonFileBlockHeightCache.ts
vendored
9
src/cache/impl/BsonFileBlockHeightCache.ts
vendored
@@ -4,7 +4,6 @@ import BSON from 'bson';
|
||||
import { BlockHeightCacheResult, BlockHeightKey, BlockHeightSwCache } from '@smartweave/cache';
|
||||
import { Benchmark, LoggerFactory } from '@smartweave/logging';
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
/**
|
||||
* An implementation of {@link BlockHeightSwCache} that stores its data in BSON files.
|
||||
* Data is flushed to disk every 10 new cache entries.
|
||||
@@ -32,6 +31,8 @@ const logger = LoggerFactory.INST.create(__filename);
|
||||
* Note: this is not performance-optimized for reading LARGE amount of contracts ;-)
|
||||
*/
|
||||
export class BsonFileBlockHeightSwCache<V = any> implements BlockHeightSwCache<V> {
|
||||
private readonly logger = LoggerFactory.INST.create('BsonFileBlockHeightSwCache');
|
||||
|
||||
// TODO: not sure why I'm using "string" as type for blockHeight...:-)
|
||||
// probably because of some issues with BSON parser...
|
||||
private readonly storage: { [key: string]: { [blockHeight: string]: V } };
|
||||
@@ -71,9 +72,9 @@ export class BsonFileBlockHeightSwCache<V = any> implements BlockHeightSwCache<V
|
||||
|
||||
this.storage[directory][height] = cache as V;
|
||||
});
|
||||
logger.debug(`loading cache for ${directory}`, benchmark.elapsed());
|
||||
this.logger.debug(`loading cache for ${directory}`, benchmark.elapsed());
|
||||
});
|
||||
logger.debug('Storage keys', Object.keys(this.storage));
|
||||
this.logger.debug('Storage keys', Object.keys(this.storage));
|
||||
|
||||
process.on('exit', () => {
|
||||
this.saveCache();
|
||||
@@ -93,7 +94,7 @@ export class BsonFileBlockHeightSwCache<V = any> implements BlockHeightSwCache<V
|
||||
|
||||
// TODO: switch to async, as currently writing cache files may slow down contract execution.
|
||||
try {
|
||||
logger.debug(`==== Storing cache update [${Object.keys(this.updatedStorage).length}] ====`);
|
||||
this.logger.debug(`==== Storing cache update [${Object.keys(this.updatedStorage).length}] ====`);
|
||||
const directoryPath = this.basePath;
|
||||
Object.keys(this.updatedStorage).forEach((key) => {
|
||||
const directory = key;
|
||||
|
||||
@@ -21,8 +21,6 @@ import {
|
||||
import { TransactionStatusResponse } from 'arweave/node/transactions';
|
||||
import { NetworkInfoInterface } from 'arweave/node/network';
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
|
||||
/**
|
||||
* An implementation of {@link Contract} that is backwards compatible with current style
|
||||
* of writing SW contracts (ie. using the "handle" function).
|
||||
@@ -30,6 +28,8 @@ const logger = LoggerFactory.INST.create(__filename);
|
||||
* It requires {@link ExecutorFactory} that is using {@link HandlerApi} generic type.
|
||||
*/
|
||||
export class HandlerBasedContract<State> implements Contract<State> {
|
||||
private readonly logger = LoggerFactory.INST.create('HandlerBasedContract');
|
||||
|
||||
private wallet?: ArWallet;
|
||||
private evaluationOptions: EvaluationOptions = new DefaultEvaluationOptions();
|
||||
public networkInfo?: NetworkInfoInterface = null;
|
||||
@@ -69,16 +69,16 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
blockHeight?: number,
|
||||
currentTx?: { interactionTxId: string; contractTxId: string }[]
|
||||
): Promise<EvalStateResult<State>> {
|
||||
logger.info('Read state for', this.contractTxId);
|
||||
this.logger.info('Read state for', this.contractTxId);
|
||||
this.maybeClearNetworkInfo();
|
||||
|
||||
const { stateEvaluator } = this.smartweave;
|
||||
const benchmark = Benchmark.measure();
|
||||
const executionContext = await this.createExecutionContext(this.contractTxId, blockHeight);
|
||||
logger.debug('context', benchmark.elapsed());
|
||||
this.logger.debug('context', benchmark.elapsed());
|
||||
benchmark.reset();
|
||||
const result = await stateEvaluator.eval(executionContext, currentTx || []);
|
||||
logger.debug('state', benchmark.elapsed());
|
||||
this.logger.debug('state', benchmark.elapsed());
|
||||
return result as EvalStateResult<State>;
|
||||
}
|
||||
|
||||
@@ -89,10 +89,10 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
tags: Tags = [],
|
||||
transfer: ArTransfer = emptyTransfer
|
||||
): Promise<InteractionResult<State, View>> {
|
||||
logger.info('View state for', this.contractTxId);
|
||||
this.logger.info('View state for', this.contractTxId);
|
||||
this.maybeClearNetworkInfo();
|
||||
if (!this.wallet) {
|
||||
logger.warn('Wallet not set.');
|
||||
this.logger.warn('Wallet not set.');
|
||||
}
|
||||
const { arweave, stateEvaluator } = this.smartweave;
|
||||
// create execution context
|
||||
@@ -121,7 +121,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
// eval current state
|
||||
const evalStateResult = await stateEvaluator.eval<State>(executionContext, []);
|
||||
|
||||
logger.debug('Creating new interaction for view state');
|
||||
this.logger.debug('Creating new interaction for view state');
|
||||
|
||||
// create interaction transaction
|
||||
const interaction: ContractInteraction<Input> = {
|
||||
@@ -129,7 +129,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
caller: executionContext.caller
|
||||
};
|
||||
|
||||
logger.trace('interaction', interaction);
|
||||
this.logger.trace('interaction', interaction);
|
||||
// TODO: what is the best/most efficient way of creating a transaction in this case?
|
||||
// creating a real transaction, with multiple calls to Arweave, seems like a huge waste.
|
||||
|
||||
@@ -153,7 +153,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
);
|
||||
|
||||
if (handleResult.type !== 'ok') {
|
||||
logger.fatal('Error while interacting with contract', {
|
||||
this.logger.fatal('Error while interacting with contract', {
|
||||
type: handleResult.type,
|
||||
error: handleResult.errorMessage
|
||||
});
|
||||
@@ -163,7 +163,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
}
|
||||
|
||||
async viewStateForTx<Input, View>(input: Input, transaction: InteractionTx): Promise<InteractionResult<State, View>> {
|
||||
logger.info(`Vies state for ${this.contractTxId}`, transaction);
|
||||
this.logger.info(`Vies state for ${this.contractTxId}`, transaction);
|
||||
this.maybeClearNetworkInfo();
|
||||
const { stateEvaluator } = this.smartweave;
|
||||
|
||||
@@ -209,15 +209,15 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
const response = await arweave.transactions.post(interactionTx);
|
||||
|
||||
if (response.status !== 200) {
|
||||
logger.error('Error while posting transaction', response);
|
||||
this.logger.error('Error while posting transaction', response);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.evaluationOptions.waitForConfirmation) {
|
||||
logger.info('Waiting for confirmation of', interactionTx.id);
|
||||
this.logger.info('Waiting for confirmation of', interactionTx.id);
|
||||
const benchmark = Benchmark.measure();
|
||||
await this.waitForConfirmation(interactionTx.id);
|
||||
logger.info('Transaction confirmed after', benchmark.elapsed());
|
||||
this.logger.info('Transaction confirmed after', benchmark.elapsed());
|
||||
}
|
||||
return interactionTx.id;
|
||||
}
|
||||
@@ -228,11 +228,11 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
const status = await arweave.transactions.getStatus(transactionId);
|
||||
|
||||
if (status.confirmed === null) {
|
||||
logger.info(`Transaction ${transactionId} not yet confirmed. Waiting another 20 seconds before next check.`);
|
||||
this.logger.info(`Transaction ${transactionId} not yet confirmed. Waiting another 20 seconds before next check.`);
|
||||
await sleep(20000);
|
||||
await this.waitForConfirmation(transactionId);
|
||||
} else {
|
||||
logger.info(`Transaction ${transactionId} confirmed`, status);
|
||||
this.logger.info(`Transaction ${transactionId} confirmed`, status);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -248,12 +248,12 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
const benchmark = Benchmark.measure();
|
||||
// if this is a "root" call (ie. original call from SmartWeave's client)
|
||||
if (this.callingContract == null) {
|
||||
logger.debug('Reading network info for root call');
|
||||
this.logger.debug('Reading network info for root call');
|
||||
currentNetworkInfo = await arweave.network.getInfo();
|
||||
this.networkInfo = currentNetworkInfo;
|
||||
} else {
|
||||
// if that's a call from within contract's source code
|
||||
logger.debug('Reusing network info from the calling contract');
|
||||
this.logger.debug('Reusing network info from the calling contract');
|
||||
|
||||
// note: the whole execution tree should use the same network info!
|
||||
// this requirement was not fulfilled in the "v1" SDK - each subsequent
|
||||
@@ -266,7 +266,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
if (blockHeight == null) {
|
||||
blockHeight = currentNetworkInfo.height;
|
||||
}
|
||||
logger.debug('network info', benchmark.elapsed());
|
||||
this.logger.debug('network info', benchmark.elapsed());
|
||||
|
||||
benchmark.reset();
|
||||
const [contractDefinition, interactions] = await Promise.all([
|
||||
@@ -282,7 +282,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
// TODO: this could be further optimized to always load interactions only up to the "root's" call requested height
|
||||
interactionsLoader.load(contractTxId, 0, this.networkInfo.height)
|
||||
]);
|
||||
logger.debug('contract and interactions load', benchmark.elapsed());
|
||||
this.logger.debug('contract and interactions load', benchmark.elapsed());
|
||||
const sortedInteractions = await interactionsSorter.sort(interactions);
|
||||
|
||||
const handler = (await executorFactory.create(contractDefinition)) as HandlerApi<State>;
|
||||
@@ -315,7 +315,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
const sortedInteractions = await interactionsSorter.sort(interactions);
|
||||
const handler = (await executorFactory.create(contractDefinition)) as HandlerApi<State>;
|
||||
|
||||
logger.debug('Creating execution context from tx:', benchmark.elapsed());
|
||||
this.logger.debug('Creating execution context from tx:', benchmark.elapsed());
|
||||
|
||||
return {
|
||||
contractDefinition,
|
||||
@@ -332,7 +332,7 @@ export class HandlerBasedContract<State> implements Contract<State> {
|
||||
|
||||
private maybeClearNetworkInfo() {
|
||||
if (this.callingContract == null) {
|
||||
logger.debug('Clearing network info for the root contract');
|
||||
this.logger.debug('Clearing network info for the root contract');
|
||||
this.networkInfo = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ import { ContractDefinition, DefinitionLoader, getTag, LoggerFactory, SmartWeave
|
||||
import Arweave from 'arweave';
|
||||
import Transaction from 'arweave/web/lib/transaction';
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
|
||||
export class ContractDefinitionLoader implements DefinitionLoader {
|
||||
private readonly logger = LoggerFactory.INST.create('ContractDefinitionLoader');
|
||||
|
||||
constructor(
|
||||
private readonly arweave: Arweave,
|
||||
// TODO: cache should be removed from the core layer and implemented in a wrapper of the core implementation
|
||||
@@ -13,7 +13,7 @@ export class ContractDefinitionLoader implements DefinitionLoader {
|
||||
|
||||
async load<State>(contractTxId: string, forcedSrcTxId?: string): Promise<ContractDefinition<State>> {
|
||||
if (!forcedSrcTxId && this.cache?.contains(contractTxId)) {
|
||||
logger.debug('ContractDefinitionLoader: Hit from cache!');
|
||||
this.logger.debug('ContractDefinitionLoader: Hit from cache!');
|
||||
return Promise.resolve(this.cache?.get(contractTxId) as ContractDefinition<State>);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,9 @@ interface ReqVariables {
|
||||
after?: string;
|
||||
}
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
|
||||
export class ContractInteractionsLoader implements InteractionsLoader {
|
||||
private readonly logger = LoggerFactory.INST.create('ContractInteractionsLoader');
|
||||
|
||||
private static readonly query = `query Transactions($tags: [TagFilter!]!, $blockFilter: BlockFilter!, $first: Int!, $after: String) {
|
||||
transactions(tags: $tags, block: $blockFilter, first: $first, sort: HEIGHT_ASC, after: $after) {
|
||||
pageInfo {
|
||||
@@ -100,7 +100,7 @@ export class ContractInteractionsLoader implements InteractionsLoader {
|
||||
txInfos.push(...transactions.edges.filter((tx) => !tx.node.parent || !tx.node.parent.id));
|
||||
}
|
||||
|
||||
logger.debug('All loaded interactions:', {
|
||||
this.logger.debug('All loaded interactions:', {
|
||||
from: fromBlockHeight,
|
||||
to: toBlockHeight,
|
||||
loaded: txInfos.length
|
||||
@@ -115,10 +115,10 @@ export class ContractInteractionsLoader implements InteractionsLoader {
|
||||
query: ContractInteractionsLoader.query,
|
||||
variables
|
||||
});
|
||||
logger.debug('GQL page load:', benchmark.elapsed());
|
||||
this.logger.debug('GQL page load:', benchmark.elapsed());
|
||||
|
||||
while (response.status === 403) {
|
||||
logger.debug(`GQL rate limiting, waiting ${ContractInteractionsLoader._30seconds}ms before next try.`);
|
||||
this.logger.debug(`GQL rate limiting, waiting ${ContractInteractionsLoader._30seconds}ms before next try.`);
|
||||
|
||||
await sleep(ContractInteractionsLoader._30seconds);
|
||||
|
||||
@@ -133,7 +133,7 @@ export class ContractInteractionsLoader implements InteractionsLoader {
|
||||
}
|
||||
|
||||
if (response.data.errors) {
|
||||
logger.error(response.data.errors);
|
||||
this.logger.error(response.data.errors);
|
||||
throw new Error('Error while loading interaction transactions');
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@ import {
|
||||
} from '@smartweave';
|
||||
import Arweave from 'arweave';
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
|
||||
// FIXME: currently this is tightly coupled with the HandlerApi
|
||||
export class DefaultStateEvaluator implements StateEvaluator {
|
||||
private readonly logger = LoggerFactory.INST.create('DefaultStateEvaluator');
|
||||
|
||||
constructor(
|
||||
private readonly arweave: Arweave,
|
||||
private readonly executionContextModifiers: ExecutionContextModifier[] = []
|
||||
@@ -48,21 +48,21 @@ export class DefaultStateEvaluator implements StateEvaluator {
|
||||
let currentState = baseState.state;
|
||||
const validity = JSON.parse(JSON.stringify(baseState.validity));
|
||||
|
||||
logger.info(
|
||||
this.logger.info(
|
||||
`Evaluating state for ${executionContext.contractDefinition.txId} [${missingInteractions.length} non-cached of ${executionContext.sortedInteractions.length} all]`
|
||||
);
|
||||
|
||||
logger.trace(
|
||||
this.logger.trace(
|
||||
'missingInteractions',
|
||||
missingInteractions.map((int) => {
|
||||
return int.node.id;
|
||||
})
|
||||
);
|
||||
|
||||
logger.trace('Init state', JSON.stringify(baseState.state));
|
||||
this.logger.trace('Init state', JSON.stringify(baseState.state));
|
||||
|
||||
for (const missingInteraction of missingInteractions) {
|
||||
logger.debug(
|
||||
this.logger.debug(
|
||||
`${missingInteraction.node.id}: ${missingInteractions.indexOf(missingInteraction) + 1}/${
|
||||
missingInteractions.length
|
||||
} [of all:${executionContext.sortedInteractions.length}]`
|
||||
@@ -72,13 +72,13 @@ export class DefaultStateEvaluator implements StateEvaluator {
|
||||
|
||||
const inputTag = this.findInputTag(missingInteraction, executionContext);
|
||||
if (!inputTag || inputTag.name !== SmartWeaveTags.INPUT) {
|
||||
logger.error(`Skipping tx with missing or invalid Input tag - ${currentInteraction.id}`);
|
||||
this.logger.error(`Skipping tx with missing or invalid Input tag - ${currentInteraction.id}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const input = this.parseInput(inputTag);
|
||||
if (!input) {
|
||||
logger.error(`Skipping tx with missing or invalid Input tag - ${currentInteraction.id}`);
|
||||
this.logger.error(`Skipping tx with missing or invalid Input tag - ${currentInteraction.id}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ export class DefaultStateEvaluator implements StateEvaluator {
|
||||
executionContext = await modify<State>(currentState, executionContext);
|
||||
currentState = stateCopy;
|
||||
}
|
||||
logger.debug('Interaction evaluation', singleInteractionBenchmark.elapsed());
|
||||
this.logger.debug('Interaction evaluation', singleInteractionBenchmark.elapsed());
|
||||
|
||||
await this.onStateUpdate<State>(
|
||||
currentInteraction,
|
||||
@@ -123,16 +123,16 @@ export class DefaultStateEvaluator implements StateEvaluator {
|
||||
new EvalStateResult(currentState, validity)
|
||||
);
|
||||
}
|
||||
logger.debug('State evaluation total:', stateEvaluationBenchmark.elapsed());
|
||||
this.logger.debug('State evaluation total:', stateEvaluationBenchmark.elapsed());
|
||||
return new EvalStateResult<State>(currentState, validity);
|
||||
}
|
||||
|
||||
private logResult<State>(result: InteractionResult<State, unknown>, currentTx: GQLNodeInterface) {
|
||||
if (result.type === 'exception') {
|
||||
logger.error(`Executing of interaction: ${currentTx.id} threw exception:`, `${result.errorMessage}`);
|
||||
this.logger.error(`Executing of interaction: ${currentTx.id} threw exception:`, `${result.errorMessage}`);
|
||||
}
|
||||
if (result.type === 'error') {
|
||||
logger.warn(`Executing of interaction: ${currentTx.id} returned error:`, result.errorMessage);
|
||||
this.logger.warn(`Executing of interaction: ${currentTx.id} returned error:`, result.errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ export class DefaultStateEvaluator implements StateEvaluator {
|
||||
try {
|
||||
return JSON.parse(inputTag.value);
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
this.logger.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,6 @@ export interface HandlerApi<State> {
|
||||
): Promise<InteractionResult<State, Result>>;
|
||||
}
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
|
||||
/**
|
||||
* A factory that produces handlers that are compatible with the "current" style of
|
||||
* writing SW contracts (ie. using "handle" function).
|
||||
@@ -32,7 +30,12 @@ const logger = LoggerFactory.INST.create(__filename);
|
||||
* First candidate for the refactor!
|
||||
*/
|
||||
export class HandlerExecutorFactory implements ExecutorFactory<HandlerApi<unknown>> {
|
||||
constructor(private readonly arweave: Arweave) {}
|
||||
private readonly logger = LoggerFactory.INST.create('HandlerExecutorFactory');
|
||||
|
||||
constructor(private readonly arweave: Arweave) {
|
||||
this.assignReadContractState = this.assignReadContractState.bind(this);
|
||||
this.assignViewContractState = this.assignViewContractState.bind(this);
|
||||
}
|
||||
|
||||
async create<State>(contractDefinition: ContractDefinition<State>): Promise<HandlerApi<State>> {
|
||||
const normalizedSource = HandlerExecutorFactory.normalizeContractSource(contractDefinition.src);
|
||||
@@ -58,7 +61,7 @@ export class HandlerExecutorFactory implements ExecutorFactory<HandlerApi<unknow
|
||||
const handler = contractFunction(swGlobal, BigNumber, clarity) as HandlerFunction<State, Input, Result>;
|
||||
const stateCopy = JSON.parse(JSON.stringify(state));
|
||||
swGlobal._activeTx = interactionTx;
|
||||
logger.debug(`SmartWeave.contract.id:`, swGlobal.contract.id);
|
||||
self.logger.debug(`SmartWeave.contract.id:`, swGlobal.contract.id);
|
||||
|
||||
self.assignReadContractState<Input, State>(swGlobal, contractDefinition, executionContext, currentTx);
|
||||
self.assignViewContractState<Input, State>(swGlobal, contractDefinition, executionContext);
|
||||
@@ -107,8 +110,7 @@ export class HandlerExecutorFactory implements ExecutorFactory<HandlerApi<unknow
|
||||
executionContext: ExecutionContext<State>
|
||||
) {
|
||||
swGlobal.contracts.viewContractState = async <View>(contractTxId: string, input: any) => {
|
||||
throw new Error('TODO implement');
|
||||
logger.debug('swGlobal.viewContractState call:', {
|
||||
this.logger.debug('swGlobal.viewContractState call:', {
|
||||
from: contractDefinition.txId,
|
||||
to: contractTxId,
|
||||
input
|
||||
@@ -126,7 +128,7 @@ export class HandlerExecutorFactory implements ExecutorFactory<HandlerApi<unknow
|
||||
currentTx: { interactionTxId: string; contractTxId: string }[]
|
||||
) {
|
||||
swGlobal.contracts.readContractState = async (contractTxId: string, height?: number, returnValidity?: boolean) => {
|
||||
logger.debug('swGlobal.readContractState call:', {
|
||||
this.logger.debug('swGlobal.readContractState call:', {
|
||||
from: contractDefinition.txId,
|
||||
to: contractTxId
|
||||
});
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
import { ConsoleLoggerFactory, LogLevel, RedStoneLogger } from '@smartweave';
|
||||
import { ISettingsParam } from 'tslog';
|
||||
import { TsLogFactory } from './node/TsLogFactory';
|
||||
import { LogLevel, RedStoneLogger } from '@smartweave/logging';
|
||||
import { ConsoleLoggerFactory } from './web/ConsoleLoggerFactory';
|
||||
|
||||
export class LoggerFactory {
|
||||
static readonly INST: LoggerFactory = typeof window === 'undefined' ? new TsLogFactory() : new ConsoleLoggerFactory();
|
||||
export interface ILoggerFactory {
|
||||
setOptions(newOptions: any, moduleName?: string): void;
|
||||
|
||||
getOptions(moduleName?: string): any;
|
||||
|
||||
logLevel(level: LogLevel, moduleName?: string): void;
|
||||
|
||||
create(moduleName?: string): RedStoneLogger;
|
||||
}
|
||||
|
||||
export class LoggerFactory implements ILoggerFactory {
|
||||
static INST: ILoggerFactory = new ConsoleLoggerFactory();
|
||||
|
||||
private constructor() {
|
||||
// not instantiable from outside
|
||||
}
|
||||
|
||||
setOptions(newOptions: ISettingsParam, moduleName?: string): void {
|
||||
setOptions(newOptions: any, moduleName?: string): void {
|
||||
LoggerFactory.INST.setOptions(newOptions, moduleName);
|
||||
}
|
||||
|
||||
getOptions(moduleName?: string): ISettingsParam {
|
||||
getOptions(moduleName?: string): any {
|
||||
return LoggerFactory.INST.getOptions(moduleName);
|
||||
}
|
||||
|
||||
@@ -24,4 +33,8 @@ export class LoggerFactory {
|
||||
create(moduleName?: string): RedStoneLogger {
|
||||
return LoggerFactory.INST.create(moduleName);
|
||||
}
|
||||
|
||||
static use(logger: ILoggerFactory) {
|
||||
LoggerFactory.INST = logger;
|
||||
}
|
||||
}
|
||||
|
||||
23
src/logging/LoggerSettings.ts
Normal file
23
src/logging/LoggerSettings.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export const LogLevelOrder = {
|
||||
silly: 0,
|
||||
trace: 1,
|
||||
debug: 2,
|
||||
info: 3,
|
||||
warn: 4,
|
||||
error: 5,
|
||||
fatal: 6
|
||||
};
|
||||
|
||||
/**
|
||||
* Log level names (silly - fatal)
|
||||
* // FIXME: generate from LogLevelOrder with some TS trickery..
|
||||
*/
|
||||
export type LogLevel = 'silly' | 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
||||
|
||||
export interface LoggerSettings {
|
||||
minLevel: LogLevel;
|
||||
}
|
||||
|
||||
export function lvlToOrder(logLevel: LogLevel) {
|
||||
return LogLevelOrder[logLevel];
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
export type LogLevel = 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' | 'silly';
|
||||
|
||||
export interface RedStoneLogger {
|
||||
fatal(message?: any, ...optionalParams: any[]);
|
||||
|
||||
|
||||
@@ -2,4 +2,5 @@ export * from './web/ConsoleLogger';
|
||||
export * from './web/ConsoleLoggerFactory';
|
||||
export * from './RedStoneLogger';
|
||||
export * from './LoggerFactory';
|
||||
export * from './LoggerSettings';
|
||||
export * from './Benchmark';
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import path from 'path';
|
||||
import { ISettingsParam, Logger } from 'tslog';
|
||||
import { LogLevel, RedStoneLogger } from '../RedStoneLogger';
|
||||
import { RedStoneLogger } from '../RedStoneLogger';
|
||||
import { ILoggerFactory, LogLevel } from '@smartweave';
|
||||
|
||||
export const defaultLoggerOptions: ISettingsParam = {
|
||||
displayFunctionName: false,
|
||||
displayFilePath: 'hidden',
|
||||
displayLoggerName: true,
|
||||
dateTimeTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
minLevel: 'debug',
|
||||
minLevel: 'info',
|
||||
overwriteConsole: false
|
||||
};
|
||||
|
||||
@@ -15,7 +16,7 @@ export const defaultLoggerOptions: ISettingsParam = {
|
||||
* A wrapper around "tslog" logging library that allows to change logging settings at runtime
|
||||
* (for each registered module independently, or globally - for all loggers).
|
||||
*/
|
||||
export class TsLogFactory {
|
||||
export class TsLogFactory implements ILoggerFactory {
|
||||
private readonly registeredLoggers: { [moduleName: string]: Logger } = {};
|
||||
private readonly registeredOptions: { [moduleName: string]: ISettingsParam } = {};
|
||||
|
||||
@@ -32,7 +33,10 @@ export class TsLogFactory {
|
||||
// if moduleName not specified
|
||||
if (!moduleName) {
|
||||
// update default options
|
||||
this.defaultOptions = newOptions;
|
||||
this.defaultOptions = {
|
||||
...this.defaultOptions,
|
||||
...newOptions
|
||||
};
|
||||
// update options for all already registered loggers
|
||||
Object.keys(this.registeredLoggers).forEach((key: string) => {
|
||||
this.registeredLoggers[key].setSettings({
|
||||
|
||||
@@ -1,35 +1,67 @@
|
||||
import { RedStoneLogger } from '@smartweave';
|
||||
import { LoggerSettings, LogLevel, lvlToOrder, RedStoneLogger } from '@smartweave';
|
||||
|
||||
export class ConsoleLogger implements RedStoneLogger {
|
||||
constructor(private readonly moduleName, public settings: LoggerSettings) {}
|
||||
|
||||
trace(message?: any, ...optionalParams: any[]) {
|
||||
console.debug(message, optionalParams);
|
||||
if (this.shouldLog('trace')) {
|
||||
// note: no 'trace' for console logger
|
||||
console.debug(this.message('trace', message), optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
error(message?: any, ...optionalParams: any[]) {
|
||||
console.error(message, optionalParams);
|
||||
if (this.shouldLog('error')) {
|
||||
console.error(this.message('error', message), optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
info(message?: any, ...optionalParams: any[]) {
|
||||
console.info(message, optionalParams);
|
||||
if (this.shouldLog('info')) {
|
||||
console.info(this.message('info', message), optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
silly(message?: any, ...optionalParams: any[]) {
|
||||
console.debug(message, optionalParams);
|
||||
if (this.shouldLog('silly')) {
|
||||
// note: no silly level for console logger
|
||||
console.debug(this.message('silly', message), optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
debug(message?: any, ...optionalParams: any[]) {
|
||||
console.debug(message, optionalParams);
|
||||
if (this.shouldLog('debug')) {
|
||||
console.debug(this.message('debug', message), optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
warn(message?: any, ...optionalParams: any[]) {
|
||||
console.warn(message, optionalParams);
|
||||
if (this.shouldLog('warn')) {
|
||||
console.warn(this.message('warn', message), optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
log(message?: any, ...optionalParams: any[]) {
|
||||
console.info(message, optionalParams);
|
||||
if (this.shouldLog('info')) {
|
||||
console.info(this.message('info', message), optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
fatal(message?: any, ...optionalParams: any[]) {
|
||||
console.error(message, optionalParams);
|
||||
if (this.shouldLog('fatal')) {
|
||||
console.error(this.message('fatal', message), optionalParams);
|
||||
}
|
||||
}
|
||||
|
||||
shouldLog(logLevel: LogLevel) {
|
||||
return lvlToOrder(logLevel) >= lvlToOrder(this.settings.minLevel);
|
||||
}
|
||||
|
||||
setSettings(settings: LoggerSettings) {
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
message(lvl: LogLevel, message: string) {
|
||||
return `${new Date().toISOString()} ${lvl.toUpperCase()} [${this.moduleName}] ${message}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { LogLevel, RedStoneLogger } from '@smartweave';
|
||||
import { ILoggerFactory, LoggerSettings, RedStoneLogger } from '@smartweave';
|
||||
import { ConsoleLogger } from './ConsoleLogger';
|
||||
import { ISettingsParam } from 'tslog';
|
||||
import { LogLevel } from '../LoggerSettings';
|
||||
|
||||
export class ConsoleLoggerFactory implements ILoggerFactory {
|
||||
private registeredLoggers: { [moduleName: string]: ConsoleLogger } = {};
|
||||
private readonly registeredOptions: { [moduleName: string]: LoggerSettings } = {};
|
||||
|
||||
private defOptions: LoggerSettings = {
|
||||
minLevel: 'info'
|
||||
};
|
||||
|
||||
export class ConsoleLoggerFactory {
|
||||
constructor() {
|
||||
this.setOptions = this.setOptions.bind(this);
|
||||
this.getOptions = this.getOptions.bind(this);
|
||||
@@ -10,19 +17,62 @@ export class ConsoleLoggerFactory {
|
||||
this.logLevel = this.logLevel.bind(this);
|
||||
}
|
||||
|
||||
setOptions(newOptions: ISettingsParam, moduleName?: string): void {
|
||||
// noop
|
||||
setOptions(newOptions: LoggerSettings, moduleName?: string): void {
|
||||
// FIXME: c/p from TsLogFactory...
|
||||
// if moduleName not specified
|
||||
if (!moduleName) {
|
||||
// update default options
|
||||
this.defOptions = newOptions;
|
||||
// update options for all already registered loggers
|
||||
Object.keys(this.registeredLoggers).forEach((key: string) => {
|
||||
this.registeredLoggers[key].setSettings({
|
||||
...this.registeredLoggers[key].settings,
|
||||
...newOptions
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// if logger already registered
|
||||
if (this.registeredLoggers[moduleName]) {
|
||||
// update its options
|
||||
this.registeredLoggers[moduleName].setSettings({
|
||||
...this.registeredLoggers[moduleName].settings,
|
||||
...newOptions
|
||||
});
|
||||
} else {
|
||||
// if logger not yet registered - save options that will be used for its creation
|
||||
this.registeredOptions[moduleName] = {
|
||||
...this.defOptions,
|
||||
...newOptions
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getOptions(moduleName?: string): ISettingsParam {
|
||||
return {};
|
||||
getOptions(moduleName?: string): LoggerSettings {
|
||||
// FIXME: c/p from TsLogFactory...
|
||||
if (!moduleName) {
|
||||
return this.defOptions;
|
||||
} else {
|
||||
if (this.registeredLoggers[moduleName]) {
|
||||
return this.registeredLoggers[moduleName].settings;
|
||||
} else if (this.registeredOptions[moduleName]) {
|
||||
return this.registeredOptions[moduleName];
|
||||
} else {
|
||||
return this.defOptions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logLevel(level: LogLevel, moduleName?: string) {
|
||||
// noop
|
||||
// FIXME: c/p from TsLogFactory...
|
||||
this.setOptions({ minLevel: level }, moduleName);
|
||||
}
|
||||
|
||||
create(moduleName = 'SWC'): RedStoneLogger {
|
||||
return new ConsoleLogger();
|
||||
if (!Object.prototype.hasOwnProperty.call(this.registeredLoggers, moduleName)) {
|
||||
this.registeredLoggers[moduleName] = new ConsoleLogger(moduleName, this.getOptions(moduleName));
|
||||
}
|
||||
|
||||
return this.registeredLoggers[moduleName];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { BlockHeightKey, BlockHeightSwCache, GQLEdgeInterface, InteractionsLoader, LoggerFactory } from '@smartweave';
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
|
||||
/**
|
||||
* This implementation InteractionsLoader tries to limit the amount of interactions
|
||||
* with GraphQL endpoint. Additionally, it is downloading only the missing interactions
|
||||
* (starting from the latest already cached) - to additionally limit the amount of "paging".
|
||||
*/
|
||||
export class CacheableContractInteractionsLoader implements InteractionsLoader {
|
||||
private readonly logger = LoggerFactory.INST.create('CacheableContractInteractionsLoader');
|
||||
|
||||
constructor(
|
||||
private readonly baseImplementation: InteractionsLoader,
|
||||
private readonly cache: BlockHeightSwCache<GQLEdgeInterface[]>
|
||||
) {}
|
||||
|
||||
async load(contractId: string, fromBlockHeight: number, toBlockHeight: number): Promise<GQLEdgeInterface[]> {
|
||||
logger.debug('Loading interactions', {
|
||||
this.logger.debug('Loading interactions', {
|
||||
contractId,
|
||||
fromBlockHeight,
|
||||
toBlockHeight
|
||||
@@ -26,7 +26,7 @@ export class CacheableContractInteractionsLoader implements InteractionsLoader {
|
||||
};
|
||||
|
||||
if (cachedHeight >= toBlockHeight) {
|
||||
logger.debug('Reusing interactions cached at higher block height:', cachedHeight);
|
||||
this.logger.debug('Reusing interactions cached at higher block height:', cachedHeight);
|
||||
return cachedValue.filter(
|
||||
(interaction: GQLEdgeInterface) =>
|
||||
interaction.node.block.height >= fromBlockHeight && interaction.node.block.height <= toBlockHeight
|
||||
@@ -41,7 +41,7 @@ export class CacheableContractInteractionsLoader implements InteractionsLoader {
|
||||
|
||||
const valueToCache = cachedValue.concat(missingInteractions);
|
||||
|
||||
logger.debug('Interactions load result:', {
|
||||
this.logger.debug('Interactions load result:', {
|
||||
cached: cachedValue.length,
|
||||
missing: missingInteractions.length,
|
||||
total: valueToCache.length,
|
||||
|
||||
@@ -3,12 +3,12 @@ import { ContractDefinition, ExecutorFactory } from '@smartweave/core';
|
||||
import { SwCache } from '@smartweave/cache';
|
||||
import { LoggerFactory } from '@smartweave/logging';
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
|
||||
/**
|
||||
* An implementation of ExecutorFactory that adds caching capabilities
|
||||
*/
|
||||
export class CacheableExecutorFactory<Api> implements ExecutorFactory<Api> {
|
||||
private readonly logger = LoggerFactory.INST.create('CacheableExecutorFactory');
|
||||
|
||||
constructor(
|
||||
arweave: Arweave,
|
||||
private readonly baseImplementation: ExecutorFactory<Api>,
|
||||
@@ -25,7 +25,7 @@ export class CacheableExecutorFactory<Api> implements ExecutorFactory<Api> {
|
||||
// as "evolve" feature changes the srcTxId for the given txId...
|
||||
const cacheKey = `${contractDefinition.txId}_${contractDefinition.srcTxId}`;
|
||||
if (!this.cache.contains(cacheKey)) {
|
||||
logger.debug('Updating executor factory cache');
|
||||
this.logger.debug('Updating executor factory cache');
|
||||
const handler = await this.baseImplementation.create(contractDefinition);
|
||||
this.cache.put(cacheKey, handler);
|
||||
}
|
||||
|
||||
@@ -10,12 +10,12 @@ import Arweave from 'arweave';
|
||||
import { GQLNodeInterface } from '@smartweave/legacy';
|
||||
import { LoggerFactory } from '@smartweave/logging';
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
|
||||
/**
|
||||
* An implementation of DefaultStateEvaluator that adds caching capabilities
|
||||
*/
|
||||
export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
||||
private readonly cLogger = LoggerFactory.INST.create('CacheableStateEvaluator');
|
||||
|
||||
constructor(
|
||||
arweave: Arweave,
|
||||
private readonly cache: BlockHeightSwCache<EvalStateResult<unknown>>,
|
||||
@@ -29,7 +29,7 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
||||
currentTx: { interactionTxId: string; contractTxId: string }[]
|
||||
): Promise<EvalStateResult<State>> {
|
||||
const requestedBlockHeight = executionContext.blockHeight;
|
||||
logger.debug(`Requested state block height: ${requestedBlockHeight}`);
|
||||
this.cLogger.debug(`Requested state block height: ${requestedBlockHeight}`);
|
||||
|
||||
let cachedState: BlockHeightCacheResult<EvalStateResult<State>> | null = null;
|
||||
|
||||
@@ -48,7 +48,7 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
||||
)) as BlockHeightCacheResult<EvalStateResult<State>>;
|
||||
|
||||
if (cachedState != null) {
|
||||
logger.debug(`Cached state for ${executionContext.contractDefinition.txId}`, {
|
||||
this.cLogger.debug(`Cached state for ${executionContext.contractDefinition.txId}`, {
|
||||
block: cachedState.cachedHeight,
|
||||
requestedBlockHeight
|
||||
});
|
||||
@@ -60,7 +60,7 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
||||
);
|
||||
}
|
||||
|
||||
logger.debug(`Interactions until [${requestedBlockHeight}]`, {
|
||||
this.cLogger.debug(`Interactions until [${requestedBlockHeight}]`, {
|
||||
total: sortedInteractionsUpToBlock.length,
|
||||
cached: sortedInteractionsUpToBlock.length - missingInteractions.length
|
||||
});
|
||||
@@ -74,7 +74,7 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
||||
if (entry.contractTxId === executionContext.contractDefinition.txId) {
|
||||
const index = missingInteractions.findIndex((tx) => tx.node.id === entry.interactionTxId);
|
||||
if (index !== -1) {
|
||||
logger.debug('Inf. Loop fix - removing interaction', {
|
||||
this.cLogger.debug('Inf. Loop fix - removing interaction', {
|
||||
contractTxId: entry.contractTxId,
|
||||
interactionTxId: entry.interactionTxId
|
||||
});
|
||||
@@ -85,7 +85,7 @@ export class CacheableStateEvaluator extends DefaultStateEvaluator {
|
||||
|
||||
// if cache is up-to date - return immediately to speed-up the whole process
|
||||
if (missingInteractions.length === 0 && cachedState) {
|
||||
logger.debug(`State up to requested height [${requestedBlockHeight}] fully cached!`);
|
||||
this.cLogger.debug(`State up to requested height [${requestedBlockHeight}] fully cached!`);
|
||||
return cachedState.cachedValue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,6 @@ export interface EvolveCompatibleState {
|
||||
evolve: string; // the transaction id of the Arweave transaction with the updated source code. odd naming convention..
|
||||
}
|
||||
|
||||
const logger = LoggerFactory.INST.create(__filename);
|
||||
|
||||
/*
|
||||
...I'm still not fully convinced to the whole "evolve" idea.
|
||||
|
||||
@@ -46,6 +44,8 @@ function isEvolveCompatible(state: any): state is EvolveCompatibleState {
|
||||
}
|
||||
|
||||
export class Evolve implements ExecutionContextModifier {
|
||||
private readonly logger = LoggerFactory.INST.create('Evolve');
|
||||
|
||||
constructor(
|
||||
private readonly definitionLoader: DefinitionLoader,
|
||||
private readonly executorFactory: ExecutorFactory<HandlerApi<unknown>>
|
||||
@@ -58,9 +58,9 @@ export class Evolve implements ExecutionContextModifier {
|
||||
executionContext: ExecutionContext<State, HandlerApi<State>>
|
||||
): Promise<ExecutionContext<State, HandlerApi<State>>> {
|
||||
const contractTxId = executionContext.contractDefinition.txId;
|
||||
logger.debug(`trying to evolve for: ${contractTxId}`);
|
||||
this.logger.debug(`trying to evolve for: ${contractTxId}`);
|
||||
if (!isEvolveCompatible(state)) {
|
||||
logger.debug('State is not evolve compatible');
|
||||
this.logger.debug('State is not evolve compatible');
|
||||
return executionContext;
|
||||
}
|
||||
const currentSrcTxId = executionContext.contractDefinition.srcTxId;
|
||||
@@ -77,7 +77,7 @@ export class Evolve implements ExecutionContextModifier {
|
||||
canEvolve = true;
|
||||
}
|
||||
if (evolve && /[a-z0-9_-]{43}/i.test(evolve) && canEvolve) {
|
||||
logger.debug('Checking evolve:', {
|
||||
this.logger.debug('Checking evolve:', {
|
||||
current: currentSrcTxId,
|
||||
evolve
|
||||
});
|
||||
@@ -86,7 +86,7 @@ export class Evolve implements ExecutionContextModifier {
|
||||
try {
|
||||
// note: that's really nasty IMO - loading original contract definition,
|
||||
// but forcing different sourceTxId...
|
||||
logger.info('Evolving to: ', evolve);
|
||||
this.logger.info('Evolving to: ', evolve);
|
||||
const newContractDefinition = await this.definitionLoader.load<State>(contractTxId, evolve);
|
||||
const newHandler = (await this.executorFactory.create<State>(newContractDefinition)) as HandlerApi<State>;
|
||||
|
||||
@@ -95,7 +95,7 @@ export class Evolve implements ExecutionContextModifier {
|
||||
contractDefinition: newContractDefinition,
|
||||
handler: newHandler
|
||||
};
|
||||
logger.debug('evolved to:', {
|
||||
this.logger.debug('evolved to:', {
|
||||
txId: modifiedContext.contractDefinition.txId,
|
||||
srcTxId: modifiedContext.contractDefinition.srcTxId
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user