Compare commits
6 Commits
builder-1.
...
v1.0.33
Author | SHA1 | Date | |
---|---|---|---|
6f10caa69d | |||
da3538051e | |||
032bdaa619 | |||
b786ed1bf8 | |||
231867a310 | |||
9d82d42680 |
@@ -9,6 +9,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
container:
|
container:
|
||||||
# image: golang:1.21
|
# image: golang:1.21
|
||||||
|
# image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:builder-1.0.32
|
||||||
image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest
|
image: ${{ secrets.DOCKERHUB_USERNAME }}/my-build-golang-runner:latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
|
234
README.md
234
README.md
@@ -1,12 +1,15 @@
|
|||||||
# Hello Gitea API
|
# Hello Gitea API
|
||||||
|
|
||||||
Простой REST API сервер, построенный на Go с использованием Gin framework.
|
Современный REST API сервер, построенный на Go с использованием Gin framework и структурированного логирования.
|
||||||
|
|
||||||
## 🚀 Возможности
|
## 🚀 Возможности
|
||||||
|
|
||||||
- ✅ REST API с JSON ответами
|
- ✅ REST API с JSON ответами
|
||||||
- ✅ Health check endpoint
|
- ✅ Health check endpoint
|
||||||
- ✅ CORS поддержка
|
- ✅ CORS поддержка
|
||||||
|
- ✅ **Современное структурированное логирование** (Go 1.24+)
|
||||||
|
- ✅ **Настраиваемые уровни и форматы логирования**
|
||||||
|
- ✅ **Системная информация и мониторинг**
|
||||||
- ✅ Мультиплатформенная сборка
|
- ✅ Мультиплатформенная сборка
|
||||||
- ✅ Docker образы для Linux AMD64/ARM64
|
- ✅ Docker образы для Linux AMD64/ARM64
|
||||||
- ✅ Автоматические релизы через Gitea Actions
|
- ✅ Автоматические релизы через Gitea Actions
|
||||||
@@ -37,6 +40,8 @@ go run main.go
|
|||||||
|
|
||||||
## 🔧 Конфигурация
|
## 🔧 Конфигурация
|
||||||
|
|
||||||
|
### Основные настройки
|
||||||
|
|
||||||
Сервер запускается на порту 8080 по умолчанию. Можно изменить через переменную окружения:
|
Сервер запускается на порту 8080 по умолчанию. Можно изменить через переменную окружения:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -44,46 +49,98 @@ export PORT=3000
|
|||||||
./hello-api
|
./hello-api
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Настройка логирования
|
||||||
|
|
||||||
|
Приложение поддерживает гибкую настройку логирования через переменные окружения:
|
||||||
|
|
||||||
|
#### Уровни логирования (`LOG_LEVEL`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Доступные уровни (по возрастанию важности):
|
||||||
|
export LOG_LEVEL=DEBUG # Все сообщения
|
||||||
|
export LOG_LEVEL=INFO # Информационные и выше
|
||||||
|
export LOG_LEVEL=WARN # Предупреждения и выше
|
||||||
|
export LOG_LEVEL=ERROR # Только ошибки
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Форматы логирования (`LOG_FORMAT`)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# JSON формат (по умолчанию) - для production
|
||||||
|
export LOG_FORMAT=json
|
||||||
|
|
||||||
|
# Текстовый формат - для разработки
|
||||||
|
export LOG_FORMAT=text
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Примеры конфигурации
|
||||||
|
|
||||||
|
**Для разработки:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_LEVEL=DEBUG
|
||||||
|
export LOG_FORMAT=text
|
||||||
|
export PORT=8080
|
||||||
|
go run main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
**Для production:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_LEVEL=INFO
|
||||||
|
export LOG_FORMAT=json
|
||||||
|
export PORT=8080
|
||||||
|
./hello-api
|
||||||
|
```
|
||||||
|
|
||||||
## 📡 API Endpoints
|
## 📡 API Endpoints
|
||||||
|
|
||||||
### GET /
|
### GET /
|
||||||
|
|
||||||
Основной endpoint
|
Основной endpoint
|
||||||
|
|
||||||
**Ответ:**
|
**Ответ:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"message": "Hello, World!",
|
"message": "Hello, World!",
|
||||||
"version": "1.0.0"
|
"version": "1.0.33"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### GET /healthz
|
### GET /healthz
|
||||||
|
|
||||||
Health check endpoint
|
Health check endpoint
|
||||||
|
|
||||||
**Ответ:**
|
**Ответ:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"version": "1.0.0"
|
"version": "1.0.33"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### GET /api/v1/info
|
### GET /api/v1/info
|
||||||
|
|
||||||
Информация о сервисе
|
Информация о сервисе
|
||||||
|
|
||||||
**Ответ:**
|
**Ответ:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"service": "hello-api",
|
"service": "hello-api",
|
||||||
"status": "running",
|
"status": "running",
|
||||||
"version": "1.0.0"
|
"version": "1.0.33"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /api/v1/echo
|
### POST /api/v1/echo
|
||||||
|
|
||||||
Echo endpoint - возвращает отправленное сообщение
|
Echo endpoint - возвращает отправленное сообщение
|
||||||
|
|
||||||
**Запрос:**
|
**Запрос:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"message": "Hello from client!"
|
"message": "Hello from client!"
|
||||||
@@ -91,18 +148,149 @@ Echo endpoint - возвращает отправленное сообщение
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Ответ:**
|
**Ответ:**
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"echo": "Hello from client!",
|
"echo": "Hello from client!",
|
||||||
"version": "1.0.0"
|
"version": "1.0.33"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🛠 Разработка
|
### GET /api/v1/config
|
||||||
|
|
||||||
|
**Новый endpoint** - системная информация и конфигурация
|
||||||
|
|
||||||
|
**Ответ:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "1.0.33",
|
||||||
|
"system_info": {
|
||||||
|
"go_version": "go1.24.0",
|
||||||
|
"os": "linux",
|
||||||
|
"architecture": "amd64",
|
||||||
|
"num_cpu": 8,
|
||||||
|
"start_time": "2024-01-15T10:30:45Z"
|
||||||
|
},
|
||||||
|
"environment": {
|
||||||
|
"PATH": "/usr/local/bin:/usr/bin:/bin",
|
||||||
|
"PORT": "8080",
|
||||||
|
"LOG_LEVEL": "INFO"
|
||||||
|
},
|
||||||
|
"uptime": "2h30m15s"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### POST /api/v1/log-test
|
||||||
|
|
||||||
|
**Новый endpoint** - тестирование уровней логирования
|
||||||
|
|
||||||
|
**Запрос:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"level": "debug",
|
||||||
|
"message": "Test log message"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ответ:**
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "logged",
|
||||||
|
"level": "debug",
|
||||||
|
"message": "Test log message"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Логирование
|
||||||
|
|
||||||
|
### Структурированное логирование
|
||||||
|
|
||||||
|
Приложение использует современную систему логирования Go 1.24+ (`log/slog`) с поддержкой:
|
||||||
|
|
||||||
|
- **Структурированных логов** в JSON и текстовом формате
|
||||||
|
- **Уровней логирования** (DEBUG, INFO, WARN, ERROR)
|
||||||
|
- **Контекстной информации** (IP клиента, User-Agent, время запроса)
|
||||||
|
- **Автоматического форматирования** времени
|
||||||
|
- **Фильтрации чувствительных данных**
|
||||||
|
|
||||||
|
### Примеры логов
|
||||||
|
|
||||||
|
#### JSON формат (production)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"time": "2024-01-15T10:30:45.123Z",
|
||||||
|
"level": "INFO",
|
||||||
|
"msg": "HTTP Request",
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/v1/config",
|
||||||
|
"status": 200,
|
||||||
|
"latency": "1.234ms",
|
||||||
|
"client_ip": "192.168.1.100",
|
||||||
|
"user_agent": "curl/7.68.0",
|
||||||
|
"timestamp": "2024-01-15T10:30:45.123Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Текстовый формат (development)
|
||||||
|
|
||||||
|
``` text
|
||||||
|
2024-01-15T10:30:45.123Z INFO HTTP Request method=GET path=/api/v1/config status=200 latency=1.234ms client_ip=192.168.1.100 user_agent="curl/7.68.0"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Основные изменения в README
|
||||||
|
|
||||||
|
### 🆕 **Новые разделы:**
|
||||||
|
|
||||||
|
1. **Конфигурация логирования** - подробное описание переменных окружения
|
||||||
|
2. **Форматы логирования** - примеры JSON и текстового формата
|
||||||
|
3. **Новые API endpoints** - `/api/v1/config` и `/api/v1/log-test`
|
||||||
|
4. **Безопасность логирования** - фильтрация чувствительных данных
|
||||||
|
|
||||||
|
### 📊 **Детальное описание логирования:**
|
||||||
|
|
||||||
|
- **Уровни логирования** (DEBUG, INFO, WARN, ERROR)
|
||||||
|
- **Форматы** (JSON для production, text для разработки)
|
||||||
|
- **Примеры логов** с реальными данными
|
||||||
|
- **Контекстная информация** в логах
|
||||||
|
|
||||||
|
### 🔧 **Практические примеры:**
|
||||||
|
|
||||||
|
- Настройка для разработки и production
|
||||||
|
- Команды для тестирования
|
||||||
|
- Отладочные приемы
|
||||||
|
|
||||||
|
### 📦 **Улучшенная структура:**
|
||||||
|
|
||||||
|
- Четкое разделение возможностей
|
||||||
|
- Пошаговые инструкции
|
||||||
|
- Примеры конфигурации
|
||||||
|
- Команды для тестирования
|
||||||
|
|
||||||
|
Теперь README полностью отражает современные возможности приложения с детальным описанием системы логирования! 🎉
|
||||||
|
|
||||||
|
### Логируемые события
|
||||||
|
|
||||||
|
- **HTTP запросы** с детальной информацией
|
||||||
|
- **Ошибки приложения** с контекстом
|
||||||
|
- **Системные события** (запуск, остановка)
|
||||||
|
- **API вызовы** с параметрами
|
||||||
|
- **Конфигурационные изменения**
|
||||||
|
|
||||||
|
### Безопасность
|
||||||
|
|
||||||
|
- **Автоматическая фильтрация** чувствительных переменных окружения
|
||||||
|
- **Безопасное логирование** без утечки секретов
|
||||||
|
- **Контролируемые уровни** для разных сред
|
||||||
|
|
||||||
|
## Разработка
|
||||||
|
|
||||||
### Зависимости
|
### Зависимости
|
||||||
|
|
||||||
- Go 1.21+
|
- Go 1.24+
|
||||||
- Gin framework
|
- Gin framework
|
||||||
|
|
||||||
### Сборка
|
### Сборка
|
||||||
@@ -128,16 +316,42 @@ GOOS=darwin GOARCH=arm64 go build -o hello-api-darwin-arm64 main.go
|
|||||||
### Тестирование
|
### Тестирование
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Запуск сервера
|
# Запуск сервера с отладочным логированием
|
||||||
|
export LOG_LEVEL=DEBUG
|
||||||
|
export LOG_FORMAT=text
|
||||||
go run main.go
|
go run main.go
|
||||||
|
|
||||||
# Тестирование API
|
# Тестирование API
|
||||||
curl http://localhost:8080/
|
curl http://localhost:8080/
|
||||||
curl http://localhost:8080/healthz
|
curl http://localhost:8080/healthz
|
||||||
curl http://localhost:8080/api/v1/info
|
curl http://localhost:8080/api/v1/info
|
||||||
|
curl http://localhost:8080/api/v1/config
|
||||||
|
|
||||||
|
# Тестирование echo
|
||||||
curl -X POST http://localhost:8080/api/v1/echo \
|
curl -X POST http://localhost:8080/api/v1/echo \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
-d '{"message":"Hello!"}'
|
-d '{"message":"Hello!"}'
|
||||||
|
|
||||||
|
# Тестирование логирования
|
||||||
|
curl -X POST http://localhost:8080/api/v1/log-test \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"level": "debug", "message": "Test log message"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Отладка
|
||||||
|
|
||||||
|
Для детального анализа работы приложения:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Включить отладочное логирование
|
||||||
|
export LOG_LEVEL=DEBUG
|
||||||
|
export LOG_FORMAT=text
|
||||||
|
|
||||||
|
# Запустить приложение
|
||||||
|
go run main.go
|
||||||
|
|
||||||
|
# В другом терминале - мониторинг логов
|
||||||
|
tail -f /var/log/application.log # если логи в файл
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🚀 CI/CD
|
## 🚀 CI/CD
|
||||||
@@ -161,8 +375,6 @@ curl -X POST http://localhost:8080/api/v1/echo \
|
|||||||
|
|
||||||
Таким образом, данный файл обеспечивает автоматическую сборку и публикацию артефактов проекта при выпуске новых версий, что упрощает процесс релиза и гарантирует наличие актуальных образов и бинарников для пользователей.
|
Таким образом, данный файл обеспечивает автоматическую сборку и публикацию артефактов проекта при выпуске новых версий, что упрощает процесс релиза и гарантирует наличие актуальных образов и бинарников для пользователей.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 📄 Лицензия
|
## 📄 Лицензия
|
||||||
|
|
||||||
MIT License
|
MIT Licens
|
||||||
|
2
go.mod
2
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module direct-dev-ru/hello_gitea
|
module direct-dev-ru/hello_gitea
|
||||||
|
|
||||||
go 1.21
|
go 1.24
|
||||||
|
|
||||||
require github.com/gin-gonic/gin v1.10.1
|
require github.com/gin-gonic/gin v1.10.1
|
||||||
|
|
||||||
|
239
main.go
239
main.go
@@ -1,21 +1,127 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"context"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "1.0.31"
|
const version = "1.0.33"
|
||||||
|
|
||||||
|
// SystemInfo holds system information
|
||||||
|
type SystemInfo struct {
|
||||||
|
GoVersion string `json:"go_version"`
|
||||||
|
OS string `json:"os"`
|
||||||
|
Architecture string `json:"architecture"`
|
||||||
|
NumCPU int `json:"num_cpu"`
|
||||||
|
StartTime string `json:"start_time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
startTime = time.Now()
|
||||||
|
logger *slog.Logger
|
||||||
|
)
|
||||||
|
|
||||||
|
// setupLogger configures structured logging with different levels for dev/prod
|
||||||
|
func setupLogger() {
|
||||||
|
var logLevel slog.Level
|
||||||
|
var logFormat string
|
||||||
|
|
||||||
|
// Determine log level from environment
|
||||||
|
switch os.Getenv("LOG_LEVEL") {
|
||||||
|
case "DEBUG":
|
||||||
|
logLevel = slog.LevelDebug
|
||||||
|
case "INFO":
|
||||||
|
logLevel = slog.LevelInfo
|
||||||
|
case "WARN":
|
||||||
|
logLevel = slog.LevelWarn
|
||||||
|
case "ERROR":
|
||||||
|
logLevel = slog.LevelError
|
||||||
|
default:
|
||||||
|
logLevel = slog.LevelInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine log format from environment
|
||||||
|
logFormat = os.Getenv("LOG_FORMAT")
|
||||||
|
if logFormat == "" {
|
||||||
|
logFormat = "json" // Default to JSON for production
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure logger based on format
|
||||||
|
var handler slog.Handler
|
||||||
|
opts := &slog.HandlerOptions{
|
||||||
|
Level: logLevel,
|
||||||
|
AddSource: true,
|
||||||
|
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
|
||||||
|
// Customize timestamp format
|
||||||
|
if a.Key == slog.TimeKey {
|
||||||
|
return slog.Attr{
|
||||||
|
Key: slog.TimeKey,
|
||||||
|
Value: slog.StringValue(a.Value.Time().Format("2006-01-02T15:04:05.000Z07:00")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if logFormat == "text" {
|
||||||
|
handler = slog.NewTextHandler(os.Stdout, opts)
|
||||||
|
} else {
|
||||||
|
handler = slog.NewJSONHandler(os.Stdout, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
logger = slog.New(handler)
|
||||||
|
slog.SetDefault(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
// logMiddleware creates a structured logging middleware for Gin
|
||||||
|
func logMiddleware() gin.HandlerFunc {
|
||||||
|
return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
|
||||||
|
// Create structured log entry
|
||||||
|
logger.Info("HTTP Request",
|
||||||
|
"method", param.Method,
|
||||||
|
"path", param.Path,
|
||||||
|
"status", param.StatusCode,
|
||||||
|
"latency", param.Latency,
|
||||||
|
"client_ip", param.ClientIP,
|
||||||
|
"user_agent", param.Request.UserAgent(),
|
||||||
|
"timestamp", param.TimeStamp.Format("2006-01-02T15:04:05.000Z07:00"),
|
||||||
|
)
|
||||||
|
return "" // Return empty string as we handle logging ourselves
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// errorLogger logs errors with structured context
|
||||||
|
func ErrorLogger(err error, context ...any) {
|
||||||
|
logger.Error("Application Error",
|
||||||
|
append([]any{"error", err.Error()}, context...)...,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Setup structured logging
|
||||||
|
setupLogger()
|
||||||
|
|
||||||
|
logger.Info("Starting application",
|
||||||
|
"version", version,
|
||||||
|
"go_version", runtime.Version(),
|
||||||
|
"os", runtime.GOOS,
|
||||||
|
"architecture", runtime.GOARCH,
|
||||||
|
"num_cpu", runtime.NumCPU(),
|
||||||
|
)
|
||||||
|
|
||||||
// Set Gin mode
|
// Set Gin mode
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
|
|
||||||
// Create router
|
// Create router with custom logger
|
||||||
r := gin.Default()
|
r := gin.New()
|
||||||
|
r.Use(logMiddleware())
|
||||||
|
r.Use(gin.Recovery())
|
||||||
|
|
||||||
// Add middleware for CORS
|
// Add middleware for CORS
|
||||||
r.Use(func(c *gin.Context) {
|
r.Use(func(c *gin.Context) {
|
||||||
@@ -33,6 +139,7 @@ func main() {
|
|||||||
|
|
||||||
// Health check endpoint
|
// Health check endpoint
|
||||||
r.GET("/healthz", func(c *gin.Context) {
|
r.GET("/healthz", func(c *gin.Context) {
|
||||||
|
logger.Debug("Health check requested", "client_ip", c.ClientIP())
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"status": "ok",
|
"status": "ok",
|
||||||
"version": version,
|
"version": version,
|
||||||
@@ -41,6 +148,10 @@ func main() {
|
|||||||
|
|
||||||
// Main endpoint
|
// Main endpoint
|
||||||
r.GET("/", func(c *gin.Context) {
|
r.GET("/", func(c *gin.Context) {
|
||||||
|
logger.Info("Root endpoint accessed",
|
||||||
|
"client_ip", c.ClientIP(),
|
||||||
|
"user_agent", c.Request.UserAgent(),
|
||||||
|
)
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"message": "Hello, World!",
|
"message": "Hello, World!",
|
||||||
"version": version,
|
"version": version,
|
||||||
@@ -51,6 +162,7 @@ func main() {
|
|||||||
api := r.Group("/api/v1")
|
api := r.Group("/api/v1")
|
||||||
{
|
{
|
||||||
api.GET("/info", func(c *gin.Context) {
|
api.GET("/info", func(c *gin.Context) {
|
||||||
|
logger.Debug("API info requested", "client_ip", c.ClientIP())
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"service": "hello-api",
|
"service": "hello-api",
|
||||||
"version": version,
|
"version": version,
|
||||||
@@ -64,17 +176,104 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := c.ShouldBindJSON(&request); err != nil {
|
if err := c.ShouldBindJSON(&request); err != nil {
|
||||||
|
logger.Warn("Invalid JSON in echo request",
|
||||||
|
"client_ip", c.ClientIP(),
|
||||||
|
"error", err.Error(),
|
||||||
|
)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
"error": "Invalid JSON",
|
"error": "Invalid JSON",
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.Info("Echo request processed",
|
||||||
|
"client_ip", c.ClientIP(),
|
||||||
|
"message_length", len(request.Message),
|
||||||
|
)
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
"echo": request.Message,
|
"echo": request.Message,
|
||||||
"version": version,
|
"version": version,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Configuration endpoint with detailed logging
|
||||||
|
api.GET("/config", func(c *gin.Context) {
|
||||||
|
logger.Debug("Configuration requested", "client_ip", c.ClientIP())
|
||||||
|
|
||||||
|
systemInfo := SystemInfo{
|
||||||
|
GoVersion: runtime.Version(),
|
||||||
|
OS: runtime.GOOS,
|
||||||
|
Architecture: runtime.GOARCH,
|
||||||
|
NumCPU: runtime.NumCPU(),
|
||||||
|
StartTime: startTime.Format(time.RFC3339),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get environment variables (excluding sensitive ones)
|
||||||
|
envVars := make(map[string]string)
|
||||||
|
sensitiveCount := 0
|
||||||
|
for _, env := range os.Environ() {
|
||||||
|
// Skip sensitive environment variables
|
||||||
|
if !isSensitiveEnvVar(env) {
|
||||||
|
envVars[env] = os.Getenv(env)
|
||||||
|
} else {
|
||||||
|
sensitiveCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("Configuration accessed",
|
||||||
|
"client_ip", c.ClientIP(),
|
||||||
|
"env_vars_count", len(envVars),
|
||||||
|
"sensitive_vars_filtered", sensitiveCount,
|
||||||
|
"uptime", time.Since(startTime).String(),
|
||||||
|
)
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"version": version,
|
||||||
|
"system_info": systemInfo,
|
||||||
|
"environment": envVars,
|
||||||
|
"uptime": time.Since(startTime).String(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// New logging endpoint to test log levels
|
||||||
|
api.POST("/log-test", func(c *gin.Context) {
|
||||||
|
var request struct {
|
||||||
|
Level string `json:"level"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.ShouldBindJSON(&request); err != nil {
|
||||||
|
logger.Warn("Invalid JSON in log-test request",
|
||||||
|
"client_ip", c.ClientIP(),
|
||||||
|
"error", err.Error(),
|
||||||
|
)
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{
|
||||||
|
"error": "Invalid JSON",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log based on requested level
|
||||||
|
switch request.Level {
|
||||||
|
case "debug":
|
||||||
|
logger.Debug(request.Message, "client_ip", c.ClientIP())
|
||||||
|
case "info":
|
||||||
|
logger.Info(request.Message, "client_ip", c.ClientIP())
|
||||||
|
case "warn":
|
||||||
|
logger.Warn(request.Message, "client_ip", c.ClientIP())
|
||||||
|
case "error":
|
||||||
|
logger.Error(request.Message, "client_ip", c.ClientIP())
|
||||||
|
default:
|
||||||
|
logger.Info(request.Message, "client_ip", c.ClientIP(), "level", request.Level)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"status": "logged",
|
||||||
|
"level": request.Level,
|
||||||
|
"message": request.Message,
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get port from environment or use default
|
// Get port from environment or use default
|
||||||
@@ -83,9 +282,37 @@ func main() {
|
|||||||
port = "8080"
|
port = "8080"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start server
|
// Start server with structured logging
|
||||||
|
logger.Info("Server starting",
|
||||||
|
"port", port,
|
||||||
|
"mode", gin.Mode(),
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
go func() {
|
||||||
|
// Graceful shutdown handling
|
||||||
|
<-ctx.Done()
|
||||||
|
logger.Info("Shutdown signal received, stopping server...")
|
||||||
|
}()
|
||||||
|
|
||||||
err := r.Run(":" + port)
|
err := r.Run(":" + port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
logger.Error("Server failed to start", "error", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isSensitiveEnvVar checks if an environment variable contains sensitive information
|
||||||
|
func isSensitiveEnvVar(env string) bool {
|
||||||
|
sensitivePrefixes := []string{
|
||||||
|
"PASSWORD", "SECRET", "KEY", "TOKEN", "CREDENTIAL",
|
||||||
|
"AWS_", "GITHUB_", "DOCKER_", "KUBERNETES_",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, prefix := range sensitivePrefixes {
|
||||||
|
if len(env) >= len(prefix) && env[:len(prefix)] == prefix {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
2
makefile
2
makefile
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
BIN_DIR=bin
|
BIN_DIR=bin
|
||||||
APP_NAME=hello-api
|
APP_NAME=hello-api
|
||||||
VERSION=1.0.31
|
VERSION=1.0.33
|
||||||
|
|
||||||
build:
|
build:
|
||||||
mkdir -p $(BIN_DIR)
|
mkdir -p $(BIN_DIR)
|
||||||
|
@@ -36,6 +36,9 @@ get_version_interactive() {
|
|||||||
echo "📝 Подтверждение:"
|
echo "📝 Подтверждение:"
|
||||||
echo " Текущая версия: $CURRENT_VERSION"
|
echo " Текущая версия: $CURRENT_VERSION"
|
||||||
echo " Новая версия: $VERSION"
|
echo " Новая версия: $VERSION"
|
||||||
|
if [ "$CURRENT_VERSION" == "$VERSION" ]; then
|
||||||
|
echo "⚠️ Новая версия совпадает с текущей. Возможно будет обновлен тег ..."
|
||||||
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
read -r -p "Продолжить? (y/N): " CONFIRM
|
read -r -p "Продолжить? (y/N): " CONFIRM
|
||||||
|
|
||||||
@@ -68,11 +71,27 @@ if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Проверяем, что нет незакоммиченных изменений
|
# Проверяем, существует ли уже тег с такой версией
|
||||||
# if ! git diff-index --quiet HEAD --; then
|
if git tag -l "v$VERSION" | grep -q "v$VERSION"; then
|
||||||
# echo "Ошибка: Есть незакоммиченные изменения. Сначала закоммитьте их."
|
echo "⚠️ Тег v$VERSION уже существует!"
|
||||||
# exit 1
|
echo ""
|
||||||
# fi
|
read -r -p "Обновить существующий тег? (y/N): " UPDATE_TAG
|
||||||
|
|
||||||
|
if [[ ! $UPDATE_TAG =~ ^[Yy]$ ]]; then
|
||||||
|
echo "❌ Обновление тега отменено"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "🔄 Обновляем существующий тег..."
|
||||||
|
|
||||||
|
# Удаляем локальный тег
|
||||||
|
git tag -d "v$VERSION" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Удаляем удаленный тег
|
||||||
|
git push origin ":refs/tags/v$VERSION" 2>/dev/null || true
|
||||||
|
|
||||||
|
echo "✅ Старый тег удален"
|
||||||
|
fi
|
||||||
|
|
||||||
# Обновляем версию в main.go
|
# Обновляем версию в main.go
|
||||||
echo "📝 Обновляем версию в main.go..."
|
echo "📝 Обновляем версию в main.go..."
|
||||||
|
Reference in New Issue
Block a user