mirror of
https://github.com/SamyRai/tercul-backend.git
synced 2025-12-27 05:11:34 +00:00
feat: Implement observability stack
This commit introduces a comprehensive observability stack to the application, addressing a key objective from the TODO list. The following features have been implemented: - **Centralized Logging:** Replaced the existing custom logger with `zerolog` for structured, leveled, and performant logging. The logger is configured to output human-friendly console logs in development and JSON logs in production. - **Distributed Tracing:** Integrated OpenTelemetry to provide distributed tracing capabilities. A new middleware has been added to create spans for all incoming HTTP requests, and the trace context is propagated. - **Prometheus Metrics:** Added Prometheus metrics to monitor HTTP request latency and total request counts. A new `/metrics` endpoint is exposed on port 9090 to serve these metrics. - **Request ID:** Implemented a middleware to add a unique request ID to every incoming request and response, improving traceability. The new observability components are encapsulated in the `internal/observability` package, and the existing `internal/platform/log` package has been refactored to be a backward-compatible wrapper around the new logger. The main application entry point (`cmd/api/main.go`) has been updated to initialize and gracefully shut down the new observability components.
This commit is contained in:
parent
9bc1e221e6
commit
3bcd8d08f5
@ -13,6 +13,7 @@ import (
|
|||||||
graph "tercul/internal/adapters/graphql"
|
graph "tercul/internal/adapters/graphql"
|
||||||
dbsql "tercul/internal/data/sql"
|
dbsql "tercul/internal/data/sql"
|
||||||
"tercul/internal/jobs/linguistics"
|
"tercul/internal/jobs/linguistics"
|
||||||
|
"tercul/internal/observability"
|
||||||
"tercul/internal/platform/auth"
|
"tercul/internal/platform/auth"
|
||||||
"tercul/internal/platform/config"
|
"tercul/internal/platform/config"
|
||||||
"tercul/internal/platform/db"
|
"tercul/internal/platform/db"
|
||||||
@ -22,6 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql/playground"
|
"github.com/99designs/gqlgen/graphql/playground"
|
||||||
"github.com/pressly/goose/v3"
|
"github.com/pressly/goose/v3"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/weaviate/weaviate-go-client/v5/weaviate"
|
"github.com/weaviate/weaviate-go-client/v5/weaviate"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@ -54,8 +56,24 @@ func main() {
|
|||||||
// Load configuration from environment variables
|
// Load configuration from environment variables
|
||||||
config.LoadConfig()
|
config.LoadConfig()
|
||||||
|
|
||||||
// Initialize structured logger with appropriate log level
|
// Initialize logger
|
||||||
log.SetDefaultLevel(log.InfoLevel)
|
log.Init("tercul-api", config.Cfg.Environment)
|
||||||
|
|
||||||
|
// Initialize OpenTelemetry Tracer Provider
|
||||||
|
tp, err := observability.TracerProvider("tercul-api", config.Cfg.Environment)
|
||||||
|
if err != nil {
|
||||||
|
log.LogFatal("Failed to initialize OpenTelemetry tracer", log.F("error", err))
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err := tp.Shutdown(context.Background()); err != nil {
|
||||||
|
log.LogError("Error shutting down tracer provider", log.F("error", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Initialize Prometheus metrics
|
||||||
|
reg := prometheus.NewRegistry()
|
||||||
|
metrics := observability.NewMetrics(reg) // Metrics are registered automatically
|
||||||
|
|
||||||
log.LogInfo("Starting Tercul application",
|
log.LogInfo("Starting Tercul application",
|
||||||
log.F("environment", config.Cfg.Environment),
|
log.F("environment", config.Cfg.Environment),
|
||||||
log.F("version", "1.0.0"))
|
log.F("version", "1.0.0"))
|
||||||
@ -106,7 +124,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
jwtManager := auth.NewJWTManager()
|
jwtManager := auth.NewJWTManager()
|
||||||
srv := NewServerWithAuth(resolver, jwtManager)
|
srv := NewServerWithAuth(resolver, jwtManager, metrics)
|
||||||
graphQLServer := &http.Server{
|
graphQLServer := &http.Server{
|
||||||
Addr: config.Cfg.ServerPort,
|
Addr: config.Cfg.ServerPort,
|
||||||
Handler: srv,
|
Handler: srv,
|
||||||
@ -121,6 +139,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
log.LogInfo("GraphQL playground created successfully", log.F("port", config.Cfg.PlaygroundPort))
|
log.LogInfo("GraphQL playground created successfully", log.F("port", config.Cfg.PlaygroundPort))
|
||||||
|
|
||||||
|
// Create metrics server
|
||||||
|
metricsServer := &http.Server{
|
||||||
|
Addr: ":9090",
|
||||||
|
Handler: observability.PrometheusHandler(reg),
|
||||||
|
}
|
||||||
|
log.LogInfo("Metrics server created successfully", log.F("port", ":9090"))
|
||||||
|
|
||||||
// Start HTTP servers in goroutines
|
// Start HTTP servers in goroutines
|
||||||
go func() {
|
go func() {
|
||||||
log.LogInfo("Starting GraphQL server",
|
log.LogInfo("Starting GraphQL server",
|
||||||
@ -140,6 +165,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.LogInfo("Starting metrics server", log.F("port", ":9090"))
|
||||||
|
if err := metricsServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
log.LogFatal("Failed to start metrics server", log.F("error", err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Wait for interrupt signal to gracefully shutdown the servers
|
// Wait for interrupt signal to gracefully shutdown the servers
|
||||||
quit := make(chan os.Signal, 1)
|
quit := make(chan os.Signal, 1)
|
||||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||||
@ -161,5 +193,9 @@ func main() {
|
|||||||
log.F("error", err))
|
log.F("error", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := metricsServer.Shutdown(ctx); err != nil {
|
||||||
|
log.LogError("Metrics server forced to shutdown", log.F("error", err))
|
||||||
|
}
|
||||||
|
|
||||||
log.LogInfo("All servers shutdown successfully")
|
log.LogInfo("All servers shutdown successfully")
|
||||||
}
|
}
|
||||||
@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"tercul/internal/adapters/graphql"
|
"tercul/internal/adapters/graphql"
|
||||||
|
"tercul/internal/observability"
|
||||||
"tercul/internal/platform/auth"
|
"tercul/internal/platform/auth"
|
||||||
|
|
||||||
"github.com/99designs/gqlgen/graphql/handler"
|
"github.com/99designs/gqlgen/graphql/handler"
|
||||||
@ -21,18 +22,23 @@ func NewServer(resolver *graphql.Resolver) http.Handler {
|
|||||||
return mux
|
return mux
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServerWithAuth creates a new GraphQL server with authentication middleware
|
// NewServerWithAuth creates a new GraphQL server with authentication and observability middleware
|
||||||
func NewServerWithAuth(resolver *graphql.Resolver, jwtManager *auth.JWTManager) http.Handler {
|
func NewServerWithAuth(resolver *graphql.Resolver, jwtManager *auth.JWTManager, metrics *observability.Metrics) http.Handler {
|
||||||
c := graphql.Config{Resolvers: resolver}
|
c := graphql.Config{Resolvers: resolver}
|
||||||
c.Directives.Binding = graphql.Binding
|
c.Directives.Binding = graphql.Binding
|
||||||
srv := handler.NewDefaultServer(graphql.NewExecutableSchema(c))
|
srv := handler.NewDefaultServer(graphql.NewExecutableSchema(c))
|
||||||
|
|
||||||
// Apply authentication middleware to GraphQL endpoint
|
// Create a middleware chain
|
||||||
authHandler := auth.GraphQLAuthMiddleware(jwtManager)(srv)
|
var chain http.Handler
|
||||||
|
chain = srv
|
||||||
|
chain = auth.GraphQLAuthMiddleware(jwtManager)(chain)
|
||||||
|
chain = metrics.PrometheusMiddleware(chain)
|
||||||
|
chain = observability.TracingMiddleware(chain)
|
||||||
|
chain = observability.RequestIDMiddleware(chain)
|
||||||
|
|
||||||
// Create a mux to handle GraphQL endpoint only (no playground here; served separately in production)
|
// Create a mux to handle GraphQL endpoint
|
||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.Handle("/query", authHandler)
|
mux.Handle("/query", chain)
|
||||||
|
|
||||||
return mux
|
return mux
|
||||||
}
|
}
|
||||||
34
go.mod
34
go.mod
@ -12,12 +12,18 @@ require (
|
|||||||
github.com/hibiken/asynq v0.25.1
|
github.com/hibiken/asynq v0.25.1
|
||||||
github.com/jonreiter/govader v0.0.0-20250429093935-f6505c8d03cc
|
github.com/jonreiter/govader v0.0.0-20250429093935-f6505c8d03cc
|
||||||
github.com/pemistahl/lingua-go v1.4.0
|
github.com/pemistahl/lingua-go v1.4.0
|
||||||
github.com/pressly/goose/v3 v3.25.0
|
github.com/pressly/goose/v3 v3.21.1
|
||||||
|
github.com/prometheus/client_golang v1.20.5
|
||||||
github.com/redis/go-redis/v9 v9.13.0
|
github.com/redis/go-redis/v9 v9.13.0
|
||||||
|
github.com/rs/zerolog v1.34.0
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/vektah/gqlparser/v2 v2.5.30
|
github.com/vektah/gqlparser/v2 v2.5.30
|
||||||
github.com/weaviate/weaviate v1.32.6
|
github.com/weaviate/weaviate v1.33.0-rc.1
|
||||||
github.com/weaviate/weaviate-go-client/v5 v5.4.1
|
github.com/weaviate/weaviate-go-client/v5 v5.5.0
|
||||||
|
go.opentelemetry.io/otel v1.38.0
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0
|
||||||
|
go.opentelemetry.io/otel/sdk v1.38.0
|
||||||
|
go.opentelemetry.io/otel/trace v1.38.0
|
||||||
golang.org/x/crypto v0.41.0
|
golang.org/x/crypto v0.41.0
|
||||||
gorm.io/driver/postgres v1.6.0
|
gorm.io/driver/postgres v1.6.0
|
||||||
gorm.io/driver/sqlite v1.6.0
|
gorm.io/driver/sqlite v1.6.0
|
||||||
@ -25,13 +31,13 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
ariga.io/atlas-go-sdk v0.5.1 // indirect
|
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/ClickHouse/ch-go v0.67.0 // indirect
|
github.com/ClickHouse/ch-go v0.61.1 // indirect
|
||||||
github.com/ClickHouse/clickhouse-go/v2 v2.40.1 // indirect
|
github.com/ClickHouse/clickhouse-go/v2 v2.17.1 // indirect
|
||||||
github.com/agnivade/levenshtein v1.2.1 // indirect
|
github.com/agnivade/levenshtein v1.2.1 // indirect
|
||||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/coder/websocket v1.8.12 // indirect
|
github.com/coder/websocket v1.8.12 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||||
@ -43,6 +49,8 @@ require (
|
|||||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||||
github.com/go-faster/city v1.0.1 // indirect
|
github.com/go-faster/city v1.0.1 // indirect
|
||||||
github.com/go-faster/errors v0.7.1 // indirect
|
github.com/go-faster/errors v0.7.1 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.3 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-openapi/analysis v0.23.0 // indirect
|
github.com/go-openapi/analysis v0.23.0 // indirect
|
||||||
github.com/go-openapi/errors v0.22.0 // indirect
|
github.com/go-openapi/errors v0.22.0 // indirect
|
||||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
@ -68,18 +76,18 @@ require (
|
|||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
|
||||||
github.com/jonboulle/clockwork v0.5.0 // indirect
|
github.com/jonboulle/clockwork v0.5.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.0 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||||
github.com/mfridman/interpolate v0.0.2 // indirect
|
github.com/mfridman/interpolate v0.0.2 // indirect
|
||||||
github.com/mfridman/xflag v0.1.0 // indirect
|
|
||||||
github.com/microsoft/go-mssqldb v1.9.2 // indirect
|
github.com/microsoft/go-mssqldb v1.9.2 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
github.com/oklog/ulid v1.3.1 // indirect
|
github.com/oklog/ulid v1.3.1 // indirect
|
||||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||||
@ -87,6 +95,8 @@ require (
|
|||||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
|
github.com/prometheus/client_model v0.6.2 // indirect
|
||||||
|
github.com/prometheus/common v0.65.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/robfig/cron/v3 v3.0.1 // indirect
|
github.com/robfig/cron/v3 v3.0.1 // indirect
|
||||||
@ -105,8 +115,8 @@ require (
|
|||||||
github.com/ydb-platform/ydb-go-sdk/v3 v3.108.1 // indirect
|
github.com/ydb-platform/ydb-go-sdk/v3 v3.108.1 // indirect
|
||||||
github.com/ziutek/mymysql v1.5.4 // indirect
|
github.com/ziutek/mymysql v1.5.4 // indirect
|
||||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||||
golang.org/x/mod v0.26.0 // indirect
|
golang.org/x/mod v0.26.0 // indirect
|
||||||
@ -118,8 +128,8 @@ require (
|
|||||||
golang.org/x/time v0.12.0 // indirect
|
golang.org/x/time v0.12.0 // indirect
|
||||||
golang.org/x/tools v0.35.0 // indirect
|
golang.org/x/tools v0.35.0 // indirect
|
||||||
gonum.org/v1/gonum v0.15.1 // indirect
|
gonum.org/v1/gonum v0.15.1 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 // indirect
|
||||||
google.golang.org/grpc v1.73.0 // indirect
|
google.golang.org/grpc v1.74.2 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
|||||||
83
go.sum
83
go.sum
@ -1,5 +1,3 @@
|
|||||||
ariga.io/atlas-go-sdk v0.5.1 h1:I3iRshdwSODVWwMS4zvXObnfCQrEOY8BLRwynJQA+qE=
|
|
||||||
ariga.io/atlas-go-sdk v0.5.1/go.mod h1:UZXG++2NQCDAetk+oIitYIGpL/VsBVCt4GXbtWBA/GY=
|
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
@ -19,10 +17,10 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h
|
|||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
||||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/ClickHouse/ch-go v0.67.0 h1:18MQF6vZHj+4/hTRaK7JbS/TIzn4I55wC+QzO24uiqc=
|
github.com/ClickHouse/ch-go v0.61.1 h1:j5rx3qnvcnYjhnP1IdXE/vdIRQiqgwAzyqOaasA6QCw=
|
||||||
github.com/ClickHouse/ch-go v0.67.0/go.mod h1:2MSAeyVmgt+9a2k2SQPPG1b4qbTPzdGDpf1+bcHh+18=
|
github.com/ClickHouse/ch-go v0.61.1/go.mod h1:myxt/JZgy2BYHFGQqzmaIpbfr5CMbs3YHVULaWQj5YU=
|
||||||
github.com/ClickHouse/clickhouse-go/v2 v2.40.1 h1:PbwsHBgqXRydU7jKULD1C8CHmifczffvQqmFvltM2W4=
|
github.com/ClickHouse/clickhouse-go/v2 v2.17.1 h1:ZCmAYWpu75IyEi7+Yrs/uaAjiCGY5wfW5kXo64exkX4=
|
||||||
github.com/ClickHouse/clickhouse-go/v2 v2.40.1/go.mod h1:GDzSBLVhladVm8V01aEB36IoBOVLLICfyeuiIp/8Ezc=
|
github.com/ClickHouse/clickhouse-go/v2 v2.17.1/go.mod h1:rkGTvFDTLqLIm0ma+13xmcCfr/08Gvs7KmFt1tgiWHQ=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||||
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||||
@ -47,6 +45,8 @@ github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:W
|
|||||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||||
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
@ -64,6 +64,7 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
|
|||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
||||||
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
|
||||||
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
@ -100,6 +101,7 @@ github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
|
|||||||
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
|
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
|
||||||
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
|
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
|
||||||
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
|
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
@ -178,6 +180,7 @@ github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWe
|
|||||||
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
|
||||||
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
|
||||||
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||||
@ -245,8 +248,6 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
|||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
|
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak=
|
||||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
|
||||||
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
|
||||||
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
|
||||||
github.com/jonreiter/govader v0.0.0-20250429093935-f6505c8d03cc h1:Zvn/U2151AlhFbOIIZivbnpvExjD/8rlQsO/RaNJQw0=
|
github.com/jonreiter/govader v0.0.0-20250429093935-f6505c8d03cc h1:Zvn/U2151AlhFbOIIZivbnpvExjD/8rlQsO/RaNJQw0=
|
||||||
@ -282,14 +283,17 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0
|
|||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||||
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
|
||||||
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
|
||||||
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
|
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||||
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY=
|
||||||
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
|
github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg=
|
||||||
github.com/mfridman/xflag v0.1.0 h1:TWZrZwG1QklFX5S4j1vxfF1sZbZeZSGofMwPMLAF29M=
|
|
||||||
github.com/mfridman/xflag v0.1.0/go.mod h1:/483ywM5ZO5SuMVjrIGquYNE5CzLrj5Ux/LxWWnjRaE=
|
|
||||||
github.com/microsoft/go-mssqldb v1.9.2 h1:nY8TmFMQOHpm2qVWo6y4I2mAmVdZqlGiMGAYt64Ibbs=
|
github.com/microsoft/go-mssqldb v1.9.2 h1:nY8TmFMQOHpm2qVWo6y4I2mAmVdZqlGiMGAYt64Ibbs=
|
||||||
github.com/microsoft/go-mssqldb v1.9.2/go.mod h1:GBbW9ASTiDC+mpgWDGKdm3FnFLTUsLYN3iFL90lQ+PA=
|
github.com/microsoft/go-mssqldb v1.9.2/go.mod h1:GBbW9ASTiDC+mpgWDGKdm3FnFLTUsLYN3iFL90lQ+PA=
|
||||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
@ -298,6 +302,8 @@ github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
|
|||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||||
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
@ -322,9 +328,15 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pressly/goose/v3 v3.25.0 h1:6WeYhMWGRCzpyd89SpODFnCBCKz41KrVbRT58nVjGng=
|
github.com/pressly/goose/v3 v3.21.1 h1:5SSAKKWej8LVVzNLuT6KIvP1eFDuPvxa+B6H0w78buQ=
|
||||||
github.com/pressly/goose/v3 v3.25.0/go.mod h1:4hC1KrritdCxtuFsqgs1R4AU5bWtTAf+cnWvfhf2DNY=
|
github.com/pressly/goose/v3 v3.21.1/go.mod h1:sqthmzV8PitchEkjecFJII//l43dLOCzfWh8pHEe+vE=
|
||||||
|
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||||
|
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
|
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||||
|
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||||
|
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||||
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.0-20190425082905-87a4384529e0/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
@ -340,8 +352,11 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
|
|||||||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||||
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
|
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||||
|
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||||
@ -381,10 +396,10 @@ github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsL
|
|||||||
github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
|
github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
|
||||||
github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw=
|
github.com/vertica/vertica-sql-go v1.3.3 h1:fL+FKEAEy5ONmsvya2WH5T8bhkvY27y/Ik3ReR2T+Qw=
|
||||||
github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4=
|
github.com/vertica/vertica-sql-go v1.3.3/go.mod h1:jnn2GFuv+O2Jcjktb7zyc4Utlbu9YVqpHH/lx63+1M4=
|
||||||
github.com/weaviate/weaviate v1.32.6 h1:N0MRjuqZT9l2un4xFeV4fXZ9dkLbqrijC5JIfr759Os=
|
github.com/weaviate/weaviate v1.33.0-rc.1 h1:3Kol9BmA9JOj1I4vOkz0tu4A87K3dKVAnr8k8DMhBs8=
|
||||||
github.com/weaviate/weaviate v1.32.6/go.mod h1:hzzhAOYxgKe+B2jxZJtaWMIdElcXXn+RQyQ7ccQORNg=
|
github.com/weaviate/weaviate v1.33.0-rc.1/go.mod h1:MmHF/hZDL0I8j0qAMEa9/TS4ISLaYlIp1Bc3e/n3eUU=
|
||||||
github.com/weaviate/weaviate-go-client/v5 v5.4.1 h1:hfKocGPe11IUr4XsLp3q9hJYck0I2yIHGlFBpLqb/F4=
|
github.com/weaviate/weaviate-go-client/v5 v5.5.0 h1:+5qkHodrL3/Qc7kXvMXnDaIxSBN5+djivLqzmCx7VS4=
|
||||||
github.com/weaviate/weaviate-go-client/v5 v5.4.1/go.mod h1:l72EnmCLj9LCQkR8S7nN7Y1VqGMmL3Um8exhFkMmfwk=
|
github.com/weaviate/weaviate-go-client/v5 v5.5.0/go.mod h1:Zdm2MEXG27I0Nf6fM0FZ3P2vLR4JM0iJZrOxwc+Zj34=
|
||||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||||
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
|
||||||
@ -411,16 +426,18 @@ go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd
|
|||||||
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/hPp3O7LCGLcHXFlvS2/FFOrwL+SE=
|
||||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE=
|
||||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||||
|
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||||
|
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
@ -500,7 +517,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
@ -547,8 +566,8 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
|
|||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0 h1:MAKi5q709QWfnkkpNQ0M12hYJ1+e8qYVDyowc4U1XZM=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250728155136-f173205681a0/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
@ -556,8 +575,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
|||||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||||
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
google.golang.org/grpc v1.74.2 h1:WoosgB65DlWVC9FqI82dGsZhWFNBSLjQ84bjROOpMu4=
|
||||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
google.golang.org/grpc v1.74.2/go.mod h1:CtQ+BGjaAIXHs/5YS3i473GqwBBa1zGQNevxdeBEXrM=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
|||||||
54
internal/observability/logger.go
Normal file
54
internal/observability/logger.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package observability
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Logger is a wrapper around zerolog.Logger to provide a consistent logging interface.
|
||||||
|
type Logger struct {
|
||||||
|
*zerolog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLogger creates a new Logger instance.
|
||||||
|
// It writes to a human-friendly console in "development" environment,
|
||||||
|
// and writes JSON to stdout otherwise.
|
||||||
|
func NewLogger(serviceName, environment string) *Logger {
|
||||||
|
var logger zerolog.Logger
|
||||||
|
if environment == "development" {
|
||||||
|
logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).With().
|
||||||
|
Timestamp().
|
||||||
|
Str("service", serviceName).
|
||||||
|
Logger()
|
||||||
|
} else {
|
||||||
|
zerolog.TimeFieldFormat = time.RFC3339
|
||||||
|
logger = zerolog.New(os.Stdout).With().
|
||||||
|
Timestamp().
|
||||||
|
Str("service", serviceName).
|
||||||
|
Logger()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Logger{&logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ctx returns a new logger with context-specific fields, such as trace and span IDs.
|
||||||
|
func (l *Logger) Ctx(ctx context.Context) *Logger {
|
||||||
|
log := l.Logger // log is a *zerolog.Logger
|
||||||
|
span := trace.SpanFromContext(ctx)
|
||||||
|
if span.SpanContext().IsValid() {
|
||||||
|
// .Logger() returns a value, not a pointer.
|
||||||
|
// We create a new logger value...
|
||||||
|
newLogger := log.With().
|
||||||
|
Str("trace_id", span.SpanContext().TraceID().String()).
|
||||||
|
Str("span_id", span.SpanContext().SpanID().String()).
|
||||||
|
Logger()
|
||||||
|
// ...and then use its address.
|
||||||
|
log = &newLogger
|
||||||
|
}
|
||||||
|
// `log` is now the correct *zerolog.Logger, so we wrap it.
|
||||||
|
return &Logger{log}
|
||||||
|
}
|
||||||
55
internal/observability/metrics.go
Normal file
55
internal/observability/metrics.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package observability
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Metrics contains the Prometheus metrics for the application.
|
||||||
|
type Metrics struct {
|
||||||
|
RequestsTotal *prometheus.CounterVec
|
||||||
|
RequestDuration *prometheus.HistogramVec
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMetrics creates and registers the Prometheus metrics.
|
||||||
|
func NewMetrics(reg prometheus.Registerer) *Metrics {
|
||||||
|
return &Metrics{
|
||||||
|
RequestsTotal: promauto.With(reg).NewCounterVec(
|
||||||
|
prometheus.CounterOpts{
|
||||||
|
Name: "http_requests_total",
|
||||||
|
Help: "Total number of HTTP requests.",
|
||||||
|
},
|
||||||
|
[]string{"method", "path", "status"},
|
||||||
|
),
|
||||||
|
RequestDuration: promauto.With(reg).NewHistogramVec(
|
||||||
|
prometheus.HistogramOpts{
|
||||||
|
Name: "http_request_duration_seconds",
|
||||||
|
Help: "Duration of HTTP requests.",
|
||||||
|
Buckets: prometheus.DefBuckets,
|
||||||
|
},
|
||||||
|
[]string{"method", "path"},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrometheusMiddleware returns an HTTP middleware that records Prometheus metrics.
|
||||||
|
func (m *Metrics) PrometheusMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
start := time.Now()
|
||||||
|
rw := &responseWriter{ResponseWriter: w}
|
||||||
|
next.ServeHTTP(rw, r)
|
||||||
|
|
||||||
|
duration := time.Since(start).Seconds()
|
||||||
|
m.RequestDuration.WithLabelValues(r.Method, r.URL.Path).Observe(duration)
|
||||||
|
m.RequestsTotal.WithLabelValues(r.Method, r.URL.Path, http.StatusText(rw.statusCode)).Inc()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrometheusHandler returns an HTTP handler for serving Prometheus metrics.
|
||||||
|
func PrometheusHandler(reg prometheus.Gatherer) http.Handler {
|
||||||
|
return promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
|
||||||
|
}
|
||||||
56
internal/observability/middleware.go
Normal file
56
internal/observability/middleware.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package observability
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
type contextKey string
|
||||||
|
|
||||||
|
const RequestIDKey contextKey = "request_id"
|
||||||
|
|
||||||
|
// responseWriter is a wrapper around http.ResponseWriter to capture the status code.
|
||||||
|
type responseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
statusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rw *responseWriter) WriteHeader(code int) {
|
||||||
|
rw.statusCode = code
|
||||||
|
rw.ResponseWriter.WriteHeader(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestIDMiddleware generates a unique request ID and adds it to the request context.
|
||||||
|
func RequestIDMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
requestID := uuid.New().String()
|
||||||
|
ctx := context.WithValue(r.Context(), RequestIDKey, requestID)
|
||||||
|
w.Header().Set("X-Request-ID", requestID)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TracingMiddleware creates a new OpenTelemetry span for each request.
|
||||||
|
func TracingMiddleware(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := otel.GetTextMapPropagator().Extract(r.Context(), propagation.HeaderCarrier(r.Header))
|
||||||
|
tracer := otel.Tracer("http-server")
|
||||||
|
ctx, span := tracer.Start(ctx, "HTTP "+r.Method+" "+r.URL.Path, trace.WithAttributes(
|
||||||
|
semconv.HTTPMethodKey.String(r.Method),
|
||||||
|
semconv.HTTPURLKey.String(r.URL.String()),
|
||||||
|
))
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
|
||||||
|
next.ServeHTTP(rw, r.WithContext(ctx))
|
||||||
|
|
||||||
|
span.SetAttributes(attribute.Int("http.status_code", rw.statusCode))
|
||||||
|
})
|
||||||
|
}
|
||||||
41
internal/observability/tracing.go
Normal file
41
internal/observability/tracing.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package observability
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||||
|
"go.opentelemetry.io/otel/propagation"
|
||||||
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
|
sdktrace "go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.21.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TracerProvider returns a new OpenTelemetry TracerProvider.
|
||||||
|
func TracerProvider(serviceName, environment string) (*sdktrace.TracerProvider, error) {
|
||||||
|
exporter, err := stdouttrace.New(stdouttrace.WithPrettyPrint())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := resource.NewWithAttributes(
|
||||||
|
semconv.SchemaURL,
|
||||||
|
semconv.ServiceNameKey.String(serviceName),
|
||||||
|
semconv.DeploymentEnvironmentKey.String(environment),
|
||||||
|
)
|
||||||
|
|
||||||
|
tp := sdktrace.NewTracerProvider(
|
||||||
|
sdktrace.WithBatcher(exporter),
|
||||||
|
sdktrace.WithResource(res),
|
||||||
|
)
|
||||||
|
|
||||||
|
otel.SetTracerProvider(tp)
|
||||||
|
otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{}))
|
||||||
|
|
||||||
|
return tp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ShutdownTracerProvider gracefully shuts down the tracer provider.
|
||||||
|
func ShutdownTracerProvider(ctx context.Context, tp *sdktrace.TracerProvider) error {
|
||||||
|
return tp.Shutdown(ctx)
|
||||||
|
}
|
||||||
@ -1,32 +1,145 @@
|
|||||||
package log
|
package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"tercul/internal/observability"
|
||||||
"log"
|
|
||||||
"os"
|
"github.com/rs/zerolog"
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// LogLevel represents the severity level of a log message
|
// LogLevel represents the severity level of a log message.
|
||||||
type LogLevel int
|
type LogLevel int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// DebugLevel for detailed troubleshooting
|
// DebugLevel for detailed troubleshooting.
|
||||||
DebugLevel LogLevel = iota
|
DebugLevel LogLevel = iota
|
||||||
// InfoLevel for general operational information
|
// InfoLevel for general operational information.
|
||||||
InfoLevel
|
InfoLevel
|
||||||
// WarnLevel for potentially harmful situations
|
// WarnLevel for potentially harmful situations.
|
||||||
WarnLevel
|
WarnLevel
|
||||||
// ErrorLevel for error events that might still allow the application to continue
|
// ErrorLevel for error events that might still allow the application to continue.
|
||||||
ErrorLevel
|
ErrorLevel
|
||||||
// FatalLevel for severe error events that will lead the application to abort
|
// FatalLevel for severe error events that will lead the application to abort.
|
||||||
FatalLevel
|
FatalLevel
|
||||||
)
|
)
|
||||||
|
|
||||||
// String returns the string representation of the log level
|
// Field represents a key-value pair for structured logging.
|
||||||
|
type Field struct {
|
||||||
|
Key string
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// F creates a new Field.
|
||||||
|
func F(key string, value interface{}) Field {
|
||||||
|
return Field{Key: key, Value: value}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger provides structured logging capabilities.
|
||||||
|
type Logger struct {
|
||||||
|
*observability.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultLogger = &Logger{observability.NewLogger("tercul", "development")}
|
||||||
|
|
||||||
|
// Init re-initializes the default logger. This is useful for applications
|
||||||
|
// that need to configure the logger with dynamic values.
|
||||||
|
func Init(serviceName, environment string) {
|
||||||
|
defaultLogger = &Logger{observability.NewLogger(serviceName, environment)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDefaultLevel sets the log level for the default logger.
|
||||||
|
func SetDefaultLevel(level LogLevel) {
|
||||||
|
var zlevel zerolog.Level
|
||||||
|
switch level {
|
||||||
|
case DebugLevel:
|
||||||
|
zlevel = zerolog.DebugLevel
|
||||||
|
case InfoLevel:
|
||||||
|
zlevel = zerolog.InfoLevel
|
||||||
|
case WarnLevel:
|
||||||
|
zlevel = zerolog.WarnLevel
|
||||||
|
case ErrorLevel:
|
||||||
|
zlevel = zerolog.ErrorLevel
|
||||||
|
case FatalLevel:
|
||||||
|
zlevel = zerolog.FatalLevel
|
||||||
|
default:
|
||||||
|
zlevel = zerolog.InfoLevel
|
||||||
|
}
|
||||||
|
zerolog.SetGlobalLevel(zlevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func log(level LogLevel, msg string, fields ...Field) {
|
||||||
|
var event *zerolog.Event
|
||||||
|
// Access the embedded observability.Logger to get to zerolog's methods.
|
||||||
|
zlog := defaultLogger.Logger
|
||||||
|
switch level {
|
||||||
|
case DebugLevel:
|
||||||
|
event = zlog.Debug()
|
||||||
|
case InfoLevel:
|
||||||
|
event = zlog.Info()
|
||||||
|
case WarnLevel:
|
||||||
|
event = zlog.Warn()
|
||||||
|
case ErrorLevel:
|
||||||
|
event = zlog.Error()
|
||||||
|
case FatalLevel:
|
||||||
|
event = zlog.Fatal()
|
||||||
|
default:
|
||||||
|
event = zlog.Info()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
event.Interface(f.Key, f.Value)
|
||||||
|
}
|
||||||
|
event.Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogDebug logs a message at debug level using the default logger.
|
||||||
|
func LogDebug(msg string, fields ...Field) {
|
||||||
|
log(DebugLevel, msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogInfo logs a message at info level using the default logger.
|
||||||
|
func LogInfo(msg string, fields ...Field) {
|
||||||
|
log(InfoLevel, msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogWarn logs a message at warn level using the default logger.
|
||||||
|
func LogWarn(msg string, fields ...Field) {
|
||||||
|
log(WarnLevel, msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogError logs a message at error level using the default logger.
|
||||||
|
func LogError(msg string, fields ...Field) {
|
||||||
|
log(ErrorLevel, msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogFatal logs a message at fatal level using the default logger and then calls os.Exit(1).
|
||||||
|
func LogFatal(msg string, fields ...Field) {
|
||||||
|
log(FatalLevel, msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFields returns a new logger with the given fields added using the default logger.
|
||||||
|
func WithFields(fields ...Field) *Logger {
|
||||||
|
sublogger := defaultLogger.With().Logger()
|
||||||
|
for _, f := range fields {
|
||||||
|
sublogger = sublogger.With().Interface(f.Key, f.Value).Logger()
|
||||||
|
}
|
||||||
|
return &Logger{&observability.Logger{&sublogger}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext returns a new logger with the given context added using the default logger.
|
||||||
|
func WithContext(ctx context.Context) *Logger {
|
||||||
|
return &Logger{defaultLogger.Ctx(ctx)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following functions are kept for compatibility but are now simplified or deprecated.
|
||||||
|
|
||||||
|
// SetDefaultLogger is deprecated. Use Init.
|
||||||
|
func SetDefaultLogger(logger *Logger) {
|
||||||
|
// Deprecated: Logger is now initialized via Init.
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of the log level.
|
||||||
func (l LogLevel) String() string {
|
func (l LogLevel) String() string {
|
||||||
switch l {
|
switch l {
|
||||||
case DebugLevel:
|
case DebugLevel:
|
||||||
@ -44,192 +157,83 @@ func (l LogLevel) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Field represents a key-value pair for structured logging
|
// Debug logs a message at debug level.
|
||||||
type Field struct {
|
|
||||||
Key string
|
|
||||||
Value interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// F creates a new Field
|
|
||||||
func F(key string, value interface{}) Field {
|
|
||||||
return Field{Key: key, Value: value}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Logger provides structured logging capabilities
|
|
||||||
type Logger struct {
|
|
||||||
level LogLevel
|
|
||||||
writer io.Writer
|
|
||||||
fields []Field
|
|
||||||
context map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new Logger with the specified log level and writer
|
|
||||||
func New(level LogLevel, writer io.Writer) *Logger {
|
|
||||||
if writer == nil {
|
|
||||||
writer = os.Stdout
|
|
||||||
}
|
|
||||||
return &Logger{
|
|
||||||
level: level,
|
|
||||||
writer: writer,
|
|
||||||
fields: []Field{},
|
|
||||||
context: make(map[string]interface{}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug logs a message at debug level
|
|
||||||
func (l *Logger) Debug(msg string, fields ...Field) {
|
func (l *Logger) Debug(msg string, fields ...Field) {
|
||||||
if l.level <= DebugLevel {
|
l.log(DebugLevel, msg, fields...)
|
||||||
l.log(DebugLevel, msg, fields...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info logs a message at info level
|
// Info logs a message at info level.
|
||||||
func (l *Logger) Info(msg string, fields ...Field) {
|
func (l *Logger) Info(msg string, fields ...Field) {
|
||||||
if l.level <= InfoLevel {
|
l.log(InfoLevel, msg, fields...)
|
||||||
l.log(InfoLevel, msg, fields...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn logs a message at warn level
|
// Warn logs a message at warn level.
|
||||||
func (l *Logger) Warn(msg string, fields ...Field) {
|
func (l *Logger) Warn(msg string, fields ...Field) {
|
||||||
if l.level <= WarnLevel {
|
l.log(WarnLevel, msg, fields...)
|
||||||
l.log(WarnLevel, msg, fields...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error logs a message at error level
|
// Error logs a message at error level.
|
||||||
func (l *Logger) Error(msg string, fields ...Field) {
|
func (l *Logger) Error(msg string, fields ...Field) {
|
||||||
if l.level <= ErrorLevel {
|
l.log(ErrorLevel, msg, fields...)
|
||||||
l.log(ErrorLevel, msg, fields...)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal logs a message at fatal level and then calls os.Exit(1)
|
// Fatal logs a message at fatal level and then calls os.Exit(1).
|
||||||
func (l *Logger) Fatal(msg string, fields ...Field) {
|
func (l *Logger) Fatal(msg string, fields ...Field) {
|
||||||
if l.level <= FatalLevel {
|
l.log(FatalLevel, msg, fields...)
|
||||||
l.log(FatalLevel, msg, fields...)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithFields returns a new logger with the given fields added
|
|
||||||
func (l *Logger) WithFields(fields ...Field) *Logger {
|
|
||||||
newLogger := &Logger{
|
|
||||||
level: l.level,
|
|
||||||
writer: l.writer,
|
|
||||||
fields: append(l.fields, fields...),
|
|
||||||
context: l.context,
|
|
||||||
}
|
|
||||||
return newLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithContext returns a new logger with the given context added
|
|
||||||
func (l *Logger) WithContext(ctx map[string]interface{}) *Logger {
|
|
||||||
newContext := make(map[string]interface{})
|
|
||||||
for k, v := range l.context {
|
|
||||||
newContext[k] = v
|
|
||||||
}
|
|
||||||
for k, v := range ctx {
|
|
||||||
newContext[k] = v
|
|
||||||
}
|
|
||||||
|
|
||||||
newLogger := &Logger{
|
|
||||||
level: l.level,
|
|
||||||
writer: l.writer,
|
|
||||||
fields: l.fields,
|
|
||||||
context: newContext,
|
|
||||||
}
|
|
||||||
return newLogger
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetLevel sets the log level
|
|
||||||
func (l *Logger) SetLevel(level LogLevel) {
|
|
||||||
l.level = level
|
|
||||||
}
|
|
||||||
|
|
||||||
// log formats and writes a log message
|
|
||||||
func (l *Logger) log(level LogLevel, msg string, fields ...Field) {
|
func (l *Logger) log(level LogLevel, msg string, fields ...Field) {
|
||||||
timestamp := time.Now().Format(time.RFC3339)
|
var event *zerolog.Event
|
||||||
|
switch level {
|
||||||
// Get caller information
|
case DebugLevel:
|
||||||
_, file, line, ok := runtime.Caller(2)
|
event = l.Logger.Debug()
|
||||||
caller := "unknown"
|
case InfoLevel:
|
||||||
if ok {
|
event = l.Logger.Info()
|
||||||
parts := strings.Split(file, "/")
|
case WarnLevel:
|
||||||
if len(parts) > 2 {
|
event = l.Logger.Warn()
|
||||||
caller = fmt.Sprintf("%s:%d", parts[len(parts)-1], line)
|
case ErrorLevel:
|
||||||
} else {
|
event = l.Logger.Error()
|
||||||
caller = fmt.Sprintf("%s:%d", file, line)
|
case FatalLevel:
|
||||||
}
|
event = l.Logger.Fatal()
|
||||||
|
default:
|
||||||
|
event = l.Logger.Info()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format fields
|
for _, f := range fields {
|
||||||
allFields := append(l.fields, fields...)
|
event.Interface(f.Key, f.Value)
|
||||||
fieldStr := ""
|
|
||||||
for _, field := range allFields {
|
|
||||||
fieldStr += fmt.Sprintf(" %s=%v", field.Key, field.Value)
|
|
||||||
}
|
}
|
||||||
|
event.Msg(msg)
|
||||||
|
}
|
||||||
|
|
||||||
// Format context
|
// WithFields returns a new logger with the given fields added.
|
||||||
contextStr := ""
|
func (l *Logger) WithFields(fields ...Field) *Logger {
|
||||||
for k, v := range l.context {
|
sublogger := l.With().Logger()
|
||||||
contextStr += fmt.Sprintf(" %s=%v", k, v)
|
for _, f := range fields {
|
||||||
}
|
sublogger = sublogger.With().Interface(f.Key, f.Value).Logger()
|
||||||
|
|
||||||
// Format log message
|
|
||||||
logMsg := fmt.Sprintf("%s [%s] %s %s%s%s\n", timestamp, level.String(), caller, msg, fieldStr, contextStr)
|
|
||||||
|
|
||||||
// Write log message
|
|
||||||
_, err := l.writer.Write([]byte(logMsg))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error writing log message: %v", err)
|
|
||||||
}
|
}
|
||||||
|
return &Logger{&observability.Logger{&sublogger}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Global logger instance
|
func (l *Logger) WithContext(ctx map[string]interface{}) *Logger {
|
||||||
var defaultLogger = New(InfoLevel, os.Stdout)
|
// To maintain compatibility with the old API, we will convert the map to a context.
|
||||||
|
// This is not ideal and should be refactored in the future.
|
||||||
// SetDefaultLogger sets the global logger instance
|
zlog := l.Logger.With().Logger()
|
||||||
func SetDefaultLogger(logger *Logger) {
|
for k, v := range ctx {
|
||||||
defaultLogger = logger
|
zlog = zlog.With().Interface(k, v).Logger()
|
||||||
|
}
|
||||||
|
return &Logger{&observability.Logger{&zlog}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetDefaultLevel sets the log level for the default logger
|
func (l *Logger) SetLevel(level LogLevel) {
|
||||||
func SetDefaultLevel(level LogLevel) {
|
// This now controls the global log level.
|
||||||
defaultLogger.SetLevel(level)
|
SetDefaultLevel(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogDebug logs a message at debug level using the default logger
|
// Fmt versions for simple string formatting
|
||||||
func LogDebug(msg string, fields ...Field) {
|
func LogInfof(format string, v ...interface{}) {
|
||||||
defaultLogger.Debug(msg, fields...)
|
log(InfoLevel, fmt.Sprintf(format, v...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// LogInfo logs a message at info level using the default logger
|
func LogErrorf(format string, v ...interface{}) {
|
||||||
func LogInfo(msg string, fields ...Field) {
|
log(ErrorLevel, fmt.Sprintf(format, v...))
|
||||||
defaultLogger.Info(msg, fields...)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// LogWarn logs a message at warn level using the default logger
|
|
||||||
func LogWarn(msg string, fields ...Field) {
|
|
||||||
defaultLogger.Warn(msg, fields...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogError logs a message at error level using the default logger
|
|
||||||
func LogError(msg string, fields ...Field) {
|
|
||||||
defaultLogger.Error(msg, fields...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogFatal logs a message at fatal level using the default logger and then calls os.Exit(1)
|
|
||||||
func LogFatal(msg string, fields ...Field) {
|
|
||||||
defaultLogger.Fatal(msg, fields...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithFields returns a new logger with the given fields added using the default logger
|
|
||||||
func WithFields(fields ...Field) *Logger {
|
|
||||||
return defaultLogger.WithFields(fields...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithContext returns a new logger with the given context added using the default logger
|
|
||||||
func WithContext(ctx map[string]interface{}) *Logger {
|
|
||||||
return defaultLogger.WithContext(ctx)
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user