@@ -32,8 +32,10 @@ import (
3232 "time"
3333
3434 "github.com/stretchr/testify/assert"
35+ "github.com/stretchr/testify/require"
3536 "go.uber.org/zap/zapcore"
3637
38+ "github.com/elastic/elastic-agent-libs/file"
3739 "github.com/elastic/elastic-agent-libs/logp"
3840)
3941
@@ -437,3 +439,67 @@ func takeAllLogsFromPath(t *testing.T, path string) []map[string]any {
437439
438440 return entries
439441}
442+
443+ func TestLoggerRotateSymlink (t * testing.T ) {
444+ dir := t .TempDir ()
445+
446+ cfg := logp .DefaultConfig (logp .DefaultEnvironment )
447+ cfg .Beat = "logger"
448+ cfg .ToFiles = true
449+ cfg .Files .Path = dir
450+ cfg .Files .RotateOnStartup = false
451+
452+ logname := cfg .Beat
453+
454+ privateFileContents := []byte ("original contents" )
455+ privateFile := filepath .Join (dir , "private" )
456+ err := os .WriteFile (privateFile , privateFileContents , 0644 )
457+ require .NoError (t , err )
458+
459+ // Plant a symlink to the private file by guessing the log filename.
460+ guessedFilename := filepath .Join (dir , fmt .Sprintf ("%s-%s.ndjson" , logname , time .Now ().Format (file .DateFormat )))
461+ err = os .Symlink (privateFile , guessedFilename )
462+ require .NoError (t , err )
463+
464+ err = logp .Configure (cfg )
465+ require .NoError (t , err )
466+
467+ logLine := "a info message"
468+ logp .L ().Info (logLine )
469+
470+ // The file rotation should have detected the destination is a symlink and rotated before writing.
471+ rotatedFilename := filepath .Join (dir , fmt .Sprintf ("%s-%s-1.ndjson" , logname , time .Now ().Format (file .DateFormat )))
472+ assertDirContents (t , dir , filepath .Base (privateFile ), filepath .Base (guessedFilename ), filepath .Base (rotatedFilename ))
473+
474+ got , err := os .ReadFile (privateFile )
475+ require .NoError (t , err )
476+ assert .Equal (t , privateFileContents , got , "The symlink target should not have been modified" )
477+
478+ got , err = os .ReadFile (rotatedFilename )
479+ require .NoError (t , err )
480+ assert .Contains (t , string (got ), logLine , "The rotated file should contain the log message" )
481+
482+ // Prevents a windows issue with cleaning up files still in use:
483+ // Error: TempDir RemoveAll cleanup: remove t.TempDir() The process cannot access the file because it is being used by another process.
484+ require .NoError (t , logp .L ().Sync ())
485+ assert .NoError (t , logp .L ().Close ())
486+ // require.NoError(t, os.Remove(privateFile))
487+ // require.NoError(t, os.Remove(guessedFilename))
488+ // require.NoError(t, os.Remove(rotatedFilename))
489+ }
490+
491+ func assertDirContents (t * testing.T , dir string , files ... string ) {
492+ t .Helper ()
493+
494+ f , err := os .Open (dir )
495+ if err != nil {
496+ t .Fatal (err )
497+ }
498+
499+ names , err := f .Readdirnames (- 1 )
500+ if err != nil {
501+ t .Fatal (err )
502+ }
503+
504+ assert .ElementsMatch (t , files , names )
505+ }
0 commit comments