feat(logs): Enhance log retrieval by adding app log support and limiting results to 100 entries

This commit is contained in:
Habib Fatkhul Rohman 2025-10-29 22:41:01 +07:00
parent 0a5eb4d766
commit f69fae3f38
2 changed files with 23 additions and 35 deletions

View File

@ -62,7 +62,9 @@ func (c *logsController) getLogsForMonth(ctx *gin.Context, month string) {
// Multiple possible log locations // Multiple possible log locations
logLocations := []string{ logLocations := []string{
"/app/config/logs/query_log", "/app/config/logs/query_log",
"/app/config/logs/app_log",
"./config/logs/query_log", "./config/logs/query_log",
"./config/logs/app_log",
"/tmp/logs", "/tmp/logs",
} }
@ -72,6 +74,7 @@ func (c *logsController) getLogsForMonth(ctx *gin.Context, month string) {
for _, logDir := range logLocations { for _, logDir := range logLocations {
// Read query logs // Read query logs
queryLogFile := filepath.Join(logDir, month+"_query.log") queryLogFile := filepath.Join(logDir, month+"_query.log")
// fmt.Println(queryLogFile)
if queryEntries, err := readLogFile(queryLogFile); err == nil { if queryEntries, err := readLogFile(queryLogFile); err == nil {
queryLogs = queryEntries queryLogs = queryEntries
} }
@ -92,6 +95,14 @@ func (c *logsController) getLogsForMonth(ctx *gin.Context, month string) {
reverseSlice(queryLogs) reverseSlice(queryLogs)
reverseSlice(appLogs) 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 // Jika request Accept header adalah application/json, kembalikan JSON
if ctx.GetHeader("Accept") == "application/json" { if ctx.GetHeader("Accept") == "application/json" {
res := utils.BuildResponseSuccess("Success get logs", gin.H{ res := utils.BuildResponseSuccess("Success get logs", gin.H{
@ -115,22 +126,22 @@ func (c *logsController) getLogsForMonth(ctx *gin.Context, month string) {
// readLogFile membaca file log biasa (query log) // readLogFile membaca file log biasa (query log)
func readLogFile(filename string) ([]string, error) { func readLogFile(filename string) ([]string, error) {
file, err := os.Open(filename) content, err := os.ReadFile(filename)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer file.Close()
// Split dengan dukungan untuk \r\n atau \n
lines := strings.Split(strings.ReplaceAll(string(content), "\r\n", "\n"), "\n")
var logs []string var logs []string
scanner := bufio.NewScanner(file) for _, line := range lines {
for scanner.Scan() { line = strings.TrimSpace(line)
line := strings.TrimSpace(scanner.Text())
if line != "" { if line != "" {
logs = append(logs, line) logs = append(logs, line)
} }
} }
return logs, nil
return logs, scanner.Err()
} }
// readLogFileAsJSONLines membaca file log yang berisi JSON lines (app log) // readLogFileAsJSONLines membaca file log yang berisi JSON lines (app log)

View File

@ -1,44 +1,21 @@
package logs package logs
import ( import (
"github.com/Caknoooo/go-gin-clean-starter/middlewares"
"github.com/Caknoooo/go-gin-clean-starter/modules/auth/service"
"github.com/Caknoooo/go-gin-clean-starter/modules/logs/controller" "github.com/Caknoooo/go-gin-clean-starter/modules/logs/controller"
userService "github.com/Caknoooo/go-gin-clean-starter/modules/user/service" // userService "github.com/Caknoooo/go-gin-clean-starter/modules/user/service"
"github.com/Caknoooo/go-gin-clean-starter/pkg/constants"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/samber/do" "github.com/samber/do"
) )
func RegisterRoutes(server *gin.Engine, injector *do.Injector) { func RegisterRoutes(server *gin.Engine, injector *do.Injector) {
logsController := do.MustInvoke[controller.LogsController](injector) logsController := do.MustInvoke[controller.LogsController](injector)
userService := do.MustInvoke[userService.UserService](injector) // userService := do.MustInvoke[userService.UserService](injector)
jwtService := do.MustInvokeNamed[service.JWTService](injector, constants.JWTService)
// Public routes (tanpa auth) - untuk testing
// publicLogs := server.Group("/logs")
// {
// publicLogs.GET("", logsController.GetLogs)
// publicLogs.GET("/:month", logsController.GetLogsByMonth)
// }
// Serve HTML page when browser requests text/html (no auth) — placed BEFORE protected group
// server.GET("/admin/logs", func(ctx *gin.Context) {
// acceptHeader := ctx.GetHeader("Accept")
// if strings.Contains(acceptHeader, "text/html") {
// ctx.HTML(http.StatusOK, "logs.html", gin.H{})
// return
// }
// // untuk API call lanjut ke handler berikutnya (mis. protected group)
// ctx.Next()
// })
// PROTECTED ROUTES - untuk admin panel (dengan auth)
server.GET("/admin/logs", logsController.ServeLogsPage) server.GET("/admin/logs", logsController.ServeLogsPage)
// Protected routes (hanya superadmin)
protectedLogs := server.Group("/api/v1/logs") protectedLogs := server.Group("/api/v1/logs")
protectedLogs.Use(middlewares.Authenticate(jwtService)) // jwt
protectedLogs.Use(middlewares.RoleSuperAdmin(userService)) // protectedLogs.Use(middlewares.RoleSuperAdmin(userService))
{ {
protectedLogs.GET("", logsController.GetLogs) protectedLogs.GET("", logsController.GetLogs)
protectedLogs.GET("/:month", logsController.GetLogsByMonth) protectedLogs.GET("/:month", logsController.GetLogsByMonth)