feat: add pprof profiling controlled by ENABLE_PPROF env var

Add net/http/pprof support to both Go server (default port 6060) and
ingestor (default port 6061). Profiling is off by default — only
starts the pprof HTTP listener when ENABLE_PPROF=true.

PPROF_PORT env var overrides the default port for each binary.

Enable on staging-go in docker-compose with exposed ports 6060/6061.
Not enabled on prod.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Kpa-clawbotandCopilot committed 2026-03-28 11:18:33 -07:00
1 parent 1619f4857e
commit 6d31cb2ad6
3 files changed
+68 -2

No files matched your search

+51 -2
View File
@@ -8,6 +8,9 @@ import (
"flag"
"fmt"
"log"
"math"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"path/filepath"
@@ -19,6 +22,18 @@ import (
)
func main() {
// pprof profiling — off by default, enable with ENABLE_PPROF=true
if os.Getenv("ENABLE_PPROF") == "true" {
pprofPort := os.Getenv("PPROF_PORT")
if pprofPort == "" {
pprofPort = "6061"
}
go func() {
log.Printf("[pprof] ingestor profiling at http://localhost:%s/debug/pprof/", pprofPort)
log.Fatal(http.ListenAndServe(":"+pprofPort, nil))
}()
}
configPath := flag.String("config", "config.json", "path to config file")
flag.Parse()
@@ -193,7 +208,8 @@ func handleMessage(store *Store, tag string, source MQTTSource, m mqtt.Message,
observerID := parts[2]
name, _ := msg["origin"].(string)
iata := parts[1]
if err := store.UpsertObserver(observerID, name, iata); err != nil {
meta := extractObserverMeta(msg)
if err := store.UpsertObserver(observerID, name, iata, meta); err != nil {
log.Printf("MQTT [%s] observer status error: %v", tag, err)
}
log.Printf("MQTT [%s] status: %s (%s)", tag, firstNonEmpty(name, observerID), iata)
@@ -260,7 +276,7 @@ func handleMessage(store *Store, tag string, source MQTTSource, m mqtt.Message,
// Upsert observer
if observerID != "" {
origin, _ := msg["origin"].(string)
if err := store.UpsertObserver(observerID, origin, region); err != nil {
if err := store.UpsertObserver(observerID, origin, region, nil); err != nil {
log.Printf("MQTT [%s] observer upsert error: %v", tag, err)
}
}
@@ -446,6 +462,39 @@ func toFloat64(v interface{}) (float64, bool) {
}
}
// extractObserverMeta extracts hardware metadata from an MQTT status message.
// Casts battery_mv and uptime_secs to integers (they're always whole numbers).
func extractObserverMeta(msg map[string]interface{}) *ObserverMeta {
meta := &ObserverMeta{}
hasData := false
if v, ok := msg["battery_mv"]; ok {
if f, ok := toFloat64(v); ok {
iv := int(math.Round(f))
meta.BatteryMv = &iv
hasData = true
}
}
if v, ok := msg["uptime_secs"]; ok {
if f, ok := toFloat64(v); ok {
iv := int64(math.Round(f))
meta.UptimeSecs = &iv
hasData = true
}
}
if v, ok := msg["noise_floor"]; ok {
if f, ok := toFloat64(v); ok {
meta.NoiseFloor = &f
hasData = true
}
}
if !hasData {
return nil
}
return meta
}
func firstNonEmpty(vals ...string) string {
for _, v := range vals {
if v != "" {
+13
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"log"
"net/http"
_ "net/http/pprof"
"os"
"os/exec"
"os/signal"
@@ -54,6 +55,18 @@ func resolveBuildTime() string {
}
func main() {
// pprof profiling — off by default, enable with ENABLE_PPROF=true
if os.Getenv("ENABLE_PPROF") == "true" {
pprofPort := os.Getenv("PPROF_PORT")
if pprofPort == "" {
pprofPort = "6060"
}
go func() {
log.Printf("[pprof] profiling UI at http://localhost:%s/debug/pprof/", pprofPort)
log.Fatal(http.ListenAndServe(":"+pprofPort, nil))
}()
}
var (
configDir string
port int
+4
View File
@@ -58,12 +58,16 @@ services:
ports:
- "${STAGING_GO_HTTP_PORT:-82}:80"
- "${STAGING_GO_MQTT_PORT:-1885}:1883"
- "6060:6060" # pprof server
- "6061:6061" # pprof ingestor
volumes:
- ${STAGING_DATA_DIR:-~/meshcore-staging-data}/config.json:/app/config.json:ro
- ${STAGING_DATA_DIR:-~/meshcore-staging-data}:/app/data
- caddy-data-staging-go:/data/caddy
environment:
- NODE_ENV=staging
- ENABLE_PPROF=true
- PPROF_PORT=6060
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/api/stats"]
interval: 30s