diff --git a/main.go b/main.go index 9534568..6e126df 100644 --- a/main.go +++ b/main.go @@ -12,6 +12,7 @@ import ( "html/template" "log" "net/http" + "strconv" "sync" "syscall" @@ -126,6 +127,17 @@ func listAgents(w http.ResponseWriter, r *http.Request) { } } + if strings.Contains(r.Header.Get("Accept"), "application") { + w.Header().Set("Content-Type", "application/json") + jsonData, err := json.Marshal(agents) + if err != nil { + http.Error(w, "Failed to encode agents to JSON", http.StatusInternalServerError) + return + } + w.Write(jsonData) + return + } + if err != nil { http.Error(w, "Failed to fetch agents", http.StatusInternalServerError) return @@ -162,12 +174,31 @@ func getAgentIds(w http.ResponseWriter, r *http.Request) { } func logsHandler(w http.ResponseWriter, r *http.Request) { - var logs[] logger.LogEntry - logs, err := logger.FetchLogs(10) + // Warning this bit me in the nose: var count is []string, but + // variable = count[0] is casted to int automatically + // when the string is a number. Jesus Christ, this is odd behavior! + levels := r.URL.Query()["level"] + countStr := r.URL.Query()["limit"] + var limit int = 10 + if len(countStr) > 0 { + parsedCount, err := strconv.Atoi(countStr[0]) + if err == nil { + limit = parsedCount + } else { + http.Error(w, "Invalid count value", http.StatusBadRequest) + return + } + } - // for i, logLine := range logs { - // logs[i].Message = strings.ReplaceAll(logLine.Message, `"`, `\"`) - // } + // This enables not only `level` GET parameters but also selecting by paths + // For example /logs/error is now identical to /logs?level=error + parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/logs/"), "/") + if (len(levels) == 0) && len(parts) > 0 && parts[0] != "" { + levels = []string{parts[0]} + } + + // Call the police... I mean logger + logs, err := logger.FetchLogs(limit, levels) if err != nil { http.Error(w, "Error fetching logs", http.StatusInternalServerError) @@ -176,16 +207,6 @@ func logsHandler(w http.ResponseWriter, r *http.Request) { renderTemplate(w, "templates/partials/logs_partial.html", logs) - -// fmt.Fprintf(w, "
") -// // for _, logEntry := range logsToSend { -// for _, logEntry := range logs { -// fmt.Fprintf(w, "

[%s] [%s] %s

", logEntry.Timestamp, logEntry.Level, logEntry.Message) -// } -// fmt.Fprintf(w, "
") - - // w.Header().Set("Content-Type", "application/json") - // json.NewEncoder(w).Encode(logs) } @@ -211,6 +232,7 @@ func main() { webMux.HandleFunc("/agentIds", getAgentIds) webMux.HandleFunc("/agents/{agentId}", agentsHandler) webMux.HandleFunc("/logs", logsHandler) + webMux.HandleFunc("/logs/{level}", logsHandler) db = database.InitDB (cfg.Database.Username, cfg.Database.Password, cfg.Database.Host, cfg.Database.Port, cfg.Database.Name) defer db.Close() diff --git a/src/logger/logger.go b/src/logger/logger.go index 5fb7a09..2075f16 100644 --- a/src/logger/logger.go +++ b/src/logger/logger.go @@ -95,21 +95,40 @@ func InsertLog(level LogLevel, message string) error { return nil } -func FetchLogs(limit int) ([]LogEntry, error) { +func FetchLogs(limit int, levels []string) ([]LogEntry, error) { lite_dbMutex.Lock() defer lite_dbMutex.Unlock() - query := `SELECT timestamp, level, message FROM logs ORDER BY timestamp DESC LIMIT ?` - rows, err := Lite_db.Query(query, limit) + if len(levels) == 0 { + levels = []string{"%"} + } + + var args []interface{} + placeholders := make([]string, len(levels)) + + for i, level := range levels { + placeholders[i] = "level LIKE ?" + args = append(args, level) + } + + query := fmt.Sprintf(` + SELECT timestamp, level, message + FROM logs + WHERE %s + ORDER BY timestamp DESC + LIMIT ?`, strings.Join(placeholders, " OR ")) + + args = append(args, limit) + + // rows, err := Lite_db.Query(query, level, limit) + rows, err := Lite_db.Query(query, args...) if err != nil { return nil, fmt.Errorf("Error fetching logs: %w", err) } defer rows.Close() - // var logs []string var logs []LogEntry for rows.Next() { - // var message string var logEntry LogEntry if err := rows.Scan( &logEntry.Timestamp, &logEntry.Level, &logEntry.Message); err != nil { return nil, fmt.Errorf("Error scanning row: %w", err) diff --git a/templates/index.html b/templates/index.html index a261aa2..501096a 100644 --- a/templates/index.html +++ b/templates/index.html @@ -90,20 +90,36 @@ @@ -140,23 +156,32 @@

                 
 
-                            
-                

Logs

-
- - - - - - - - - + +
+

Details

+

Select an agent to view details.

+
- -
-

Details

-

Select an agent to view details.

+ +

Logs

+ +
+ + + + + + + +
+ +
+
diff --git a/templates/partials/logs_partial.html b/templates/partials/logs_partial.html index d2a771c..2ab5f80 100644 --- a/templates/partials/logs_partial.html +++ b/templates/partials/logs_partial.html @@ -1,5 +1,5 @@ {{range .}} -

- ts={{.Timestamp}} level={{.Level}} msg="{{.Message}}" -

+
+ ts={{.Timestamp}} level={{.Level}}msg="{{.Message}}" +
{{end}}