syslog_test.go 7.68 KB
Newer Older
1 2 3
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
4

5
// +build !windows,!nacl,!plan9
6

7 8 9
package syslog

import (
10
	"bufio"
11
	"fmt"
12
	"io"
13
	"io/ioutil"
14 15
	"log"
	"net"
16
	"os"
17
	"sync"
18
	"testing"
19
	"time"
20 21
)

22
func runPktSyslog(c net.PacketConn, done chan<- string) {
23
	var buf [4096]byte
24
	var rcvd string
25
	ct := 0
26
	for {
27 28 29
		var n int
		var err error

30
		c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
31
		n, _, err = c.ReadFrom(buf[:])
32 33
		rcvd += string(buf[:n])
		if err != nil {
34 35 36 37 38 39
			if oe, ok := err.(*net.OpError); ok {
				if ct < 3 && oe.Temporary() {
					ct++
					continue
				}
			}
40 41 42
			break
		}
	}
43
	c.Close()
44 45 46
	done <- rcvd
}

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
var crashy = false

func runStreamSyslog(l net.Listener, done chan<- string, wg *sync.WaitGroup) {
	for {
		var c net.Conn
		var err error
		if c, err = l.Accept(); err != nil {
			return
		}
		wg.Add(1)
		go func(c net.Conn) {
			defer wg.Done()
			c.SetReadDeadline(time.Now().Add(5 * time.Second))
			b := bufio.NewReader(c)
			for ct := 1; !crashy || ct&7 != 0; ct++ {
				s, err := b.ReadString('\n')
				if err != nil {
					break
				}
				done <- s
			}
			c.Close()
		}(c)
	}
}

func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) {
	if n == "udp" || n == "tcp" {
		la = "127.0.0.1:0"
	} else {
		// unix and unixgram: choose an address if none given
		if la == "" {
			// use ioutil.TempFile to get a name that is unique
			f, err := ioutil.TempFile("", "syslogtest")
			if err != nil {
				log.Fatal("TempFile: ", err)
			}
			f.Close()
			la = f.Name()
		}
		os.Remove(la)
	}

	wg = new(sync.WaitGroup)
	if n == "udp" || n == "unixgram" {
		l, e := net.ListenPacket(n, la)
		if e != nil {
			log.Fatalf("startServer failed: %v", e)
		}
		addr = l.LocalAddr().String()
		sock = l
		wg.Add(1)
		go func() {
			defer wg.Done()
			runPktSyslog(l, done)
		}()
	} else {
		l, e := net.Listen(n, la)
		if e != nil {
			log.Fatalf("startServer failed: %v", e)
		}
		addr = l.Addr().String()
		sock = l
		wg.Add(1)
		go func() {
			defer wg.Done()
			runStreamSyslog(l, done, wg)
		}()
	}
	return
}

func TestWithSimulated(t *testing.T) {
	msg := "Test 123"
	transport := []string{"unix", "unixgram", "udp", "tcp"}

	for _, tr := range transport {
		done := make(chan string)
125 126 127
		addr, sock, srvWG := startServer(tr, "", done)
		defer srvWG.Wait()
		defer sock.Close()
128 129 130 131 132 133 134 135 136 137 138 139 140
		if tr == "unix" || tr == "unixgram" {
			defer os.Remove(addr)
		}
		s, err := Dial(tr, addr, LOG_INFO|LOG_USER, "syslog_test")
		if err != nil {
			t.Fatalf("Dial() failed: %v", err)
		}
		err = s.Info(msg)
		if err != nil {
			t.Fatalf("log failed: %v", err)
		}
		check(t, msg, <-done)
		s.Close()
141
	}
142 143 144 145 146
}

func TestFlap(t *testing.T) {
	net := "unix"
	done := make(chan string)
147 148
	addr, sock, srvWG := startServer(net, "", done)
	defer srvWG.Wait()
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
	defer os.Remove(addr)
	defer sock.Close()

	s, err := Dial(net, addr, LOG_INFO|LOG_USER, "syslog_test")
	if err != nil {
		t.Fatalf("Dial() failed: %v", err)
	}
	msg := "Moo 2"
	err = s.Info(msg)
	if err != nil {
		t.Fatalf("log failed: %v", err)
	}
	check(t, msg, <-done)

	// restart the server
164 165
	_, sock2, srvWG2 := startServer(net, addr, done)
	defer srvWG2.Wait()
166 167 168 169 170 171 172 173 174 175 176
	defer sock2.Close()

	// and try retransmitting
	msg = "Moo 3"
	err = s.Info(msg)
	if err != nil {
		t.Fatalf("log failed: %v", err)
	}
	check(t, msg, <-done)

	s.Close()
177 178 179
}

func TestNew(t *testing.T) {
180 181 182
	if LOG_LOCAL7 != 23<<3 {
		t.Fatalf("LOG_LOCAL7 has wrong value")
	}
183 184 185
	if testing.Short() {
		// Depends on syslog daemon running, and sometimes it's not.
		t.Skip("skipping syslog test during -short")
186
	}
187 188

	s, err := New(LOG_INFO|LOG_USER, "the_tag")
189 190 191 192 193 194 195 196
	if err != nil {
		t.Fatalf("New() failed: %s", err)
	}
	// Don't send any messages.
	s.Close()
}

