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  }