SpringLog
SpringLog は名前付きのロガーです。その静的メンバーは、レベル、ハンドラー、リスナーといったロギング全体の設定を担います。
使う理由
コードは SpringLog を通してログを出し、レコードの出力先はバックエンドが決めます。これでライブラリとアプリケーションが同じ API を使い、コードを変えずにロガー名ごとのレベルを設定できます。
- ロガーはクラスに
@Log()を付けて得るか、定数として宣言します:const log = SpringLog('neon.checkout');。constのロガーは、ログを出すまでコストがかかりません。 - レベルは名前のドットに従います。
neon.checkout.paymentは、自身のレベルが設定されていればそれを、なければneon.checkout、次にneon、最後にルートのレベルを使います。 - ルートのレベルは、変更しなければ
infoです。 - バックエンドがない場合、
printLogHandlerが各レコードを 1 行で書きます:[logger] LEVEL: message。エラーがあればその後に続きます(スタックトレースは出力しません)。出力先は Dart VM では標準出力、Flutter ではデバイスログ、Web ではブラウザーのコンソールです。ターミナルでは行に色が付きます。
例
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"); } }
重要な違い:
@Logは Lombok の@Slf4jに当たりますが、ゲッターはコンパイラープラグインではなくビルドで生成されます。- レベル名は
warnではなくwarningです(設定ではどちらも使えます)。 withFieldsは SLF4J の MDC に当たりますが、スコープはスレッドではなく Dart のゾーンなので、asyncのコードにも引き継がれます。- レコードは
SpringLog.handlerが配送します。Logback や Log4j の設定ファイルはありません。Spring Boot を使う場合、レベルはlogging.level.<name>プロパティから設定されます。