SpringLog

SpringLog は名前付きのロガーです。その静的メンバーは、レベル、ハンドラー、リスナーといったロギング全体の設定を担います。

使う理由

コードは SpringLog を通してログを出し、レコードの出力先はバックエンドが決めます。これでライブラリとアプリケーションが同じ API を使い、コードを変えずにロガー名ごとのレベルを設定できます。

例

import 'package:spring_core_dart/spring_core_dart.dart';
import 'package:neon/spring_gen/app.spring.dart'; // 生成されたファイル

@Service()
@Log(topic: 'neon.checkout') // ロガー名を明示的に指定
class Checkout {
  Checkout(this.gateway);
  final PaymentGateway gateway;

  void pay(Order order) {
    log.info('order placed', fields: {'order': order.id});
    log.debug(() => 'basket: ${order.dump()}'); // 有効なときだけ組み立てる
    try {
      gateway.charge(order);
    } on PaymentException catch (e, s) {
      log.error('payment failed', error: e, stackTrace: s);
    }
  }
}

// `dart:math` も `log` を宣言しているので、そこでは別のゲッター名を選ぶ。
@Component()
@Log(fieldName: 'logger') // ロガー 'neon.Payments'
class Payments {
  void refund() => logger.warning('refund');
}

コードでのレベル、フィールド、リスナー:

const log = SpringLog('neon.jobs');

void configureLogging() {
  SpringLog.level = SpringLogLevel.warning; // ルートのレベル
  SpringLog.setLevel('neon.checkout', SpringLogLevel.debug); // その配下も
}

Future<void> runJob(String jobId) =>
    // 内部で出したすべてのレコード(`await` の後も含む)に `job` が付く。
    SpringLog.withFields({'job': jobId}, () async {
      log.info('started');
      await Future<void>.delayed(const Duration(seconds: 1));
      log.info('finished');
    });

void captureInTest() {
  final records = <SpringLogRecord>[];
  final stop = SpringLog.listen(records.add); // ハンドラーの後に呼ばれる
  log.warning('disk almost full');
  stop();
}

@Log は、そのクラスをスキャンする @ComponentScan の .spring.dart ファイルに log ゲッターを書きます。クラスのライブラリでそのファイルをインポートしてください。デフォルトのロガー名は <package>.<Class> です。

メンバー

@Log:

名前 型 デフォルト 意味
topic String? null ロガー名。null なら <package>.<Class>。
fieldName String 'log' 生成されるゲッターの名前。

SpringLog のインスタンスメンバー:

名前 意味
SpringLog(name) ロガーを作る。通常は const。
name ロガー名。レベルの検索に使う。
trace(message, {fields})、debug(...)、info(...) そのレベルでログを出す。
warning(message, {error, stackTrace, fields})、error(...) そのレベルでログを出す。エラーとスタックトレースは省略可能。
log(level, message, {error, stackTrace, fields}) 実行時に選んだレベルでログを出す。
isEnabled(level) このロガーが現在 level のレコードを出すか。

message にはテキストを返す関数も渡せます。その関数はレコードが出力されるときだけ呼ばれます。それ以外のオブジェクトは toString でテキストになります。

SpringLog の静的メンバー:

名前 意味
level ルートのレベル。設定しなければ SpringLogLevel.info。
setLevel(name, level) ロガーとその配下のレベルを設定する。null で再び継承に戻る。
levelOf(name) ロガー名に適用されるレベル。
configuredLevels、resetLevels() setLevel で設定したレベル。すべて削除する。
handler レコードの出力先(SpringLogHandler)。バックエンドが置き換えなければ printLogHandler。
listen(listener) 出力されるすべてのレコードを listener にも送る。停止する関数を返す。
withFields(fields, body) body の中で出したすべてのレコード(非同期処理も含む)に fields を加えて実行する。
currentFields 現在実行中のコードに withFields が設定したフィールド。
hold() 返された SpringLogHold で release() するまでレコードを保留する。レベルが決まる前の起動コードが使う。

SpringLogLevel: trace、debug、info、warning、error、およびロガーを無効にする off。

SpringLogRecord(ハンドラーとリスナーが受け取るもの): level、loggerName、message、error、stackTrace、fields、time、sequence。

その他のヘルパー: printLogHandlerColours は色付けを強制的にオン/オフします。parseSpringLogLevel(text) は設定に書かれたレベル名を読みます(warn と warning のどちらも可)。LogFormatUtils.formatValue(value) は外部から来た値を引用符で囲み、改行や制御文字を置き換えて、ログ行を偽造できないようにします。

Spring (Java) との対応

// Spring's commons-logging facade
private static final Log log = LogFactory.getLog(Checkout.class);
log.info("order placed");

// Lombok
@Slf4j(topic = "neon.payments")
class Payments { void refund() { log.warn("refund"); } }

重要な違い:

関連項目