Send every log line to a syslog server as well (closes #28)
check / check (push) Successful in 3m3s
check / check (push) Successful in 3m3s
With SWWAF_LOG_REMOTE_URL set (syslog+udp, syslog+tcp or syslog+tls), every line on stdout is also sent as the message of an RFC 5424 record, octet-counted over TCP and TLS, from a bounded buffer that drops its oldest line when full, so a slow or unreachable server holds up nothing. Failed connections are retried with backoff; lines sent, dropped and waiting are metrics. At a stop the lines still waiting get at most two seconds. SWWAF_LOG_REMOTE_APP_NAME defaults to SWWAF_INSTANCE_NAME; while sending, an app name RFC 5424 does not allow stops the start. Standard library only: log/syslog writes only the older format. Model: opus-5-5
This commit was merged in pull request #84.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package smallwebwaf
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/url"
|
||||
"testing"
|
||||
"testing/synctest"
|
||||
"time"
|
||||
|
||||
"sneak.berlin/go/smallwebwaf/internal/remotelog"
|
||||
)
|
||||
|
||||
// The stop's tests run in a synctest bubble, where the time package runs
|
||||
// on a clock of the test's own, so that how long the stop takes can be
|
||||
// told exactly. The sender is held up by its process log, not by the
|
||||
// network: a goroutine of the bubble that waits on the network keeps that
|
||||
// clock from moving on.
|
||||
|
||||
func TestStopWaitsForTheSenderToFinish(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
took := stopHeldSender(t, time.Second)
|
||||
if took != time.Second {
|
||||
t.Errorf("the stop took %s, want the second the sender took", took)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestStopWaitsForTheSenderAtMostTwoSeconds(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
synctest.Test(t, func(t *testing.T) {
|
||||
took := stopHeldSender(t, time.Minute)
|
||||
if took != 2*time.Second {
|
||||
t.Errorf("the stop took %s, want 2s", took)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// heldLog holds each line written to it until it is closed.
|
||||
type heldLog chan struct{}
|
||||
|
||||
// Write waits until the log is closed.
|
||||
func (l heldLog) Write(p []byte) (int, error) {
|
||||
<-l
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// stopHeldSender starts sending to an endpoint the sender cannot connect
|
||||
// to, holds the sender as it logs that failure until release has passed,
|
||||
// stops the sending, and returns how long the stop took. It returns once
|
||||
// the sender has ended, as a bubble must.
|
||||
func stopHeldSender(t *testing.T, release time.Duration) time.Duration {
|
||||
t.Helper()
|
||||
|
||||
log := make(heldLog)
|
||||
sender := remotelog.New(remotelog.Params{
|
||||
// No port is 65536, so each attempt to connect fails at once,
|
||||
// before it reaches the network.
|
||||
URL: &url.URL{Scheme: remotelog.SchemeTCP, Host: "127.0.0.1:65536"},
|
||||
Buffer: 1,
|
||||
})
|
||||
|
||||
stopSending := startSending(t.Context(), sender,
|
||||
slog.New(slog.NewJSONHandler(log, nil)))
|
||||
|
||||
synctest.Wait()
|
||||
time.AfterFunc(release, func() { close(log) })
|
||||
|
||||
stopped := time.Now()
|
||||
|
||||
stopSending()
|
||||
|
||||
took := time.Since(stopped)
|
||||
|
||||
time.Sleep(release)
|
||||
synctest.Wait()
|
||||
|
||||
return took
|
||||
}
|
||||
Reference in New Issue
Block a user