Home | History | Annotate | Download | only in syslog
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build !windows,!nacl,!plan9
      6 
      7 package syslog
      8 
      9 import (
     10 	"bufio"
     11 	"fmt"
     12 	"io"
     13 	"io/ioutil"
     14 	"log"
     15 	"net"
     16 	"os"
     17 	"runtime"
     18 	"sync"
     19 	"testing"
     20 	"time"
     21 )
     22 
     23 func runPktSyslog(c net.PacketConn, done chan<- string) {
     24 	var buf [4096]byte
     25 	var rcvd string
     26 	ct := 0
     27 	for {
     28 		var n int
     29 		var err error
     30 
     31 		c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
     32 		n, _, err = c.ReadFrom(buf[:])
     33 		rcvd += string(buf[:n])
     34 		if err != nil {
     35 			if oe, ok := err.(*net.OpError); ok {
     36 				if ct < 3 && oe.Temporary() {
     37 					ct++
     38 					continue
     39 				}
     40 			}
     41 			break
     42 		}
     43 	}
     44 	c.Close()
     45 	done <- rcvd
     46 }
     47 
     48 var crashy = false
     49 
     50 func runStreamSyslog(l net.Listener, done chan<- string, wg *sync.WaitGroup) {
     51 	for {
     52 		var c net.Conn
     53 		var err error
     54 		if c, err = l.Accept(); err != nil {
     55 			return
     56 		}
     57 		wg.Add(1)
     58 		go func(c net.Conn) {
     59 			defer wg.Done()
     60 			c.SetReadDeadline(time.Now().Add(5 * time.Second))
     61 			b := bufio.NewReader(c)
     62 			for ct := 1; !crashy || ct&7 != 0; ct++ {
     63 				s, err := b.ReadString('\n')
     64 				if err != nil {
     65 					break
     66 				}
     67 				done <- s
     68 			}
     69 			c.Close()
     70 		}(c)
     71 	}
     72 }
     73 
     74 func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) {
     75 	if n == "udp" || n == "tcp" {
     76 		la = "127.0.0.1:0"
     77 	} else {
     78 		// unix and unixgram: choose an address if none given
     79 		if la == "" {
     80 			// use ioutil.TempFile to get a name that is unique
     81 			f, err := ioutil.TempFile("", "syslogtest")
     82 			if err != nil {
     83 				log.Fatal("TempFile: ", err)
     84 			}
     85 			f.Close()
     86 			la = f.Name()
     87 		}
     88 		os.Remove(la)
     89 	}
     90 
     91 	wg = new(sync.WaitGroup)
     92 	if n == "udp" || n == "unixgram" {
     93 		l, e := net.ListenPacket(n, la)
     94 		if e != nil {
     95 			log.Fatalf("startServer failed: %v", e)
     96 		}
     97 		addr = l.LocalAddr().String()
     98 		sock = l
     99 		wg.Add(1)
    100 		go func() {
    101 			defer wg.Done()
    102 			runPktSyslog(l, done)
    103 		}()
    104 	} else {
    105 		l, e := net.Listen(n, la)
    106 		if e != nil {
    107 			log.Fatalf("startServer failed: %v", e)
    108 		}
    109 		addr = l.Addr().String()
    110 		sock = l
    111 		wg.Add(1)
    112 		go func() {
    113 			defer wg.Done()
    114 			runStreamSyslog(l, done, wg)
    115 		}()
    116 	}
    117 	return
    118 }
    119 
    120 func TestWithSimulated(t *testing.T) {
    121 	msg := "Test 123"
    122 	transport := []string{"unix", "unixgram", "udp", "tcp"}
    123 
    124 	if runtime.GOOS == "darwin" {
    125 		switch runtime.GOARCH {
    126 		case "arm", "arm64":
    127 			transport = []string{"udp", "tcp"}
    128 		}
    129 	}
    130 
    131 	for _, tr := range transport {
    132 		done := make(chan string)
    133 		addr, sock, srvWG := startServer(tr, "", done)
    134 		defer srvWG.Wait()
    135 		defer sock.Close()
    136 		if tr == "unix" || tr == "unixgram" {
    137 			defer os.Remove(addr)
    138 		}
    139 		s, err := Dial(tr, addr, LOG_INFO|LOG_USER, "syslog_test")
    140 		if err != nil {
    141 			t.Fatalf("Dial() failed: %v", err)
    142 		}
    143 		err = s.Info(msg)
    144 		if err != nil {
    145 			t.Fatalf("log failed: %v", err)
    146 		}
    147 		check(t, msg, <-done)
    148 		s.Close()
    149 	}
    150 }
    151 
    152 func TestFlap(t *testing.T) {
    153 	if runtime.GOOS == "darwin" {
    154 		switch runtime.GOARCH {
    155 		case "arm", "arm64":
    156 			t.Skipf("skipping on %s/%s", runtime.GOOS, runtime.GOARCH)
    157 		}
    158 	}
    159 
    160 	net := "unix"
    161 	done := make(chan string)
    162 	addr, sock, srvWG := startServer(net, "", done)
    163 	defer srvWG.Wait()
    164 	defer os.Remove(addr)
    165 	defer sock.Close()
    166 
    167 	s, err := Dial(net, addr, LOG_INFO|LOG_USER, "syslog_test")
    168 	if err != nil {
    169 		t.Fatalf("Dial() failed: %v", err)
    170 	}
    171 	msg := "Moo 2"
    172 	err = s.Info(msg)
    173 	if err != nil {
    174 		t.Fatalf("log failed: %v", err)
    175 	}
    176 	check(t, msg, <-done)
    177 
    178 	// restart the server
    179 	_, sock2, srvWG2 := startServer(net, addr, done)
    180 	defer srvWG2.Wait()
    181 	defer sock2.Close()
    182 
    183 	// and try retransmitting
    184 	msg = "Moo 3"
    185 	err = s.Info(msg)
    186 	if err != nil {
    187 		t.Fatalf("log failed: %v", err)
    188 	}
    189 	check(t, msg, <-done)
    190 
    191 	s.Close()
    192 }
    193 
    194 func TestNew(t *testing.T) {
    195 	if LOG_LOCAL7 != 23<<3 {
    196 		t.Fatalf("LOG_LOCAL7 has wrong value")
    197 	}
    198 	if testing.Short() {
    199 		// Depends on syslog daemon running, and sometimes it's not.
    200 		t.Skip("skipping syslog test during -short")
    201 	}
    202 
    203 	s, err := New(LOG_INFO|LOG_USER, "the_tag")
    204 	if err != nil {
    205 		t.Fatalf("New() failed: %s", err)
    206 	}
    207 	// Don't send any messages.
    208 	s.Close()
    209 }
    210 
    211 func TestNewLogger(t *testing.T) {
    212 	if testing.Short() {
    213 		t.Skip("skipping syslog test during -short")
    214 	}
    215 	f, err := NewLogger(LOG_USER|LOG_INFO, 0)
    216 	if f == nil {
    217 		t.Error(err)
    218 	}
    219 }
    220 
    221 func TestDial(t *testing.T) {
    222 	if testing.Short() {
    223 		t.Skip("skipping syslog test during -short")
    224 	}
    225 	f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test")
    226 	if f != nil {
    227 		t.Fatalf("Should have trapped bad priority")
    228 	}
    229 	f, err = Dial("", "", -1, "syslog_test")
    230 	if f != nil {
    231 		t.Fatalf("Should have trapped bad priority")
    232 	}
    233 	l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test")
    234 	if err != nil {
    235 		t.Fatalf("Dial() failed: %s", err)
    236 	}
    237 	l.Close()
    238 }
    239 
    240 func check(t *testing.T, in, out string) {
    241 	tmpl := fmt.Sprintf("<%d>%%s %%s syslog_test[%%d]: %s\n", LOG_USER+LOG_INFO, in)
    242 	if hostname, err := os.Hostname(); err != nil {
    243 		t.Error("Error retrieving hostname")
    244 	} else {
    245 		var parsedHostname, timestamp string
    246 		var pid int
    247 		if n, err := fmt.Sscanf(out, tmpl, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
    248 			t.Errorf("Got %q, does not match template %q (%d %s)", out, tmpl, n, err)
    249 		}
    250 	}
    251 }
    252 
    253 func TestWrite(t *testing.T) {
    254 	tests := []struct {
    255 		pri Priority
    256 		pre string
    257 		msg string
    258 		exp string
    259 	}{
    260 		{LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"},
    261 		{LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"},
    262 		// Write should not add \n if there already is one
    263 		{LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"},
    264 	}
    265 
    266 	if hostname, err := os.Hostname(); err != nil {
    267 		t.Fatalf("Error retrieving hostname")
    268 	} else {
    269 		for _, test := range tests {
    270 			done := make(chan string)
    271 			addr, sock, srvWG := startServer("udp", "", done)
    272 			defer srvWG.Wait()
    273 			defer sock.Close()
    274 			l, err := Dial("udp", addr, test.pri, test.pre)
    275 			if err != nil {
    276 				t.Fatalf("syslog.Dial() failed: %v", err)
    277 			}
    278 			defer l.Close()
    279 			_, err = io.WriteString(l, test.msg)
    280 			if err != nil {
    281 				t.Fatalf("WriteString() failed: %v", err)
    282 			}
    283 			rcvd := <-done
    284 			test.exp = fmt.Sprintf("<%d>", test.pri) + test.exp
    285 			var parsedHostname, timestamp string
    286 			var pid int
    287 			if n, err := fmt.Sscanf(rcvd, test.exp, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
    288 				t.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, test.exp, n, err)
    289 			}
    290 		}
    291 	}
    292 }
    293 
    294 func TestConcurrentWrite(t *testing.T) {
    295 	addr, sock, srvWG := startServer("udp", "", make(chan string, 1))
    296 	defer srvWG.Wait()
    297 	defer sock.Close()
    298 	w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?")
    299 	if err != nil {
    300 		t.Fatalf("syslog.Dial() failed: %v", err)
    301 	}
    302 	var wg sync.WaitGroup
    303 	for i := 0; i < 10; i++ {
    304 		wg.Add(1)
    305 		go func() {
    306 			defer wg.Done()
    307 			err := w.Info("test")
    308 			if err != nil {
    309 				t.Errorf("Info() failed: %v", err)
    310 				return
    311 			}
    312 		}()
    313 	}
    314 	wg.Wait()
    315 }
    316 
    317 func TestConcurrentReconnect(t *testing.T) {
    318 	crashy = true
    319 	defer func() { crashy = false }()
    320 
    321 	const N = 10
    322 	const M = 100
    323 	net := "unix"
    324 	if runtime.GOOS == "darwin" {
    325 		switch runtime.GOARCH {
    326 		case "arm", "arm64":
    327 			net = "tcp"
    328 		}
    329 	}
    330 	done := make(chan string, N*M)
    331 	addr, sock, srvWG := startServer(net, "", done)
    332 	if net == "unix" {
    333 		defer os.Remove(addr)
    334 	}
    335 
    336 	// count all the messages arriving
    337 	count := make(chan int)
    338 	go func() {
    339 		ct := 0
    340 		for range done {
    341 			ct++
    342 			// we are looking for 500 out of 1000 events
    343 			// here because lots of log messages are lost
    344 			// in buffers (kernel and/or bufio)
    345 			if ct > N*M/2 {
    346 				break
    347 			}
    348 		}
    349 		count <- ct
    350 	}()
    351 
    352 	var wg sync.WaitGroup
    353 	wg.Add(N)
    354 	for i := 0; i < N; i++ {
    355 		go func() {
    356 			defer wg.Done()
    357 			w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag")
    358 			if err != nil {
    359 				t.Fatalf("syslog.Dial() failed: %v", err)
    360 			}
    361 			defer w.Close()
    362 			for i := 0; i < M; i++ {
    363 				err := w.Info("test")
    364 				if err != nil {
    365 					t.Errorf("Info() failed: %v", err)
    366 					return
    367 				}
    368 			}
    369 		}()
    370 	}
    371 	wg.Wait()
    372 	sock.Close()
    373 	srvWG.Wait()
    374 	close(done)
    375 
    376 	select {
    377 	case <-count:
    378 	case <-time.After(100 * time.Millisecond):
    379 		t.Error("timeout in concurrent reconnect")
    380 	}
    381 }
    382