diff --git a/src/logger.go b/src/logger.go index 5cfbbc3..4cdc017 100644 --- a/src/logger.go +++ b/src/logger.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "io" "os" "path" @@ -8,6 +9,9 @@ import ( "git.sr.ht/~spc/go-log" ) +var sosReportFolder = "/etc/sos.extras.d" +var sosReportFile = "rhc-worker-logs" + // SetupLogger sets up the logger for the application and returns the log file. // It creates a log folder if it doesn't exist, opens a log file, sets the log level // based on the "YGG_LOG_LEVEL" environment variable, configures the log output to @@ -15,15 +19,13 @@ import ( // such as date-time, filename, and line number. // Returns a pointer to an os.File representing the opened log file. func setupLogger(logFolder string, fileName string) *os.File { - // Check if path exists, if not, create it. - if _, err := os.Stat(logFolder); err != nil { - if err := os.Mkdir(logFolder, os.ModePerm); err != nil { - log.Error(err) - } + if err := checkAndCreateDirectory(logFolder); err != nil { + log.Error(err) } + logFilePath := path.Join(logFolder, fileName) // open log file - logFile, err := os.Create(path.Join(logFolder, fileName)) + logFile, err := os.Create(logFilePath) if err != nil { log.Error(err) } @@ -43,6 +45,9 @@ func setupLogger(logFolder string, fileName string) *os.File { log.SetLevel(log.LevelInfo) } + // Initialization for the sosreport extras plugin + setupSosExtrasReport(logFilePath) + // set log output multWriter := io.MultiWriter(os.Stdout, logFile) log.SetOutput(multWriter) @@ -52,3 +57,26 @@ func setupLogger(logFolder string, fileName string) *os.File { return logFile } + +// setupSosExtrasReport sets up the sos report file for the sos_extra plugin to +// collect the logs for the worker, which is a special file that points out to +// the current path of the logfile for the worker. +func setupSosExtrasReport(logFilePath string) { + if err := checkAndCreateDirectory(sosReportFolder); err != nil { + log.Error(err) + } + + logFile, err := os.Create(path.Join(sosReportFolder, sosReportFile)) + if err != nil { + log.Error(err) + } + defer logFile.Close() + + // sosreport expects that the file content will be in the following format: + // ":/path/to/your/log/file.{log,txt,...}", this will trigger sosreport to + // collect the file without the need to have a special plugin in sosreport. + content := fmt.Sprintf(":%s", logFilePath) + if _, err := logFile.WriteString(content); err != nil { + log.Error(err) + } +} diff --git a/src/logger_test.go b/src/logger_test.go index ea2de56..139303e 100644 --- a/src/logger_test.go +++ b/src/logger_test.go @@ -1,7 +1,9 @@ package main import ( + "fmt" "os" + "path" "path/filepath" "testing" @@ -81,3 +83,30 @@ func TestSetupLogger(t *testing.T) { }) } } + +func TestSetupSosExtrasReport(t *testing.T) { + // FIXME: We are overriding the globals for the below variables, not the + // best approach, but works for now. + sosReportFile = "log-file" + sosReportFolder = t.TempDir() + fileContent := path.Join(sosReportFolder, sosReportFile, "test-file") + expectedFileContent := fmt.Sprintf(":%s", fileContent) + + setupSosExtrasReport(fileContent) + if _, err := os.Stat(sosReportFolder); os.IsNotExist(err) { + t.Errorf("Log folder not created: %v", err) + } + + logFilePath := filepath.Join(sosReportFolder, sosReportFile) + if _, err := os.Stat(logFilePath); os.IsNotExist(err) { + t.Errorf("SOS report file not created: %v", err) + } + + logFile, err := os.ReadFile(logFilePath) + if err != nil { + t.Errorf("Failed to read file: %v", err) + } + if string(logFile) != expectedFileContent { + t.Errorf("File content does not match") + } +} diff --git a/src/main.go b/src/main.go index 6556087..13c828f 100644 --- a/src/main.go +++ b/src/main.go @@ -21,9 +21,10 @@ const logFileName = "rhc-worker-bash.log" var yggdDispatchSocketAddr string var config *Config -// main is the entry point of the application. It initializes values from the environment, -// sets up the logger, establishes a connection with the dispatcher, registers as a handler, -// listens for incoming messages, and starts accepting connections as a Worker service. +// main is the entry point of the application. It initializes values from the +// environment, sets up the logger, establishes a connection with the +// dispatcher, registers as a handler, listens for incoming messages, and +// starts accepting connections as a Worker service. // Note: The function blocks and runs indefinitely until the server is stopped. func main() { var yggSocketAddrExists bool // Has to be separately declared otherwise grpc.Dial doesn't work diff --git a/src/util.go b/src/util.go index fca3471..85f302d 100644 --- a/src/util.go +++ b/src/util.go @@ -156,3 +156,17 @@ func loadConfigOrDefault(filePath string) *Config { setDefaultValues(config) return config } + +// Helper function to check if a directory exists, if not, create the +// directory, otherwise, return nil. If it fails to create the directory, the +// function will return the error raised by `os.Mkdir` to the caller. +func checkAndCreateDirectory(folder string) error { + // Check if path exists, if not, create it. + if _, err := os.Stat(folder); err != nil { + if err := os.Mkdir(folder, os.ModePerm); err != nil { + return err + } + } + + return nil +}