Skip to content

Commit

Permalink
Merge pull request #11 from hvuhsg/development
Browse files Browse the repository at this point in the history
Development of otel
  • Loading branch information
hvuhsg authored Oct 20, 2024
2 parents 71308d8 + 099a056 commit 218d7f4
Show file tree
Hide file tree
Showing 13 changed files with 408 additions and 22 deletions.
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,17 @@ The server supports automated health checks for backend services. You can config
Authorization: "Bearer abc123"
```

### 8. OpenTelemetry Integration
The server includes built-in support for OpenTelemetry, enabling comprehensive observability through distributed tracing, metrics, and logging. This integration helps monitor application performance, troubleshoot issues, and understand system behavior in distributed environments.

```yaml
version: '...'
open_telemetry:
endpoint: "localhost:4317"
sample_ratio: 0.01 # == 1%
```

## Configuration Example

Here’s a generic example of how you can configure the reverse proxy:
Expand All @@ -144,6 +155,10 @@ ssl:
keyfile: /path/to/your/ssl/keyfile
certfile: /path/to/your/ssl/certfile
open_telemetry:
endpoint: "localhost:4317"
sample_ratio: 0.01 # == 1%
services:
- domain: your-domain.com
endpoints:
Expand Down
4 changes: 4 additions & 0 deletions cmd/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ version: '0.0.1'
host: localhost
port: 8004

open_telemetry:
endpoint: "localhost:4317"
sample_ratio: 0.03

services:
- domain: localhost
endpoints:
Expand Down
2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func main() {

log.Default().Println("Config loaded successfully")

server := gatego.New(config)
server := gatego.New(config, version)

err = server.Run()
if err != nil {
Expand Down
15 changes: 15 additions & 0 deletions config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@
],
"description": "SSL configuration for the server."
},
"open_telemetry": {
"type": "object",
"properties": {
"endpoint": {
"type": "string",
"description": "GRPC connection string for open telemetry collection agent"
},
"sample_ratio": {
"type":"number",
"exclusiveMinimum": 0,
"maximum": 1
}
},
"required": ["sample_ratio", "endpoint"]
},
"services": {
"type": "array",
"items": {
Expand Down
88 changes: 88 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"os"
"regexp"
"slices"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -180,11 +181,40 @@ type TLS struct {
CertFile *string `yaml:"certfile"`
}

type OTEL struct {
Endpoint string `yaml:"endpoint"`
SampleRatio float64 `yaml:"sample_ratio"`
}

func (otel OTEL) validate() error {
if len(otel.Endpoint) > 0 {
if err := isValidGRPCAddress(otel.Endpoint); err != nil {
return err
}
}

if otel.SampleRatio < 0 {
return errors.New("OpenTelemetry sample ratio MUST be above 0")
}

if otel.SampleRatio == 0 {
return errors.New("OpenTelemetry sample ratio is missing or equales to 0")
}

if otel.SampleRatio > 1 {
return errors.New("OpenTelemetry sample ratio CAN NOT be above 1")
}

return nil
}

type Config struct {
Version string `yaml:"version"`
Host string `yaml:"host"` // listen host
Port uint16 `yaml:"port"` // listen port

OTEL *OTEL `yaml:"open_telemetry"`

// TLS options
SSL TLS `yaml:"ssl"`

Expand All @@ -210,6 +240,12 @@ func (c Config) Validate(currentVersion string) error {
return errors.New("host is required")
}

if c.OTEL != nil {
if err := (*c.OTEL).validate(); err != nil {
return err
}
}

for _, service := range c.Services {
if err := service.validate(); err != nil {
return err
Expand Down Expand Up @@ -317,3 +353,55 @@ func isValidMethod(method string) bool {

return slices.Contains(methods, method)
}

func isValidGRPCAddress(address string) error {
if address == "" {
return fmt.Errorf("address cannot be empty")
}

// Split host and port
host, portStr, err := net.SplitHostPort(address)
if err != nil {
return fmt.Errorf("invalid address format: %v", err)
}

// Validate port
port, err := strconv.Atoi(portStr)
if err != nil {
return fmt.Errorf("invalid port number: %v", err)
}
if port < 1 || port > 65535 {
return fmt.Errorf("port number must be between 1 and 65535")
}

// Empty host means localhost/0.0.0.0, which is valid
if host == "" {
return nil
}

// Check if host is IPv4 or IPv6
if ip := net.ParseIP(host); ip != nil {
return nil
}

// Validate hostname format
hostnameRegex := regexp.MustCompile(`^[a-zA-Z0-9]([a-zA-Z0-9\-\.]*[a-zA-Z0-9])?$`)
if !hostnameRegex.MatchString(host) {
return fmt.Errorf("invalid hostname format")
}

// Check hostname length
if len(host) > 253 {
return fmt.Errorf("hostname too long")
}

// Validate hostname parts
parts := strings.Split(host, ".")
for _, part := range parts {
if len(part) > 63 {
return fmt.Errorf("hostname label too long")
}
}

return nil
}
24 changes: 24 additions & 0 deletions dev/otel/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
services:
# Jaeger
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # Jaeger UI
- "14250:14250" # Model used by collector
environment:
- COLLECTOR_OTLP_ENABLED=true

# OpenTelemetry Collector
otel-collector:
image: otel/opentelemetry-collector-contrib:latest
command: ["--config=/etc/otel-collector-config.yaml"]
volumes:
- ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
ports:
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP http receiver
- "8888:8888" # Prometheus metrics exposed by the collector
- "8889:8889" # Prometheus exporter metrics
- "13133:13133" # Health check extension
depends_on:
- jaeger
38 changes: 38 additions & 0 deletions dev/otel/otel-collector-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318

processors:
batch:
timeout: 1s
send_batch_size: 1024

memory_limiter:
check_interval: 1s
limit_mib: 1000
spike_limit_mib: 200

exporters:
otlp:
endpoint: "jaeger:4317"
tls:
insecure: true

debug:
verbosity: detailed

extensions:
health_check:
endpoint: 0.0.0.0:13133

service:
extensions: [health_check]
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [otlp, debug]
10 changes: 7 additions & 3 deletions gatego.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gatego

import (
"context"
"fmt"
"log"
"net/http"
Expand All @@ -12,14 +13,17 @@ import (

type GateGo struct {
config config.Config
ctx context.Context
}

func New(config config.Config) *GateGo {
return &GateGo{config: config}
func New(config config.Config, version string) *GateGo {
ctx := context.Background()
ctx = context.WithValue(ctx, "version", version)
return &GateGo{config: config, ctx: ctx}
}

func (gg GateGo) Run() error {
table, err := NewHandlersTable(gg.config.Services)
table, err := NewHandlersTable(gg.ctx, gg.config.OTEL, gg.config.Services)
if err != nil {
return err
}
Expand Down
20 changes: 19 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,24 @@ require gopkg.in/yaml.v3 v3.0.1
require (
github.com/hashicorp/go-version v1.7.0
github.com/tdewolff/minify/v2 v2.21.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0
go.opentelemetry.io/otel/trace v1.31.0
google.golang.org/grpc v1.67.1
)

require golang.org/x/text v0.19.0 // indirect
require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
go.opentelemetry.io/otel/metric v1.31.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/text v0.19.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/protobuf v1.35.1 // indirect
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
Expand All @@ -27,6 +42,9 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/testify v1.9.0
github.com/tdewolff/parse/v2 v2.7.17 // indirect
go.opentelemetry.io/otel v1.31.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0
go.opentelemetry.io/otel/sdk v1.31.0
golang.org/x/net v0.30.0
golang.org/x/time v0.7.0
)
41 changes: 39 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
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/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4=
github.com/getkin/kin-openapi v0.128.0/go.mod h1:OZrfXzUfGrNbsKj+xmFBx6E5c6yH3At/tAKSc2UszXM=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/invopop/yaml v0.3.1 h1:f0+ZpmhfBSS4MhG+4HYseMdJhoeeopbSKbq5Rpeelso=
Expand All @@ -32,8 +43,8 @@ github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tdewolff/minify/v2 v2.21.0 h1:nAPP1UVx0aK1xsQh/JiG3xyEnnqWw+agPstn+V6Pkto=
Expand All @@ -45,12 +56,38 @@ github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0 h1:FFeLy03iVTXP6ffeN2iXrxfGsZGCjVx0/4KlizjyBwU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.31.0/go.mod h1:TMu73/k1CP8nBUpDLc71Wj/Kf7ZS9FK5b53VapRsP9o=
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
Loading

0 comments on commit 218d7f4

Please sign in to comment.