import 'dart:convert'; import 'dart:developer'; import 'package:flutter/foundation.dart'; import 'package:talker_flutter/talker_flutter.dart'; class HelpLog { HelpLog._(); static final HelpLog _instance = HelpLog._(); static HelpLog get instance => _instance; final Talker _talker = TalkerFlutter.init(); factory HelpLog() => _instance; Talker get tLog => _talker; /// DevTools / 控制台里可按名称过滤。 static const String logName = 'HelpLog'; Future initLogger() async { /// app 信息 } /// 仅 [kDebugMode] 输出;内容经安全格式化,不会因无法 JSON 编码而中断业务逻辑。 static void print( Object? message, { String? tag, String name = logName, }) { if (!kDebugMode) return; try { final text = _stringify(message); final line = tag != null ? '[debug-tag: $tag] ===== $text' : text; log(line, name: name); } catch (e, st) { log( '[SkyLog.print failed] $e', name: name, error: e, stackTrace: st, ); } } /// 尽量输出缩进 JSON;无法编码时退回 [toString],不向外抛错。 static String _stringify(Object? value) { if (value == null) return 'null'; if (value is String) return value; if (value is num || value is bool) return value.toString(); const encoder = JsonEncoder.withIndent(' '); if (value is Map) { try { return encoder.convert(value); } catch (_) { return value.toString(); } } if (value is List) { try { return encoder.convert(value); } catch (_) { return value.toString(); } } if (value is Iterable) { try { return encoder.convert(value.toList()); } catch (_) { return value.toString(); } } try { return encoder.convert(value); } catch (_) { try { return '${value.runtimeType}: $value'; } catch (_) { return ''; } } } /// 错误日志走 Talker;若 Talker 抛错,在 Debug 下用 [log] 兜底,仍不向外抛。 static void error( Object? message, { String? tag, StackTrace? stackTrace, }) { final text = message == null ? 'null' : '$message'; try { _instance._talker.logCustom( CustomLog( text, settings: _instance._talker.settings, tag: tag, logLevel: LogLevel.error, stackTrace: stackTrace, ), ); } catch (e, st) { if (kDebugMode) { log( '[SkyLog.error fallback] $text | $e', name: logName, error: e, stackTrace: st, ); } } } } class CustomLog extends TalkerLog { final String? tag; final TalkerSettings settings; final LogLevel? _logLevel; final StackTrace? _stackTrace; CustomLog( super.message, { this.tag, required this.settings, LogLevel logLevel = LogLevel.debug, StackTrace? stackTrace, }) : _logLevel = logLevel, _stackTrace = stackTrace; @override String? get key => tag; @override LogLevel? get logLevel => _logLevel; @override StackTrace? get stackTrace => _stackTrace; @override AnsiPen? get pen => settings.colors[TalkerKey.fromLogLevel(logLevel ?? LogLevel.info)]; }