Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configs and Log Refractor #53

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 12 additions & 13 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,19 @@ type Config struct {
AllowedOrigins []string // Field for the allowed origins
CronCleanupFrequency time.Duration // Field for configuring key cleanup cron
}
Logging struct {
Level string
}
}

// LoadConfig loads the application configuration from environment variables or defaults
func LoadConfig() *Config {
var AppConfig *Config

func init() {
err := godotenv.Load()
if err != nil {
slog.Debug("Warning: .env file not found, falling back to system environment variables.")
}

return &Config{
DiceDBAdmin: struct {
Addr string
Username string
Password string
}{
Addr: getEnv("DICEDB_ADMIN_ADDR", "localhost:7379"), // Default DiceDB Admin address
Username: getEnv("DICEDB_ADMIN_USERNAME", "diceadmin"), // Default DiceDB Admin username
Password: getEnv("DICEDB_ADMIN_PASSWORD", ""), // Default DiceDB Admin password
},
AppConfig = &Config{
DiceDB: struct {
Addr string
Username string
Expand All @@ -77,6 +71,11 @@ func LoadConfig() *Config {
AllowedOrigins: getEnvArray("ALLOWED_ORIGINS", []string{"http://localhost:3000"}), // Default allowed origins
CronCleanupFrequency: time.Duration(getEnvInt("CRON_CLEANUP_FREQUENCY_MINS", 15)) * time.Minute, // Default cron cleanup frequency
},
Logging: struct {
Level string
}{
Level: getEnv("LOGGING_LEVEL", "info"),
},
}
}

Expand Down
87 changes: 87 additions & 0 deletions internal/logger/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package logger

import (
"context"
"fmt"
"log"
"log/slog"
"server/config"
"strings"
"time"
)

type CustomLogHandler struct {
level slog.Level
attrs map[string]interface{}
group string
}

func NewCustomLogHandler(level slog.Level) *CustomLogHandler {
return &CustomLogHandler{
level: level,
attrs: make(map[string]interface{}),
}
}

func (h *CustomLogHandler) Enabled(_ context.Context, level slog.Level) bool {
return level >= h.level
}

func (h *CustomLogHandler) Handle(_ context.Context, record slog.Record) error {
if !h.Enabled(nil, record.Level) {
return nil
}

message := fmt.Sprintf("[%s] [%s] %s", time.Now().Format(time.RFC3339), record.Level, record.Message)

// Append attributes
for k, v := range h.attrs {
message += fmt.Sprintf(" | %s=%v", k, v)
}

// Log to standard output
log.Println(message)
return nil
}

func (h *CustomLogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
newHandler := *h
newHandler.attrs = make(map[string]interface{})

for k, v := range h.attrs {
newHandler.attrs[k] = v
}

for _, attr := range attrs {
newHandler.attrs[attr.Key] = attr.Value.Any()
}
return &newHandler
}

func (h *CustomLogHandler) WithGroup(name string) slog.Handler {
newHandler := *h
newHandler.group = name
return &newHandler
}

func ParseLogLevel(levelStr string) slog.Level {
switch strings.ToLower(levelStr) {
case "debug":
return slog.LevelDebug
case "info":
return slog.LevelInfo
case "warn", "warning":
return slog.LevelWarn
case "error":
return slog.LevelError
default:
return slog.LevelInfo
}
}

func New() *slog.Logger {
levelStr := config.AppConfig.Logging.Level
level := ParseLogLevel(levelStr)
handler := NewCustomLogHandler(level)
return slog.New(handler)
}
3 changes: 1 addition & 2 deletions internal/middleware/cors.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import (

// Updated enableCors function to return a boolean indicating if OPTIONS was handled
func handleCors(w http.ResponseWriter, r *http.Request) bool {
configValue := config.LoadConfig()
allAllowedOrigins := configValue.Server.AllowedOrigins
allAllowedOrigins := config.AppConfig.Server.AllowedOrigins
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why linter is failing here even though the config.LoadConfig is changed here..getting linter error where this function is called in internal/middleware/ratelimiter.go

origin := r.Header.Get("Origin")
allowed := false

Expand Down
3 changes: 1 addition & 2 deletions internal/tests/integration/commands/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ type TestCase struct {
}

func NewHTTPCommandExecutor() (*HTTPCommandExecutor, error) {
configValue := config.LoadConfig()
diceClient, err := db.InitDiceClient(configValue, false)
diceClient, err := db.InitDiceClient(config.AppConfig, false)
if err != nil {
return nil, fmt.Errorf("failed to initialize DiceDB client: %v", err)
}
Expand Down
15 changes: 6 additions & 9 deletions internal/tests/integration/ratelimiter_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ import (
)

func TestRateLimiterWithinLimit(t *testing.T) {
configValue := config.LoadConfig()
limit := configValue.Server.RequestLimitPerMin
window := configValue.Server.RequestWindowSec
limit := config.AppConfig.Server.RequestLimitPerMin
window := config.AppConfig.Server.RequestWindowSec

w, r, rateLimiter := util.SetupRateLimiter(limit, window)

Expand All @@ -24,9 +23,8 @@ func TestRateLimiterWithinLimit(t *testing.T) {
}

func TestRateLimiterExceedsLimit(t *testing.T) {
configValue := config.LoadConfig()
limit := configValue.Server.RequestLimitPerMin
window := configValue.Server.RequestWindowSec
limit := config.AppConfig.Server.RequestLimitPerMin
window := config.AppConfig.Server.RequestWindowSec

w, r, rateLimiter := util.SetupRateLimiter(limit, window)

Expand All @@ -42,9 +40,8 @@ func TestRateLimiterExceedsLimit(t *testing.T) {
}

func TestRateLimitHeadersSet(t *testing.T) {
configValue := config.LoadConfig()
limit := configValue.Server.RequestLimitPerMin
window := configValue.Server.RequestWindowSec
limit := config.AppConfig.Server.RequestLimitPerMin
window := config.AppConfig.Server.RequestWindowSec

w, r, rateLimiter := util.SetupRateLimiter(limit, window)

Expand Down
5 changes: 2 additions & 3 deletions internal/tests/stress/ratelimiter_stress_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ import (
)

func TestRateLimiterUnderStress(t *testing.T) {
configValue := config.LoadConfig()
limit := configValue.Server.RequestLimitPerMin
window := configValue.Server.RequestWindowSec
limit := config.AppConfig.Server.RequestLimitPerMin
window := config.AppConfig.Server.RequestWindowSec

_, r, rateLimiter := util.SetupRateLimiter(limit, window)

Expand Down
13 changes: 7 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@ import (
"os"
"server/config"
"server/internal/db"
"server/internal/logger"
"server/internal/server"
"sync"

_ "github.com/joho/godotenv/autoload"
)

func main() {
configValue := config.LoadConfig()
diceDBAdminClient, err := db.InitDiceClient(configValue, true)
slog.SetDefault(logger.New())
diceDBAdminClient, err := db.InitDiceClient(config.AppConfig, true)
if err != nil {
slog.Error("Failed to initialize DiceDB Admin client: %v", slog.Any("err", err))
os.Exit(1)
}

diceDBClient, err := db.InitDiceClient(configValue, false)
diceDBClient, err := db.InitDiceClient(config.AppConfig, false)
if err != nil {
slog.Error("Failed to initialize DiceDB client: %v", slog.Any("err", err))
os.Exit(1)
Expand All @@ -31,14 +32,14 @@ func main() {
ctx, cancel := context.WithCancel(context.Background())
wg := sync.WaitGroup{}
// Register a cleanup manager, this runs user DiceDB instance cleanup job at configured frequency
cleanupManager := server.NewCleanupManager(diceDBAdminClient, diceDBClient, configValue.Server.CronCleanupFrequency)
cleanupManager := server.NewCleanupManager(diceDBAdminClient, diceDBClient, config.AppConfig.Server.CronCleanupFrequency)
wg.Add(1)
go cleanupManager.Run(ctx, &wg)

// Create mux and register routes
mux := http.NewServeMux()
httpServer := server.NewHTTPServer(":8080", mux, diceDBAdminClient, diceDBClient, configValue.Server.RequestLimitPerMin,
configValue.Server.RequestWindowSec)
httpServer := server.NewHTTPServer(":8080", mux, diceDBAdminClient, diceDBClient, config.AppConfig.Server.RequestLimitPerMin,
config.AppConfig.Server.RequestWindowSec)
mux.HandleFunc("/health", httpServer.HealthCheck)
mux.HandleFunc("/shell/exec/{cmd}", httpServer.CliHandler)
mux.HandleFunc("/search", httpServer.SearchHandler)
Expand Down
3 changes: 1 addition & 2 deletions util/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,8 @@ func ParseHTTPRequest(r *http.Request) (*cmds.CommandRequest, error) {
return nil, errors.New("invalid command")
}

configValue := config.LoadConfig()
// Check if the command is blocklisted
if err := BlockListedCommand(command); err != nil && !configValue.Server.IsTestEnv {
if err := BlockListedCommand(command); err != nil && !config.AppConfig.Server.IsTestEnv {
return nil, err
}

Expand Down
Loading