wms-be/modules/logs/controller/log_controller.go

173 lines
3.9 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
}
// Split dengan dukungan untuk \r\n atau \n
lines := strings.Split(strings.ReplaceAll(string(content), "\r\n", "\n"), "\n")
var logs []string
for _, line := range lines {
line = strings.TrimSpace(line)
if line != "" {
logs = append(logs, line)
}
}
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]
}
}