package caddy import ( "encoding/json" "fmt" "net/http" "strings" "time" ) type LogEntry struct { rawLogEntry Timestamp time.Time Duration time.Duration } type logRequest struct { RemoteIP string `json:"remote_ip"` RemotePort string `json:"remote_port"` ClientIP string `json:"client_ip"` Proto string `json:"proto"` Method string `json:"method"` Host string `json:"host"` URI string `json:"uri"` Headers http.Header `json:"headers"` } type rawLogEntry struct { Level string `json:"level"` Timestamp float64 `json:"ts"` Logger string `json:"logger"` Message string `json:"msg"` Upstream string `json:"upstream"` Duration float64 `json:"duration"` Request logRequest `json:"request"` } func FromJSON(jsonData []byte) (logEntry LogEntry, err error) { rawLogEntry := rawLogEntry{} err = json.Unmarshal(jsonData, &rawLogEntry) if err != nil { return logEntry, fmt.Errorf("failed to JSON unmarshal caddy log entry: %w", err) } rawLogEntry.Request.Host = stripPort(rawLogEntry.Request.Host) seconds := int64(rawLogEntry.Timestamp) nanoSeconds := int64((rawLogEntry.Timestamp - float64(seconds)) * 1e9) logEntry.Timestamp = time.Unix(seconds, nanoSeconds) logEntry.Duration = time.Duration(float64(time.Second) * rawLogEntry.Duration) logEntry.rawLogEntry = rawLogEntry return logEntry, nil } func stripPort(host string) string { portIndex := strings.Index(host, ":") if portIndex >= 0 { return host[:portIndex] } return host }