diff --git a/.gitignore b/.gitignore index 3ce9510..c2bb4f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ config.json hikan.ru -*.server* \ No newline at end of file +*.server* +*.log \ No newline at end of file diff --git a/main.go b/main.go index efa4762..fbd1df8 100644 --- a/main.go +++ b/main.go @@ -8,26 +8,33 @@ import ( "main/mvc/models" "main/tools" "net/http" + "os" "time" ) func main() { var err error var app *models.App + var logFile *os.File + defer logFile.Close() // Инициализация приложения if app, err = models.InitApp(); err != nil { log.Fatal(err) } - - // Добавление префикса в виде домена сервера к записям в лог - log.SetPrefix(fmt.Sprintf("%s | ", app.Cfg.ServerDomain)) - - // Настройка маршрутов - router := setupRoutes(app) - // Запуск тикеров { + // Переоткрытие файла с логами периодически чтобы он не стал слишком большим + tools.Ticker(func() { + newLogFile, err := tools.SetupLogging(app.Cfg.LogFilePath) + if err != nil { + log.Fatal(err) + } + logFile.Close() + logFile = newLogFile + log.Println("Logging system restarted") + }, time.Second*app.Cfg.LogSystemRestartInterval) + // Обновление последнего прослушанного трека ластфм tools.Ticker(func() { var lastTrack string @@ -51,6 +58,9 @@ func main() { }, time.Second*app.Cfg.CacheLogInterval) } + // Настройка маршрутов + router := setupRoutes(app) + // Запуск сервера if ok, err := tools.IsIPInUse(app.Cfg.ServerIP); err != nil { log.Fatal(err) diff --git a/mvc/controllers/controllers_pages/main.go b/mvc/controllers/controllers_pages/main.go index c2810f8..c1ecd9c 100644 --- a/mvc/controllers/controllers_pages/main.go +++ b/mvc/controllers/controllers_pages/main.go @@ -21,8 +21,8 @@ func MainPageHandler(app *models.App) http.HandlerFunc { // Количество запросов, обработанных сервером за 24ч if r.Method == "COUNT" { - var count []byte - if count, err = tools.GetJournalctlLogsCount("server", "duration: ", 24); err != nil { + var count int + if count, err = tools.GetLogFileEntriesCount(app.Cfg.LogFilePath, "duration: ", 24); err != nil { log.Printf("%s", err.Error()) } sendCount(w, count) @@ -71,10 +71,10 @@ func sendMainPage(w http.ResponseWriter, data []byte) { } // Ответ на метод COUNT -func sendCount(w http.ResponseWriter, data []byte) { +func sendCount(w http.ResponseWriter, data int) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.WriteHeader(http.StatusOK) - w.Write(data) + fmt.Fprint(w, data) } // Ответ на метод LOVE diff --git a/mvc/models/config.go b/mvc/models/config.go index 5f1037c..fda71d7 100644 --- a/mvc/models/config.go +++ b/mvc/models/config.go @@ -18,11 +18,13 @@ type Config struct { } type pathsConfig struct { - PostsDir string `json:"posts_dir"` - AssetsDir string `json:"assets_dir"` - TemplatesDir string `json:"templates_dir"` - TemplatesExt string `json:"templates_ext"` - PostsMaxCountOnPage int `json:"max_posts_per_page"` + PostsDir string `json:"posts_dir"` + AssetsDir string `json:"assets_dir"` + TemplatesDir string `json:"templates_dir"` + TemplatesExt string `json:"templates_ext"` + LogFilePath string `json:"log_file_path"` + LogSystemRestartInterval time.Duration `json:"log_system_restart_interval"` + PostsMaxCountOnPage int `json:"max_posts_per_page"` } type serverConfig struct { diff --git a/tools/logs.go b/tools/logs.go index 378cd5c..67cff20 100644 --- a/tools/logs.go +++ b/tools/logs.go @@ -1,29 +1,81 @@ package tools import ( - "bytes" + "bufio" "fmt" - "os/exec" - "runtime" + "log" + "os" + "strings" + "time" ) -func GetJournalctlLogsCount(serviceName, grepPattern string, hoursAgo int) ([]byte, error) { +func SetupLogging(logFilePath string) (*os.File, error) { - if runtime.GOOS != "linux" { - return nil, fmt.Errorf("not a linux") + // Проверяем размер файла + var append = true + if fi, err := os.Stat(logFilePath); err == nil { + if fi.Size() > 10*1024*1024 { // 10 МБ + append = false + } } - cmd := exec.Command("sh", "-c", - fmt.Sprintf("journalctl -u %s --since '%d hours ago' | grep -c '%s'", - serviceName, hoursAgo, grepPattern)) + // Логи дописываются в файл если он меньше 10 МБ, иначе + // файл перезаписывается + var flags int + if append { + flags = os.O_CREATE | os.O_WRONLY | os.O_APPEND + } else { + flags = os.O_CREATE | os.O_WRONLY | os.O_TRUNC + } - var output bytes.Buffer - cmd.Stdout = &output - - err := cmd.Run() + file, err := os.OpenFile(logFilePath, flags, 0666) if err != nil { return nil, err } - return output.Bytes(), nil + log.SetOutput(file) + return file, nil +} + +func GetLogFileEntriesCount(logFilePath, grepPattern string, hoursAgo int) (int, error) { + // Если файл не существует - ошибка + if _, err := os.Stat(logFilePath); os.IsNotExist(err) { + return 0, fmt.Errorf("log file not found: %s", logFilePath) + } + + // Открывает файл для чтения + file, err := os.Open(logFilePath) + if err != nil { + return 0, fmt.Errorf("failed to open log file: %v", err) + } + defer file.Close() + + // Вычисляется временная граница (текущее время - hoursAgo) + timeThreshold := time.Now().Add(-time.Duration(hoursAgo) * time.Hour) + count := 0 + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + line := scanner.Text() + + // Парсим дату из строки (формат "2006/01/02 15:04:05") + if len(line) < 20 { // Минимальная длина для даты + continue + } + logTime, err := time.Parse("2006/01/02 15:04:05", line[:19]) + if err != nil { + continue // Пропускаются строки где не дата + } + + // Проверка попадает ли дана во временной интервал + if logTime.After(timeThreshold) && strings.Contains(line, grepPattern) { + count++ + } + } + + if err := scanner.Err(); err != nil { + return 0, fmt.Errorf("error reading log file: %v", err) + } + + return count, nil }