various: HTTP server; migrate to cobra pattern for repository
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: Ifac6e992b77dfaf92e3059944aa871f16a6a6964
This commit is contained in:
parent
e0ec475a81
commit
b894833ac7
6 changed files with 182 additions and 0 deletions
15
cmd/watchdog/main.go
Normal file
15
cmd/watchdog/main.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package watchdog
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"log"
|
||||
)
|
||||
|
||||
func Main() {
|
||||
configPath := flag.String("config", "config.yaml", "path to config file")
|
||||
flag.Parse()
|
||||
|
||||
if err := Run(*configPath); err != nil {
|
||||
log.Fatalf("Error: %v", err)
|
||||
}
|
||||
}
|
||||
68
cmd/watchdog/root.go
Normal file
68
cmd/watchdog/root.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package watchdog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"notashelf.dev/watchdog/internal/aggregate"
|
||||
"notashelf.dev/watchdog/internal/api"
|
||||
"notashelf.dev/watchdog/internal/config"
|
||||
"notashelf.dev/watchdog/internal/normalize"
|
||||
)
|
||||
|
||||
func Run(configPath string) error {
|
||||
// Load configuration
|
||||
cfg, err := config.Load(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("Loaded config for domain: %s", cfg.Site.Domain)
|
||||
|
||||
// Initialize components
|
||||
pathNormalizer := normalize.NewPathNormalizer(cfg.Site.Path)
|
||||
pathRegistry := aggregate.NewPathRegistry(cfg.Limits.MaxPaths)
|
||||
refRegistry := normalize.NewReferrerRegistry(cfg.Limits.MaxSources)
|
||||
metricsAgg := aggregate.NewMetricsAggregator(pathRegistry, *cfg)
|
||||
|
||||
// Register Prometheus metrics
|
||||
promRegistry := prometheus.NewRegistry()
|
||||
metricsAgg.MustRegister(promRegistry)
|
||||
|
||||
// Create HTTP handlers
|
||||
ingestionHandler := api.NewIngestionHandler(*cfg, pathNormalizer, pathRegistry, refRegistry, metricsAgg)
|
||||
|
||||
// Setup routes
|
||||
mux := http.NewServeMux()
|
||||
|
||||
// Metrics endpoint
|
||||
mux.Handle(cfg.Server.MetricsPath, promhttp.HandlerFor(promRegistry, promhttp.HandlerOpts{
|
||||
EnableOpenMetrics: true,
|
||||
}))
|
||||
|
||||
// Ingestion endpoint
|
||||
mux.Handle(cfg.Server.IngestionPath, ingestionHandler)
|
||||
|
||||
// Health check endpoint
|
||||
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("OK"))
|
||||
})
|
||||
|
||||
// Serve static files from /web/ if the directory exists
|
||||
if info, err := os.Stat("web"); err == nil && info.IsDir() {
|
||||
log.Println("Serving static files from /web/")
|
||||
mux.Handle("/web/", http.StripPrefix("/web/", http.FileServer(http.Dir("web"))))
|
||||
}
|
||||
|
||||
// Start server
|
||||
log.Printf("Starting server on %s", cfg.Server.ListenAddr)
|
||||
log.Printf("Metrics endpoint: %s", cfg.Server.MetricsPath)
|
||||
log.Printf("Ingestion endpoint: %s", cfg.Server.IngestionPath)
|
||||
|
||||
return http.ListenAndServe(cfg.Server.ListenAddr, mux)
|
||||
}
|
||||
56
config.example.yaml
Normal file
56
config.example.yaml
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Watchdog Analytics Configuration Example
|
||||
|
||||
site:
|
||||
# Your site's primary domain (required)
|
||||
domain: "example.com"
|
||||
|
||||
# Salt rotation for IP hashing (optional, default: daily)
|
||||
# Options: "daily", "hourly"
|
||||
salt_rotation: "daily"
|
||||
|
||||
# Which dimensions to collect
|
||||
collect:
|
||||
pageviews: true
|
||||
country: true
|
||||
device: true
|
||||
# Referrer collection mode: "off", "domain"
|
||||
# "domain" - collect only the domain part (e.g., "google.com")
|
||||
# "off" - don't collect referrer data
|
||||
referrer: "domain"
|
||||
|
||||
# Custom events to track (optional)
|
||||
custom_events:
|
||||
- "signup"
|
||||
- "purchase"
|
||||
- "download"
|
||||
|
||||
# Path normalization options
|
||||
path:
|
||||
# Remove query strings from paths (e.g., /page?id=1 -> /page)
|
||||
strip_query: true
|
||||
# Remove fragments from paths (e.g., /page#section -> /page)
|
||||
strip_fragment: true
|
||||
# Collapse numeric segments to :id (e.g., /user/123 -> /user/:id)
|
||||
collapse_numeric_segments: true
|
||||
# Maximum number of path segments to keep
|
||||
max_segments: 5
|
||||
# Normalize trailing slashes (e.g., /page/ -> /page)
|
||||
normalize_trailing_slash: true
|
||||
|
||||
# Cardinality limits to prevent metric explosion
|
||||
limits:
|
||||
# Maximum number of unique paths to track
|
||||
max_paths: 10000
|
||||
# Maximum number of unique referrer sources to track
|
||||
max_sources: 500
|
||||
# Maximum events per minute (for rate limiting, not yet implemented)
|
||||
max_events_per_minute: 1000
|
||||
|
||||
# Server configuration
|
||||
server:
|
||||
# Address to listen on
|
||||
listen_addr: ":8080"
|
||||
# Prometheus metrics endpoint
|
||||
metrics_path: "/metrics"
|
||||
# Event ingestion endpoint
|
||||
ingestion_path: "/api/event"
|
||||
14
go.mod
14
go.mod
|
|
@ -5,3 +5,17 @@ go 1.25.5
|
|||
require gopkg.in/yaml.v3 v3.0.1
|
||||
|
||||
require golang.org/x/net v0.51.0
|
||||
|
||||
require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/sys v0.41.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
)
|
||||
|
|
|
|||
22
go.sum
22
go.sum
|
|
@ -1,5 +1,27 @@
|
|||
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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
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/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
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.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
|
||||
golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
|
|||
7
main.go
Normal file
7
main.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package main
|
||||
|
||||
import "notashelf.dev/watchdog/cmd/watchdog"
|
||||
|
||||
func main() {
|
||||
watchdog.Main()
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue