From 27b1f1fb355de2b852ef40be3c9a7ddb60c0ee47 Mon Sep 17 00:00:00 2001 From: Piotr Biernat Date: Tue, 29 Nov 2022 22:40:58 +0100 Subject: [PATCH] :Huge REFACTORING --- .drone.yml | 62 ++++ .env.dist | 4 + Dockerfile.server | 30 ++ Dockerfile.worker | 30 ++ Makefile | 19 ++ cmd/main.go | 7 - deploy/image-build.sh | 33 ++ deploy/image-push.sh | 10 + .gitignore => src/.gitignore | 5 +- src/cmd/server/main.go | 72 +++++ src/cmd/worker/main.go | 132 ++++++++ src/go.mod | 49 +++ src/go.sum | 333 +++++++++++++++++++++ src/internal/app/config/env.go | 22 ++ src/internal/app/database/connect.go | 16 + src/internal/app/definition/catalog.go | 17 ++ src/internal/app/definition/error.go | 9 + src/internal/app/definition/health.go | 5 + src/internal/app/event/basket.go | 13 + src/internal/app/event/event.go | 12 + src/internal/app/log.go | 20 ++ src/internal/app/server/catalog_handler.go | 63 ++++ src/internal/app/server/config.go | 15 + src/internal/app/server/health_handler.go | 13 + src/internal/app/server/router.go | 31 ++ src/internal/app/server/server.go | 101 +++++++ src/internal/app/service/catalog.go | 41 +++ src/pkg/amqp/connect.go | 26 ++ src/pkg/amqp/pubsub.go | 84 ++++++ src/pkg/consul/discovery.go | 107 +++++++ src/pkg/fluentd/config.go | 14 + src/pkg/fluentd/logger.go | 41 +++ 32 files changed, 1427 insertions(+), 9 deletions(-) create mode 100644 .drone.yml create mode 100644 .env.dist create mode 100644 Dockerfile.server create mode 100644 Dockerfile.worker create mode 100644 Makefile delete mode 100644 cmd/main.go create mode 100755 deploy/image-build.sh create mode 100755 deploy/image-push.sh rename .gitignore => src/.gitignore (95%) create mode 100644 src/cmd/server/main.go create mode 100644 src/cmd/worker/main.go create mode 100644 src/go.mod create mode 100644 src/go.sum create mode 100644 src/internal/app/config/env.go create mode 100644 src/internal/app/database/connect.go create mode 100644 src/internal/app/definition/catalog.go create mode 100644 src/internal/app/definition/error.go create mode 100644 src/internal/app/definition/health.go create mode 100644 src/internal/app/event/basket.go create mode 100644 src/internal/app/event/event.go create mode 100644 src/internal/app/log.go create mode 100644 src/internal/app/server/catalog_handler.go create mode 100644 src/internal/app/server/config.go create mode 100644 src/internal/app/server/health_handler.go create mode 100644 src/internal/app/server/router.go create mode 100644 src/internal/app/server/server.go create mode 100644 src/internal/app/service/catalog.go create mode 100644 src/pkg/amqp/connect.go create mode 100644 src/pkg/amqp/pubsub.go create mode 100644 src/pkg/consul/discovery.go create mode 100644 src/pkg/fluentd/config.go create mode 100644 src/pkg/fluentd/logger.go diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..303b906 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,62 @@ +kind: pipeline +type: docker +name: default + +steps: +- name: static_check + image: golang:latest + commands: + - go install honnef.co/go/tools/cmd/staticcheck@latest + - cd src && staticcheck ./... + volumes: + - name: gopath + path: /go + +- name: lint + image: golang:latest + commands: + - go install golang.org/x/lint/golint@latest + - golint ./src/... + volumes: + - name: gopath + path: /go + +- name: analyze + image: golang:latest + commands: + - cd src && go vet ./... + volumes: + - name: gopath + path: /go + +- name: publish_image + image: plugins/docker + environment: + DOCKER_USERNAME: + from_secret: registry_username + DOCKER_PASSWORD: + from_secret: registry_password + commands: + - sleep 5 + - ./deploy/image-build.sh + - ./deploy/image-push.sh + volumes: + - name: docker-sock + path: /var/run + when: + branch: + - main + +services: +- name: docker + image: docker:dind + privileged: true + volumes: + - name: docker-sock + path: /var/run + +volumes: +- name: gopath + temp: {} +- name: docker-sock + temp: {} diff --git a/.env.dist b/.env.dist new file mode 100644 index 0000000..84c5d45 --- /dev/null +++ b/.env.dist @@ -0,0 +1,4 @@ +SERVER_ADDR=:80 +DATABASE_URL=postgres://postgres:12345678@postgres-db:5432/egommerce +MONGODB_URL=mongodb://mongodb:12345678@mongo-db:27017 +EVENTBUS_URL=amqp://guest:guest@api-eventbus:5672 \ No newline at end of file diff --git a/Dockerfile.server b/Dockerfile.server new file mode 100644 index 0000000..2010485 --- /dev/null +++ b/Dockerfile.server @@ -0,0 +1,30 @@ +# Builder +FROM golang:alpine AS builder + +ARG BIN_OUTPUT=/go/bin/server +ARG GO_MAIN=cmd/server/main.go + +WORKDIR /go/src/app +COPY src ./ + +RUN go mod download && \ + export CGO_ENABLED=0 ; export GOOS=linux ; export GOARCH=amd64 && \ + go build -ldflags="-w -s" -o $BIN_OUTPUT $GO_MAIN + + +# Destination image - server +FROM gcr.io/distroless/base-debian10 + +ARG BIN_OUTPUT=/go/bin/server + +LABEL author="Piotr Biernat" +LABEL service="catalog-svc" +LABEL vendor="Egommerce" +LABEL version="1.0" + +WORKDIR / +COPY --from=builder $BIN_OUTPUT / +COPY .env.dist /.env + +EXPOSE 80 +ENTRYPOINT ["/server"] diff --git a/Dockerfile.worker b/Dockerfile.worker new file mode 100644 index 0000000..14d3173 --- /dev/null +++ b/Dockerfile.worker @@ -0,0 +1,30 @@ +# Builder +FROM golang:alpine AS builder + +ARG BIN_OUTPUT=/go/bin/worker +ARG GO_WORKER=cmd/worker/main.go + +WORKDIR /go/src/app +COPY src ./ + +RUN go mod download && \ + export CGO_ENABLED=0 ; export GOOS=linux ; export GOARCH=amd64 && \ + go build -ldflags="-w -s" -o $BIN_OUTPUT $GO_WORKER + + +# Destination image - worker +FROM gcr.io/distroless/base-debian10 + +ARG BIN_OUTPUT=/go/bin/worker + +LABEL author="Piotr Biernat" +LABEL service="catalog-worker" +LABEL vendor="Egommerce" +LABEL version="1.0" + +WORKDIR / +COPY --from=builder $BIN_OUTPUT / +COPY .env.dist /.env + +EXPOSE 80 +ENTRYPOINT ["/worker"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2d234f9 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +DEPLOY_DIR := ./deploy +SRC_DIR := ./src + +## DEPLOY PART +build-image-dev: + - sh ${DEPLOY_DIR}/image-build.sh dev + +build-image-prod: + - sh ${DEPLOY_DIR}/image-build.sh + +push-image-prod: + - sh ${DEPLOY_DIR}/image-push.sh + +# (GOLANG) APP PART +app-run: + - make -C ${SRC_DIR} run + +app-build: + - make -C ${SRC_DIR} build diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index d701af0..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,7 +0,0 @@ -package main - -import "fmt" - -func main() { - fmt.Println("Catalog services") -} diff --git a/deploy/image-build.sh b/deploy/image-build.sh new file mode 100755 index 0000000..905f77e --- /dev/null +++ b/deploy/image-build.sh @@ -0,0 +1,33 @@ +#!/bin/sh +# RUN IN REPO ROOT DIR !! + +# export DOCKER_BUILDKIT=1 + +export IMAGE_BASE="git.pbiernat.dev/egommerce/catalog" +export SERVER_IMAGE="$IMAGE_BASE-svc" +export WORKER_IMAGE="$IMAGE_BASE-worker" + +TARGET=${1:-latest} +KIND=${2:-all} + +if [ $KIND = "svc" ] || [ $KIND = "all" ]; then + echo "Building: $SERVER_IMAGE:$TARGET" + if [ $TARGET = "dev" ] + then + docker build --rm --no-cache -t "$SERVER_IMAGE:$TARGET" -f Dockerfile.server . # >/dev/null 2>&1 + else + docker build --rm --cache-from "$SERVER_IMAGE:$TARGET" -t "$SERVER_IMAGE:$TARGET" -f Dockerfile.server . >/dev/null 2>&1 + fi +fi + +if [ $KIND = "worker" ] || [ $KIND = "all" ]; then + echo "Building: $WORKER_IMAGE:$TARGET" + if [ $TARGET = "dev" ] + then + docker build --rm --no-cache -t "$WORKER_IMAGE:$TARGET" -f Dockerfile.worker . # >/dev/null 2>&1 + else + docker build --rm --cache-from "$WORKER_IMAGE:$TARGET" -t "$WORKER_IMAGE:$TARGET" -f Dockerfile.worker . >/dev/null 2>&1 + fi +fi + +echo "Done." diff --git a/deploy/image-push.sh b/deploy/image-push.sh new file mode 100755 index 0000000..900a286 --- /dev/null +++ b/deploy/image-push.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# RUN IN REPO ROOT DIR !! + +export IMAGE_BASE="git.pbiernat.dev/egommerce/catalog" +export SERVER_IMAGE="$IMAGE_BASE-svc" +export WORKER_IMAGE="$IMAGE_BASE-worker" + +echo $DOCKER_PASSWORD | docker login git.pbiernat.dev -u $DOCKER_USERNAME --password-stdin +docker push "$SERVER_IMAGE:latest" +docker push "$WORKER_IMAGE:latest" diff --git a/.gitignore b/src/.gitignore similarity index 95% rename from .gitignore rename to src/.gitignore index f4d432a..94e30ce 100644 --- a/.gitignore +++ b/src/.gitignore @@ -12,6 +12,7 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out -# Dependency directories (remove the comment below to include it) -# vendor/ +.env +# Dependency directories (remove the comment below to include it) +vendor/ diff --git a/src/cmd/server/main.go b/src/cmd/server/main.go new file mode 100644 index 0000000..989c6b5 --- /dev/null +++ b/src/cmd/server/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "log" + "os" + "strconv" + + "git.pbiernat.dev/egommerce/catalog-service/internal/app/config" + "git.pbiernat.dev/egommerce/catalog-service/internal/app/database" + "git.pbiernat.dev/egommerce/catalog-service/internal/app/server" + "git.pbiernat.dev/egommerce/catalog-service/pkg/amqp" + "git.pbiernat.dev/egommerce/catalog-service/pkg/fluentd" +) + +const ( + defAppName = "catalog-svc" + defAppDomain = "catalog-svc" + defNetAddr = ":80" + defLoggerAddr = "api-logger:24224" + defDbURL = "postgres://postgres:12345678@postgres-db:5432/egommerce" + defMongoDbURL = "mongodb://mongodb:12345678@mongo-db:27017" + defEventBusURL = "amqp://guest:guest@api-eventbus:5672" + ebEventsExchange = "api-events" + ebEventsQueue = "catalog-svc" +) + +func main() { + if config.ErrLoadingEnvs != nil { + log.Panicln("Error loading .env file", config.ErrLoadingEnvs) + } + + c := new(server.Config) + c.AppName = config.GetEnv("APP_NAME", defAppName) + c.AppDomain = config.GetEnv("APP_DOMAIN", defAppDomain) + c.NetAddr = config.GetEnv("SERVER_ADDR", defNetAddr) + c.Port, _ = strconv.Atoi(c.NetAddr[1:]) + c.LoggerAddr = config.GetEnv("LOGGER_ADDR", defLoggerAddr) + c.DbURL = config.GetEnv("DATABASE_URL", defDbURL) + c.EventBusURL = config.GetEnv("EVENTBUS_URL", defEventBusURL) + c.EventBusExchange = ebEventsExchange + + logHost, logPort := fluentd.ParseAddr(c.LoggerAddr) + logger := fluentd.NewLogger(c.AppName, logHost, logPort) + defer logger.Close() + + // db conn + dbConn, err := database.Connect(c.DbURL) + if err != nil { // fixme: add wait-for-db... + logger.Log("Failed to connect to Database server: %v\n", err) + os.Exit(1) + } + defer dbConn.Close() + + // eventbus conn + ebConn, ebCh, err := amqp.Open(c.EventBusURL) + if err != nil { + logger.Log("Failed to connect to EventBus server: %v\n", err) + os.Exit(1) + } + defer ebCh.Close() + defer amqp.Close(ebConn) + + err = amqp.NewExchange(ebCh, c.EventBusExchange) + if err != nil { + logger.Log("Failed to declare EventBus exchange: %v\n", err) + os.Exit(1) + } + + // start server + srv := server.NewServer(c, logger, dbConn, ebCh) + srv.StartWithGracefulShutdown() +} diff --git a/src/cmd/worker/main.go b/src/cmd/worker/main.go new file mode 100644 index 0000000..65dd8a5 --- /dev/null +++ b/src/cmd/worker/main.go @@ -0,0 +1,132 @@ +package main + +import ( + "fmt" + "log" + "os" + + "git.pbiernat.dev/egommerce/catalog-service/internal/app/config" + "git.pbiernat.dev/egommerce/catalog-service/internal/app/database" + "git.pbiernat.dev/egommerce/catalog-service/internal/app/server" + "git.pbiernat.dev/egommerce/catalog-service/pkg/amqp" + "git.pbiernat.dev/egommerce/catalog-service/pkg/fluentd" + "github.com/google/uuid" +) + +const ( + defAppName = "catalog-worker" + defLoggerAddr = "api-logger:24224" + defDbURL = "postgres://postgres:12345678@postgres-db:5432/egommerce" + defMongoDbURL = "mongodb://mongodb:12345678@mongo-db:27017" + defEventBusURL = "amqp://guest:guest@api-eventbus:5672" + ebEventsExchange = "api-events" + ebEventsQueue = "catalog-worker" +) + +func main() { + id := uuid.New().String()[24:] + if config.ErrLoadingEnvs != nil { + log.Panicln("Error loading .env file", config.ErrLoadingEnvs) + } + + c := new(server.Config) + c.AppName = config.GetEnv("APP_NAME", defAppName) + "#:" + id + c.LoggerAddr = config.GetEnv("LOGGER_ADDR", defLoggerAddr) + c.DbURL = config.GetEnv("DATABASE_URL", defDbURL) + c.EventBusURL = config.GetEnv("EVENTBUS_URL", defEventBusURL) + c.EventBusExchange = ebEventsExchange + c.EventBusQueue = ebEventsQueue + // c.EventBusQueue = fmt.Sprintf("%s-%s", ebEventsQueue, id) + + logHost, logPort := fluentd.ParseAddr(c.LoggerAddr) + logger := fluentd.NewLogger(c.AppName, logHost, logPort) + defer logger.Close() + + // db conn + dbConn, err := database.Connect(c.DbURL) + if err != nil { // fixme: add wait-for-db... + logger.Log("Failed to connect to Database server: %v\n", err) + os.Exit(1) + } + defer dbConn.Close() + + // eventbus conn + ebConn, ebCh, err := amqp.Open(c.EventBusURL) + if err != nil { + logger.Log("Failed to connect to EventBus server: %v\n", err) + os.Exit(1) + } + defer ebCh.Close() + defer amqp.Close(ebConn) + + err = amqp.NewExchange(ebCh, c.EventBusExchange) + if err != nil { + logger.Log("Failed to declare EventBus exchange: %v\n", err) + os.Exit(1) + } + + // create and bind queues + _, err = ebCh.QueueDeclare( + c.EventBusQueue, // name + false, // durable + false, // delete when unused + false, // exclusive + false, // no-wait + nil, // arguments + ) + if err != nil { + logger.Log("Failed to declare EventBus queue: %v\n", err) + os.Exit(1) + } + + err = amqp.BindQueueToExchange(ebCh, c.EventBusQueue, c.EventBusExchange, "warehouse.catalog.stockUpdated") + if err != nil { + logger.Log("Failed to prepare EventBus queue: %v\n", err) + os.Exit(1) + } + + // event consume + msgs, err := ebCh.Consume( + c.EventBusQueue, // queue + "", // consumer + false, // auto-ack + false, // exclusive + false, // no-local + false, // no-wait + nil, // args + ) + if err != nil { + logger.Log("Failed to register a consumer: %s", err) + os.Exit(1) + } + + var forever chan struct{} + go func() { + for d := range msgs { + msg, err := amqp.Deserialize(d.Body) + if err != nil { + logger.Log("json error:", err) + d.Reject(false) // FIXME: how to handle erros in queue...???? + continue + } + event := fmt.Sprintf("%s", msg["event"]) + data := (msg["data"]).(map[string]interface{}) + logger.Log("Message<%s>: %s\n", event, data) + + // switch true { + // case strings.Contains(event, amqp.EVENT_PRODUCT_ADD): + // // create new basket and add product to it + // logger.Log("Event: %s", amqp.EVENT_PRODUCT_ADD) + // case strings.Contains(event, amqp.EVENT_PRODUCT_REMOVE): + // // remove product from basket + // logger.Log("Event: %s", amqp.EVENT_PRODUCT_REMOVE) + // } + + logger.Log("ACK: %s", event) + d.Ack(false) + } + }() + + logger.Log("Waiting for messages...") + <-forever +} diff --git a/src/go.mod b/src/go.mod new file mode 100644 index 0000000..f2f8ab1 --- /dev/null +++ b/src/go.mod @@ -0,0 +1,49 @@ +module git.pbiernat.dev/egommerce/catalog-service + +go 1.18 + +require ( + github.com/fluent/fluent-logger-golang v1.9.0 + github.com/gofiber/fiber/v2 v2.40.1 + github.com/google/uuid v1.3.0 + github.com/hashicorp/consul/api v1.17.0 + github.com/jackc/pgx/v4 v4.17.2 + github.com/joho/godotenv v1.4.0 + github.com/streadway/amqp v1.0.0 +) + +require ( + github.com/andybalholm/brotli v1.0.4 // indirect + github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da // indirect + github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect + github.com/fatih/color v1.9.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.1 // indirect + github.com/hashicorp/go-hclog v0.12.0 // indirect + github.com/hashicorp/go-immutable-radix v1.0.0 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.5.4 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/jackc/chunkreader/v2 v2.0.1 // indirect + github.com/jackc/pgconn v1.13.0 // indirect + github.com/jackc/pgio v1.0.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.1 // indirect + github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect + github.com/jackc/pgtype v1.12.0 // indirect + github.com/jackc/puddle v1.3.0 // indirect + github.com/klauspost/compress v1.15.9 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.4.1 // indirect + github.com/philhofer/fwd v1.1.1 // indirect + github.com/rivo/uniseg v0.2.0 // indirect + github.com/tinylib/msgp v1.1.6 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasthttp v1.41.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect + golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect + golang.org/x/text v0.3.7 // indirect +) diff --git a/src/go.sum b/src/go.sum new file mode 100644 index 0000000..d2ebb07 --- /dev/null +++ b/src/go.sum @@ -0,0 +1,333 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= +github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= +github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fluent/fluent-logger-golang v1.9.0 h1:zUdY44CHX2oIUc7VTNZc+4m+ORuO/mldQDA7czhWXEg= +github.com/fluent/fluent-logger-golang v1.9.0/go.mod h1:2/HCT/jTy78yGyeNGQLGQsjF3zzzAuy6Xlk6FCMV5eU= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofiber/fiber/v2 v2.40.1 h1:pc7n9VVpGIqNsvg9IPLQhyFEMJL8gCs1kneH5D1pIl4= +github.com/gofiber/fiber/v2 v2.40.1/go.mod h1:Gko04sLksnHbzLSRBFWPFdzM9Ws9pRxvvIaohJK1dsk= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hashicorp/consul/api v1.17.0 h1:aqytbw31uCPNn37ST+717IyGod+P1eTgSGu3yjRo4bs= +github.com/hashicorp/consul/api v1.17.0/go.mod h1:ZNwemOPAdgtV4cCx9fqxNmw+PI3vliW6gYin2WD+F2g= +github.com/hashicorp/consul/sdk v0.13.0 h1:lce3nFlpv8humJL8rNrrGHYSKc3q+Kxfeg3Ii1m6ZWU= +github.com/hashicorp/consul/sdk v0.13.0/go.mod h1:0hs/l5fOVhJy/VdcoaNqUSi2AUs95eF5WKtv+EYIQqE= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI= +github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= +github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= +github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= +github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= +github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= +github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0 h1:eHK/5clGOatcjX3oWGBO/MpxpbHzSwud5EWTSCI+MX0= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/philhofer/fwd v1.1.1 h1:GdGcTjf5RNAxwS4QLsiMzJYj5KEvPJD3Abr261yRQXQ= +github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo= +github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/tinylib/msgp v1.1.6 h1:i+SbKraHhnrf9M5MYmvQhFnbLhAXSDWF8WWsuyRdocw= +github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= +github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= +github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= +github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220906165146-f3363e06e74c h1:yKufUcDwucU5urd+50/Opbt4AYpqthk7wHpHok8f1lo= +golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= diff --git a/src/internal/app/config/env.go b/src/internal/app/config/env.go new file mode 100644 index 0000000..9dbe349 --- /dev/null +++ b/src/internal/app/config/env.go @@ -0,0 +1,22 @@ +package config + +import ( + "os" + + "github.com/joho/godotenv" +) + +var ErrLoadingEnvs error + +func init() { + ErrLoadingEnvs = godotenv.Load() +} + +func GetEnv(name string, defVal string) string { // FIXME defVal and return types + env := os.Getenv(name) + if env == "" { + return defVal + } + + return env +} diff --git a/src/internal/app/database/connect.go b/src/internal/app/database/connect.go new file mode 100644 index 0000000..b44cfdd --- /dev/null +++ b/src/internal/app/database/connect.go @@ -0,0 +1,16 @@ +package database + +import ( + "context" + + "github.com/jackc/pgx/v4/pgxpool" +) + +func Connect(connStr string) (*pgxpool.Pool, error) { + conn, err := pgxpool.Connect(context.Background(), connStr) + if err != nil { + return nil, err + } + + return conn, nil +} diff --git a/src/internal/app/definition/catalog.go b/src/internal/app/definition/catalog.go new file mode 100644 index 0000000..3a796ed --- /dev/null +++ b/src/internal/app/definition/catalog.go @@ -0,0 +1,17 @@ +package definition + +type AddProductToBasketRequest struct { + ProductID int `json:"product_id"` +} + +type ProductAddToBasketResponse struct { + BasketID string `json:"basket_id"` +} + +type RemoveProductFromBasketRequest struct { + ProductID int `json:"product_id"` +} + +type RemoveProductFromBasketResponse struct { + BasketID string `json:"basket_id"` +} diff --git a/src/internal/app/definition/error.go b/src/internal/app/definition/error.go new file mode 100644 index 0000000..28f7dbf --- /dev/null +++ b/src/internal/app/definition/error.go @@ -0,0 +1,9 @@ +package definition + +type ErrorResponse struct { + Error string `json:"error"` +} + +func Error(err string) *ErrorResponse { + return &ErrorResponse{err} +} diff --git a/src/internal/app/definition/health.go b/src/internal/app/definition/health.go new file mode 100644 index 0000000..6920fca --- /dev/null +++ b/src/internal/app/definition/health.go @@ -0,0 +1,5 @@ +package definition + +type HealthResponse struct { + Status string `json:"status,omitempty"` +} diff --git a/src/internal/app/event/basket.go b/src/internal/app/event/basket.go new file mode 100644 index 0000000..0e8f578 --- /dev/null +++ b/src/internal/app/event/basket.go @@ -0,0 +1,13 @@ +package event + +type ProductAddedToBasketEvent struct { + *Event + ProductID string `json:"product_id"` + BasketID string `json:"basket_id"` +} + +type ProductRemovedFromBasketEvent struct { + *Event + ProductID string `json:"product_id"` + BasketID string `json:"basket_id"` +} diff --git a/src/internal/app/event/event.go b/src/internal/app/event/event.go new file mode 100644 index 0000000..3dd7a42 --- /dev/null +++ b/src/internal/app/event/event.go @@ -0,0 +1,12 @@ +package event + +type Event struct { + RequestID string `json:"request_id"` +} + +func NewEvent(reqID string) *Event { + em := new(Event) + em.RequestID = reqID + + return em +} diff --git a/src/internal/app/log.go b/src/internal/app/log.go new file mode 100644 index 0000000..6a3a86a --- /dev/null +++ b/src/internal/app/log.go @@ -0,0 +1,20 @@ +package app + +import ( + "log" +) + +const AppName = "catalog-svc" + +func Panic(v ...any) { + log.Panicln(AppName+":", v) +} + +func Panicf(format string, v ...any) { + log.Panicf(AppName+": "+format, v...) +} + +func Panicln(v ...any) { + v = append([]any{AppName + ":"}, v...) + log.Panicln(v...) +} diff --git a/src/internal/app/server/catalog_handler.go b/src/internal/app/server/catalog_handler.go new file mode 100644 index 0000000..20a09ac --- /dev/null +++ b/src/internal/app/server/catalog_handler.go @@ -0,0 +1,63 @@ +package server + +import ( + "strconv" + + "github.com/gofiber/fiber/v2" + "github.com/google/uuid" + + def "git.pbiernat.dev/egommerce/catalog-service/internal/app/definition" + "git.pbiernat.dev/egommerce/catalog-service/internal/app/service" +) + +func prepareBasket(c *fiber.Ctx) string { + basketID := c.Cookies(service.BasketIdCookieName) + if len(basketID) == 0 { + basketID = uuid.New().String() + c.Cookie(&fiber.Cookie{Name: service.BasketIdCookieName, Value: basketID}) + } + + return basketID +} + +func (s *Server) AddProductToBasketHandler(c *fiber.Ctx) error { + reqID, _ := s.GetRequestID(c) + data := new(def.AddProductToBasketRequest) + if err := c.BodyParser(data); err != nil { + return err + } + + productID := strconv.Itoa(data.ProductID) + // vlaidate, pre check... etc + + basketID := prepareBasket(c) + catalog := service.NewCatalogService(s.ebCh, s.log) + if err := catalog.AddProductToBasket(reqID, productID, basketID); err != nil { + return err + } + + return c.JSON(&def.ProductAddToBasketResponse{ + BasketID: basketID, + }) +} + +func (s *Server) RemoveProductFromBasketHandler(c *fiber.Ctx) error { + reqID, _ := s.GetRequestID(c) + data := new(def.RemoveProductFromBasketRequest) + if err := c.BodyParser(data); err != nil { + return err + } + + productID := strconv.Itoa(data.ProductID) + // vlaidate, pre check... etc + + basketID := prepareBasket(c) + catalog := service.NewCatalogService(s.ebCh, s.log) + if err := catalog.RemoveProductFromBasket(reqID, productID, basketID); err != nil { + return err + } + + return c.JSON(&def.RemoveProductFromBasketResponse{ + BasketID: basketID, + }) +} diff --git a/src/internal/app/server/config.go b/src/internal/app/server/config.go new file mode 100644 index 0000000..89e4ea1 --- /dev/null +++ b/src/internal/app/server/config.go @@ -0,0 +1,15 @@ +package server + +type Config struct { + AppName string + AppDomain string + NetAddr string + // Host string + Port int + LoggerAddr string + DbURL string + MongoDbUrl string + EventBusURL string + EventBusExchange string + EventBusQueue string +} diff --git a/src/internal/app/server/health_handler.go b/src/internal/app/server/health_handler.go new file mode 100644 index 0000000..7ba52ee --- /dev/null +++ b/src/internal/app/server/health_handler.go @@ -0,0 +1,13 @@ +package server + +import ( + "github.com/gofiber/fiber/v2" + + def "git.pbiernat.dev/egommerce/catalog-service/internal/app/definition" +) + +func (s *Server) HealthHandler(c *fiber.Ctx) error { + return c.JSON(&def.HealthResponse{ + Status: "OK", + }) +} diff --git a/src/internal/app/server/router.go b/src/internal/app/server/router.go new file mode 100644 index 0000000..40477a9 --- /dev/null +++ b/src/internal/app/server/router.go @@ -0,0 +1,31 @@ +package server + +import ( + "git.pbiernat.dev/egommerce/catalog-service/pkg/fluentd" + "github.com/gofiber/fiber/v2" +) + +func SetupRoutes(s *Server) { + api := s.App.Group("/api") + v1 := api.Group("/v1") + v1.Post("/product", s.AddProductToBasketHandler) + v1.Delete("/product", s.RemoveProductFromBasketHandler) + + s.App.Get("/health", s.HealthHandler) +} + +func SetupMiddlewares(s *Server) { + s.App.Use(LoggingMiddleware(s.log)) +} + +// Middlewares +func LoggingMiddleware(log *fluentd.Logger) func(c *fiber.Ctx) error { + return func(c *fiber.Ctx) error { + log.Log("Request: %s, remote: %s, via: %s", + c.Request().URI().String(), + c.Context().RemoteIP().String(), + string(c.Context().UserAgent())) + + return c.Next() + } +} diff --git a/src/internal/app/server/server.go b/src/internal/app/server/server.go new file mode 100644 index 0000000..d635e44 --- /dev/null +++ b/src/internal/app/server/server.go @@ -0,0 +1,101 @@ +package server + +import ( + "os" + "os/signal" + "time" + + "github.com/gofiber/fiber/v2" + "github.com/jackc/pgx/v4/pgxpool" + "github.com/streadway/amqp" + + discovery "git.pbiernat.dev/egommerce/catalog-service/pkg/consul" + "git.pbiernat.dev/egommerce/catalog-service/pkg/fluentd" +) + +type Server struct { + *fiber.App + log *fluentd.Logger + db *pgxpool.Pool + ebCh *amqp.Channel + name string + addr string +} + +type RequestID struct { + RequestID string `reqHeader:"x-request-id"` +} + +func NewServer(conf *Config, logger *fluentd.Logger, db *pgxpool.Pool, ebCh *amqp.Channel) *Server { + discovery, err := discovery.NewService(conf.AppName, conf.AppDomain, conf.Port) + if err != nil { + logger.Log("Error connecting to api-registry: %v", err) + } + + logger.Log("Registering service with name: %s, address: %s", discovery.Name, discovery.Address) + err = discovery.Register() + if err != nil { + logger.Log(err.Error()) + } + + cnf := fiber.Config{ + AppName: conf.AppName, + ServerHeader: conf.AppDomain, + ReadTimeout: time.Millisecond * 50, + WriteTimeout: time.Millisecond * 50, + IdleTimeout: time.Millisecond * 50, + } + s := &Server{ + fiber.New(cnf), + logger, + db, + ebCh, + conf.AppName, + conf.NetAddr, + } + + SetupMiddlewares(s) + SetupRoutes(s) + + return s +} + +func (s *Server) Start() { + err := s.Listen(s.addr) + s.log.Log("Starting error: %v", err) +} + +func (s *Server) StartWithGracefulShutdown() { + idle := make(chan struct{}) + + go func() { + sigint := make(chan os.Signal, 1) + signal.Notify(sigint, os.Interrupt) + <-sigint + + if err := s.Shutdown(); err != nil { + s.log.Log("Server is not shutting down! Reason: %v", err) + } + + s.log.Log("Servier is going down...") + // remove info from registry and close all connection here... + + close(idle) + }() + + if err := s.Listen(s.addr); err != nil { + s.log.Log("Server is not running! Reason: %v", err) + } + + <-idle +} + +func (s *Server) GetRequestID(c *fiber.Ctx) (string, error) { + var hdr = new(RequestID) + if err := c.ReqHeaderParser(hdr); err != nil { + return "", err + } + + return hdr.RequestID, nil + +} diff --git a/src/internal/app/service/catalog.go b/src/internal/app/service/catalog.go new file mode 100644 index 0000000..17a7b43 --- /dev/null +++ b/src/internal/app/service/catalog.go @@ -0,0 +1,41 @@ +package service + +import ( + "git.pbiernat.dev/egommerce/catalog-service/internal/app/event" + "git.pbiernat.dev/egommerce/catalog-service/pkg/amqp" + "git.pbiernat.dev/egommerce/catalog-service/pkg/fluentd" + base "github.com/streadway/amqp" +) + +const ( + BasketIdCookieName = "basket_id" +) + +type CatalogService struct { + ebCh *base.Channel + log *fluentd.Logger +} + +func NewCatalogService(chn *base.Channel, log *fluentd.Logger) *CatalogService { + return &CatalogService{chn, log} +} + +func (s *CatalogService) AddProductToBasket(reqID, productID, basketID string) error { + s.log.Log("Added product#%s to the basket#%s", productID, basketID) + + msg := &event.ProductAddedToBasketEvent{Event: event.NewEvent(reqID), BasketID: basketID, ProductID: productID} + amqp.Publish(s.ebCh, "api-events", "catalog.basket.productAddedToBasket", msg) + amqp.Publish(s.ebCh, "api-events", "catalog.warehouse.productAddedToBasket", msg) + + return nil +} + +func (s *CatalogService) RemoveProductFromBasket(reqID, productID, basketID string) error { + s.log.Log("Removed product#%s from basket#%s", productID, basketID) + + msg := &event.ProductRemovedFromBasketEvent{Event: event.NewEvent(reqID), BasketID: basketID, ProductID: productID} + amqp.Publish(s.ebCh, "api-events", "catalog.basket.productRemovedFromBasket", msg) + amqp.Publish(s.ebCh, "api-events", "catalog.warehouse.productRemovedFromBasket", msg) + + return nil +} diff --git a/src/pkg/amqp/connect.go b/src/pkg/amqp/connect.go new file mode 100644 index 0000000..9429823 --- /dev/null +++ b/src/pkg/amqp/connect.go @@ -0,0 +1,26 @@ +package amqp + +import ( + "log" + + "github.com/streadway/amqp" +) + +func Open(url string) (*amqp.Connection, *amqp.Channel, error) { + conn, err := amqp.Dial(url) + if err != nil { + return nil, nil, err + } + + ch, err := conn.Channel() + if err != nil { + log.Printf("Failed to open a channel: %v\n", err) + return nil, nil, err + } + + return conn, ch, nil +} + +func Close(conn *amqp.Connection) { + conn.Close() +} diff --git a/src/pkg/amqp/pubsub.go b/src/pkg/amqp/pubsub.go new file mode 100644 index 0000000..91dfb68 --- /dev/null +++ b/src/pkg/amqp/pubsub.go @@ -0,0 +1,84 @@ +package amqp + +import ( + "bytes" + "encoding/json" + "fmt" + "log" + + "github.com/streadway/amqp" +) + +type Message map[string]interface{} + +func Serialize(msg any /*Message*/) (string, error) { + var b bytes.Buffer + encoder := json.NewEncoder(&b) + err := encoder.Encode(msg) + return b.String(), err +} + +func Deserialize(b []byte) (Message, error) { + var msg Message + buf := bytes.NewBuffer(b) + decoder := json.NewDecoder(buf) + err := decoder.Decode(&msg) + return msg, err +} + +func NewExchange(chn *amqp.Channel, name string) error { + err := chn.ExchangeDeclare( + name, + "direct", // type + true, // durable + false, // auto-deleted + false, // internal + false, // no-wait + nil, // arguments + ) // FIXME extend arguments when needed... + + if err != nil { + return err + } + + return nil +} + +func Publish(chn *amqp.Channel, name, routingKey string, msg any) error { + // var jsonData []byte + jsonData, err := Serialize(msg) + if err != nil { + return err + } + + msgBody := fmt.Sprintf(`{"event":"%T","data":%s}`, msg, jsonData) // FIXME %T - simplify + chn.Publish( + name, // exchange name + routingKey, // routing key + false, // mandatory + false, // immediate + amqp.Publishing{ + ContentType: "application/json", + Body: []byte(msgBody), + }, + ) + + return nil +} + +func BindQueueToExchange(chn *amqp.Channel, queueName, exchName, routingKey string) error { + err := chn.QueueBind( + queueName, // queue name + routingKey, // routing key + exchName, // exchange name + false, + nil, + ) + if err != nil { + log.Printf("Failed to bind a queue: %s\n", queueName) + + return err + } + + return nil +} diff --git a/src/pkg/consul/discovery.go b/src/pkg/consul/discovery.go new file mode 100644 index 0000000..3a73054 --- /dev/null +++ b/src/pkg/consul/discovery.go @@ -0,0 +1,107 @@ +package consul + +import ( + "log" + "strconv" + "time" + + consul "github.com/hashicorp/consul/api" +) + +type Service struct { + Name string + Address string + Port int + TTL time.Duration + ConsulAgent *consul.Agent +} + +func NewService(appName, appDomain string, port int) (*Service, error) { + s := new(Service) + s.Name = appName + s.Address = appDomain + s.Port = port + s.TTL = time.Second * 15 + + client, err := consul.NewClient(newClientConfig()) + if err != nil { + return nil, err + } + s.ConsulAgent = client.Agent() + + // tags := s.GetTags() + + return s, nil +} + +func newClientConfig() *consul.Config { + conf := consul.DefaultConfig() + conf.Address = "api-registry:8500" // FIXME:consul server + + return conf +} + +func (s *Service) Register() error { + serviceDef := &consul.AgentServiceRegistration{ + Name: s.Name, + Address: s.Address, + Port: s.Port, + Tags: s.getTags(), + Check: &consul.AgentServiceCheck{ + TTL: s.TTL.String(), + }, + } + + if err := s.ConsulAgent.ServiceRegister(serviceDef); err != nil { + return err + } + go s.UpdateTTL(serviceDef /*service.Check*/) + + return nil +} + +func (s *Service) UpdateTTL(service *consul.AgentServiceRegistration /*check func() (bool, error)*/) { + ticker := time.NewTicker(s.TTL / 2) + for range ticker.C { + s.update(service /*check*/) + } +} + +func (s *Service) update(service *consul.AgentServiceRegistration /*check func() (bool, error)*/) { + // ok, err := check() + // if !ok { + // log.Printf("err=\"Check failed\" msg=\"%s\"", err.Error()) + // if err := s.ConsulAgent.FailTTL("service:"+s.Name, err.Error()); err != nil { + // log.Println(err) + // } + // } else { + // log.Println("Updating service info...") + if err := s.ConsulAgent.PassTTL("service:"+service.Name, ""); err != nil { + log.Println(err) + } + // } +} + +func (s *Service) getTags() []string { + name, addr, port := s.Name, s.Address, strconv.Itoa(s.Port) + + tags := []string{ + "traefik.enable=true", + "traefik.http.routers." + name + ".rule=Host(`" + addr + "`)", + "traefik.http.routers." + name + ".entryPoints=https", + "traefik.http.routers." + name + ".service=" + name, + "traefik.http.routers." + name + ".middlewares=compress,requestid", + "traefik.http.routers." + name + ".tls=true", + "traefik.http.services." + name + ".loadbalancer.server.scheme=http", + "traefik.http.services." + name + ".loadbalancer.server.port=" + port, + "traefik.http.services." + name + ".loadbalancer.passhostheader=false", + "traefik.http.middlewares.compress.compress=true", + "traefik.http.middlewares.requestid.plugin.requestid.headerName=X-Request-ID", + // "traefik.http.services." + name + ".loadbalancer.healthcheck.path=/health", + // "traefik.http.services." + name + ".loadbalancer.healthcheck.interval=10s", + // "traefik.tls.certificates.certfile=/certs/client.cert", + // "traefik.tls.certificates.keyfile=/certs/client.key", + } + + return tags +} diff --git a/src/pkg/fluentd/config.go b/src/pkg/fluentd/config.go new file mode 100644 index 0000000..48255d2 --- /dev/null +++ b/src/pkg/fluentd/config.go @@ -0,0 +1,14 @@ +package fluentd + +import ( + "strconv" + "strings" +) + +func ParseAddr(addr string) (string, int) { + p := strings.Split(addr, ":") + fHost := p[0] + fPort, _ := strconv.Atoi(p[1]) + + return fHost, fPort +} diff --git a/src/pkg/fluentd/logger.go b/src/pkg/fluentd/logger.go new file mode 100644 index 0000000..2ffa50d --- /dev/null +++ b/src/pkg/fluentd/logger.go @@ -0,0 +1,41 @@ +package fluentd + +import ( + "fmt" + "log" + + "github.com/fluent/fluent-logger-golang/fluent" +) + +type Logger struct { + fluent *fluent.Fluent + appName string +} + +func NewLogger(appName, fHost string, fPort int) *Logger { + config := fluent.Config{ + FluentHost: fHost, + FluentPort: fPort, + // WriteTimeout: -1, + } + fluent, err := fluent.New(config) + if err != nil { + log.Panicf("Error connecting to %s: %v", fHost, err) + } + + return &Logger{fluent, appName} +} + +func (l *Logger) Log(format string, v ...any) { + mapData := map[string]string{ + "message": fmt.Sprintf(format, v...), + } + err := l.fluent.Post(l.appName, mapData) + if err != nil { + log.Println("Error sending log: ", err) + } +} + +func (l *Logger) Close() error { + return l.fluent.Close() +}