github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/logmon/logging/rotator_test.go (about)

     1  package logging
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"math/rand"
     7  	"os"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/nomad/helper/testlog"
    12  	"github.com/hashicorp/nomad/testutil"
    13  	"github.com/stretchr/testify/require"
    14  	"go.uber.org/goleak"
    15  )
    16  
    17  var (
    18  	baseFileName = "redis.stdout"
    19  )
    20  
    21  func TestFileRotator_IncorrectPath(t *testing.T) {
    22  	defer goleak.VerifyNone(t)
    23  
    24  	_, err := NewFileRotator("/foo", baseFileName, 10, 10, testlog.HCLogger(t))
    25  	require.Error(t, err)
    26  	require.Contains(t, err.Error(), "no such file or directory")
    27  }
    28  
    29  func TestFileRotator_CreateNewFile(t *testing.T) {
    30  	defer goleak.VerifyNone(t)
    31  
    32  	path := t.TempDir()
    33  
    34  	fr, err := NewFileRotator(path, baseFileName, 10, 10, testlog.HCLogger(t))
    35  	require.NoError(t, err)
    36  	defer fr.Close()
    37  
    38  	_, err = os.Stat(filepath.Join(path, "redis.stdout.0"))
    39  	require.NoError(t, err)
    40  }
    41  
    42  func TestFileRotator_OpenLastFile(t *testing.T) {
    43  	defer goleak.VerifyNone(t)
    44  
    45  	path := t.TempDir()
    46  
    47  	fname1 := filepath.Join(path, "redis.stdout.0")
    48  	fname2 := filepath.Join(path, "redis.stdout.2")
    49  
    50  	f1, err := os.Create(fname1)
    51  	require.NoError(t, err)
    52  	f1.Close()
    53  
    54  	f2, err := os.Create(fname2)
    55  	require.NoError(t, err)
    56  	f2.Close()
    57  
    58  	fr, err := NewFileRotator(path, baseFileName, 10, 10, testlog.HCLogger(t))
    59  	require.NoError(t, err)
    60  	defer fr.Close()
    61  
    62  	require.Equal(t, fname2, fr.currentFile.Name())
    63  }
    64  
    65  func TestFileRotator_WriteToCurrentFile(t *testing.T) {
    66  	defer goleak.VerifyNone(t)
    67  
    68  	path := t.TempDir()
    69  
    70  	fname1 := filepath.Join(path, "redis.stdout.0")
    71  	f1, err := os.Create(fname1)
    72  	require.NoError(t, err)
    73  	f1.Close()
    74  
    75  	fr, err := NewFileRotator(path, baseFileName, 10, 5, testlog.HCLogger(t))
    76  	require.NoError(t, err)
    77  	defer fr.Close()
    78  
    79  	fr.Write([]byte("abcde"))
    80  
    81  	testutil.WaitForResult(func() (bool, error) {
    82  		fi, err := os.Stat(fname1)
    83  		if err != nil {
    84  			return false, fmt.Errorf("failed to stat file %v: %w", fname1, err)
    85  		}
    86  		actual := fi.Size()
    87  		if actual != 5 {
    88  			return false, fmt.Errorf("expected size %d but found %d", 5, actual)
    89  		}
    90  
    91  		return true, nil
    92  	}, func(err error) {
    93  		require.NoError(t, err)
    94  	})
    95  }
    96  
    97  func TestFileRotator_RotateFiles(t *testing.T) {
    98  	defer goleak.VerifyNone(t)
    99  
   100  	path := t.TempDir()
   101  
   102  	fr, err := NewFileRotator(path, baseFileName, 10, 5, testlog.HCLogger(t))
   103  	require.NoError(t, err)
   104  	defer fr.Close()
   105  
   106  	str := "abcdefgh"
   107  	nw, err := fr.Write([]byte(str))
   108  	require.NoError(t, err)
   109  	require.Equal(t, len(str), nw)
   110  
   111  	testutil.WaitForResult(func() (bool, error) {
   112  		fname1 := filepath.Join(path, "redis.stdout.0")
   113  		fi, err := os.Stat(fname1)
   114  		if err != nil {
   115  			return false, fmt.Errorf("failed to stat file %v: %w", fname1, err)
   116  		}
   117  		if fi.Size() != 5 {
   118  			return false, fmt.Errorf("expected size: %v, actual: %v", 5, fi.Size())
   119  		}
   120  
   121  		fname2 := filepath.Join(path, "redis.stdout.1")
   122  		if _, err := os.Stat(fname2); err != nil {
   123  			return false, fmt.Errorf("expected file %v to exist", fname2)
   124  		}
   125  
   126  		if fi2, err := os.Stat(fname2); err == nil {
   127  			if fi2.Size() != 3 {
   128  				return false, fmt.Errorf("expected size: %v, actual: %v", 3, fi2.Size())
   129  			}
   130  		} else {
   131  			return false, fmt.Errorf("error getting the file info: %v", err)
   132  		}
   133  
   134  		return true, nil
   135  	}, func(err error) {
   136  		require.NoError(t, err)
   137  	})
   138  }
   139  
   140  func TestFileRotator_RotateFiles_Boundary(t *testing.T) {
   141  	defer goleak.VerifyNone(t)
   142  
   143  	path := t.TempDir()
   144  
   145  	fr, err := NewFileRotator(path, baseFileName, 10, 5, testlog.HCLogger(t))
   146  	require.NoError(t, err)
   147  	defer fr.Close()
   148  
   149  	// We will write three times:
   150  	// 1st: Write with new lines spanning two files
   151  	// 2nd: Write long string with no new lines
   152  	// 3rd: Write a single new line
   153  	expectations := [][]byte{
   154  		[]byte("ab\n"),
   155  		[]byte("cdef\n"),
   156  		[]byte("12345"),
   157  		[]byte("67890"),
   158  		[]byte("\n"),
   159  	}
   160  
   161  	for _, str := range []string{"ab\ncdef\n", "1234567890", "\n"} {
   162  		nw, err := fr.Write([]byte(str))
   163  		require.NoError(t, err)
   164  		require.Equal(t, len(str), nw)
   165  	}
   166  
   167  	testutil.WaitForResult(func() (bool, error) {
   168  
   169  		for i, exp := range expectations {
   170  			fname := filepath.Join(path, fmt.Sprintf("redis.stdout.%d", i))
   171  			fi, err := os.Stat(fname)
   172  			if err != nil {
   173  				return false, fmt.Errorf("failed to stat file %v: %w", fname, err)
   174  			}
   175  			if int(fi.Size()) != len(exp) {
   176  				return false, fmt.Errorf("expected size: %v, actual: %v", len(exp), fi.Size())
   177  			}
   178  		}
   179  
   180  		return true, nil
   181  	}, func(err error) {
   182  		require.NoError(t, err)
   183  	})
   184  }
   185  
   186  func TestFileRotator_WriteRemaining(t *testing.T) {
   187  	defer goleak.VerifyNone(t)
   188  
   189  	path := t.TempDir()
   190  
   191  	fname1 := filepath.Join(path, "redis.stdout.0")
   192  	err := ioutil.WriteFile(fname1, []byte("abcd"), 0600)
   193  	require.NoError(t, err)
   194  
   195  	fr, err := NewFileRotator(path, baseFileName, 10, 5, testlog.HCLogger(t))
   196  	require.NoError(t, err)
   197  	defer fr.Close()
   198  
   199  	str := "efghijkl"
   200  	nw, err := fr.Write([]byte(str))
   201  	require.NoError(t, err)
   202  	require.Equal(t, len(str), nw)
   203  
   204  	testutil.WaitForResult(func() (bool, error) {
   205  		fi, err := os.Stat(fname1)
   206  		if err != nil {
   207  			return false, fmt.Errorf("error getting the file info: %v", err)
   208  		}
   209  		if fi.Size() != 5 {
   210  			return false, fmt.Errorf("expected size: %v, actual: %v", 5, fi.Size())
   211  		}
   212  
   213  		fname2 := filepath.Join(path, "redis.stdout.1")
   214  		if _, err := os.Stat(fname2); err != nil {
   215  			return false, fmt.Errorf("expected file %v to exist", fname2)
   216  		}
   217  
   218  		if fi2, err := os.Stat(fname2); err == nil {
   219  			if fi2.Size() != 5 {
   220  				return false, fmt.Errorf("expected size: %v, actual: %v", 5, fi2.Size())
   221  			}
   222  		} else {
   223  			return false, fmt.Errorf("error getting the file info: %v", err)
   224  		}
   225  
   226  		fname3 := filepath.Join(path, "redis.stdout.2")
   227  		if _, err := os.Stat(fname3); err != nil {
   228  			return false, fmt.Errorf("expected file %v to exist", fname3)
   229  		}
   230  
   231  		if fi3, err := os.Stat(fname3); err == nil {
   232  			if fi3.Size() != 2 {
   233  				return false, fmt.Errorf("expected size: %v, actual: %v", 2, fi3.Size())
   234  			}
   235  		} else {
   236  			return false, fmt.Errorf("error getting the file info: %v", err)
   237  		}
   238  
   239  		return true, nil
   240  	}, func(err error) {
   241  		require.NoError(t, err)
   242  	})
   243  
   244  }
   245  
   246  func TestFileRotator_PurgeOldFiles(t *testing.T) {
   247  	defer goleak.VerifyNone(t)
   248  
   249  	path := t.TempDir()
   250  
   251  	fr, err := NewFileRotator(path, baseFileName, 2, 2, testlog.HCLogger(t))
   252  	require.NoError(t, err)
   253  	defer fr.Close()
   254  
   255  	str := "abcdeghijklmn"
   256  	nw, err := fr.Write([]byte(str))
   257  	require.NoError(t, err)
   258  	require.Equal(t, len(str), nw)
   259  
   260  	testutil.WaitForResult(func() (bool, error) {
   261  		f, err := ioutil.ReadDir(path)
   262  		if err != nil {
   263  			return false, fmt.Errorf("failed to read dir %v: %w", path, err)
   264  		}
   265  
   266  		if len(f) != 2 {
   267  			return false, fmt.Errorf("expected number of files: %v, got: %v %v", 2, len(f), f)
   268  		}
   269  
   270  		return true, nil
   271  	}, func(err error) {
   272  		require.NoError(t, err)
   273  	})
   274  }
   275  
   276  func BenchmarkRotator(b *testing.B) {
   277  	kb := 1024
   278  	for _, inputSize := range []int{kb, 2 * kb, 4 * kb, 8 * kb, 16 * kb, 32 * kb, 64 * kb, 128 * kb, 256 * kb} {
   279  		b.Run(fmt.Sprintf("%dKB", inputSize/kb), func(b *testing.B) {
   280  			benchmarkRotatorWithInputSize(inputSize, b)
   281  		})
   282  	}
   283  }
   284  
   285  func benchmarkRotatorWithInputSize(size int, b *testing.B) {
   286  	path := b.TempDir()
   287  
   288  	fr, err := NewFileRotator(path, baseFileName, 5, 1024*1024, testlog.HCLogger(b))
   289  	require.NoError(b, err)
   290  	defer fr.Close()
   291  
   292  	b.ResetTimer()
   293  
   294  	// run the Fib function b.N times
   295  	for n := 0; n < b.N; n++ {
   296  		// Generate some input
   297  		data := make([]byte, size)
   298  		_, err := rand.Read(data)
   299  		require.NoError(b, err)
   300  
   301  		// Insert random new lines
   302  		for i := 0; i < 100; i++ {
   303  			index := rand.Intn(size)
   304  			data[index] = '\n'
   305  		}
   306  
   307  		// Write the data
   308  		_, err = fr.Write(data)
   309  		require.NoError(b, err)
   310  	}
   311  }