20 files changed,
320 insertions(+),
24 deletions(-)
Author:
Smirnov Oleksandr
ss2316544@gmail.com
Committed by:
GitHub
noreply@github.com
Committed at:
2024-10-05 23:23:51 +0300
Parent:
a4b3d8e
jump to
M
.env.example
··· 1 1 APP_ENV=debug 2 -APP_URL=http://localhost:3000 3 -SERVER_PORT=3000 2 +APP_URL=http://localhost:8000 3 +SERVER_PORT=8000 4 4 PASSWORD_SALT=onasty 5 +METRICS_PORT=8001 5 6 6 7 LOG_LEVEL=debug 7 8 LOG_FORMAT=text ··· 13 14 14 15 POSTGRES_USERNAME=onasty 15 16 POSTGRES_PASSWORD=qwerty 16 -POSTGRES_HOST=127.0.0.1 17 +POSTGRES_HOST=postgres 17 18 POSTGRES_PORT=5432 18 19 POSTGRES_DATABASE=onasty 19 20 POSTGRESQL_DSN="postgres://$POSTGRES_USERNAME:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DATABASE?sslmode=disable" 21 +MIGRATION_DSN="postgres://$POSTGRES_USERNAME:$POSTGRES_PASSWORD@localhost:$POSTGRES_PORT/$POSTGRES_DATABASE?sslmode=disable" 20 22 21 23 MAILGUN_FROM=onasty@mail.com 22 24 MAILGUN_DOMAI='<domain>'
A
Dockerfile
··· 1 +FROM golang:1.23.1-alpine AS builder 2 + 3 +WORKDIR /app 4 + 5 +COPY go.mod go.sum ./ 6 +RUN go mod download 7 + 8 +COPY cmd cmd 9 +COPY internal internal 10 + 11 +RUN CGO_ENABLED=0 GOOS=linux go build -ldflags='-w -s' -o /onasty ./cmd/server 12 + 13 +FROM scratch 14 +COPY --from=builder /onasty /onasty 15 +ENTRYPOINT ["/onasty"]
M
Taskfile.yml
··· 11 11 - go build -o .bin/onasty ./cmd/server/ 12 12 13 13 run: 14 - - task: build 15 - - .bin/onasty 14 + - docker compose up -d --build core 16 15 17 16 lint: 18 17 - golangci-lint run 19 18 20 19 docker:up: 21 - - docker compose up -d 20 + - docker compose up -d --build --remove-orphans 22 21 23 22 docker:down: 24 23 aliases: [docker:stop]
M
cmd/server/main.go
··· 15 15 "github.com/olexsmir/onasty/internal/jwtutil" 16 16 "github.com/olexsmir/onasty/internal/logger" 17 17 "github.com/olexsmir/onasty/internal/mailer" 18 + "github.com/olexsmir/onasty/internal/metrics" 18 19 "github.com/olexsmir/onasty/internal/service/notesrv" 19 20 "github.com/olexsmir/onasty/internal/service/usersrv" 20 21 "github.com/olexsmir/onasty/internal/store/psql/noterepo" ··· 87 88 // http server 88 89 srv := httpserver.NewServer(cfg.ServerPort, handler.Handler()) 89 90 go func() { 90 - slog.Debug("starting http server", "port", cfg.ServerPort) 91 + slog.Info("starting http server", "port", cfg.ServerPort) 91 92 if err := srv.Start(); !errors.Is(err, http.ErrServerClosed) { 92 93 slog.Error("failed to start http server", "error", err) 93 94 } 94 95 }() 96 + 97 + // metrics 98 + if cfg.MetricsEnabled { 99 + mSrv := httpserver.NewServer(cfg.MetricsPort, metrics.Handler()) 100 + go func() { 101 + slog.Info("starting metrics server", "port", cfg.MetricsPort) 102 + if err := mSrv.Start(); !errors.Is(err, http.ErrServerClosed) { 103 + slog.Error("failed to start metrics server", "error", err) 104 + } 105 + }() 106 + } 95 107 96 108 // graceful shutdown 97 109 quit := make(chan os.Signal, 1)
M
docker-compose.yml
··· 1 1 services: 2 + core: 3 + image: onasty:core 4 + container_name: onasty-core 5 + build: 6 + context: . 7 + dockerfile: Dockerfile 8 + env_file: .env 9 + ports: 10 + - 8000:8000 11 + - 8001:8001 12 + 2 13 postgres: 3 14 image: postgres:16-alpine 4 15 container_name: onasty-postgres ··· 10 21 - .docker/postgres:/var/lib/postgresql/data 11 22 ports: 12 23 - 5432:5432 24 + 25 + prometheus: 26 + image: prom/prometheus 27 + container_name: onasty-prometheus 28 + user: root 29 + volumes: 30 + - ./.docker/prometheus:/prometheus 31 + - ./infra/prometheus:/etc/prometheus 32 + ports: 33 + - 9090:9090 34 + 35 + grafana: 36 + image: grafana/grafana:11.1.6 37 + container_name: onasty-grafana 38 + user: root 39 + environment: 40 + - GF_SECURITY_ADMIN_USER=admin 41 + - GF_SECURITY_ADMIN_PASSWORD=admin 42 + volumes: 43 + - ./.docker/grafana:/var/lib/grafana 44 + - ./infra/grafana/datasources.yml:/etc/grafana/provisioning/datasources/datasources.yml 45 + ports: 46 + - 3069:3000 47 + 48 + loki: 49 + image: grafana/loki:3.2.0 50 + command: ["--pattern-ingester.enabled=true", "-config.file=/etc/loki/config.yaml"] 51 + ports: 52 + - 3100:3100 53 + volumes: 54 + - ./infra/loki/config.yaml:/etc/loki/config.yaml 55 + 56 + promtail: 57 + image: grafana/promtail:3.0.0 58 + command: -config.file=/etc/promtail/config.yaml 59 + volumes: 60 + - /var/run/docker.sock:/var/run/docker.sock 61 + - ./infra/promtail/config.yaml:/etc/promtail/config.yaml
M
go.mod
··· 11 11 github.com/jackc/pgx-gofrs-uuid v0.0.0-20230224015001-1d428863c2e2 12 12 github.com/jackc/pgx/v5 v5.7.1 13 13 github.com/mailgun/mailgun-go/v4 v4.16.0 14 + github.com/prometheus/client_golang v1.20.4 14 15 github.com/stretchr/testify v1.9.0 15 16 github.com/testcontainers/testcontainers-go v0.33.0 16 17 github.com/testcontainers/testcontainers-go/modules/postgres v0.33.0 ··· 21 22 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect 22 23 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect 23 24 github.com/Microsoft/go-winio v0.6.2 // indirect 25 + github.com/beorn7/perks v1.0.1 // indirect 24 26 github.com/bytedance/sonic v1.11.6 // indirect 25 27 github.com/bytedance/sonic/loader v0.1.1 // indirect 26 28 github.com/cenkalti/backoff/v4 v4.2.1 // indirect 29 + github.com/cespare/xxhash/v2 v2.3.0 // indirect 27 30 github.com/cloudwego/base64x v0.1.4 // indirect 28 31 github.com/cloudwego/iasm v0.2.0 // indirect 29 32 github.com/containerd/log v0.1.0 // indirect ··· 60 63 github.com/jackc/pgx/v4 v4.18.2 // indirect 61 64 github.com/jackc/puddle/v2 v2.2.2 // indirect 62 65 github.com/json-iterator/go v1.1.12 // indirect 63 - github.com/klauspost/compress v1.17.4 // indirect 66 + github.com/klauspost/compress v1.17.9 // indirect 64 67 github.com/klauspost/cpuid/v2 v2.2.7 // indirect 65 68 github.com/leodido/go-urn v1.4.0 // indirect 66 69 github.com/lib/pq v1.10.9 // indirect ··· 77 80 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 78 81 github.com/modern-go/reflect2 v1.0.2 // indirect 79 82 github.com/morikuni/aec v1.0.0 // indirect 83 + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 80 84 github.com/opencontainers/go-digest v1.0.0 // indirect 81 85 github.com/opencontainers/image-spec v1.1.0 // indirect 82 86 github.com/pelletier/go-toml/v2 v2.2.2 // indirect 83 87 github.com/pkg/errors v0.9.1 // indirect 84 88 github.com/pmezard/go-difflib v1.0.0 // indirect 85 89 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect 90 + github.com/prometheus/client_model v0.6.1 // indirect 91 + github.com/prometheus/common v0.55.0 // indirect 92 + github.com/prometheus/procfs v0.15.1 // indirect 86 93 github.com/shirou/gopsutil/v3 v3.23.12 // indirect 87 94 github.com/shoenig/go-m1cpu v0.1.6 // indirect 88 95 github.com/sirupsen/logrus v1.9.3 // indirect ··· 96 103 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect 97 104 go.opentelemetry.io/otel/metric v1.29.0 // indirect 98 105 go.opentelemetry.io/otel/trace v1.29.0 // indirect 99 - go.uber.org/atomic v1.7.0 // indirect 106 + go.uber.org/atomic v1.9.0 // indirect 100 107 golang.org/x/arch v0.8.0 // indirect 101 108 golang.org/x/crypto v0.27.0 // indirect 102 109 golang.org/x/net v0.29.0 // indirect
M
go.sum
··· 9 9 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 10 10 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 11 11 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 12 +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 13 +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 12 14 github.com/ahmetb/go-linq v3.0.0+incompatible h1:qQkjjOXKrKOTy83X8OpRmnKflXKQIL/mC/gMVVDMhOA= 13 15 github.com/ahmetb/go-linq v3.0.0+incompatible/go.mod h1:PFffvbdbtw+QTB0WKRP0cNht7vnCfnGlEpak/DVg5cY= 14 16 github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= ··· 17 19 github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 18 20 github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= 19 21 github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 22 +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 23 +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 20 24 github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= 21 25 github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 22 26 github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= ··· 169 173 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 170 174 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 171 175 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 172 -github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= 173 -github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= 176 +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= 177 +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 174 178 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 175 179 github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 176 180 github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= ··· 178 182 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 179 183 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 180 184 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 181 -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= 182 -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= 185 +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 186 +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 183 187 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 184 188 github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 185 189 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 186 190 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 187 191 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 192 +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 193 +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 188 194 github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 189 195 github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 190 196 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= ··· 227 233 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 228 234 github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 229 235 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 236 +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 237 +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 230 238 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 231 239 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 232 240 github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= ··· 240 248 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 241 249 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= 242 250 github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 251 +github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= 252 +github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= 253 +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= 254 +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= 255 +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= 256 +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= 257 +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= 258 +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= 243 259 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 244 260 github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 245 261 github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= ··· 314 330 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 315 331 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 316 332 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 317 -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= 318 -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 333 +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= 334 +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 319 335 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 320 336 go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 321 337 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
A
infra/loki/config.yaml
··· 1 +auth_enabled: false 2 + 3 +server: 4 + http_listen_port: 3100 5 + 6 +limits_config: 7 + allow_structured_metadata: false 8 + 9 +common: 10 + path_prefix: /tmp/loki 11 + storage: 12 + filesystem: 13 + chunks_directory: /tmp/loki/chunks 14 + rules_directory: /tmp/loki/rules 15 + replication_factor: 1 16 + ring: 17 + instance_addr: 127.0.0.1 18 + kvstore: 19 + store: inmemory 20 + 21 +schema_config: 22 + configs: 23 + - from: 2020-10-24 24 + store: boltdb-shipper 25 + object_store: filesystem 26 + schema: v11 27 + index: 28 + prefix: index_ 29 + period: 24h
A
infra/prometheus/prometheus.yml
··· 1 +global: 2 + scrape_interval: 5s 3 + scrape_timeout: 2s 4 + evaluation_interval: 15s 5 + 6 +scrape_configs: 7 + - job_name: core 8 + metrics_path: /metrics 9 + scheme: http 10 + follow_redirects: true 11 + honor_timestamps: true 12 + static_configs: 13 + - targets: [core:8001]
A
infra/promtail/config.yaml
··· 1 +server: 2 + http_listen_port: 9080 3 + 4 +positions: 5 + filename: /tmp/positions.yaml 6 + 7 +clients: 8 + - url: http://loki:3100/loki/api/v1/push 9 + 10 +scrape_configs: 11 + - job_name: containers 12 + docker_sd_configs: 13 + - host: unix:///var/run/docker.sock 14 + relabel_configs: 15 + - source_labels: [__meta_docker_container_name] 16 + target_label: container 17 + regex: '/(.+)' 18 + replacement: '$1' 19 + - source_labels: [__meta_docker_container_id] 20 + target_label: container_id
M
internal/config/config.go
··· 22 22 MailgunAPIKey string 23 23 VerificationTokenTTL time.Duration 24 24 25 + MetricsEnabled bool 26 + MetricsPort string 27 + 25 28 LogLevel string 26 29 LogFormat string 27 30 LogShowLine bool ··· 49 52 VerificationTokenTTL: mustParseDurationOrPanic( 50 53 getenvOrDefault("VERIFICATION_TOKEN_TTL", "24h"), 51 54 ), 55 + 56 + MetricsPort: getenvOrDefault("METRICS_PORT", "3001"), 57 + MetricsEnabled: getenvOrDefault("METRICS_ENABLED", "true") == "true", 52 58 53 59 LogLevel: getenvOrDefault("LOG_LEVEL", "debug"), 54 60 LogFormat: getenvOrDefault("LOG_FORMAT", "json"),
M
internal/mailer/mailgun.go
··· 5 5 "log/slog" 6 6 7 7 "github.com/mailgun/mailgun-go/v4" 8 + "github.com/olexsmir/onasty/internal/metrics" 9 + "github.com/olexsmir/onasty/internal/transport/http/reqid" 8 10 ) 9 11 10 12 var _ Mailer = (*Mailgun)(nil) ··· 27 29 msg := m.mg.NewMessage(m.from, subject, "", to) 28 30 msg.SetHtml(content) 29 31 32 + slog.InfoContext(ctx, "email sent", "to", to) 33 + 30 34 _, _, err := m.mg.Send(ctx, msg) 35 + if err != nil { 36 + metrics.RecordEmailFailed(reqid.GetContext(ctx)) 37 + return err 38 + } 31 39 32 - slog.InfoContext(ctx, "email sent", "to", to) 33 40 slog.DebugContext(ctx, "email sent", "subject", subject, "content", content, "err", err) 41 + metrics.RecordEmailSent() 34 42 35 - return err 43 + return nil 36 44 }
A
internal/metrics/http_metrics.go
··· 1 +package metrics 2 + 3 +import ( 4 + "time" 5 + 6 + "github.com/prometheus/client_golang/prometheus" 7 + "github.com/prometheus/client_golang/prometheus/promauto" 8 +) 9 + 10 +var ( 11 + successfulHTTPRequest = promauto.NewCounterVec(prometheus.CounterOpts{ //nolint:exhaustruct 12 + Name: "http_successful_requests_total", 13 + Help: "the total number of successful http requests", 14 + ConstLabels: map[string]string{"status": "success"}, 15 + }, []string{"method", "uri"}) 16 + 17 + failedHTTPRequest = promauto.NewCounterVec(prometheus.CounterOpts{ //nolint:exhaustruct 18 + Name: "http_failed_requests_total", 19 + Help: "the total number of failed http requests", 20 + ConstLabels: map[string]string{"status": "failure"}, 21 + }, []string{"method", "uri"}) 22 + 23 + latencyHTTPRequest = promauto.NewHistogramVec(prometheus.HistogramOpts{ //nolint:exhaustruct 24 + Name: "http_request_latency_seconds", 25 + Help: "the latency of http requests in seconds", 26 + Buckets: prometheus.DefBuckets, 27 + }, []string{"method", "uri"}) 28 +) 29 + 30 +func RecordSuccessfulRequestMetric(method, uri string) { 31 + go successfulHTTPRequest.With(prometheus.Labels{ 32 + "method": method, 33 + "uri": uri, 34 + }).Inc() 35 +} 36 + 37 +func RecordFailedRequestMetric(method, uri string) { 38 + go failedHTTPRequest.With(prometheus.Labels{ 39 + "method": method, 40 + "uri": uri, 41 + }).Inc() 42 +} 43 + 44 +func RecordLatencyRequestMetric(method, uri string, latency time.Duration) { 45 + go latencyHTTPRequest.With(prometheus.Labels{ 46 + "method": method, 47 + "uri": uri, 48 + }).Observe(latency.Seconds()) 49 +}
A
internal/metrics/mail_metrics.go
··· 1 +package metrics 2 + 3 +import ( 4 + "github.com/prometheus/client_golang/prometheus" 5 + "github.com/prometheus/client_golang/prometheus/promauto" 6 +) 7 + 8 +var ( 9 + emailSentSuccessfully = promauto.NewCounter(prometheus.CounterOpts{ //nolint:exhaustruct 10 + Name: "mail_sent_total", 11 + Help: "the total number of successfully sent email", 12 + }) 13 + 14 + emailFailedToSend = promauto.NewCounterVec(prometheus.CounterOpts{ //nolint:exhaustruct 15 + Name: "mail_failed_total", 16 + Help: "the total number of email that failed to send", 17 + }, []string{"request_id"}) 18 +) 19 + 20 +func RecordEmailSent() { 21 + go emailSentSuccessfully.Inc() 22 +} 23 + 24 +func RecordEmailFailed(reqid string) { 25 + go emailFailedToSend.With(prometheus.Labels{ 26 + "request_id": reqid, 27 + }).Inc() 28 +}
M
internal/transport/http/apiv1/middleware.go
··· 3 3 import ( 4 4 "context" 5 5 "strings" 6 + "time" 6 7 7 8 "github.com/gin-gonic/gin" 8 9 "github.com/gofrs/uuid/v5" 10 + "github.com/olexsmir/onasty/internal/metrics" 9 11 "github.com/olexsmir/onasty/internal/models" 10 12 ) 11 13 ··· 50 52 } 51 53 52 54 c.Next() 55 +} 56 + 57 +func (a *APIV1) metricsMiddleware(c *gin.Context) { 58 + start := time.Now() 59 + c.Next() 60 + latency := time.Since(start) 61 + 62 + metrics.RecordLatencyRequestMetric(c.Request.Method, c.Request.RequestURI, latency) 63 + 64 + if c.Writer.Status() >= 200 && c.Writer.Status() < 300 { 65 + metrics.RecordSuccessfulRequestMetric(c.Request.Method, c.Request.RequestURI) 66 + } 67 + 68 + if c.Writer.Status() >= 400 { 69 + metrics.RecordFailedRequestMetric(c.Request.Method, c.Request.RequestURI) 70 + } 53 71 } 54 72 55 73 //nolint:unused // TODO: remove me later
M
internal/transport/http/middlewares.go
··· 5 5 "time" 6 6 7 7 "github.com/gin-gonic/gin" 8 - "github.com/olexsmir/onasty/internal/transport/http/reqid" 9 8 ) 10 9 11 10 func (t *Transport) logger() gin.HandlerFunc { ··· 22 21 } 23 22 24 23 lvl := slog.LevelInfo 25 - if c.Writer.Status() >= 500 { 24 + if c.Writer.Status() >= 400 { 26 25 lvl = slog.LevelError 27 26 } 28 27 ··· 30 29 c.Request.Context(), 31 30 lvl, 32 31 c.Errors.ByType(gin.ErrorTypePrivate).String(), 33 - slog.String("request_id", reqid.Get(c)), 34 32 slog.String("latency", latency.String()), 35 33 slog.String("method", c.Request.Method), 36 34 slog.Int("status_code", c.Writer.Status()),
M
migrations/Taskfile.yml
··· 10 10 - migrate create -ext sql -dir {{.MIGRATIONS_DIR}} {{ .CLI_ARGS }} 11 11 12 12 up: 13 - - migrate -database $POSTGRESQL_DSN -path {{.MIGRATIONS_DIR}} up 13 + - migrate -database $MIGRATION_DSN -path {{.MIGRATIONS_DIR}} up 14 14 15 15 down: 16 - - migrate -database $POSTGRESQL_DSN -path {{.MIGRATIONS_DIR}} down 1 16 + - migrate -database $MIGRATION_DSN -path {{.MIGRATIONS_DIR}} down 1 17 17 18 18 drop: 19 - - migrate -database $POSTGRESQL_DSN -path {{.MIGRATIONS_DIR}} drop 19 + - migrate -database $MIGRATION_DSN -path {{.MIGRATIONS_DIR}} drop 20 20 21 21 current-version: 22 - - migrate -database $POSTGRESQL_DSN -path {{.MIGRATIONS_DIR}} version 22 + - migrate -database $MIGRATION_DSN -path {{.MIGRATIONS_DIR}} version