feat(go): expose runtime ingest metrics

This commit is contained in:
lingniu
2026-07-02 19:06:24 +08:00
parent 1dfd540700
commit 896e841902
18 changed files with 602 additions and 28 deletions

View File

@@ -18,6 +18,7 @@ import (
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope"
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/health"
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/metrics"
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/observability"
)
@@ -33,6 +34,7 @@ func main() {
os.Exit(1)
}
defer conn.Close()
registry := metrics.NewRegistry()
health.Start(ctx, logger, health.NewServer(env("HEALTH_ADDR", ""), "nats-kafka-bridge", []health.Check{
{Name: "nats", Check: func(context.Context) error {
if conn.Status() != nats.CONNECTED {
@@ -40,7 +42,7 @@ func main() {
}
return nil
}},
}))
}, registry))
js, err := conn.JetStream()
if err != nil {
logger.Error("nats jetstream init failed", "error", err)
@@ -78,7 +80,7 @@ func main() {
"durable", cfg.NATSDurable,
"filter", cfg.NATSFilter,
"kafka_brokers", strings.Join(cfg.KafkaBrokers, ","))
runBridge(ctx, logger, sub, writer, cfg)
runBridge(ctx, logger, registry, sub, writer, cfg)
}
type config struct {
@@ -135,7 +137,7 @@ type bridgeMessage struct {
ack func() error
}
func runBridge(ctx context.Context, logger *slog.Logger, sub natsPullSubscription, writer kafkaBatchWriter, cfg config) {
func runBridge(ctx context.Context, logger *slog.Logger, registry *metrics.Registry, sub natsPullSubscription, writer kafkaBatchWriter, cfg config) {
for {
if ctx.Err() != nil {
return
@@ -161,7 +163,7 @@ func runBridge(ctx context.Context, logger *slog.Logger, sub natsPullSubscriptio
})
}
operationCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), cfg.OperationWait)
err = bridgeBatch(operationCtx, writer, bridgeMessages, cfg.Route)
err = bridgeBatch(operationCtx, registry, writer, bridgeMessages, cfg.Route)
cancel()
if err != nil {
logger.Error("bridge batch failed", "count", len(bridgeMessages), "error", err)
@@ -194,32 +196,56 @@ func ensureStream(js nats.JetStreamContext, cfg config) error {
return err
}
func bridgeBatch(ctx context.Context, writer kafkaBatchWriter, messages []bridgeMessage, route map[string]string) error {
func bridgeBatch(ctx context.Context, registry *metrics.Registry, writer kafkaBatchWriter, messages []bridgeMessage, route map[string]string) error {
if len(messages) == 0 {
return nil
}
kafkaMessages := make([]kafka.Message, 0, len(messages))
for _, message := range messages {
addBridgeSubjectMetric(registry, "vehicle_bridge_messages_total", message.subject, "received")
topic, ok := route[message.subject]
if !ok || topic == "" {
addBridgeSubjectMetric(registry, "vehicle_bridge_messages_total", message.subject, "route_error")
return fmt.Errorf("kafka topic not configured for nats subject %q", message.subject)
}
kafkaMessages = append(kafkaMessages, kafkaMessage(topic, message.data))
}
if err := writer.WriteMessages(ctx, kafkaMessages...); err != nil {
for _, message := range kafkaMessages {
addBridgeTopicMetric(registry, "vehicle_bridge_kafka_writes_total", message.Topic, "error")
}
return err
}
for _, message := range kafkaMessages {
addBridgeTopicMetric(registry, "vehicle_bridge_kafka_writes_total", message.Topic, "ok")
}
for _, message := range messages {
if message.ack == nil {
continue
}
if err := message.ack(); err != nil {
addBridgeSubjectMetric(registry, "vehicle_bridge_nats_acks_total", message.subject, "error")
return err
}
addBridgeSubjectMetric(registry, "vehicle_bridge_nats_acks_total", message.subject, "ok")
}
return nil
}
func addBridgeSubjectMetric(registry *metrics.Registry, name string, subject string, status string) {
if registry == nil {
return
}
registry.IncCounter(name, metrics.Labels{"subject": subject, "status": status})
}
func addBridgeTopicMetric(registry *metrics.Registry, name string, topic string, status string) {
if registry == nil {
return
}
registry.IncCounter(name, metrics.Labels{"topic": topic, "status": status})
}
func kafkaMessage(topic string, data []byte) kafka.Message {
var env envelope.FrameEnvelope
message := kafka.Message{Topic: topic, Value: data}