Implement IP API daemon with GeoIP database support
- Create modular architecture with separate packages for config, database, HTTP, logging, and state management - Implement Cobra CLI with daemon command - Set up Uber FX dependency injection - Add Chi router with health check and IP lookup endpoints - Implement GeoIP database downloader with automatic updates - Add state persistence for tracking database download times - Include comprehensive test coverage for all components - Configure structured logging with slog - Add Makefile with test, lint, and build targets - Support both IPv4 and IPv6 lookups - Return country, city, ASN, and location data in JSON format
This commit is contained in:
49
internal/log/log.go
Normal file
49
internal/log/log.go
Normal file
@@ -0,0 +1,49 @@
|
||||
// Package log provides structured logging functionality.
|
||||
package log
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"git.eeqj.de/sneak/ipapi/internal/config"
|
||||
)
|
||||
|
||||
// New creates a new logger instance based on configuration.
|
||||
func New(cfg *config.Config) *slog.Logger {
|
||||
var level slog.Level
|
||||
switch strings.ToLower(cfg.LogLevel) {
|
||||
case "debug":
|
||||
level = slog.LevelDebug
|
||||
case "info":
|
||||
level = slog.LevelInfo
|
||||
case "warn", "warning":
|
||||
level = slog.LevelWarn
|
||||
case "error":
|
||||
level = slog.LevelError
|
||||
default:
|
||||
level = slog.LevelInfo
|
||||
}
|
||||
|
||||
opts := &slog.HandlerOptions{
|
||||
Level: level,
|
||||
}
|
||||
|
||||
var handler slog.Handler
|
||||
if isTerminal() {
|
||||
handler = slog.NewTextHandler(os.Stdout, opts)
|
||||
} else {
|
||||
handler = slog.NewJSONHandler(os.Stdout, opts)
|
||||
}
|
||||
|
||||
return slog.New(handler)
|
||||
}
|
||||
|
||||
func isTerminal() bool {
|
||||
fileInfo, err := os.Stdout.Stat()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return (fileInfo.Mode() & os.ModeCharDevice) != 0
|
||||
}
|
||||
38
internal/log/log_test.go
Normal file
38
internal/log/log_test.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.eeqj.de/sneak/ipapi/internal/config"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
logLevel string
|
||||
}{
|
||||
{"debug level", "debug"},
|
||||
{"info level", "info"},
|
||||
{"warn level", "warn"},
|
||||
{"warning level", "warning"},
|
||||
{"error level", "error"},
|
||||
{"invalid level", "invalid"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := &config.Config{
|
||||
LogLevel: tt.logLevel,
|
||||
}
|
||||
logger := New(cfg)
|
||||
if logger == nil {
|
||||
t.Fatal("expected logger, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsTerminal(t *testing.T) {
|
||||
// Just test that it doesn't panic
|
||||
_ = isTerminal()
|
||||
}
|
||||
Reference in New Issue
Block a user