DB 시간포멧 수정. access log 추가

This commit is contained in:
Lectom C Han
2025-12-10 11:53:48 +09:00
parent c1af8f1b61
commit 02a9734961
5 changed files with 127 additions and 6 deletions

View File

@@ -3,13 +3,18 @@ package main
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"log" "log"
"net/url" "net/url"
"os" "os"
"path/filepath"
"strconv"
"strings" "strings"
"sync"
"time" "time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/logger"
"geoip-rest/internal/geo" "geoip-rest/internal/geo"
"geoip-rest/internal/schedule" "geoip-rest/internal/schedule"
@@ -45,6 +50,8 @@ func main() {
ReadBufferSize: 16 * 1024, // allow larger request headers (e.g., proxy cookies) ReadBufferSize: 16 * 1024, // allow larger request headers (e.g., proxy cookies)
}) })
app.Use(newFileLogger(env("ACCESS_LOG_PATH", "/log/api-access.log")))
app.Get("/", func(c *fiber.Ctx) error { app.Get("/", func(c *fiber.Ctx) error {
return c.JSON(fiber.Map{ return c.JSON(fiber.Map{
"service": "geoip-rest", "service": "geoip-rest",
@@ -107,6 +114,86 @@ func main() {
} }
} }
func newFileLogger(path string) fiber.Handler {
if path == "" {
return func(c *fiber.Ctx) error { return c.Next() }
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
log.Printf("access log disabled (mkdir failed: %v)", err)
return func(c *fiber.Ctx) error { return c.Next() }
}
maxBytes := int64(envInt("ACCESS_LOG_MAX_BYTES", 10*1024*1024))
writer, err := newRotatingWriter(path, maxBytes)
if err != nil {
log.Printf("access log disabled (open failed: %v)", err)
return func(c *fiber.Ctx) error { return c.Next() }
}
format := "${time} ${ip} ${method} ${path} ${protocol} ${status} ${latency_human} headers=${reqHeaders}\n"
cfg := logger.Config{
Format: format,
TimeFormat: time.RFC3339,
TimeZone: "Asia/Seoul",
Output: writer,
}
return logger.New(cfg)
}
type rotatingWriter struct {
mu sync.Mutex
path string
maxBytes int64
file *os.File
}
func newRotatingWriter(path string, maxBytes int64) (*rotatingWriter, error) {
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return nil, err
}
return &rotatingWriter{
path: path,
maxBytes: maxBytes,
file: f,
}, nil
}
func (w *rotatingWriter) Write(p []byte) (int, error) {
w.mu.Lock()
defer w.mu.Unlock()
if err := w.rotateIfNeeded(len(p)); err != nil {
return 0, err
}
return w.file.Write(p)
}
func (w *rotatingWriter) rotateIfNeeded(incoming int) error {
info, err := w.file.Stat()
if err != nil {
return err
}
if info.Size()+int64(incoming) <= w.maxBytes {
return nil
}
_ = w.file.Close()
ts := time.Now().Format("20060102-150405")
rotated := fmt.Sprintf("%s.%s", w.path, ts)
if err := os.Rename(w.path, rotated); err != nil {
// attempt to reopen original to keep logging
w.file, _ = os.OpenFile(w.path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
return err
}
f, err := os.OpenFile(w.path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
if err != nil {
return err
}
w.file = f
return nil
}
func env(key, fallback string) string { func env(key, fallback string) string {
if val := os.Getenv(key); val != "" { if val := os.Getenv(key); val != "" {
return val return val
@@ -129,6 +216,18 @@ func envBool(key string, fallback bool) bool {
} }
} }
func envInt(key string, fallback int) int {
val := os.Getenv(key)
if val == "" {
return fallback
}
parsed, err := strconv.Atoi(val)
if err != nil {
return fallback
}
return parsed
}
func sanitizeDBURL(raw string) string { func sanitizeDBURL(raw string) string {
u, err := url.Parse(raw) u, err := url.Parse(raw)
if err != nil { if err != nil {

View File

@@ -29,7 +29,7 @@ func main() {
csvPath := env("USER_PROGRAM_INFO_CSV", defaultCSVPath) csvPath := env("USER_PROGRAM_INFO_CSV", defaultCSVPath)
updateDir := env("USER_PROGRAM_UPDATE_DIR", defaultUpdateDir) updateDir := env("USER_PROGRAM_UPDATE_DIR", defaultUpdateDir)
schema := env("USER_PROGRAM_INFO_SCHEMA", defaultSchema) schema := env("USER_PROGRAM_INFO_SCHEMA", env("POSTGRES_SCHEMA", defaultSchema))
logDir := env("USER_PROGRAM_IMPORT_LOG_DIR", defaultLogDir) logDir := env("USER_PROGRAM_IMPORT_LOG_DIR", defaultLogDir)
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)

View File

@@ -49,6 +49,8 @@ var (
timeLayouts = []string{ timeLayouts = []string{
"2006-01-02 15:04:05.000", "2006-01-02 15:04:05.000",
"2006-01-02 15:04:05", "2006-01-02 15:04:05",
time.RFC3339,
"2006-01-02T15:04:05.000Z07:00",
} }
) )
@@ -63,6 +65,10 @@ func EnsureUserProgramReplica(ctx context.Context, conn *pgx.Conn, csvPath, sche
logDir = "log" logDir = "log"
} }
if err := ensureSchema(ctx, conn, schema); err != nil {
return err
}
if err := createReplicaTable(ctx, conn, schema, ReplicaTable); err != nil { if err := createReplicaTable(ctx, conn, schema, ReplicaTable); err != nil {
return err return err
} }
@@ -143,6 +149,14 @@ CREATE TABLE IF NOT EXISTS %s (
return err return err
} }
func ensureSchema(ctx context.Context, conn *pgx.Conn, schema string) error {
if schema == "" {
return nil
}
_, err := conn.Exec(ctx, fmt.Sprintf(`CREATE SCHEMA IF NOT EXISTS %s`, pgx.Identifier{schema}.Sanitize()))
return err
}
type importResult struct { type importResult struct {
rowsCopied int64 rowsCopied int64
rowsUpserted int64 rowsUpserted int64

View File

@@ -69,11 +69,12 @@ func NewMySQLConfigFromEnv() (MySQLConfig, error) {
} }
func NewPathsFromEnv() (Paths, error) { func NewPathsFromEnv() (Paths, error) {
schema := env("USER_PROGRAM_INFO_SCHEMA", env("POSTGRES_SCHEMA", DefaultSchema))
paths := Paths{ paths := Paths{
UpdateDir: env("USER_PROGRAM_UPDATE_DIR", DefaultUpdateDir), UpdateDir: env("USER_PROGRAM_UPDATE_DIR", DefaultUpdateDir),
LogDir: env("USER_PROGRAM_IMPORT_LOG_DIR", DefaultLogDir), LogDir: env("USER_PROGRAM_IMPORT_LOG_DIR", DefaultLogDir),
InitialCSV: env("USER_PROGRAM_INFO_CSV", DefaultInitialCSV), InitialCSV: env("USER_PROGRAM_INFO_CSV", DefaultInitialCSV),
Schema: env("USER_PROGRAM_INFO_SCHEMA", DefaultSchema), Schema: schema,
} }
for _, dir := range []string{paths.UpdateDir, paths.LogDir} { for _, dir := range []string{paths.UpdateDir, paths.LogDir} {

View File

@@ -188,8 +188,8 @@ func scanRow(rows *sql.Rows) ([]string, error) {
userCompany sql.NullString userCompany sql.NullString
userDepartment sql.NullString userDepartment sql.NullString
userPosition sql.NullString userPosition sql.NullString
userLoginTime sql.NullString userLoginTime sql.NullTime
createdAt sql.NullString createdAt sql.NullTime
userFamilyFlag sql.NullString userFamilyFlag sql.NullString
) )
@@ -225,8 +225,8 @@ func scanRow(rows *sql.Rows) ([]string, error) {
nullToString(userCompany), nullToString(userCompany),
nullToString(userDepartment), nullToString(userDepartment),
nullToString(userPosition), nullToString(userPosition),
nullToString(userLoginTime), formatTimestamp(userLoginTime),
nullToString(createdAt), formatTimestamp(createdAt),
nullToString(userFamilyFlag), nullToString(userFamilyFlag),
}, nil }, nil
} }
@@ -241,3 +241,10 @@ func nullToString(v sql.NullString) string {
func netAddr(host string, port int) string { func netAddr(host string, port int) string {
return fmt.Sprintf("%s:%d", host, port) return fmt.Sprintf("%s:%d", host, port)
} }
func formatTimestamp(t sql.NullTime) string {
if !t.Valid {
return ""
}
return t.Time.In(kst()).Format("2006-01-02 15:04:05.000")
}