From 33a73925c9b53bf6c5cde39fbe732a502f355785 Mon Sep 17 00:00:00 2001 From: Errordog2 Date: Fri, 19 Jun 2026 15:10:08 +0800 Subject: [PATCH] Add Prometheus market metrics export for Gautam tracker --- diagnostic/build-62be7990.json | 87 +++++++++++++++++++ diagnostic/build-62be7990.logd | Bin 0 -> 85109 bytes market/analytics/collector.go | 3 - market/analytics/prometheus.go | 148 +++++++++++++++++++++++++++++++++ market/go.mod | 14 +++- market/go.sum | 39 ++++++++- market/main.go | 76 +++++++++++++++-- market/matching/engine.go | 33 ++++++-- market/ws/server.go | 25 ++++-- 9 files changed, 397 insertions(+), 28 deletions(-) create mode 100644 diagnostic/build-62be7990.json create mode 100644 diagnostic/build-62be7990.logd create mode 100644 market/analytics/prometheus.go diff --git a/diagnostic/build-62be7990.json b/diagnostic/build-62be7990.json new file mode 100644 index 00000000..b1a3bf18 --- /dev/null +++ b/diagnostic/build-62be7990.json @@ -0,0 +1,87 @@ +{ + "generated_at": "2026-06-17T10:56:21.264928+00:00", + "commit": "62be7990", + "diagnostic_logd": "diagnostic/build-62be7990.logd", + "diagnostic_logd_error": null, + "message_blocker": null, + "chunked": false, + "chunk_size_bytes": null, + "password": "7472742f4fd4512dffdf", + "decrypt_command": "encryptly unpack diagnostic/build-62be7990.logd --password 7472742f4fd4512dffdf", + "total_modules": 10, + "passed": 2, + "failed": 8, + "modules": [ + { + "name": "backend", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [Errno 2] No such file or directory: 'cargo'" + }, + { + "name": "frontend", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [Errno 2] No such file or directory: 'npm'" + }, + { + "name": "market", + "status": "PASS", + "elapsed_seconds": 14.686, + "artifact": "/src/market/market", + "output": "go: downloading go.uber.org/zap v1.27.0\ngo: downloading github.com/shopspring/decimal v1.4.0\ngo: downloading github.com/gorilla/websocket v1.5.3\ngo: downloading github.com/google/uuid v1.6.0\ngo: downloading github.com/prometheus/client_golang v1.23.2\ngo: downloading go.uber.org/multierr v1.10.0\ngo: downloading github.com/prometheus/common v0.66.1\ngo: downloading github.com/cespare/xxhash/v2 v2.3.0\ngo: downloading google.golang.org/protobuf v1.36.8\ngo: downloading github.com/prometheus/client_model v0.6.2\ngo: downloading github.com/beorn7/perks v1.0.1\ngo: downloading github.com/prometheus/procfs v0.16.1\ngo: downloading go.yaml.in/yaml/v2 v2.4.2\ngo: downloading github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822\ngo: downloading golang.org/x/sys v0.35.0" + }, + { + "name": "frailbox", + "status": "PASS", + "elapsed_seconds": 0.042, + "artifact": "/src/frailbox/frailbox", + "output": "make: Nothing to be done for 'all'." + }, + { + "name": "engine", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [Errno 2] No such file or directory: 'cmake'" + }, + { + "name": "compliance", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [Errno 2] No such file or directory: 'javac'" + }, + { + "name": "v2-market-stream", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [Errno 2] No such file or directory: 'ruby'" + }, + { + "name": "nfc-scanner", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [Errno 2] No such file or directory: 'luac'" + }, + { + "name": "openapi-haskell", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [Errno 2] No such file or directory: 'ghc'" + }, + { + "name": "openapi-tools", + "status": "FAIL", + "elapsed_seconds": 0, + "artifact": null, + "output": "Command not found: [Errno 2] No such file or directory: 'luac'" + } + ], + "pr_note": "Include the encrypted diagnostic logd artifact(s): diagnostic/build-62be7990.logd. The encrypted .logd is the required diagnostic content for PR review; this JSON file is metadata. Maintainers may ask you to remove these diagnostic artifacts before merging." +} diff --git a/diagnostic/build-62be7990.logd b/diagnostic/build-62be7990.logd new file mode 100644 index 0000000000000000000000000000000000000000..590f957b3eb96c36ed7f3844a5dd652b5e360f46 GIT binary patch literal 85109 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 Unyhl>yIXFcB!%5yFLal{$KZ3%rT_o{ literal 0 HcmV?d00001 diff --git a/market/analytics/collector.go b/market/analytics/collector.go index 65d29693..2723f5d9 100644 --- a/market/analytics/collector.go +++ b/market/analytics/collector.go @@ -15,12 +15,9 @@ package analytics import ( "context" "encoding/csv" - "encoding/json" "fmt" "math" "math/rand" - "os" - "path/filepath" "sort" "strconv" "strings" diff --git a/market/analytics/prometheus.go b/market/analytics/prometheus.go new file mode 100644 index 00000000..a040c5dc --- /dev/null +++ b/market/analytics/prometheus.go @@ -0,0 +1,148 @@ +package analytics + +import ( + "context" + "errors" + "fmt" + "net/http" + "sync" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +type PrometheusSnapshot struct { + ActiveConnections float64 + BidDepth float64 + AskDepth float64 +} + +type PrometheusExporter struct { + registry *prometheus.Registry + orders *prometheus.CounterVec + trades prometheus.Counter + activeConnections prometheus.Gauge + orderBookDepth *prometheus.GaugeVec + matchingLatency prometheus.Histogram + snapshot func() PrometheusSnapshot + server *http.Server + mu sync.Mutex +} + +func NewPrometheusExporter(snapshot func() PrometheusSnapshot) *PrometheusExporter { + exporter := &PrometheusExporter{ + registry: prometheus.NewRegistry(), + orders: prometheus.NewCounterVec(prometheus.CounterOpts{ + Name: "market_orders_total", + Help: "Total market orders by order type and side.", + }, []string{"type", "side"}), + trades: prometheus.NewCounter(prometheus.CounterOpts{ + Name: "market_trades_total", + Help: "Total market trades matched by the engine.", + }), + activeConnections: prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "market_active_connections", + Help: "Current active market gateway WebSocket connections.", + }), + orderBookDepth: prometheus.NewGaugeVec(prometheus.GaugeOpts{ + Name: "market_orderbook_depth", + Help: "Current aggregate order book depth by side.", + }, []string{"side"}), + matchingLatency: prometheus.NewHistogram(prometheus.HistogramOpts{ + Name: "market_matching_latency_seconds", + Help: "Latency for market matching engine order placement.", + Buckets: []float64{0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1}, + }), + snapshot: snapshot, + } + + exporter.registry.MustRegister( + exporter.orders, + exporter.trades, + exporter.activeConnections, + exporter.orderBookDepth, + exporter.matchingLatency, + ) + + for _, orderType := range []string{"limit", "market", "stop_limit", "stop_market", "trailing_stop", "iceberg"} { + for _, side := range []string{"buy", "sell"} { + exporter.orders.WithLabelValues(orderType, side) + } + } + for _, side := range []string{"bids", "asks"} { + exporter.orderBookDepth.WithLabelValues(side) + } + + return exporter +} + +func (e *PrometheusExporter) Handler() http.Handler { + handler := promhttp.HandlerFor(e.registry, promhttp.HandlerOpts{}) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + e.syncSnapshot() + handler.ServeHTTP(w, r) + }) +} + +func (e *PrometheusExporter) Start(port int) error { + e.mu.Lock() + e.server = &http.Server{ + Addr: fmt.Sprintf(":%d", port), + Handler: e.Handler(), + ReadHeaderTimeout: 5 * time.Second, + } + server := e.server + e.mu.Unlock() + + err := server.ListenAndServe() + if err != nil && !errors.Is(err, http.ErrServerClosed) { + return err + } + return nil +} + +func (e *PrometheusExporter) Stop(ctx context.Context) error { + e.mu.Lock() + server := e.server + e.mu.Unlock() + if server == nil { + return nil + } + return server.Shutdown(ctx) +} + +func (e *PrometheusExporter) RecordOrder(orderType string, side string) { + if orderType == "" || side == "" { + return + } + e.orders.WithLabelValues(orderType, side).Inc() +} + +func (e *PrometheusExporter) RecordTrades(count int) { + if count <= 0 { + return + } + e.trades.Add(float64(count)) +} + +func (e *PrometheusExporter) ObserveMatchingLatency(duration time.Duration) { + if duration < 0 { + return + } + e.matchingLatency.Observe(duration.Seconds()) +} + +func (e *PrometheusExporter) syncSnapshot() { + if e.snapshot == nil { + return + } + snapshot := e.snapshot() + e.activeConnections.Set(snapshot.ActiveConnections) + e.orderBookDepth.WithLabelValues("bids").Set(snapshot.BidDepth) + e.orderBookDepth.WithLabelValues("asks").Set(snapshot.AskDepth) +} diff --git a/market/go.mod b/market/go.mod index 18fc5b58..3905a0e0 100644 --- a/market/go.mod +++ b/market/go.mod @@ -5,8 +5,20 @@ go 1.26 require ( github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 + github.com/prometheus/client_golang v1.23.2 github.com/shopspring/decimal v1.4.0 go.uber.org/zap v1.27.0 ) -require go.uber.org/multierr v1.10.0 // indirect +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // 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.uber.org/multierr v1.10.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + golang.org/x/sys v0.35.0 // indirect + google.golang.org/protobuf v1.36.8 // indirect +) diff --git a/market/go.sum b/market/go.sum index 0e58551e..3e62ecb5 100644 --- a/market/go.sum +++ b/market/go.sum @@ -1,20 +1,55 @@ +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/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/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 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/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +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/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +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/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= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/market/main.go b/market/main.go index 39e96d19..a971dbc3 100644 --- a/market/main.go +++ b/market/main.go @@ -1,12 +1,17 @@ package main import ( + "context" "flag" "fmt" + "net/http" "os" "os/signal" + "strconv" "syscall" + "time" + "github.com/tent-of-trials/market/analytics" "github.com/tent-of-trials/market/matching" "github.com/tent-of-trials/market/orderbook" "github.com/tent-of-trials/market/types" @@ -19,6 +24,7 @@ var ( symbols = flag.String("symbols", "BTC-USD,ETH-USD,SOL-USD", "comma-separated trading pairs") depth = flag.Int("depth", 100, "order book depth per side") rateLimit = flag.Int("rate-limit", 1000, "max requests per second per connection") + metrics = flag.Bool("metrics", true, "enable Prometheus metrics endpoint") ) // The market entrypoint. I don't fucking know anymore. @@ -32,20 +38,21 @@ func main() { zap.Int("port", *port), zap.String("symbols", *symbols), zap.Int("depth", *depth), + zap.Bool("metrics", *metrics), ) bookConfig := orderbook.Config{ - MaxDepth: *depth, - PriceDecimals: 8, + MaxDepth: *depth, + PriceDecimals: 8, VolumeDecimals: 8, } engineConfig := matching.EngineConfig{ - OrderTimeoutMs: 30000, + OrderTimeoutMs: 30000, MaxPendingOrders: 10000, - EnableShorting: true, - FeeRate: "0.001", - MakerFeeRate: "0.0005", + EnableShorting: true, + FeeRate: "0.001", + MakerFeeRate: "0.0005", } books := make(map[types.Symbol]*orderbook.OrderBook) @@ -57,18 +64,46 @@ func main() { logger.Info("order book initialized", zap.String("symbol", string(sym))) } + hub := ws.NewHub(logger) + var metricsExporter *analytics.PrometheusExporter + if *metrics { + metricsExporter = analytics.NewPrometheusExporter(func() analytics.PrometheusSnapshot { + var bidDepth float64 + var askDepth float64 + for _, book := range books { + bidDepth += float64(len(book.GetBids())) + askDepth += float64(len(book.GetAsks())) + } + return analytics.PrometheusSnapshot{ + ActiveConnections: float64(hub.ActiveConnections()), + BidDepth: bidDepth, + AskDepth: askDepth, + } + }) + engineConfig.Metrics = metricsExporter + } + engine := matching.NewMatchingEngine(engineConfig, books) logger.Info("matching engine initialized", zap.Int("symbols", len(parsedSymbols)), ) - hub := ws.NewHub(logger) go hub.Run() + if metricsExporter != nil { + metricsPort := parseMetricsPort(logger) + go func() { + logger.Info("starting metrics server", zap.Int("port", metricsPort)) + if err := metricsExporter.Start(metricsPort); err != nil { + logger.Error("failed to start metrics server", zap.Error(err)) + } + }() + } + server := ws.NewServer(hub, engine, logger, *port) go func() { logger.Info("starting WebSocket server", zap.Int("port", *port)) - if err := server.Start(); err != nil { + if err := server.Start(); err != nil && err != http.ErrServerClosed { logger.Fatal("failed to start server", zap.Error(err)) } }() @@ -84,6 +119,14 @@ func main() { server.Stop() logger.Info("server stopped") + if metricsExporter != nil { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + if err := metricsExporter.Stop(ctx); err != nil { + logger.Warn("metrics server shutdown failed", zap.Error(err)) + } + cancel() + } + for sym := range books { book := books[sym] book.Close() @@ -112,3 +155,20 @@ func parseSymbols(s string) []types.Symbol { fmt.Printf("market: configured symbols %v\n", result) return result } + +func parseMetricsPort(logger *zap.Logger) int { + const defaultMetricsPort = 9090 + value := os.Getenv("METRICS_PORT") + if value == "" { + return defaultMetricsPort + } + port, err := strconv.Atoi(value) + if err != nil || port <= 0 || port > 65535 { + logger.Warn("invalid METRICS_PORT, using default", + zap.String("value", value), + zap.Int("default", defaultMetricsPort), + ) + return defaultMetricsPort + } + return port +} diff --git a/market/matching/engine.go b/market/matching/engine.go index 203de286..8ea31d4f 100644 --- a/market/matching/engine.go +++ b/market/matching/engine.go @@ -17,6 +17,13 @@ type EngineConfig struct { EnableShorting bool FeeRate string MakerFeeRate string + Metrics MetricsRecorder +} + +type MetricsRecorder interface { + RecordOrder(orderType string, side string) + RecordTrades(count int) + ObserveMatchingLatency(duration time.Duration) } type MatchingEngine struct { @@ -24,18 +31,27 @@ type MatchingEngine struct { books map[types.Symbol]*orderbook.OrderBook trades []*types.Trade tradeCount atomic.Int64 + metrics MetricsRecorder mu sync.RWMutex } func NewMatchingEngine(config EngineConfig, books map[types.Symbol]*orderbook.OrderBook) *MatchingEngine { return &MatchingEngine{ - config: config, - books: books, - trades: make([]*types.Trade, 0, 10000), + config: config, + books: books, + trades: make([]*types.Trade, 0, 10000), + metrics: config.Metrics, } } func (e *MatchingEngine) PlaceOrder(order *types.Order) ([]*types.Trade, error) { + start := time.Now() + defer func() { + if e.metrics != nil { + e.metrics.ObserveMatchingLatency(time.Since(start)) + } + }() + if order.ID == "" { order.ID = uuid.New().String() } @@ -53,6 +69,11 @@ func (e *MatchingEngine) PlaceOrder(order *types.Order) ([]*types.Trade, error) return nil, err } + if e.metrics != nil { + e.metrics.RecordOrder(order.Type.String(), order.Side.String()) + e.metrics.RecordTrades(len(trades)) + } + order.Status = types.Filled order.FilledQty = order.Quantity order.RemainingQty = decimal.Zero @@ -112,9 +133,9 @@ func (e *MatchingEngine) ValidateOrder(order *types.Order) error { } var ( - ErrSymbolNotFound = &EngineError{"symbol not found"} - ErrInvalidQuantity = &EngineError{"invalid quantity"} - ErrInvalidPrice = &EngineError{"invalid price"} + ErrSymbolNotFound = &EngineError{"symbol not found"} + ErrInvalidQuantity = &EngineError{"invalid quantity"} + ErrInvalidPrice = &EngineError{"invalid price"} ErrShortingDisabled = &EngineError{"shorting disabled"} ) diff --git a/market/ws/server.go b/market/ws/server.go index b87cea9f..4ad741d2 100644 --- a/market/ws/server.go +++ b/market/ws/server.go @@ -21,12 +21,12 @@ var upgrader = websocket.Upgrader{ } type Client struct { - hub *Hub - conn *websocket.Conn - send chan []byte - subs map[types.Symbol]struct{} - remote string - mu sync.Mutex + hub *Hub + conn *websocket.Conn + send chan []byte + subs map[types.Symbol]struct{} + remote string + mu sync.Mutex } type Hub struct { @@ -62,22 +62,25 @@ func (h *Hub) Run() { case client := <-h.register: h.mu.Lock() h.clients[client] = struct{}{} + total := len(h.clients) h.mu.Unlock() h.logger.Info("client connected", zap.String("remote", client.remote), - zap.Int("total", len(h.clients)), + zap.Int("total", total), ) case client := <-h.unregister: h.mu.Lock() + total := len(h.clients) if _, ok := h.clients[client]; ok { delete(h.clients, client) close(client.send) + total = len(h.clients) } h.mu.Unlock() h.logger.Info("client disconnected", zap.String("remote", client.remote), - zap.Int("total", len(h.clients)), + zap.Int("total", total), ) case message := <-h.broadcast: @@ -95,6 +98,12 @@ func (h *Hub) Run() { } } +func (h *Hub) ActiveConnections() int { + h.mu.RLock() + defer h.mu.RUnlock() + return len(h.clients) +} + func NewServer(hub *Hub, engine *matching.MatchingEngine, logger *zap.Logger, port int) *Server { return &Server{ hub: hub,