func TestNewLogger(t *testing.T) {
197 198
	if testing.Short() {
		t.Skip("skipping syslog test during -short")
199
	}
200
	f, err := NewLogger(LOG_USER|LOG_INFO, 0)
201
	if f == nil {
202
		t.Error(err)
203 204 205 206
	}
}

func TestDial(t *testing.T) {
207 208
	if testing.Short() {
		t.Skip("skipping syslog test during -short")
209
	}
210 211 212 213 214 215 216 217 218
	f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test")
	if f != nil {
		t.Fatalf("Should have trapped bad priority")
	}
	f, err = Dial("", "", -1, "syslog_test")
	if f != nil {
		t.Fatalf("Should have trapped bad priority")
	}
	l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test")
219 220 221 222 223 224
	if err != nil {
		t.Fatalf("Dial() failed: %s", err)
	}
	l.Close()
}

225 226
func check(t *testing.T, in, out string) {
	tmpl := fmt.Sprintf("<%d>%%s %%s syslog_test[%%d]: %s\n", LOG_USER+LOG_INFO, in)
227
	if hostname, err := os.Hostname(); err != nil {
228
		t.Error("Error retrieving hostname")
229
	} else {
230 231 232 233
		var parsedHostname, timestamp string
		var pid int
		if n, err := fmt.Sscanf(out, tmpl, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
			t.Errorf("Got %q, does not match template %q (%d %s)", out, tmpl, n, err)
234
		}
235 236 237 238
	}
}

func TestWrite(t *testing.T) {
239 240 241 242 243 244
	tests := []struct {
		pri Priority
		pre string
		msg string
		exp string
	}{
245 246
		{LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"},
		{LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"},
247
		// Write should not add \n if there already is one
248
		{LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"},
249
	}
250

251 252 253 254 255
	if hostname, err := os.Hostname(); err != nil {
		t.Fatalf("Error retrieving hostname")
	} else {
		for _, test := range tests {
			done := make(chan string)
256 257
			addr, sock, srvWG := startServer("udp", "", done)
			defer srvWG.Wait()
258 259
			defer sock.Close()
			l, err := Dial("udp", addr, test.pri, test.pre)
260
			if err != nil {
261
				t.Fatalf("syslog.Dial() failed: %v", err)
262
			}
263
			defer l.Close()
264 265
			_, err = io.WriteString(l, test.msg)
			if err != nil {
266
				t.Fatalf("WriteString() failed: %v", err)
267 268
			}
			rcvd := <-done
269
			test.exp = fmt.Sprintf("<%d>", test.pri) + test.exp
270 271
			var parsedHostname, timestamp string
			var pid int
272 273
			if n, err := fmt.Sscanf(rcvd, test.exp, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
				t.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, test.exp, n, err)
274
			}
275
		}
276 277
	}
}
278 279

func TestConcurrentWrite(t *testing.T) {
280 281
	addr, sock, srvWG := startServer("udp", "", make(chan string, 1))
	defer srvWG.Wait()
282 283 284 285 286 287 288 289 290
	defer sock.Close()
	w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?")
	if err != nil {
		t.Fatalf("syslog.Dial() failed: %v", err)
	}
	var wg sync.WaitGroup
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
291
			defer wg.Done()
292 293 294 295 296 297 298 299 300 301 302 303 304 305
			err := w.Info("test")
			if err != nil {
				t.Errorf("Info() failed: %v", err)
				return
			}
		}()
	}
	wg.Wait()
}

func TestConcurrentReconnect(t *testing.T) {
	crashy = true
	defer func() { crashy = false }()

306 307
	const N = 10
	const M = 100
308
	net := "unix"
309
	done := make(chan string, N*M)
310 311 312 313 314 315 316 317 318 319 320 321
	addr, sock, srvWG := startServer(net, "", done)
	defer os.Remove(addr)

	// count all the messages arriving
	count := make(chan int)
	go func() {
		ct := 0
		for _ = range done {
			ct++
			// we are looking for 500 out of 1000 events
			// here because lots of log messages are lost
			// in buffers (kernel and/or bufio)
322
			if ct > N*M/2 {
323 324 325 326 327 328 329
				break
			}
		}
		count <- ct
	}()

	var wg sync.WaitGroup
330 331
	wg.Add(N)
	for i := 0; i < N; i++ {
332
		go func() {
333
			defer wg.Done()
334 335 336 337
			w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag")
			if err != nil {
				t.Fatalf("syslog.Dial() failed: %v", err)
			}
338 339
			defer w.Close()
			for i := 0; i < M; i++ {
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
				err := w.Info("test")
				if err != nil {
					t.Errorf("Info() failed: %v", err)
					return
				}
			}
		}()
	}
	wg.Wait()
	sock.Close()
	srvWG.Wait()
	close(done)

	select {
	case <-count:
	case <-time.After(100 * time.Millisecond):
		t.Error("timeout in concurrent reconnect")
	}
}