Enhance build system and logging configuration

- Add scripts/build.sh to compile binaries into bin/ directory

- Move all zerolog setup logic from cmd/server/main.go to pkg/config

- Add log level configuration support (trace, debug, info, warn, error, fatal, panic)

- Simplify cmd/server/main.go from 57 to 27 lines (53% reduction)

- Update .gitignore to use bin/ directory instead of individual files

- Document build process and bin directory in AGENTS.md

- Maintain backward compatibility with all existing functionality
This commit is contained in:
2026-04-04 13:24:33 +02:00
parent 9855f521f3
commit 00e796c608
6 changed files with 119 additions and 36 deletions

3
.gitignore vendored
View File

@@ -2,8 +2,7 @@
*.exe
*.test
*.out
server
greet
bin/
# Dependency directories
vendor/

View File

@@ -355,12 +355,37 @@ go test ./...
go test ./pkg/greet/
```
### 5. Make Changes
### 5. Build Binaries
The project uses a build script to compile binaries into the `bin/` directory:
```bash
# Build both server and greet binaries
./scripts/build.sh
# This creates:
# - ./bin/server - The web server binary
# - ./bin/greet - The CLI greeting tool
```
**Binary Usage:**
```bash
# Run the server
./bin/server
# Use the greet CLI
./bin/greet # Output: Hello world!
./bin/greet John # Output: Hello John!
```
**The `bin/` directory is gitignored** to prevent binary files from being committed to the repository.
### 6. Make Changes
- Edit source files in `pkg/` or `cmd/`
- Follow existing patterns and interfaces
- Add tests for new functionality
### 6. Stop and Restart
### 7. Stop and Restart
```bash
./scripts/start-server.sh restart
```

View File

@@ -2,49 +2,19 @@ package main
import (
"context"
"os"
"DanceLessonsCoach/pkg/config"
"DanceLessonsCoach/pkg/server"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
// Initialize Zerolog with default console format first
zerolog.SetGlobalLevel(zerolog.TraceLevel)
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr}
// Check if JSON logging is requested via environment variable
// This allows JSON logging even during config loading
jsonLogging := os.Getenv("DLC_LOGGING_JSON") == "true"
if jsonLogging {
// JSON output for structured logging
log.Logger = log.Output(os.Stderr)
} else {
// Console output for initial logging
log.Logger = log.Output(consoleWriter)
}
// Load configuration
// Load configuration (this will also setup logging)
cfg, err := config.LoadConfig()
if err != nil {
log.Fatal().Err(err).Msg("Failed to load configuration")
}
// Reconfigure logging based on loaded configuration (overrides env var)
if cfg.Logging.JSON {
// JSON output for structured logging
log.Logger = log.Output(os.Stderr)
} else {
// Keep console output
log.Logger = log.Output(consoleWriter)
}
log.Info().Bool("json_logging", cfg.Logging.JSON).Msg("Logging configured")
// Create readiness context to control readiness state
readyCtx, readyCancel := context.WithCancel(context.Background())
defer readyCancel()

View File

@@ -22,6 +22,10 @@ logging:
# When true, logs are output in JSON format instead of console format
json: false
# Log level (default: "trace")
# Options: "trace", "debug", "info", "warn", "error", "fatal", "panic"
level: trace
# Telemetry configuration (OpenTelemetry)
telemetry:
# Enable OpenTelemetry tracing (default: false)
@@ -53,6 +57,7 @@ telemetry:
# DLC_SERVER_PORT=8080
# DLC_SHUTDOWN_TIMEOUT=30s
# DLC_LOGGING_JSON=false
# DLC_LOGGING_LEVEL=trace
# DLC_TELEMETRY_ENABLED=true
# DLC_TELEMETRY_OTLP_ENDPOINT="jaeger:4317"
# DLC_TELEMETRY_SERVICE_NAME="DanceLessonsCoach"

View File

@@ -3,8 +3,10 @@ package config
import (
"fmt"
"os"
"strings"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/viper"
)
@@ -31,6 +33,7 @@ type ShutdownConfig struct {
// LoggingConfig holds logging-related configuration
type LoggingConfig struct {
JSON bool `mapstructure:"json"`
Level string `mapstructure:"level"`
}
// TelemetryConfig holds OpenTelemetry-related configuration
@@ -54,11 +57,16 @@ type SamplerConfig struct {
func LoadConfig() (*Config, error) {
v := viper.New()
// Set up initial console logging for config loading messages
consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr}
log.Logger = log.Output(consoleWriter)
// Set default values
v.SetDefault("server.host", "0.0.0.0")
v.SetDefault("server.port", 8080)
v.SetDefault("shutdown.timeout", 30*time.Second)
v.SetDefault("logging.json", false)
v.SetDefault("logging.level", "trace")
// Telemetry defaults
v.SetDefault("telemetry.enabled", false)
@@ -97,6 +105,7 @@ func LoadConfig() (*Config, error) {
v.BindEnv("server.port", "DLC_SERVER_PORT")
v.BindEnv("shutdown.timeout", "DLC_SHUTDOWN_TIMEOUT")
v.BindEnv("logging.json", "DLC_LOGGING_JSON")
v.BindEnv("logging.level", "DLC_LOGGING_LEVEL")
// Telemetry environment variables
v.BindEnv("telemetry.enabled", "DLC_TELEMETRY_ENABLED")
@@ -113,11 +122,23 @@ func LoadConfig() (*Config, error) {
return nil, fmt.Errorf("config unmarshal error: %w", err)
}
// Configure log output format (JSON or console) first
if config.Logging.JSON {
log.Logger = log.Output(os.Stderr)
} else {
consoleWriter := zerolog.ConsoleWriter{Out: os.Stderr}
log.Logger = log.Output(consoleWriter)
}
// Setup logging based on configuration
config.SetupLogging()
log.Info().
Str("host", config.Server.Host).
Int("port", config.Server.Port).
Dur("shutdown_timeout", config.Shutdown.Timeout).
Bool("logging_json", config.Logging.JSON).
Str("logging_level", config.Logging.Level).
Bool("telemetry_enabled", config.Telemetry.Enabled).
Str("telemetry_service", config.Telemetry.ServiceName).
Msg("Configuration loaded")
@@ -159,3 +180,40 @@ func (c *Config) GetSamplerType() string {
func (c *Config) GetSamplerRatio() float64 {
return c.Telemetry.Sampler.Ratio
}
// GetLogLevel returns the logging level
func (c *Config) GetLogLevel() string {
return c.Logging.Level
}
// SetupLogging configures zerolog based on the configuration
func (c *Config) SetupLogging() {
// Parse log level
level := parseLogLevel(c.GetLogLevel())
zerolog.SetGlobalLevel(level)
zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
}
// parseLogLevel converts a string log level to zerolog.Level
func parseLogLevel(level string) zerolog.Level {
switch strings.ToLower(level) {
case "trace":
return zerolog.TraceLevel
case "debug":
return zerolog.DebugLevel
case "info":
return zerolog.InfoLevel
case "warn", "warning":
return zerolog.WarnLevel
case "error":
return zerolog.ErrorLevel
case "fatal":
return zerolog.FatalLevel
case "panic":
return zerolog.PanicLevel
default:
log.Warn().Str("level", level).Msg("Unknown log level, defaulting to trace")
return zerolog.TraceLevel
}
}

26
scripts/build.sh Executable file
View File

@@ -0,0 +1,26 @@
#!/bin/bash
# DanceLessonsCoach Build Script
# Builds binaries into the bin/ directory
set -e
echo "🔨 Building DanceLessonsCoach binaries..."
# Create bin directory if it doesn't exist
mkdir -p bin
# Build server binary
echo "📦 Building server..."
go build -o bin/server ./cmd/server
# Build greet CLI binary
echo "📦 Building greet CLI..."
go build -o bin/greet ./cmd/greet
echo "✅ Build complete!"
echo " Server binary: ./bin/server"
echo " Greet binary: ./bin/greet"
echo ""
echo "💡 To run the server: ./bin/server"
echo "💡 To use the greet CLI: ./bin/greet [name]"