1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
package logging
import (
"log/slog"
"net"
"net/http"
"os"
"strings"
)
var (
AppLog *slog.Logger
SecurityLog *slog.Logger
)
// InitLoggers initializes the application and security loggers.
func InitLoggers() {
// For now, both loggers write to stdout.
// This could be configured to write to different files (e.g., app.log, security.log)
appWriter := os.Stdout
securityWriter := os.Stdout
logLevel := slog.LevelInfo
if strings.ToUpper(os.Getenv("LOG_LEVEL")) == "DEBUG" {
logLevel = slog.LevelDebug
}
AppLog = slog.New(slog.NewTextHandler(appWriter, &slog.HandlerOptions{
Level: logLevel,
AddSource: false, // Include source file and line number for app logs
}))
SecurityLog = slog.New(slog.NewTextHandler(securityWriter, &slog.HandlerOptions{
Level: logLevel,
// The default TextHandler format is `time=... level=... msg=... key=value`.
// This is structured and easily parsable, so we'll stick with it.
}))
}
// GetClientIP extracts the client IP address from the request.
// It prioritizes the X-Forwarded-For header to work behind a reverse proxy.
func GetClientIP(r *http.Request) string {
// Check the X-Forwarded-For header first.
forwardedFor := r.Header.Get("X-Forwarded-For")
if forwardedFor != "" {
// The header can contain a comma-separated list of IPs. The client IP is the first one.
ips := strings.Split(forwardedFor, ",")
return strings.TrimSpace(ips[0])
}
// If the header is not present, fall back to RemoteAddr.
ip, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
// If parsing fails (e.g., no port), return the raw RemoteAddr.
return r.RemoteAddr
}
return ip
}
|