github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/cmd/geth/logging_test.go (about) 1 //go:build integrationtests 2 3 // Copyright 2023 The go-ethereum Authors 4 // This file is part of go-ethereum. 5 // 6 // go-ethereum is free software: you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation, either version 3 of the License, or 9 // (at your option) any later version. 10 // 11 // go-ethereum is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. 18 19 package main 20 21 import ( 22 "bufio" 23 "bytes" 24 "encoding/json" 25 "fmt" 26 "io" 27 "math/rand" 28 "os" 29 "os/exec" 30 "strings" 31 "testing" 32 33 "github.com/ethereum/go-ethereum/internal/reexec" 34 ) 35 36 func runSelf(args ...string) ([]byte, error) { 37 cmd := &exec.Cmd{ 38 Path: reexec.Self(), 39 Args: append([]string{"geth-test"}, args...), 40 } 41 return cmd.CombinedOutput() 42 } 43 44 func split(input io.Reader) []string { 45 var output []string 46 scanner := bufio.NewScanner(input) 47 scanner.Split(bufio.ScanLines) 48 for scanner.Scan() { 49 output = append(output, strings.TrimSpace(scanner.Text())) 50 } 51 return output 52 } 53 54 func censor(input string, start, end int) string { 55 if len(input) < end { 56 return input 57 } 58 return input[:start] + strings.Repeat("X", end-start) + input[end:] 59 } 60 61 func TestLogging(t *testing.T) { 62 t.Parallel() 63 testConsoleLogging(t, "terminal", 6, 24) 64 testConsoleLogging(t, "logfmt", 2, 26) 65 } 66 67 func testConsoleLogging(t *testing.T, format string, tStart, tEnd int) { 68 haveB, err := runSelf("--log.format", format, "logtest") 69 if err != nil { 70 t.Fatal(err) 71 } 72 readFile, err := os.Open(fmt.Sprintf("testdata/logging/logtest-%v.txt", format)) 73 if err != nil { 74 t.Fatal(err) 75 } 76 defer readFile.Close() 77 wantLines := split(readFile) 78 haveLines := split(bytes.NewBuffer(haveB)) 79 for i, want := range wantLines { 80 if i > len(haveLines)-1 { 81 t.Fatalf("format %v, line %d missing, want:%v", format, i, want) 82 } 83 have := haveLines[i] 84 for strings.Contains(have, "Unknown config environment variable") { 85 // This can happen on CI runs. Drop it. 86 haveLines = append(haveLines[:i], haveLines[i+1:]...) 87 have = haveLines[i] 88 } 89 90 // Black out the timestamp 91 have = censor(have, tStart, tEnd) 92 want = censor(want, tStart, tEnd) 93 if have != want { 94 t.Logf(nicediff([]byte(have), []byte(want))) 95 t.Fatalf("format %v, line %d\nhave %v\nwant %v", format, i, have, want) 96 } 97 } 98 if len(haveLines) != len(wantLines) { 99 t.Errorf("format %v, want %d lines, have %d", format, len(haveLines), len(wantLines)) 100 } 101 } 102 103 func TestJsonLogging(t *testing.T) { 104 t.Parallel() 105 haveB, err := runSelf("--log.format", "json", "logtest") 106 if err != nil { 107 t.Fatal(err) 108 } 109 readFile, err := os.Open("testdata/logging/logtest-json.txt") 110 if err != nil { 111 t.Fatal(err) 112 } 113 defer readFile.Close() 114 wantLines := split(readFile) 115 haveLines := split(bytes.NewBuffer(haveB)) 116 for i, wantLine := range wantLines { 117 if i > len(haveLines)-1 { 118 t.Fatalf("format %v, line %d missing, want:%v", "json", i, wantLine) 119 } 120 haveLine := haveLines[i] 121 for strings.Contains(haveLine, "Unknown config environment variable") { 122 // This can happen on CI runs. Drop it. 123 haveLines = append(haveLines[:i], haveLines[i+1:]...) 124 haveLine = haveLines[i] 125 } 126 var have, want []byte 127 { 128 var h map[string]any 129 if err := json.Unmarshal([]byte(haveLine), &h); err != nil { 130 t.Fatal(err) 131 } 132 h["t"] = "xxx" 133 have, _ = json.Marshal(h) 134 } 135 { 136 var w map[string]any 137 if err := json.Unmarshal([]byte(wantLine), &w); err != nil { 138 t.Fatal(err) 139 } 140 w["t"] = "xxx" 141 want, _ = json.Marshal(w) 142 } 143 if !bytes.Equal(have, want) { 144 // show an intelligent diff 145 t.Logf(nicediff(have, want)) 146 t.Errorf("file content wrong") 147 } 148 } 149 } 150 151 func TestVmodule(t *testing.T) { 152 t.Parallel() 153 checkOutput := func(level int, want, wantNot string) { 154 t.Helper() 155 output, err := runSelf("--log.format", "terminal", "--verbosity=0", "--log.vmodule", fmt.Sprintf("logtestcmd_active.go=%d", level), "logtest") 156 if err != nil { 157 t.Fatal(err) 158 } 159 if len(want) > 0 && !strings.Contains(string(output), want) { // trace should be present at 5 160 t.Errorf("failed to find expected string ('%s') in output", want) 161 } 162 if len(wantNot) > 0 && strings.Contains(string(output), wantNot) { // trace should be present at 5 163 t.Errorf("string ('%s') should not be present in output", wantNot) 164 } 165 } 166 checkOutput(5, "log at level trace", "") // trace should be present at 5 167 checkOutput(4, "log at level debug", "log at level trace") // debug should be present at 4, but trace should be missing 168 checkOutput(3, "log at level info", "log at level debug") // info should be present at 3, but debug should be missing 169 checkOutput(2, "log at level warn", "log at level info") // warn should be present at 2, but info should be missing 170 checkOutput(1, "log at level error", "log at level warn") // error should be present at 1, but warn should be missing 171 } 172 173 func nicediff(have, want []byte) string { 174 var i = 0 175 for ; i < len(have) && i < len(want); i++ { 176 if want[i] != have[i] { 177 break 178 } 179 } 180 var end = i + 40 181 var start = i - 50 182 if start < 0 { 183 start = 0 184 } 185 var h, w string 186 if end < len(have) { 187 h = string(have[start:end]) 188 } else { 189 h = string(have[start:]) 190 } 191 if end < len(want) { 192 w = string(want[start:end]) 193 } else { 194 w = string(want[start:]) 195 } 196 return fmt.Sprintf("have vs want:\n%q\n%q\n", h, w) 197 } 198 199 func TestFileOut(t *testing.T) { 200 t.Parallel() 201 var ( 202 have, want []byte 203 err error 204 path = fmt.Sprintf("%s/test_file_out-%d", os.TempDir(), rand.Int63()) 205 ) 206 t.Cleanup(func() { os.Remove(path) }) 207 if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "logtest"); err != nil { 208 t.Fatal(err) 209 } 210 if have, err = os.ReadFile(path); err != nil { 211 t.Fatal(err) 212 } 213 if !bytes.Equal(have, want) { 214 // show an intelligent diff 215 t.Logf(nicediff(have, want)) 216 t.Errorf("file content wrong") 217 } 218 } 219 220 func TestRotatingFileOut(t *testing.T) { 221 t.Parallel() 222 var ( 223 have, want []byte 224 err error 225 path = fmt.Sprintf("%s/test_file_out-%d", os.TempDir(), rand.Int63()) 226 ) 227 t.Cleanup(func() { os.Remove(path) }) 228 if want, err = runSelf(fmt.Sprintf("--log.file=%s", path), "--log.rotate", "logtest"); err != nil { 229 t.Fatal(err) 230 } 231 if have, err = os.ReadFile(path); err != nil { 232 t.Fatal(err) 233 } 234 if !bytes.Equal(have, want) { 235 // show an intelligent diff 236 t.Logf(nicediff(have, want)) 237 t.Errorf("file content wrong") 238 } 239 }