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 }