46
46
import java .io .File ;
47
47
import java .io .IOException ;
48
48
import java .io .InputStream ;
49
+ import java .io .LineNumberReader ;
50
+
49
51
import java .io .UncheckedIOException ;
50
52
import java .net .URI ;
51
53
import java .nio .charset .StandardCharsets ;
61
63
import java .util .HashMap ;
62
64
import java .util .HashSet ;
63
65
import java .util .LinkedHashMap ;
66
+ import java .util .LinkedList ;
64
67
import java .util .List ;
65
68
import java .util .Map ;
66
69
import java .util .Objects ;
@@ -82,13 +85,22 @@ public class ElasticsearchNode implements TestClusterConfiguration {
82
85
private static final Logger LOGGER = Logging .getLogger (ElasticsearchNode .class );
83
86
private static final int ES_DESTROY_TIMEOUT = 20 ;
84
87
private static final TimeUnit ES_DESTROY_TIMEOUT_UNIT = TimeUnit .SECONDS ;
88
+
85
89
private static final int NODE_UP_TIMEOUT = 2 ;
86
90
private static final TimeUnit NODE_UP_TIMEOUT_UNIT = TimeUnit .MINUTES ;
87
91
private static final int ADDITIONAL_CONFIG_TIMEOUT = 15 ;
88
92
private static final TimeUnit ADDITIONAL_CONFIG_TIMEOUT_UNIT = TimeUnit .SECONDS ;
89
93
private static final List <String > OVERRIDABLE_SETTINGS = Arrays .asList (
90
94
"path.repo" ,
91
95
"discovery.seed_providers"
96
+
97
+ );
98
+
99
+ private static final int TAIL_LOG_MESSAGES_COUNT = 40 ;
100
+ private static final List <String > MESSAGES_WE_DONT_CARE_ABOUT = Arrays .asList (
101
+ "Option UseConcMarkSweepGC was deprecated" ,
102
+ "is a pre-release version of Elasticsearch" ,
103
+ "max virtual memory areas vm.max_map_count"
92
104
);
93
105
94
106
private final String path ;
@@ -693,14 +705,73 @@ private void logProcessInfo(String prefix, ProcessHandle.Info info) {
693
705
}
694
706
695
707
private void logFileContents (String description , Path from ) {
696
- LOGGER .error ("{} `{}`" , description , this );
697
- try (Stream <String > lines = Files .lines (from , StandardCharsets .UTF_8 )) {
698
- lines
699
- .map (line -> " " + line )
700
- .forEach (LOGGER ::error );
708
+ final Map <String , Integer > errorsAndWarnings = new LinkedHashMap <>();
709
+ LinkedList <String > ring = new LinkedList <>();
710
+ try (LineNumberReader reader = new LineNumberReader (Files .newBufferedReader (from ))) {
711
+ for (String line = reader .readLine (); line != null ; line = reader .readLine ()) {
712
+ final String lineToAdd ;
713
+ if (ring .isEmpty ()) {
714
+ lineToAdd = line ;
715
+ } else {
716
+ if (line .startsWith ("[" )) {
717
+ lineToAdd = line ;
718
+ // check to see if the previous message (possibly combined from multiple lines) was an error or
719
+ // warning as we want to show all of them
720
+ String previousMessage = normalizeLogLine (ring .getLast ());
721
+ if (MESSAGES_WE_DONT_CARE_ABOUT .stream ().noneMatch (previousMessage ::contains ) &&
722
+ (previousMessage .contains ("ERROR" ) || previousMessage .contains ("WARN" ))) {
723
+ errorsAndWarnings .put (
724
+ previousMessage ,
725
+ errorsAndWarnings .getOrDefault (previousMessage , 0 ) + 1
726
+ );
727
+ }
728
+ } else {
729
+ // We combine multi line log messages to make sure we never break exceptions apart
730
+ lineToAdd = ring .removeLast () + "\n " + line ;
731
+ }
732
+ }
733
+ ring .add (lineToAdd );
734
+ if (ring .size () >= TAIL_LOG_MESSAGES_COUNT ) {
735
+ ring .removeFirst ();
736
+ }
737
+ }
701
738
} catch (IOException e ) {
702
739
throw new UncheckedIOException ("Failed to tail log " + this , e );
703
740
}
741
+
742
+ if (errorsAndWarnings .isEmpty () == false || ring .isEmpty () == false ) {
743
+ LOGGER .error ("\n === {} `{}` ===" , description , this );
744
+ }
745
+ if (errorsAndWarnings .isEmpty () == false ) {
746
+ LOGGER .lifecycle ("\n » ↓ errors and warnings from " + from + " ↓" );
747
+ errorsAndWarnings .forEach ((message , count ) -> {
748
+ LOGGER .lifecycle ("» " + message .replace ("\n " , "\n » " ));
749
+ if (count > 1 ) {
750
+ LOGGER .lifecycle ("» ↑ repeated " + count + " times ↑" );
751
+ }
752
+ });
753
+ }
754
+
755
+ ring .removeIf (line -> MESSAGES_WE_DONT_CARE_ABOUT .stream ().anyMatch (line ::contains ));
756
+
757
+ if (ring .isEmpty () == false ) {
758
+ LOGGER .lifecycle ("» ↓ last " + TAIL_LOG_MESSAGES_COUNT + " non error or warning messages from " + from + " ↓" );
759
+ ring .forEach (message -> {
760
+ if (errorsAndWarnings .containsKey (normalizeLogLine (message )) == false ) {
761
+ LOGGER .lifecycle ("» " + message .replace ("\n " , "\n » " ));
762
+ }
763
+ });
764
+ }
765
+ }
766
+
767
+ private String normalizeLogLine (String line ) {
768
+ if (line .contains ("ERROR" )) {
769
+ return line .substring (line .indexOf ("ERROR" ));
770
+ }
771
+ if (line .contains ("WARN" )) {
772
+ return line .substring (line .indexOf ("WARN" ));
773
+ }
774
+ return line ;
704
775
}
705
776
706
777
private void waitForProcessToExit (ProcessHandle processHandle ) {
0 commit comments