@@ -467,6 +467,11 @@ class DevLogger {
467
467
return LogLevel .info;
468
468
}
469
469
470
+ // Compiled regex for ANSI escape sequences - reused for performance
471
+ static final _ansiCodePattern = RegExp (
472
+ r'(?:\x1B\[[0-9;]*m|\[(?:38;5;)?\d+(?:;\d+)*m)' ,
473
+ );
474
+
470
475
/// Flush the Logger buffer as a single log entry
471
476
///
472
477
/// This method is critical for handling Logger package's multi-line output.
@@ -476,63 +481,94 @@ class DevLogger {
476
481
/// This method:
477
482
/// 1. Combines all buffered lines into a single log entry
478
483
/// 2. Detects the appropriate log level from emoji/text indicators
479
- /// 3. Cleans up box-drawing characters and ANSI color codes
480
- /// 4. Triggers FAB update via MonitoringDataProvider
484
+ /// 3. Extracts the core message for list display
485
+ /// 4. Preserves full content in stackTrace for detail view
486
+ /// 5. Triggers FAB update via MonitoringDataProvider
481
487
///
482
488
/// IMPORTANT: Must call MonitoringDataProvider.instance.triggerUpdate()
483
489
/// after adding the log, otherwise FAB won't update for Logger package logs.
484
490
void _flushLoggerBuffer () {
485
491
if (_loggerBuffer.isEmpty) return ;
486
492
487
- // Combine all lines
488
- final combinedMessage = _loggerBuffer.join ('\n ' );
493
+ // Combine all lines for full content
494
+ final fullMessage = _loggerBuffer.join ('\n ' );
489
495
490
496
// Detect log level from the combined message
491
- // 使用更健壮的检测策略
492
- LogLevel detectedLevel = _detectLogLevel (combinedMessage);
493
-
494
- // Clean up the message for display
495
- String cleanMessage = combinedMessage
496
- // Remove box drawing characters but keep emojis
497
- .replaceAll (RegExp (r'[┌─├│└╟╚╔╗╝═║╠┄]' ), '' )
498
- // Remove ANSI escape sequences (color codes) but preserve the content
499
- .replaceAll (RegExp (r'\x1B\[[0-9;]*m' ), '' )
500
- .replaceAll (RegExp (r'\[38;5;\d+m' ), '' )
501
- .replaceAll (RegExp (r'\[\d+m' ), '' )
502
- .replaceAll ('[0m' , '' )
503
- .split ('\n ' )
504
- .map ((line) => line.trim ())
505
- .where ((line) => line.isNotEmpty)
506
- .join ('\n ' );
507
-
508
- // Extract main message and stack trace if present
509
- String ? mainMessage;
510
- String ? stackTrace;
511
-
512
- final lines = cleanMessage.split ('\n ' );
513
- if (lines.isNotEmpty) {
514
- // First line is usually the main message
515
- mainMessage = lines[0 ];
497
+ LogLevel detectedLevel = _detectLogLevel (fullMessage);
498
+
499
+ // Extract the core message for list display
500
+ String ? coreMessage;
501
+
502
+ // Find the line with emoji or actual log content (not stack trace or timestamps)
503
+ for (final line in _loggerBuffer) {
504
+ // Remove box drawing characters and ANSI codes for checking
505
+ final cleanLine = line
506
+ .replaceAll (RegExp (r'[┌─├│└╟╚╔╗╝═║╠┄]' ), '' )
507
+ .replaceAll (_ansiCodePattern, '' )
508
+ .trim ();
516
509
517
- // If there are more lines and they look like stack trace, treat them as such
518
- if (lines.length > 1 ) {
519
- final stackLines = lines.sublist (1 );
520
- if (stackLines.any ((line) => line.contains ('#' ) || line.contains ('package:' ))) {
521
- stackTrace = stackLines.join ('\n ' );
522
- } else {
523
- // Not a stack trace, include in main message
524
- mainMessage = cleanMessage;
510
+ if (cleanLine.isEmpty) continue ;
511
+
512
+ // Skip stack trace lines
513
+ if (cleanLine.startsWith ('#' ) ||
514
+ (cleanLine.contains ('.dart:' ) && ! cleanLine.contains (RegExp (r'[\u{1F300}-\u{1F9FF}]' , unicode: true ))) ||
515
+ (cleanLine.contains ('package:' ) && ! cleanLine.contains (RegExp (r'[\u{1F300}-\u{1F9FF}]' , unicode: true )))) {
516
+ continue ;
517
+ }
518
+
519
+ // Skip timestamp lines (e.g., "16:49:04.562 (+0:00:01.083551)")
520
+ if (RegExp (r'^\d{2}:\d{2}:\d{2}\.\d+' ).hasMatch (cleanLine)) {
521
+ continue ;
522
+ }
523
+
524
+ // This line likely contains the actual log message
525
+ // Logger package messages often have emojis like 🐛, 💡, ⛔, etc.
526
+ if (cleanLine.contains (RegExp (r'[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]' , unicode: true )) ||
527
+ (! cleanLine.startsWith ('#' ) && cleanLine.length > 2 )) {
528
+ coreMessage = cleanLine;
529
+ break ;
530
+ }
531
+ }
532
+
533
+ // If no core message found, use the first non-empty, non-decoration line
534
+ if (coreMessage == null || coreMessage.isEmpty) {
535
+ for (final line in _loggerBuffer) {
536
+ final cleanLine = line
537
+ .replaceAll (RegExp (r'[┌─├│└╟╚╔╗╝═║╠┄]' ), '' )
538
+ .replaceAll (_ansiCodePattern, '' )
539
+ .trim ();
540
+ if (cleanLine.isNotEmpty && ! cleanLine.startsWith ('#' )) {
541
+ coreMessage = cleanLine;
542
+ break ;
525
543
}
526
544
}
527
545
}
528
546
547
+ // Fallback to cleaned full message if still no core message
548
+ if (coreMessage == null || coreMessage.isEmpty) {
549
+ coreMessage = fullMessage
550
+ .replaceAll (RegExp (r'[┌─├│└╟╚╔╗╝═║╠┄]' ), '' )
551
+ .replaceAll (_ansiCodePattern, '' )
552
+ .split ('\n ' )
553
+ .map ((line) => line.trim ())
554
+ .firstWhere ((line) => line.isNotEmpty, orElse: () => 'Logger output' );
555
+ }
556
+
557
+ // Clean ANSI codes from full message while preserving box drawing characters
558
+ // Use a single comprehensive regex for better performance
559
+ final cleanedFullMessage = fullMessage.replaceAll (
560
+ RegExp (r'(?:\x1B\[[0-9;]*m|\[(?:38;5;)?\d+(?:;\d+)*m)' ),
561
+ '' ,
562
+ );
563
+
529
564
// Create a single log entry
565
+ // Use core message for display in list, cleaned full content in stackTrace for detail view
530
566
final entry = LogEntry (
531
567
timestamp: _loggerBufferStartTime ?? DateTime .now (),
532
568
level: detectedLevel,
533
- message: mainMessage ?? cleanMessage ,
569
+ message: coreMessage ,
534
570
error: null ,
535
- stackTrace: stackTrace,
571
+ stackTrace: cleanedFullMessage, // Store cleaned formatted output for detail view
536
572
);
537
573
538
574
_logs.addLast (entry);
0 commit comments