refactor: разделение кода на пакеты и улучшение архитектуры
- Создана новая структура проекта с разделением на пакеты - Добавлены интерфейсы для всех сервисов (Git, Quartz, Files, Build) - Реализован Dependency Injection для сервисов - Добавлены middleware для логирования, Request ID и Response Time - Создан пакет конфигурации с валидацией - Улучшено логирование через интерфейс - Добавлены обработчики HTTP в отдельных пакетах - Создана структура для тестирования - Добавлены конфигурационные файлы и документация
This commit is contained in:
51
Dockerfile-refactored
Normal file
51
Dockerfile-refactored
Normal file
@@ -0,0 +1,51 @@
|
||||
# Многоэтапная сборка для Go приложения
|
||||
FROM golang:1.21-alpine AS builder
|
||||
|
||||
# Устанавливаем необходимые пакеты для сборки
|
||||
RUN apk add --no-cache git ca-certificates tzdata
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем go mod файлы
|
||||
COPY go.mod go.sum ./
|
||||
|
||||
# Скачиваем зависимости
|
||||
RUN go mod download
|
||||
|
||||
# Копируем исходный код
|
||||
COPY . .
|
||||
|
||||
# Собираем приложение из cmd/server
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./cmd/server
|
||||
|
||||
# Финальный образ
|
||||
FROM alpine:latest
|
||||
|
||||
# Устанавливаем необходимые пакеты для работы
|
||||
RUN apk --no-cache add ca-certificates git tzdata
|
||||
|
||||
# Создаем пользователя для безопасности
|
||||
RUN addgroup -g 1001 -S appgroup && \
|
||||
adduser -u 1001 -S appuser -G appgroup
|
||||
|
||||
# Устанавливаем рабочую директорию
|
||||
WORKDIR /app
|
||||
|
||||
# Копируем собранное приложение из builder этапа
|
||||
COPY --from=builder /app/main .
|
||||
|
||||
# Копируем конфигурационные файлы
|
||||
COPY --from=builder /app/configs ./configs
|
||||
|
||||
# Меняем владельца файлов
|
||||
RUN chown -R appuser:appgroup /app
|
||||
|
||||
# Переключаемся на непривилегированного пользователя
|
||||
USER appuser
|
||||
|
||||
# Открываем порт
|
||||
EXPOSE 3000
|
||||
|
||||
# Запускаем приложение
|
||||
CMD ["./main"]
|
||||
155
Makefile-refactored
Normal file
155
Makefile-refactored
Normal file
@@ -0,0 +1,155 @@
|
||||
.PHONY: help build run test clean docker-build docker-run docker-stop install-deps
|
||||
|
||||
# Переменные
|
||||
BINARY_NAME=go-webhook-server
|
||||
DOCKER_IMAGE=go-webhook-server
|
||||
DOCKER_CONTAINER=go-webhook-server
|
||||
BUILD_DIR=cmd/server
|
||||
|
||||
# Помощь
|
||||
help: ## Показать справку по командам
|
||||
@echo "Доступные команды:"
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
# Установка зависимостей
|
||||
install-deps: ## Установить Go зависимости
|
||||
go mod download
|
||||
go mod tidy
|
||||
|
||||
# Сборка
|
||||
build: install-deps ## Собрать бинарный файл
|
||||
@echo "Сборка $(BINARY_NAME)..."
|
||||
go build -o $(BINARY_NAME) $(BUILD_DIR)/main.go
|
||||
@echo "Сборка завершена: $(BINARY_NAME)"
|
||||
|
||||
# Запуск
|
||||
run: build ## Запустить сервис локально
|
||||
@echo "Запуск $(BINARY_NAME)..."
|
||||
./$(BINARY_NAME)
|
||||
|
||||
# Запуск без сборки
|
||||
run-dev: ## Запустить сервис в режиме разработки
|
||||
@echo "Запуск в режиме разработки..."
|
||||
cd $(BUILD_DIR) && go run main.go
|
||||
|
||||
# Тестирование
|
||||
test: install-deps ## Запустить тесты
|
||||
@echo "Запуск тестов..."
|
||||
go test -v ./internal/...
|
||||
go test -v ./pkg/...
|
||||
|
||||
# Тестирование с покрытием
|
||||
test-coverage: install-deps ## Запустить тесты с покрытием
|
||||
@echo "Запуск тестов с покрытием..."
|
||||
go test -v -coverprofile=coverage.out ./internal/...
|
||||
go test -v -coverprofile=pkg-coverage.out ./pkg/...
|
||||
go tool cover -html=coverage.out -o coverage.html
|
||||
go tool cover -html=pkg-coverage.out -o pkg-coverage.html
|
||||
@echo "Отчеты покрытия созданы: coverage.html, pkg-coverage.html"
|
||||
|
||||
# Очистка
|
||||
clean: ## Очистить собранные файлы
|
||||
@echo "Очистка..."
|
||||
rm -f $(BINARY_NAME)
|
||||
rm -f coverage.out pkg-coverage.out
|
||||
rm -f coverage.html pkg-coverage.html
|
||||
@echo "Очистка завершена"
|
||||
|
||||
# Docker команды
|
||||
docker-build: ## Собрать Docker образ
|
||||
@echo "Сборка Docker образа..."
|
||||
docker build -t $(DOCKER_IMAGE) .
|
||||
@echo "Docker образ собран: $(DOCKER_IMAGE)"
|
||||
|
||||
docker-run: docker-build ## Запустить Docker контейнер
|
||||
@echo "Запуск Docker контейнера..."
|
||||
docker run -d \
|
||||
--name $(DOCKER_CONTAINER) \
|
||||
-p 3000:3000 \
|
||||
-v obsidian_repo:/obsidian:ro \
|
||||
-v quartz_repo:/quartz:ro \
|
||||
-v public_site:/public \
|
||||
$(DOCKER_IMAGE)
|
||||
@echo "Docker контейнер запущен: $(DOCKER_CONTAINER)"
|
||||
|
||||
docker-stop: ## Остановить Docker контейнер
|
||||
@echo "Остановка Docker контейнера..."
|
||||
docker stop $(DOCKER_CONTAINER) || true
|
||||
docker rm $(DOCKER_CONTAINER) || true
|
||||
@echo "Docker контейнер остановлен"
|
||||
|
||||
docker-logs: ## Показать логи Docker контейнера
|
||||
docker logs -f $(DOCKER_CONTAINER)
|
||||
|
||||
# Docker Compose команды
|
||||
compose-up: ## Запустить сервис через Docker Compose
|
||||
@echo "Запуск через Docker Compose..."
|
||||
docker-compose up -d
|
||||
@echo "Сервис запущен"
|
||||
|
||||
compose-down: ## Остановить сервис через Docker Compose
|
||||
@echo "Остановка через Docker Compose..."
|
||||
docker-compose down
|
||||
@echo "Сервис остановлен"
|
||||
|
||||
compose-logs: ## Показать логи Docker Compose
|
||||
docker-compose logs -f
|
||||
|
||||
# Проверка состояния
|
||||
status: ## Показать статус сервиса
|
||||
@echo "Проверка статуса сервиса..."
|
||||
@curl -s http://localhost:3000/health || echo "Сервис недоступен"
|
||||
|
||||
# Webhook тест
|
||||
test-webhook: ## Отправить тестовый webhook
|
||||
@echo "Отправка тестового webhook..."
|
||||
@curl -X POST http://localhost:3000/webhook
|
||||
@echo ""
|
||||
|
||||
# Проверка структуры
|
||||
lint: ## Проверить код линтером
|
||||
@echo "Проверка кода..."
|
||||
@if command -v golangci-lint > /dev/null; then \
|
||||
golangci-lint run; \
|
||||
else \
|
||||
echo "golangci-lint не установлен. Установите: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest"; \
|
||||
fi
|
||||
|
||||
# Форматирование кода
|
||||
fmt: ## Отформатировать код
|
||||
@echo "Форматирование кода..."
|
||||
go fmt ./...
|
||||
@echo "Код отформатирован"
|
||||
|
||||
# Проверка зависимостей
|
||||
deps-check: ## Проверить зависимости
|
||||
@echo "Проверка зависимостей..."
|
||||
go mod verify
|
||||
go list -m all
|
||||
|
||||
# Полная пересборка
|
||||
rebuild: clean build ## Полная пересборка проекта
|
||||
|
||||
# Разработка
|
||||
dev: ## Запуск в режиме разработки с автоперезагрузкой
|
||||
@echo "Запуск в режиме разработки..."
|
||||
@if command -v air > /dev/null; then \
|
||||
air; \
|
||||
else \
|
||||
echo "Air не установлен. Установите: go install github.com/cosmtrek/air@latest"; \
|
||||
cd $(BUILD_DIR) && go run main.go; \
|
||||
fi
|
||||
|
||||
# Создание структуры проекта
|
||||
create-structure: ## Создать структуру директорий
|
||||
@echo "Создание структуры проекта..."
|
||||
mkdir -p cmd/server
|
||||
mkdir -p internal/{config,handlers,services,middleware}
|
||||
mkdir -p pkg/logger
|
||||
mkdir -p api
|
||||
mkdir -p configs
|
||||
mkdir -p tests/{unit,integration}
|
||||
mkdir -p scripts
|
||||
mkdir -p docs
|
||||
mkdir -p deployments
|
||||
@echo "Структура проекта создана"
|
||||
212
README-REFACTORED.md
Normal file
212
README-REFACTORED.md
Normal file
@@ -0,0 +1,212 @@
|
||||
# Go Webhook Server - Рефакторенная версия
|
||||
|
||||
Рефакторенная версия webhook сервера с разделением на пакеты и улучшенной архитектурой.
|
||||
|
||||
## 🏗️ **Новая структура проекта**
|
||||
|
||||
```
|
||||
go-webhook-server/
|
||||
├── cmd/
|
||||
│ └── server/
|
||||
│ └── main.go # Точка входа приложения
|
||||
├── internal/
|
||||
│ ├── config/
|
||||
│ │ └── config.go # Конфигурация приложения
|
||||
│ ├── handlers/
|
||||
│ │ ├── webhook.go # Обработчик webhook'ов
|
||||
│ │ └── health.go # Health check обработчик
|
||||
│ ├── services/
|
||||
│ │ ├── build.go # Основной сервис сборки
|
||||
│ │ ├── git.go # Git операции
|
||||
│ │ ├── quartz.go # Сборка Quartz
|
||||
│ │ ├── files.go # Файловые операции
|
||||
│ │ └── types.go # Общие типы
|
||||
│ └── middleware/
|
||||
│ └── logging.go # HTTP логирование
|
||||
├── pkg/
|
||||
│ └── logger/
|
||||
│ └── logger.go # Интерфейс логгера
|
||||
├── api/
|
||||
│ └── routes.go # Определение роутов
|
||||
├── configs/
|
||||
│ └── config.yaml # YAML конфигурация
|
||||
├── tests/ # Тесты
|
||||
├── scripts/ # Скрипты
|
||||
└── docs/ # Документация
|
||||
```
|
||||
|
||||
## 🔧 **Основные улучшения**
|
||||
|
||||
### **1. Разделение ответственности**
|
||||
- **Config** - управление конфигурацией
|
||||
- **Services** - бизнес-логика
|
||||
- **Handlers** - HTTP обработчики
|
||||
- **Middleware** - промежуточное ПО
|
||||
- **API** - определение роутов
|
||||
|
||||
### **2. Интерфейсы и абстракции**
|
||||
```go
|
||||
type GitService interface {
|
||||
UpdateRepository() error
|
||||
IsRepositoryExists() bool
|
||||
}
|
||||
|
||||
type QuartzService interface {
|
||||
BuildSite() error
|
||||
InstallDependencies() error
|
||||
}
|
||||
|
||||
type BuildService interface {
|
||||
BuildSite() BuildResult
|
||||
}
|
||||
```
|
||||
|
||||
### **3. Dependency Injection**
|
||||
```go
|
||||
// Инициализация сервисов
|
||||
gitService := services.NewGitService(cfg, log)
|
||||
quartzService := services.NewQuartzService(cfg, log)
|
||||
fileService := services.NewFileService(cfg, log)
|
||||
buildService := services.NewBuildService(cfg, log, gitService, quartzService, fileService)
|
||||
```
|
||||
|
||||
### **4. Улучшенное логирование**
|
||||
- Структурированное логирование
|
||||
- Request ID для отслеживания
|
||||
- Уровни логирования
|
||||
- Контекстная информация
|
||||
|
||||
### **5. Middleware**
|
||||
- **RequestIDMiddleware** - уникальный ID для каждого запроса
|
||||
- **ResponseTimeMiddleware** - время ответа
|
||||
- **LoggingMiddleware** - детальное логирование HTTP
|
||||
|
||||
## 🚀 **Запуск**
|
||||
|
||||
### **Локально**
|
||||
```bash
|
||||
cd cmd/server
|
||||
go run main.go
|
||||
```
|
||||
|
||||
### **Сборка**
|
||||
```bash
|
||||
go build -o go-webhook-server cmd/server/main.go
|
||||
```
|
||||
|
||||
### **Docker**
|
||||
```bash
|
||||
docker build -t go-webhook-server .
|
||||
docker run -p 3000:3000 go-webhook-server
|
||||
```
|
||||
|
||||
## 📝 **Конфигурация**
|
||||
|
||||
### **Переменные окружения**
|
||||
```bash
|
||||
PORT=3000
|
||||
OBSIDIAN_PATH=/obsidian
|
||||
QUARTZ_PATH=/quartz
|
||||
PUBLIC_PATH=/public
|
||||
GIT_BRANCH=main
|
||||
GIT_REMOTE=origin
|
||||
SERVER_TIMEOUT=30
|
||||
```
|
||||
|
||||
### **YAML конфигурация**
|
||||
```yaml
|
||||
server:
|
||||
port: "3000"
|
||||
timeout: 30
|
||||
|
||||
paths:
|
||||
obsidian: "/obsidian"
|
||||
quartz: "/quartz"
|
||||
public: "/public"
|
||||
|
||||
git:
|
||||
branch: "main"
|
||||
remote: "origin"
|
||||
```
|
||||
|
||||
## 🧪 **Тестирование**
|
||||
|
||||
### **Unit тесты**
|
||||
```bash
|
||||
go test ./internal/...
|
||||
```
|
||||
|
||||
### **Integration тесты**
|
||||
```bash
|
||||
go test ./tests/integration/...
|
||||
```
|
||||
|
||||
## 🔄 **API эндпоинты**
|
||||
|
||||
### **POST /webhook**
|
||||
Запускает процесс сборки сайта.
|
||||
|
||||
**Ответ:**
|
||||
```json
|
||||
{
|
||||
"status": "accepted",
|
||||
"message": "Build process started",
|
||||
"request_id": "20250127103000-abc123",
|
||||
"timestamp": "2025-01-27T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### **GET /health**
|
||||
Проверка состояния сервиса.
|
||||
|
||||
**Ответ:**
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"timestamp": "2025-01-27T10:30:00Z",
|
||||
"service": "go-webhook-server",
|
||||
"version": "1.0.0",
|
||||
"request_id": "20250127103000-abc123"
|
||||
}
|
||||
```
|
||||
|
||||
## 📊 **Мониторинг**
|
||||
|
||||
### **Заголовки ответа**
|
||||
- `X-Request-ID` - уникальный ID запроса
|
||||
- `X-Response-Time` - время ответа
|
||||
|
||||
### **Логирование**
|
||||
- Все HTTP запросы логируются с деталями
|
||||
- Request ID для отслеживания цепочки запросов
|
||||
- Структурированные логи в формате JSON
|
||||
|
||||
## 🚀 **Преимущества новой архитектуры**
|
||||
|
||||
1. **Тестируемость** - легко писать unit тесты
|
||||
2. **Переиспользование** - компоненты можно использовать в других проектах
|
||||
3. **Читаемость** - код легче понимать и поддерживать
|
||||
4. **Расширяемость** - проще добавлять новую функциональность
|
||||
5. **Соответствие стандартам** - структура соответствует Go best practices
|
||||
6. **Dependency Injection** - легко заменять реализации
|
||||
7. **Интерфейсы** - четкое разделение контрактов
|
||||
|
||||
## 🔮 **Планы развития**
|
||||
|
||||
- [ ] Добавление метрик Prometheus
|
||||
- [ ] Конфигурация через файлы
|
||||
- [ ] Graceful shutdown для сборки
|
||||
- [ ] Очередь сборок
|
||||
- [ ] Уведомления о результатах
|
||||
- [ ] API для мониторинга сборок
|
||||
- [ ] Аутентификация webhook'ов
|
||||
- [ ] Rate limiting
|
||||
- [ ] OpenAPI документация
|
||||
|
||||
## 📚 **Зависимости**
|
||||
|
||||
- **Go 1.20+**
|
||||
- **Gin** - веб-фреймворк
|
||||
- **go-git/v5** - Git клиент
|
||||
- **logrus** - логирование
|
||||
- **YAML** - конфигурация (планируется)
|
||||
30
api/routes.go
Normal file
30
api/routes.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"go-webhook-server/internal/handlers"
|
||||
"go-webhook-server/internal/middleware"
|
||||
"go-webhook-server/pkg/logger"
|
||||
)
|
||||
|
||||
// SetupRoutes настраивает все роуты приложения
|
||||
func SetupRoutes(
|
||||
webhookHandler *handlers.WebhookHandler,
|
||||
healthHandler *handlers.HealthHandler,
|
||||
log logger.Logger,
|
||||
) *gin.Engine {
|
||||
// Настраиваем Gin
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
router := gin.New()
|
||||
|
||||
// Добавляем middleware
|
||||
router.Use(middleware.RequestIDMiddleware())
|
||||
router.Use(middleware.ResponseTimeMiddleware())
|
||||
router.Use(middleware.LoggingMiddleware(log))
|
||||
|
||||
// Роуты
|
||||
router.POST("/webhook", webhookHandler.HandleWebhook)
|
||||
router.GET("/health", healthHandler.HandleHealth)
|
||||
|
||||
return router
|
||||
}
|
||||
76
cmd/server/main.go
Normal file
76
cmd/server/main.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"go-webhook-server/api"
|
||||
"go-webhook-server/internal/config"
|
||||
"go-webhook-server/internal/handlers"
|
||||
"go-webhook-server/internal/services"
|
||||
"go-webhook-server/pkg/logger"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Инициализация логгера
|
||||
log := logger.New()
|
||||
log.Info("Starting go-webhook-server...")
|
||||
|
||||
// Загрузка конфигурации
|
||||
cfg := config.Load()
|
||||
if err := cfg.Validate(); err != nil {
|
||||
log.Fatalf("Configuration validation failed: %v", err)
|
||||
}
|
||||
|
||||
log.Infof("Configuration loaded: Port=%s, ObsidianPath=%s, QuartzPath=%s, PublicPath=%s, GitBranch=%s, GitRemote=%s",
|
||||
cfg.Server.Port, cfg.Paths.Obsidian, cfg.Paths.Quartz, cfg.Paths.Public, cfg.Git.Branch, cfg.Git.Remote)
|
||||
|
||||
// Инициализация сервисов
|
||||
gitService := services.NewGitService(cfg, log)
|
||||
quartzService := services.NewQuartzService(cfg, log)
|
||||
fileService := services.NewFileService(cfg, log)
|
||||
buildService := services.NewBuildService(cfg, log, gitService, quartzService, fileService)
|
||||
|
||||
// Инициализация обработчиков
|
||||
webhookHandler := handlers.NewWebhookHandler(buildService, log)
|
||||
healthHandler := handlers.NewHealthHandler(log)
|
||||
|
||||
// Настройка роутов
|
||||
router := api.SetupRoutes(webhookHandler, healthHandler, log)
|
||||
|
||||
// Создание HTTP сервера
|
||||
addr := ":" + cfg.Server.Port
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: router,
|
||||
}
|
||||
|
||||
// Запуск сервера в горутине
|
||||
go func() {
|
||||
log.Infof("Starting webhook server on port %s", cfg.Server.Port)
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Ожидаем сигнал для graceful shutdown
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
|
||||
log.Info("Shutting down server...")
|
||||
|
||||
// Graceful shutdown с таймаутом
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(cfg.Server.Timeout)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
log.Errorf("Server forced to shutdown: %v", err)
|
||||
}
|
||||
|
||||
log.Info("Server exited")
|
||||
}
|
||||
12
configs/config.yaml
Normal file
12
configs/config.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
server:
|
||||
port: "3000"
|
||||
timeout: 30
|
||||
|
||||
paths:
|
||||
obsidian: "/obsidian"
|
||||
quartz: "/quartz"
|
||||
public: "/public"
|
||||
|
||||
git:
|
||||
branch: "main"
|
||||
remote: "origin"
|
||||
101
internal/config/config.go
Normal file
101
internal/config/config.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Config содержит все настройки приложения
|
||||
type Config struct {
|
||||
Server ServerConfig `yaml:"server"`
|
||||
Paths PathsConfig `yaml:"paths"`
|
||||
Git GitConfig `yaml:"git"`
|
||||
}
|
||||
|
||||
// ServerConfig содержит настройки HTTP сервера
|
||||
type ServerConfig struct {
|
||||
Port string `yaml:"port"`
|
||||
Timeout int `yaml:"timeout"` // в секундах
|
||||
}
|
||||
|
||||
// PathsConfig содержит пути к директориям
|
||||
type PathsConfig struct {
|
||||
Obsidian string `yaml:"obsidian"`
|
||||
Quartz string `yaml:"quartz"`
|
||||
Public string `yaml:"public"`
|
||||
}
|
||||
|
||||
// GitConfig содержит настройки Git
|
||||
type GitConfig struct {
|
||||
Branch string `yaml:"branch"`
|
||||
Remote string `yaml:"remote"`
|
||||
}
|
||||
|
||||
// Load загружает конфигурацию из переменных окружения
|
||||
func Load() *Config {
|
||||
config := &Config{
|
||||
Server: ServerConfig{
|
||||
Port: getEnv("PORT", "3000"),
|
||||
Timeout: getEnvAsInt("SERVER_TIMEOUT", 30),
|
||||
},
|
||||
Paths: PathsConfig{
|
||||
Obsidian: getEnv("OBSIDIAN_PATH", "/obsidian"),
|
||||
Quartz: getEnv("QUARTZ_PATH", "/quartz"),
|
||||
Public: getEnv("PUBLIC_PATH", "/public"),
|
||||
},
|
||||
Git: GitConfig{
|
||||
Branch: getEnv("GIT_BRANCH", "main"),
|
||||
Remote: getEnv("GIT_REMOTE", "origin"),
|
||||
},
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// Validate проверяет корректность конфигурации
|
||||
func (c *Config) Validate() error {
|
||||
if c.Server.Port == "" {
|
||||
return errors.New("server port is required")
|
||||
}
|
||||
|
||||
if c.Paths.Obsidian == "" {
|
||||
return errors.New("obsidian path is required")
|
||||
}
|
||||
|
||||
if c.Paths.Quartz == "" {
|
||||
return errors.New("quartz path is required")
|
||||
}
|
||||
|
||||
if c.Paths.Public == "" {
|
||||
return errors.New("public path is required")
|
||||
}
|
||||
|
||||
if c.Git.Branch == "" {
|
||||
return errors.New("git branch is required")
|
||||
}
|
||||
|
||||
if c.Git.Remote == "" {
|
||||
return errors.New("git remote is required")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getEnv получает значение переменной окружения или возвращает значение по умолчанию
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// getEnvAsInt получает значение переменной окружения как int или возвращает значение по умолчанию
|
||||
func getEnvAsInt(key string, defaultValue int) int {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
if intValue, err := strconv.Atoi(value); err == nil {
|
||||
return intValue
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
37
internal/handlers/health.go
Normal file
37
internal/handlers/health.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go-webhook-server/pkg/logger"
|
||||
)
|
||||
|
||||
// HealthHandler обработчик для health check эндпоинта
|
||||
type HealthHandler struct {
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewHealthHandler создает новый экземпляр health обработчика
|
||||
func NewHealthHandler(log logger.Logger) *HealthHandler {
|
||||
return &HealthHandler{
|
||||
logger: log,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleHealth обрабатывает health check запрос
|
||||
func (h *HealthHandler) HandleHealth(c *gin.Context) {
|
||||
requestID := c.GetString("request_id")
|
||||
logger := h.logger.WithField("request_id", requestID)
|
||||
|
||||
logger.Debug("Health check request received")
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"status": "ok",
|
||||
"timestamp": time.Now().UTC().Format(time.RFC3339),
|
||||
"service": "go-webhook-server",
|
||||
"version": "1.0.0",
|
||||
"request_id": requestID,
|
||||
})
|
||||
}
|
||||
50
internal/handlers/webhook.go
Normal file
50
internal/handlers/webhook.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go-webhook-server/internal/services"
|
||||
"go-webhook-server/pkg/logger"
|
||||
)
|
||||
|
||||
// WebhookHandler обработчик для webhook эндпоинта
|
||||
type WebhookHandler struct {
|
||||
buildService services.BuildService
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewWebhookHandler создает новый экземпляр webhook обработчика
|
||||
func NewWebhookHandler(buildService services.BuildService, log logger.Logger) *WebhookHandler {
|
||||
return &WebhookHandler{
|
||||
buildService: buildService,
|
||||
logger: log,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleWebhook обрабатывает webhook запрос
|
||||
func (h *WebhookHandler) HandleWebhook(c *gin.Context) {
|
||||
requestID := c.GetString("request_id")
|
||||
logger := h.logger.WithField("request_id", requestID)
|
||||
|
||||
logger.Info("Webhook received, starting site rebuild...")
|
||||
|
||||
// Запускаем сборку в горутине для асинхронной обработки
|
||||
go func() {
|
||||
result := h.buildService.BuildSite()
|
||||
if result.Success {
|
||||
logger.Info("Webhook build completed successfully")
|
||||
} else {
|
||||
logger.Errorf("Webhook build failed: %s", result.Error)
|
||||
}
|
||||
}()
|
||||
|
||||
// Сразу возвращаем ответ
|
||||
c.JSON(http.StatusAccepted, gin.H{
|
||||
"status": "accepted",
|
||||
"message": "Build process started",
|
||||
"request_id": requestID,
|
||||
"timestamp": time.Now().UTC().Format(time.RFC3339),
|
||||
})
|
||||
}
|
||||
67
internal/middleware/logging.go
Normal file
67
internal/middleware/logging.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go-webhook-server/pkg/logger"
|
||||
)
|
||||
|
||||
// LoggingMiddleware создает middleware для логирования HTTP запросов
|
||||
func LoggingMiddleware(log logger.Logger) gin.HandlerFunc {
|
||||
return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
|
||||
// Логируем детали запроса
|
||||
log.WithFields(map[string]interface{}{
|
||||
"status": param.StatusCode,
|
||||
"latency": param.Latency,
|
||||
"client_ip": param.ClientIP,
|
||||
"method": param.Method,
|
||||
"path": param.Path,
|
||||
"user_agent": param.Request.UserAgent(),
|
||||
}).Info("HTTP Request")
|
||||
|
||||
// Возвращаем пустую строку, так как логирование уже выполнено
|
||||
return ""
|
||||
})
|
||||
}
|
||||
|
||||
// RequestIDMiddleware добавляет уникальный ID к каждому запросу
|
||||
func RequestIDMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
requestID := c.GetHeader("X-Request-ID")
|
||||
if requestID == "" {
|
||||
requestID = generateRequestID()
|
||||
}
|
||||
|
||||
c.Set("request_id", requestID)
|
||||
c.Header("X-Request-ID", requestID)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// ResponseTimeMiddleware добавляет время ответа в заголовки
|
||||
func ResponseTimeMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
start := time.Now()
|
||||
|
||||
c.Next()
|
||||
|
||||
duration := time.Since(start)
|
||||
c.Header("X-Response-Time", duration.String())
|
||||
}
|
||||
}
|
||||
|
||||
// generateRequestID генерирует простой ID запроса
|
||||
func generateRequestID() string {
|
||||
return time.Now().Format("20060102150405") + "-" + randomString(6)
|
||||
}
|
||||
|
||||
// randomString генерирует случайную строку указанной длины
|
||||
func randomString(length int) string {
|
||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
b := make([]byte, length)
|
||||
for i := range b {
|
||||
b[i] = charset[time.Now().UnixNano()%int64(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
88
internal/services/build.go
Normal file
88
internal/services/build.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"go-webhook-server/internal/config"
|
||||
"go-webhook-server/pkg/logger"
|
||||
)
|
||||
|
||||
// BuildService интерфейс для сборки сайта
|
||||
type BuildService interface {
|
||||
BuildSite() BuildResult
|
||||
}
|
||||
|
||||
// buildServiceImpl реализация сервиса сборки
|
||||
type buildServiceImpl struct {
|
||||
config *config.Config
|
||||
logger logger.Logger
|
||||
gitService GitService
|
||||
quartzService QuartzService
|
||||
fileService FileService
|
||||
}
|
||||
|
||||
// NewBuildService создает новый экземпляр сервиса сборки
|
||||
func NewBuildService(
|
||||
cfg *config.Config,
|
||||
log logger.Logger,
|
||||
git GitService,
|
||||
quartz QuartzService,
|
||||
files FileService,
|
||||
) BuildService {
|
||||
return &buildServiceImpl{
|
||||
config: cfg,
|
||||
logger: log,
|
||||
gitService: git,
|
||||
quartzService: quartz,
|
||||
fileService: files,
|
||||
}
|
||||
}
|
||||
|
||||
// BuildSite выполняет полную сборку сайта
|
||||
func (b *buildServiceImpl) BuildSite() BuildResult {
|
||||
b.logger.Info("Starting site build process...")
|
||||
|
||||
// Проверяем существование репозитория
|
||||
if !b.gitService.IsRepositoryExists() {
|
||||
b.logger.Error("Repository not found")
|
||||
return BuildResult{
|
||||
Success: false,
|
||||
Message: "Repository not found",
|
||||
Error: "Git repository does not exist at specified path",
|
||||
}
|
||||
}
|
||||
|
||||
// Обновляем репозиторий
|
||||
if err := b.gitService.UpdateRepository(); err != nil {
|
||||
b.logger.Errorf("Failed to update repository: %v", err)
|
||||
return BuildResult{
|
||||
Success: false,
|
||||
Message: "Failed to update repository",
|
||||
Error: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// Собираем сайт с помощью Quartz
|
||||
if err := b.quartzService.BuildSite(); err != nil {
|
||||
b.logger.Errorf("Failed to build Quartz site: %v", err)
|
||||
return BuildResult{
|
||||
Success: false,
|
||||
Message: "Failed to build Quartz site",
|
||||
Error: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
// Копируем собранные файлы в публичную директорию
|
||||
if err := b.fileService.CopyBuiltSite(); err != nil {
|
||||
b.logger.Errorf("Failed to copy built site: %v", err)
|
||||
return BuildResult{
|
||||
Success: false,
|
||||
Message: "Failed to copy built site",
|
||||
Error: err.Error(),
|
||||
}
|
||||
}
|
||||
|
||||
b.logger.Info("Site built successfully!")
|
||||
return BuildResult{
|
||||
Success: true,
|
||||
Message: "Site built successfully",
|
||||
}
|
||||
}
|
||||
78
internal/services/files.go
Normal file
78
internal/services/files.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"go-webhook-server/internal/config"
|
||||
"go-webhook-server/pkg/logger"
|
||||
)
|
||||
|
||||
// FileService интерфейс для файловых операций
|
||||
type FileService interface {
|
||||
CopyBuiltSite() error
|
||||
ClearPublicDirectory() error
|
||||
EnsurePublicDirectory() error
|
||||
}
|
||||
|
||||
// fileServiceImpl реализация файлового сервиса
|
||||
type fileServiceImpl struct {
|
||||
config *config.Config
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewFileService создает новый экземпляр файлового сервиса
|
||||
func NewFileService(cfg *config.Config, log logger.Logger) FileService {
|
||||
return &fileServiceImpl{
|
||||
config: cfg,
|
||||
logger: log,
|
||||
}
|
||||
}
|
||||
|
||||
// CopyBuiltSite копирует собранные файлы в публичную директорию
|
||||
func (f *fileServiceImpl) CopyBuiltSite() error {
|
||||
f.logger.Info("Copying built site to public directory...")
|
||||
|
||||
// Очищаем публичную директорию
|
||||
if err := f.ClearPublicDirectory(); err != nil {
|
||||
return fmt.Errorf("failed to clear public directory: %w", err)
|
||||
}
|
||||
|
||||
// Создаем публичную директорию заново
|
||||
if err := f.EnsurePublicDirectory(); err != nil {
|
||||
return fmt.Errorf("failed to create public directory: %w", err)
|
||||
}
|
||||
|
||||
// Проверяем существование директории с собранными файлами
|
||||
quartzPublicPath := filepath.Join(f.config.Paths.Quartz, "public")
|
||||
if _, err := os.Stat(quartzPublicPath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("Quartz public directory not found: %s", quartzPublicPath)
|
||||
}
|
||||
|
||||
// Копируем файлы с помощью cp команды
|
||||
cmd := exec.Command("cp", "-r", quartzPublicPath+"/.", f.config.Paths.Public+"/")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to copy built site: %w", err)
|
||||
}
|
||||
|
||||
f.logger.Info("Site files copied successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClearPublicDirectory очищает публичную директорию
|
||||
func (f *fileServiceImpl) ClearPublicDirectory() error {
|
||||
if err := os.RemoveAll(f.config.Paths.Public); err != nil {
|
||||
return fmt.Errorf("failed to clear public directory: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// EnsurePublicDirectory создает публичную директорию если она не существует
|
||||
func (f *fileServiceImpl) EnsurePublicDirectory() error {
|
||||
if err := os.MkdirAll(f.config.Paths.Public, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create public directory: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
93
internal/services/git.go
Normal file
93
internal/services/git.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"go-webhook-server/internal/config"
|
||||
"go-webhook-server/pkg/logger"
|
||||
)
|
||||
|
||||
// GitService интерфейс для Git операций
|
||||
type GitService interface {
|
||||
UpdateRepository() error
|
||||
IsRepositoryExists() bool
|
||||
}
|
||||
|
||||
// gitServiceImpl реализация Git сервиса
|
||||
type gitServiceImpl struct {
|
||||
config *config.Config
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewGitService создает новый экземпляр Git сервиса
|
||||
func NewGitService(cfg *config.Config, log logger.Logger) GitService {
|
||||
return &gitServiceImpl{
|
||||
config: cfg,
|
||||
logger: log,
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateRepository обновляет репозиторий из удаленного источника
|
||||
func (g *gitServiceImpl) UpdateRepository() error {
|
||||
g.logger.Info("Updating repository...")
|
||||
|
||||
// Открываем репозиторий
|
||||
repo, err := git.PlainOpen(g.config.Paths.Obsidian)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open repository: %w", err)
|
||||
}
|
||||
|
||||
// Получаем worktree
|
||||
worktree, err := repo.Worktree()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get worktree: %w", err)
|
||||
}
|
||||
|
||||
// Проверяем текущую ветку
|
||||
head, err := repo.Head()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get HEAD: %w", err)
|
||||
}
|
||||
|
||||
g.logger.Infof("Current branch: %s", head.Name().Short())
|
||||
|
||||
// Выполняем git pull
|
||||
err = worktree.Pull(&git.PullOptions{
|
||||
RemoteName: g.config.Git.Remote,
|
||||
ReferenceName: plumbing.NewBranchReferenceName(g.config.Git.Branch),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
if err == git.NoErrAlreadyUpToDate {
|
||||
g.logger.Info("Repository is already up to date")
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to pull from remote: %w", err)
|
||||
}
|
||||
|
||||
g.logger.Info("Repository updated successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsRepositoryExists проверяет существование Git репозитория
|
||||
func (g *gitServiceImpl) IsRepositoryExists() bool {
|
||||
gitPath := filepath.Join(g.config.Paths.Obsidian, ".git")
|
||||
|
||||
// Проверяем существование .git директории
|
||||
if _, err := filepath.Abs(gitPath); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Пытаемся открыть репозиторий
|
||||
repo, err := git.PlainOpen(g.config.Paths.Obsidian)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Проверяем что это действительно Git репозиторий
|
||||
_, err = repo.Head()
|
||||
return err == nil
|
||||
}
|
||||
85
internal/services/quartz.go
Normal file
85
internal/services/quartz.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"go-webhook-server/internal/config"
|
||||
"go-webhook-server/pkg/logger"
|
||||
)
|
||||
|
||||
// QuartzService интерфейс для сборки Quartz сайта
|
||||
type QuartzService interface {
|
||||
BuildSite() error
|
||||
InstallDependencies() error
|
||||
}
|
||||
|
||||
// quartzServiceImpl реализация Quartz сервиса
|
||||
type quartzServiceImpl struct {
|
||||
config *config.Config
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
// NewQuartzService создает новый экземпляр Quartz сервиса
|
||||
func NewQuartzService(cfg *config.Config, log logger.Logger) QuartzService {
|
||||
return &quartzServiceImpl{
|
||||
config: cfg,
|
||||
logger: log,
|
||||
}
|
||||
}
|
||||
|
||||
// BuildSite собирает сайт с помощью Quartz
|
||||
func (q *quartzServiceImpl) BuildSite() error {
|
||||
q.logger.Info("Building site with Quartz...")
|
||||
|
||||
// Проверяем существование package.json в директории Quartz
|
||||
packageJSONPath := filepath.Join(q.config.Paths.Quartz, "package.json")
|
||||
if _, err := os.Stat(packageJSONPath); os.IsNotExist(err) {
|
||||
return fmt.Errorf("package.json not found in Quartz directory: %s", q.config.Paths.Quartz)
|
||||
}
|
||||
|
||||
// Устанавливаем зависимости если необходимо
|
||||
if err := q.InstallDependencies(); err != nil {
|
||||
return fmt.Errorf("failed to install dependencies: %w", err)
|
||||
}
|
||||
|
||||
// Выполняем сборку Quartz
|
||||
cmd := exec.Command("npm", "run", "quartz", "build", "--", "-d", q.config.Paths.Obsidian)
|
||||
cmd.Dir = q.config.Paths.Quartz
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
q.logger.Info("Executing Quartz build command...")
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to build Quartz site: %w", err)
|
||||
}
|
||||
|
||||
q.logger.Info("Quartz build completed successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// InstallDependencies устанавливает npm зависимости
|
||||
func (q *quartzServiceImpl) InstallDependencies() error {
|
||||
// Проверяем существование node_modules
|
||||
nodeModulesPath := filepath.Join(q.config.Paths.Quartz, "node_modules")
|
||||
if _, err := os.Stat(nodeModulesPath); os.IsNotExist(err) {
|
||||
q.logger.Info("Installing npm dependencies...")
|
||||
|
||||
cmd := exec.Command("npm", "install")
|
||||
cmd.Dir = q.config.Paths.Quartz
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to install npm dependencies: %w", err)
|
||||
}
|
||||
|
||||
q.logger.Info("npm dependencies installed successfully")
|
||||
} else {
|
||||
q.logger.Debug("node_modules already exists, skipping npm install")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
28
internal/services/types.go
Normal file
28
internal/services/types.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package services
|
||||
|
||||
// BuildResult результат сборки сайта
|
||||
type BuildResult struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
// BuildStatus статус сборки
|
||||
type BuildStatus string
|
||||
|
||||
const (
|
||||
BuildStatusPending BuildStatus = "pending"
|
||||
BuildStatusRunning BuildStatus = "running"
|
||||
BuildStatusCompleted BuildStatus = "completed"
|
||||
BuildStatusFailed BuildStatus = "failed"
|
||||
)
|
||||
|
||||
// BuildInfo детальная информация о сборке
|
||||
type BuildInfo struct {
|
||||
ID string `json:"id"`
|
||||
Status BuildStatus `json:"status"`
|
||||
StartTime string `json:"start_time"`
|
||||
EndTime string `json:"end_time,omitempty"`
|
||||
Duration string `json:"duration,omitempty"`
|
||||
Result BuildResult `json:"result,omitempty"`
|
||||
}
|
||||
130
pkg/logger/logger.go
Normal file
130
pkg/logger/logger.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Logger интерфейс для логирования
|
||||
type Logger interface {
|
||||
Info(args ...interface{})
|
||||
Infof(format string, args ...interface{})
|
||||
Error(args ...interface{})
|
||||
Errorf(format string, args ...interface{})
|
||||
Warn(args ...interface{})
|
||||
Warnf(format string, args ...interface{})
|
||||
Debug(args ...interface{})
|
||||
Debugf(format string, args ...interface{})
|
||||
Fatalf(format string, args ...interface{})
|
||||
WithField(key string, value interface{}) Logger
|
||||
WithFields(fields map[string]interface{}) Logger
|
||||
}
|
||||
|
||||
// logrusLogger реализация логгера через logrus
|
||||
type logrusLogger struct {
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
// New создает новый экземпляр логгера
|
||||
func New() Logger {
|
||||
logger := logrus.New()
|
||||
logger.SetFormatter(&logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
})
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
|
||||
return &logrusLogger{logger: logger}
|
||||
}
|
||||
|
||||
// NewWithLevel создает логгер с указанным уровнем
|
||||
func NewWithLevel(level string) Logger {
|
||||
logger := logrus.New()
|
||||
logger.SetFormatter(&logrus.TextFormatter{
|
||||
FullTimestamp: true,
|
||||
})
|
||||
|
||||
switch level {
|
||||
case "debug":
|
||||
logger.SetLevel(logrus.DebugLevel)
|
||||
case "info":
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
case "warn":
|
||||
logger.SetLevel(logrus.WarnLevel)
|
||||
case "error":
|
||||
logger.SetLevel(logrus.ErrorLevel)
|
||||
default:
|
||||
logger.SetLevel(logrus.InfoLevel)
|
||||
}
|
||||
|
||||
return &logrusLogger{logger: logger}
|
||||
}
|
||||
|
||||
// Info логирует информационное сообщение
|
||||
func (l *logrusLogger) Info(args ...interface{}) {
|
||||
l.logger.Info(args...)
|
||||
}
|
||||
|
||||
// Fatalf логирует критическую ошибку и завершает программу
|
||||
func (l *logrusLogger) Fatalf(format string, args ...interface{}) {
|
||||
l.logger.Fatalf(format, args...)
|
||||
}
|
||||
|
||||
// Infof логирует форматированное информационное сообщение
|
||||
func (l *logrusLogger) Infof(format string, args ...interface{}) {
|
||||
l.logger.Infof(format, args...)
|
||||
}
|
||||
|
||||
// Error логирует сообщение об ошибке
|
||||
func (l *logrusLogger) Error(args ...interface{}) {
|
||||
l.logger.Error(args...)
|
||||
}
|
||||
|
||||
// Errorf логирует форматированное сообщение об ошибке
|
||||
func (l *logrusLogger) Errorf(format string, args ...interface{}) {
|
||||
l.logger.Errorf(format, args...)
|
||||
}
|
||||
|
||||
// Warn логирует предупреждение
|
||||
func (l *logrusLogger) Warn(args ...interface{}) {
|
||||
l.logger.Warn(args...)
|
||||
}
|
||||
|
||||
// Warnf логирует форматированное предупреждение
|
||||
func (l *logrusLogger) Warnf(format string, args ...interface{}) {
|
||||
l.logger.Warnf(format, args...)
|
||||
}
|
||||
|
||||
// Debug логирует отладочное сообщение
|
||||
func (l *logrusLogger) Debug(args ...interface{}) {
|
||||
l.logger.Debug(args...)
|
||||
}
|
||||
|
||||
// Debugf логирует форматированное отладочное сообщение
|
||||
func (l *logrusLogger) Debugf(format string, args ...interface{}) {
|
||||
l.logger.Debugf(format, args...)
|
||||
}
|
||||
|
||||
// WithField добавляет поле к логгеру
|
||||
func (l *logrusLogger) WithField(key string, value interface{}) Logger {
|
||||
// Создаем новый логгер с полем
|
||||
newLogger := logrus.New()
|
||||
newLogger.SetFormatter(l.logger.Formatter)
|
||||
newLogger.SetLevel(l.logger.Level)
|
||||
newLogger.SetOutput(l.logger.Out)
|
||||
|
||||
// Добавляем поле к контексту
|
||||
newLogger.WithField(key, value)
|
||||
return &logrusLogger{logger: newLogger}
|
||||
}
|
||||
|
||||
// WithFields добавляет поля к логгеру
|
||||
func (l *logrusLogger) WithFields(fields map[string]interface{}) Logger {
|
||||
// Создаем новый логгер с полями
|
||||
newLogger := logrus.New()
|
||||
newLogger.SetFormatter(l.logger.Formatter)
|
||||
newLogger.SetLevel(l.logger.Level)
|
||||
newLogger.SetOutput(l.logger.Out)
|
||||
|
||||
// Добавляем поля к контексту
|
||||
newLogger.WithFields(fields)
|
||||
return &logrusLogger{logger: newLogger}
|
||||
}
|
||||
Reference in New Issue
Block a user