Source

contracts/logging.ts

import {
  LoggerFactory,
  Logging,
  Logger,
  LogLevel,
  MiniLogger,
  NumericLogLevels,
  StringLike,
} from "@decaf-ts/logging";
import { LoggingConfig } from "@decaf-ts/logging";
import { Context as Ctx } from "fabric-contract-api";
import { InternalError } from "@decaf-ts/db-decorators";

/**
 * @description Logger implementation for Fabric chaincode contracts
 * @summary Adapts the standard logging interface to work with Fabric's chaincode context
 *
 * @param {string} context - The logging context name
 * @param {Partial<LoggingConfig> | undefined} conf - Optional logging configuration
 * @param {Ctx} ctx - The Fabric chaincode context
 *
 * @class ContractLogger
 * @extends {MiniLogger}
 * @example
 * ```typescript
 * // In a Fabric chaincode contract
 * import { ContractLogger } from '@decaf-ts/for-fabric';
 *
 * export class MyContract extends Contract {
 *   @Transaction()
 *   async myFunction(ctx: Context): Promise<void> {
 *     const logger = new ContractLogger('MyContract', { level: 'info' }, ctx);
 *
 *     logger.info('Processing transaction');
 *     logger.debug('Transaction details:', { ... });
 *
 *     // Do something
 *
 *     logger.info('Transaction complete');
 *   }
 * }
 * ```
 */
export class ContractLogger extends MiniLogger {
  /**
   * @description The underlying Fabric logger instance
   */
  protected logger!: Logger;

  constructor(
    context: string,
    conf: Partial<LoggingConfig> | undefined,
    ctx?: Ctx
  ) {
    super(context, conf);

    if (!ctx) {
      this.logger = new MiniLogger(context, conf);
    } else {
      this.logger = ctx.logging.getLogger(context) as unknown as Logger;
    }
  }

  /**
   * @description Logs a message at the specified level
   * @summary Overrides the base log method to use the Fabric context's logger
   * @param {LogLevel} level - The log level
   * @param {StringLike | Error} msg - The message to log
   * @param {Error} [stack] - Optional stack trace for errors
   * @return {void}
   */
  protected override log(
    level: LogLevel,
    msg: StringLike | Error,
    stack?: Error
  ) {
    if (
      NumericLogLevels[this.config("level") as LogLevel] <
      NumericLogLevels[level]
    )
      return;

    let method;
    switch (level) {
      case LogLevel.info:
        method = this.logger.info;
        break;
      case LogLevel.verbose:
        method = this.logger.verbose;
        break;
      case LogLevel.debug:
        method = this.logger.debug;
        break;
      case LogLevel.error:
        method = this.logger.error;
        break;
      case LogLevel.silly:
        method = this.logger.silly;
        break;
      default:
        throw new InternalError("Invalid log level");
    }
    method.call(this.logger, this.createLog(level, msg, stack));
  }
}

/**
 * @description Factory function for creating ContractLogger instances
 * @summary Creates a new ContractLogger with the given context, config, and Fabric context
 * @param {string} object - The logging context name
 * @param {Partial<LoggingConfig> | undefined} config - Optional logging configuration
 * @param {Ctx} ctx - The Fabric chaincode context
 * @return {ContractLogger} A new ContractLogger instance
 * @function factory
 * @memberOf module:fabric.contracts
 */
const factory: LoggerFactory = (
  object?: string,
  config?: Partial<LoggingConfig>,
  ctx?: Ctx
) => {
  return new ContractLogger(
    object || ContractLogger.name,
    config || {},
    ctx as Ctx
  );
};

// Set the factory as the default logger factory
Logging.setFactory(factory);