feat(go): expose gateway connection close metrics
This commit is contained in:
@@ -146,6 +146,7 @@ func (s *TCPServer) ListenAndServe(ctx context.Context) error {
|
||||
}()
|
||||
default:
|
||||
s.recordConnectionRejection("max_connections")
|
||||
s.recordConnectionClose("max_connections")
|
||||
s.logger.Warn("tcp connection rejected: max connections reached", "protocol", s.protocol.Protocol, "remote", conn.RemoteAddr().String())
|
||||
_ = conn.Close()
|
||||
}
|
||||
@@ -172,6 +173,7 @@ func (s *TCPServer) handleConnection(ctx context.Context, conn net.Conn) {
|
||||
frames, remainder, extractErr := s.protocol.Extract(pending)
|
||||
if extractErr != nil {
|
||||
log.Warn("frame extraction failed", "error", extractErr)
|
||||
s.recordConnectionClose("extract_error")
|
||||
return
|
||||
}
|
||||
pending = remainder
|
||||
@@ -181,17 +183,21 @@ func (s *TCPServer) handleConnection(ctx context.Context, conn net.Conn) {
|
||||
}
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
s.recordConnectionClose("eof")
|
||||
return
|
||||
}
|
||||
var netErr net.Error
|
||||
if errors.As(err, &netErr) && netErr.Timeout() {
|
||||
log.Warn("tcp connection idle timeout")
|
||||
s.recordConnectionClose("read_timeout")
|
||||
return
|
||||
}
|
||||
log.Warn("tcp read failed", "error", err)
|
||||
s.recordConnectionClose("read_error")
|
||||
return
|
||||
}
|
||||
if ctx.Err() != nil {
|
||||
s.recordConnectionClose("context_cancelled")
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -386,6 +392,16 @@ func (s *TCPServer) recordConnectionRejection(reason string) {
|
||||
})
|
||||
}
|
||||
|
||||
func (s *TCPServer) recordConnectionClose(reason string) {
|
||||
if s.metrics == nil {
|
||||
return
|
||||
}
|
||||
s.metrics.IncCounter("vehicle_gateway_connection_closes_total", metrics.Labels{
|
||||
"protocol": string(s.protocol.Protocol),
|
||||
"reason": reason,
|
||||
})
|
||||
}
|
||||
|
||||
func identityStatus(env envelope.FrameEnvelope) string {
|
||||
if strings.TrimSpace(env.VIN) != "" {
|
||||
return "resolved"
|
||||
|
||||
@@ -164,6 +164,30 @@ func TestTCPServerRecordsConnectionRejectionMetric(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTCPServerRecordsConnectionCloseReasonMetric(t *testing.T) {
|
||||
registry := metrics.NewRegistry()
|
||||
server, err := NewTCPServer(TCPServerConfig{
|
||||
Protocol: TCPProtocol{
|
||||
Protocol: envelope.ProtocolJT808,
|
||||
Addr: ":0",
|
||||
Extract: jt808.ExtractFrames,
|
||||
Parse: jt808.ParseFrame,
|
||||
},
|
||||
Sink: &recordingSink{},
|
||||
Logger: slog.New(slog.NewTextHandler(testWriter{t: t}, nil)),
|
||||
Metrics: registry,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("NewTCPServer() error = %v", err)
|
||||
}
|
||||
|
||||
server.recordConnectionClose("read_timeout")
|
||||
|
||||
if text := registry.Render(); !strings.Contains(text, `vehicle_gateway_connection_closes_total{protocol="JT808",reason="read_timeout"} 1`) {
|
||||
t.Fatalf("connection close metric missing:\n%s", text)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTCPServerPublishesBadFrameOnlyToRaw(t *testing.T) {
|
||||
good := buildGBFrame(0x02, 0xfe, "LNBSCB3D4R1234567", nil)
|
||||
good[len(good)-1] ^= 0xff
|
||||
|
||||
Reference in New Issue
Block a user