diff --git a/config.go b/config.go new file mode 100644 index 0000000..d5dd12d --- /dev/null +++ b/config.go @@ -0,0 +1,117 @@ +package main + +import ( + "os" + + "github.com/logdyhq/logdy-core/http" + "github.com/logdyhq/logdy-core/modes" + "github.com/spf13/cobra" +) + +type ConfVal interface { + string | int64 | bool +} + +func getConfigValue[T ConfVal](arg ...T) T { + var zero T + + for _, v := range arg { + // Check if v is not the zero value of its type + switch any(v).(type) { + case string: + if any(v).(string) != "" { + return v + } + case int64: + if any(v).(int64) != 0 { + return v + } + case bool: + // For bool, we consider true as non-zero + // If you want to treat false as valid too, adjust this condition + if any(v).(bool) { + return v + } + } + } + + return zero +} + +func getFlagString(name string, cmd *cobra.Command, def bool) string { + v, _ := cmd.Flags().GetString(name) + if def { + return v + } + if !def && cmd.Flags().Changed(name) { + return v + } + return "" +} + +func getFlagBool(name string, cmd *cobra.Command, def bool) bool { + v, _ := cmd.Flags().GetBool(name) + if def { + return v + } + if !def && cmd.Flags().Changed(name) { + return v + } + return false +} + +func getFlagInt(name string, cmd *cobra.Command, def bool) int64 { + v, _ := cmd.Flags().GetInt64(name) + if def { + return v + } + if !def && cmd.Flags().Changed(name) { + return v + } + return 0 +} + +// this function controls the precedence of arguments for string values passed: +// 1. cli +// 2. env +// 3. default +func getStringCfgVal(cli, env string, cmd *cobra.Command) string { + return getConfigValue(getFlagString(cli, cmd, false), os.Getenv(env), getFlagString(cli, cmd, true)) +} + +// this function controls the precedence of arguments for bool values passed: +// 1. cli +// 2. default +func getBoolCfgVal(cli string, cmd *cobra.Command) bool { + return getConfigValue(getFlagBool(cli, cmd, false), getFlagBool(cli, cmd, true)) +} + +// this function controls the precedence of arguments for int values passed: +// 1. cli +// 2. default +func getIntCfgVal(cli string, cmd *cobra.Command) int64 { + return getConfigValue(getFlagInt(cli, cmd, false), getFlagInt(cli, cmd, true)) +} + +func parseConfig(cmd *cobra.Command) { + config = &http.Config{ + HttpPathPrefix: "", + } + + const prefix = "LOGDY_" + + config.ServerPort = getStringCfgVal("port", prefix+"PORT", cmd) + config.ServerIp = getStringCfgVal("ui-ip", prefix+"UI_IP", cmd) + config.UiPass = getStringCfgVal("ui-pass", prefix+"UI_PASS", cmd) + config.ConfigFilePath = getStringCfgVal("config", prefix+"CONFIG", cmd) + config.AppendToFile = getStringCfgVal("append-to-file", prefix+"APPEND_TO_FILE", cmd) + config.ApiKey = getStringCfgVal("api-key", prefix+"API_KEY", cmd) + + config.BulkWindowMs = getIntCfgVal("bulk-window", cmd) + config.MaxMessageCount = getIntCfgVal("max-message-count", cmd) + + config.AppendToFileRaw = getBoolCfgVal("append-to-file-raw", cmd) + config.AnalyticsDisabled = getBoolCfgVal("no-analytics", cmd) + modes.FallthroughGlobal = getBoolCfgVal("fallthrough", cmd) + modes.DisableANSICodeStripping = getBoolCfgVal("disable-ansi-code-stripping", cmd) +} diff --git a/main.go b/main.go index 279720c..20d1bd3 100644 --- a/main.go +++ b/main.go @@ -191,26 +191,6 @@ func startWebServer(cmd *cobra.Command) { http.StartWebserver(config) } -func parseConfig(cmd *cobra.Command) { - config = &http.Config{ - HttpPathPrefix: "", - } - - config.ServerPort, _ = cmd.Flags().GetString("port") - config.ServerIp, _ = cmd.Flags().GetString("ui-ip") - config.UiPass, _ = cmd.Flags().GetString("ui-pass") - config.ConfigFilePath, _ = cmd.Flags().GetString("config") - config.BulkWindowMs, _ = cmd.Flags().GetInt64("bulk-window") - config.AppendToFile, _ = cmd.Flags().GetString("append-to-file") - config.ApiKey, _ = cmd.Flags().GetString("api-key") - config.AppendToFileRaw, _ = cmd.Flags().GetBool("append-to-file-raw") - config.MaxMessageCount, _ = cmd.Flags().GetInt64("max-message-count") - config.AnalyticsDisabled, _ = cmd.Flags().GetBool("no-analytics") - - modes.FallthroughGlobal, _ = cmd.Flags().GetBool("fallthrough") - modes.DisableANSICodeStripping, _ = cmd.Flags().GetBool("disable-ansi-code-stripping") -} - func init() { utils.InitLogger() http.InitChannel() @@ -220,12 +200,13 @@ func init() { UtilsCmd.AddCommand(utilsCutByDateCmd) UtilsCmd.AddCommand(utilsCutByLineNumberCmd) - rootCmd.PersistentFlags().StringP("port", "p", "8080", "Port on which the Web UI will be served") - rootCmd.PersistentFlags().StringP("ui-ip", "", "127.0.0.1", "Bind Web UI server to a specific IP address") - rootCmd.PersistentFlags().StringP("ui-pass", "", "", "Password that will be used to authenticate in the UI") - rootCmd.PersistentFlags().StringP("config", "", "", "Path to a file where a config (json) for the UI is located") - rootCmd.PersistentFlags().StringP("append-to-file", "", "", "Path to a file where message logs will be appended, the file will be created if it doesn't exist") - rootCmd.PersistentFlags().StringP("api-key", "", "", "API key (send as a header "+http.API_KEY_HEADER_NAME+")") + rootCmd.PersistentFlags().StringP("port", "p", "8080", "Port on which the Web UI will be served (env: LOGDY_PORT)") + rootCmd.PersistentFlags().StringP("ui-ip", "", "127.0.0.1", "Bind Web UI server to a specific IP address (env: LOGDY_UI_IP)") + rootCmd.PersistentFlags().StringP("ui-pass", "", "", "Password that will be used to authenticate in the UI (env: LOGDY_UI_PASS)") + rootCmd.PersistentFlags().StringP("config", "", "", "Path to a file where a config (json) for the UI is located (env: LOGDY_CONFIG)") + rootCmd.PersistentFlags().StringP("append-to-file", "", "", "Path to a file where message logs will be appended, the file will be created if it doesn't exist (env: LOGDY_APPEND_TO_FILE)") + rootCmd.PersistentFlags().StringP("api-key", "", "", "API key (send as a header "+http.API_KEY_HEADER_NAME+") (env: LOGDY_API_KEY)") + rootCmd.PersistentFlags().Int64P("bulk-window", "", 100, "A time window during which log messages are gathered and send in a bulk to a client. Decreasing this window will improve the 'real-time' feeling of messages presented on the screen but could decrease UI performance") rootCmd.PersistentFlags().Int64P("max-message-count", "", 100_000, "Max number of messages that will be stored in a buffer for further retrieval. On buffer overflow, oldest messages will be removed.") rootCmd.PersistentFlags().BoolP("verbose", "v", false, "Verbose logs") diff --git a/tests/follow_test.go b/tests/follow_test.go index 9af1158..9bea97a 100644 --- a/tests/follow_test.go +++ b/tests/follow_test.go @@ -15,7 +15,6 @@ import ( ) func readOutput(t *testing.T, stdout io.ReadCloser, outputChan chan string, followChan chan bool, wg *sync.WaitGroup) { - defer wg.Done() reader := bufio.NewReader(stdout) for { line, err := reader.ReadString('\n') @@ -70,7 +69,7 @@ func TestLogdyE2E_FollowFullRead(t *testing.T) { }() // Start logdy process in follow mode with full-read and fallthrough enabled - cmd := exec.Command("go", "run", "../main.go", "follow", "--full-read", "-t", pipeName) + cmd := exec.Command("go", "run", "../.", "follow", "--full-read", "-t", pipeName) // Get stdout pipe stdout, err := cmd.StdoutPipe() @@ -135,7 +134,7 @@ func TestLogdyE2E_FollowFullRead(t *testing.T) { if err := cmd.Process.Kill(); err != nil { t.Errorf("Failed to kill process: %v", err) } - + cmd.Wait() // Wait for the output reader goroutine to finish wg.Wait() diff --git a/tests/forward_test.go b/tests/forward_test.go index ba6d688..7355974 100644 --- a/tests/forward_test.go +++ b/tests/forward_test.go @@ -48,7 +48,7 @@ func TestLogdyE2E_Forward(t *testing.T) { wgServer.Wait() // Start logdy process - cmd := exec.Command("go", "run", "../main.go", "forward", "8475") + cmd := exec.Command("go", "run", "../.", "forward", "8475") // Get stdin pipe stdin, err := cmd.StdinPipe() @@ -104,7 +104,7 @@ func TestLogdyE2E_Forward(t *testing.T) { if err := cmd.Process.Kill(); err != nil { t.Errorf("Failed to kill process: %v", err) } - + cmd.Wait() // Verify received messages assert.Equal(t, len(testLines), len(msgReceived)) for i, testLine := range testLines { diff --git a/tests/socket_test.go b/tests/socket_test.go index 03b31b0..17d39a5 100644 --- a/tests/socket_test.go +++ b/tests/socket_test.go @@ -24,10 +24,12 @@ func TestLogdyE2E_Socket(t *testing.T) { wg.Add(3) // Expect 3 messages (1 from each port) // Start logdy process with -t flag for stdout output - cmd := exec.Command("go", "run", "../main.go", "socket", "-t", "8475", "8476", "8477") + cmd := exec.Command("go", "run", "../.", "socket", "-t", "8475", "8476", "8477") // Get stdout pipe for verifying messages stdout, err := cmd.StdoutPipe() assert.NoError(t, err) + stderr, err := cmd.StderrPipe() + assert.NoError(t, err) // Start reading stdout in background go func() { @@ -46,6 +48,20 @@ func TestLogdyE2E_Socket(t *testing.T) { } }() + go func() { + scanner := bufio.NewScanner(stderr) + for scanner.Scan() { + line := scanner.Text() + if line != "exit status 1" { + t.Log(line) + t.Error("Stderr produced content!") + // if error is: panic: listen tcp 127.0.0.1:8080: bind: address already in use + // the previous test has not closed the process lsof -i :8080 + return + } + } + }() + // Start the process err = cmd.Start() assert.NoError(t, err) @@ -110,7 +126,7 @@ func TestLogdyE2E_Socket(t *testing.T) { if err := cmd.Process.Kill(); err != nil { t.Errorf("Failed to kill process: %v", err) } - + cmd.Wait() // Verify we received messages from all ports assert.Equal(t, 3, len(msgReceived), "Expected 3 messages, got %d", len(msgReceived)) for i, port := range ports { diff --git a/tests/stdin_test.go b/tests/stdin_test.go index bd26dad..400ec9f 100644 --- a/tests/stdin_test.go +++ b/tests/stdin_test.go @@ -121,6 +121,7 @@ func runCmd(cmds []string, t *testing.T) { case <-time.After(2 * time.Second): // Force kill if it didn't exit cleanly cmd.Process.Kill() + cmd.Wait() } // Verify output matches input @@ -131,9 +132,9 @@ func runCmd(cmds []string, t *testing.T) { } func TestLogdyE2E_NoCommand(t *testing.T) { - runCmd([]string{"run", "../main.go", "-t"}, t) + runCmd([]string{"run", "../.", "-t"}, t) } func TestLogdyE2E_StdinCommand(t *testing.T) { - runCmd([]string{"run", "../main.go", "stdin", "-t"}, t) + runCmd([]string{"run", "../.", "stdin", "-t"}, t) }