176 lines
4.0 KiB
Go
176 lines
4.0 KiB
Go
package controller
|
|
|
|
import (
|
|
"bufio"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/Caknoooo/go-gin-clean-starter/pkg/constants"
|
|
"github.com/Caknoooo/go-gin-clean-starter/pkg/utils"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/samber/do"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type (
|
|
LogsController interface {
|
|
GetLogs(ctx *gin.Context)
|
|
GetLogsByMonth(ctx *gin.Context)
|
|
ServeLogsPage(ctx *gin.Context)
|
|
}
|
|
|
|
logsController struct {
|
|
db *gorm.DB
|
|
}
|
|
)
|
|
|
|
func NewLogsController(injector *do.Injector) LogsController {
|
|
db := do.MustInvokeNamed[*gorm.DB](injector, constants.DB)
|
|
return &logsController{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
func (c *logsController) ServeLogsPage(ctx *gin.Context) {
|
|
acceptHeader := ctx.GetHeader("Accept")
|
|
if strings.Contains(acceptHeader, "text/html") {
|
|
month := strings.ToLower(time.Now().Format("January"))
|
|
ctx.HTML(http.StatusOK, "logs.html", gin.H{
|
|
"Month": month,
|
|
"QueryLogs": []string{},
|
|
"AppLogs": []string{},
|
|
})
|
|
return
|
|
}
|
|
ctx.Next()
|
|
}
|
|
|
|
func (c *logsController) GetLogs(ctx *gin.Context) {
|
|
month := strings.ToLower(time.Now().Format("January"))
|
|
c.getLogsForMonth(ctx, month)
|
|
}
|
|
|
|
func (c *logsController) GetLogsByMonth(ctx *gin.Context) {
|
|
month := ctx.Param("month")
|
|
c.getLogsForMonth(ctx, month)
|
|
}
|
|
|
|
func (c *logsController) getLogsForMonth(ctx *gin.Context, month string) {
|
|
// Multiple possible log locations
|
|
logLocations := []string{
|
|
"/app/config/logs/query_log",
|
|
"/app/config/logs/app_log",
|
|
"./config/logs/query_log",
|
|
"./config/logs/app_log",
|
|
"/tmp/logs",
|
|
}
|
|
|
|
var queryLogs []string
|
|
var appLogs []string
|
|
|
|
for _, logDir := range logLocations {
|
|
// Read query logs
|
|
queryLogFile := filepath.Join(logDir, month+"_query.log")
|
|
// fmt.Println(queryLogFile)
|
|
if queryEntries, err := readLogFile(queryLogFile); err == nil {
|
|
queryLogs = queryEntries
|
|
}
|
|
|
|
// Read app logs - read as JSON lines
|
|
appLogFile := filepath.Join(logDir, month+"_app.log")
|
|
if appEntries, err := readLogFileAsJSONLines(appLogFile); err == nil {
|
|
appLogs = appEntries
|
|
}
|
|
|
|
// If both found, break early
|
|
if len(queryLogs) > 0 && len(appLogs) > 0 {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Reverse slices untuk menampilkan log terbaru di atas
|
|
reverseSlice(queryLogs)
|
|
reverseSlice(appLogs)
|
|
|
|
// Public access - maybe limit the logs or show recent only
|
|
if len(queryLogs) > 100 {
|
|
queryLogs = queryLogs[:100] // Limit to 100 latest
|
|
}
|
|
if len(appLogs) > 100 {
|
|
appLogs = appLogs[:100] // Limit to 100 latest
|
|
}
|
|
|
|
// Jika request Accept header adalah application/json, kembalikan JSON
|
|
if ctx.GetHeader("Accept") == "application/json" {
|
|
res := utils.BuildResponseSuccess("Success get logs", gin.H{
|
|
"month": month,
|
|
"query_logs": queryLogs,
|
|
"app_logs": appLogs,
|
|
"total_query": len(queryLogs),
|
|
"total_app": len(appLogs),
|
|
})
|
|
ctx.JSON(http.StatusOK, res)
|
|
return
|
|
}
|
|
|
|
// Default kembalikan HTML page
|
|
ctx.HTML(http.StatusOK, "logs.html", gin.H{
|
|
"Month": month,
|
|
"QueryLogs": queryLogs,
|
|
"AppLogs": appLogs,
|
|
})
|
|
}
|
|
|
|
// readLogFile membaca file log biasa (query log)
|
|
func readLogFile(filename string) ([]string, error) {
|
|
content, err := os.ReadFile(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Normalisasi newline (biar Windows/Linux aman)
|
|
text := strings.ReplaceAll(string(content), "\r\n", "\n")
|
|
|
|
// Pisahkan antar entry pakai double newline
|
|
rawEntries := strings.Split(text, "\n\n")
|
|
|
|
var logs []string
|
|
for _, entry := range rawEntries {
|
|
entry = strings.TrimSpace(entry)
|
|
if entry != "" {
|
|
logs = append(logs, entry)
|
|
}
|
|
}
|
|
return logs, nil
|
|
}
|
|
|
|
// readLogFileAsJSONLines membaca file log yang berisi JSON lines (app log)
|
|
func readLogFileAsJSONLines(filename string) ([]string, error) {
|
|
file, err := os.Open(filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer file.Close()
|
|
|
|
var logs []string
|
|
scanner := bufio.NewScanner(file)
|
|
for scanner.Scan() {
|
|
line := strings.TrimSpace(scanner.Text())
|
|
if line != "" {
|
|
logs = append(logs, line)
|
|
}
|
|
}
|
|
|
|
return logs, scanner.Err()
|
|
}
|
|
|
|
// Helper function untuk reverse slice
|
|
func reverseSlice(s []string) {
|
|
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
|
|
s[i], s[j] = s[j], s[i]
|
|
}
|
|
}
|