package main import ( "context" "log" "net" "strings" ) // udpReadBufBytes is the SO_RCVBUF size requested. Bursts of ~10K lines/sec at // ~200B each comfortably fit; the kernel may cap below this. const udpReadBufBytes = 4 << 20 // udpPacketBuf is the per-read buffer. A single nginx log line easily fits in // a few KB; 64K is the practical UDP datagram ceiling. const udpPacketBuf = 64 << 10 // UDPListener receives nginx_ipng_stats_logtail datagrams on a local socket, // parses each packet as one log line, and forwards LogRecords to ch. type UDPListener struct { addr string v4bits int v6bits int ch chan<- LogRecord prom *PromStore // optional; bumps UDP ingest counters } func NewUDPListener(addr string, v4bits, v6bits int, ch chan<- LogRecord) *UDPListener { return &UDPListener{addr: addr, v4bits: v4bits, v6bits: v6bits, ch: ch} } // SetProm wires a PromStore so the listener can report received/success/consumed counts. func (u *UDPListener) SetProm(p *PromStore) { u.prom = p } // Run listens until ctx is cancelled. func (u *UDPListener) Run(ctx context.Context) { laddr, err := net.ResolveUDPAddr("udp", u.addr) if err != nil { log.Fatalf("udp: resolve %s: %v", u.addr, err) } conn, err := net.ListenUDP("udp", laddr) if err != nil { log.Fatalf("udp: listen %s: %v", u.addr, err) } defer conn.Close() if err := conn.SetReadBuffer(udpReadBufBytes); err != nil { log.Printf("udp: SetReadBuffer(%d): %v", udpReadBufBytes, err) } log.Printf("udp: listening on %s", conn.LocalAddr()) go func() { <-ctx.Done() conn.Close() }() buf := make([]byte, udpPacketBuf) for { n, _, err := conn.ReadFromUDP(buf) if err != nil { if ctx.Err() != nil { return } log.Printf("udp: read: %v", err) continue } if u.prom != nil { u.prom.IncUDPPacket() } line := strings.TrimRight(string(buf[:n]), "\r\n") rec, ok := ParseUDPLine(line, u.v4bits, u.v6bits) if !ok { continue } if u.prom != nil { u.prom.IncUDPSuccess() } select { case u.ch <- rec: if u.prom != nil { u.prom.IncUDPConsumed() } default: // Channel full — drop rather than block the read loop. } } }