Skip to content

Commit

Permalink
added support for env vars as config values
Browse files Browse the repository at this point in the history
  • Loading branch information
Piotr committed Feb 26, 2025
1 parent 1904321 commit 75de0ef
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 35 deletions.
117 changes: 117 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
@@ -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)
}
33 changes: 7 additions & 26 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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")
Expand Down
5 changes: 2 additions & 3 deletions tests/follow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()

Expand Down
4 changes: 2 additions & 2 deletions tests/forward_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down
20 changes: 18 additions & 2 deletions tests/socket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
5 changes: 3 additions & 2 deletions tests/stdin_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}

0 comments on commit 75de0ef

Please sign in to comment.