Codebase list golang-github-racksec-srslog / 094dc7c
New upstream version 0.0~git20180709.a4725f0 Arnaud Rebillout 3 years ago
10 changed file(s) with 433 addition(s) and 118 deletion(s). Raw diff Collapse all Expand all
0 .cover
8989 w.Write([]byte("these are some bytes"))
9090 ```
9191
92 If you need further control over connection attempts, you can use the DialWithCustomDialer
93 function. To continue with the DialWithTLSConfig example:
94
95 ```
96 netDialer := &net.Dialer{Timeout: time.Second*5} // easy timeouts
97 realNetwork := "tcp" // real network, other vars your dail func can close over
98 dial := func(network, addr string) (net.Conn, error) {
99 // cannot use "network" here as it'll simply be "custom" which will fail
100 return tls.DialWithDialer(netDialer, realNetwork, addr, &config)
101 }
102
103 w, err := DialWithCustomDialer("custom", "192.168.0.52:514", syslog.LOG_ERR, "testtag", dial)
104 ```
105
106 Your custom dial func can set timeouts, proxy connections, and do whatever else it needs before returning a net.Conn.
107
92108 # Generating TLS Certificates
93109
94110 We've provided a script that you can use to generate a self-signed keypair:
3636 dialers := map[string]dialerFunctionWrapper{
3737 "": dialerFunctionWrapper{"unixDialer", w.unixDialer},
3838 "tcp+tls": dialerFunctionWrapper{"tlsDialer", w.tlsDialer},
39 "custom": dialerFunctionWrapper{"customDialer", w.customDialer},
3940 }
4041 dialer, ok := dialers[w.network]
4142 if !ok {
8485 }
8586 return sc, hostname, err
8687 }
88
89 // customDialer uses the custom dialer when the Writer was created
90 // giving developers total control over how connections are made and returned.
91 // Note it does not check if cdialer is nil, as it should only be referenced from getDialer.
92 func (w *Writer) customDialer() (serverConn, string, error) {
93 c, err := w.customDial(w.network, w.raddr)
94 var sc serverConn
95 hostname := w.hostname
96 if err == nil {
97 sc = &netConn{conn: c}
98 if hostname == "" {
99 hostname = c.LocalAddr().String()
100 }
101 }
102 return sc, hostname, err
103 }
22 import (
33 "crypto/tls"
44 "crypto/x509"
5 "errors"
56 "io/ioutil"
7 "net"
68 "testing"
79 )
810
4244 dialer = w.getDialer()
4345 if "basicDialer" != dialer.Name {
4446 t.Errorf("should get basicDialer, got: %v", dialer)
47 }
48
49 w.network = "custom"
50 w.customDial = func(string, string) (net.Conn, error) { return nil, nil }
51 dialer = w.getDialer()
52 if "customDialer" != dialer.Name {
53 t.Errorf("should get customDialer, got: %v", dialer)
4554 }
4655 }
4756
195204 t.Errorf("should not interfere with hostname")
196205 }
197206 }
207
208 func TestCustomDialer(t *testing.T) {
209 // A custom dialer can really be anything, so we don't test an actual connection
210 // instead we test the behavior of this code path
211
212 nwork, addr := "custom", "custom_addr_to_pass"
213 w := Writer{
214 priority: LOG_ERR,
215 tag: "tag",
216 hostname: "",
217 network: nwork,
218 raddr: addr,
219 customDial: func(n string, a string) (net.Conn, error) {
220 if n != nwork || a != addr {
221 return nil, errors.New("Unexpected network or address, expected: (" +
222 nwork + ":" + addr + ") but received (" + n + ":" + a + ")")
223 }
224 return fakeConn{addr: &fakeAddr{nwork, addr}}, nil
225 },
226 }
227
228 _, hostname, err := w.customDialer()
229
230 if err != nil {
231 t.Errorf("failed to dial: %v", err)
232 }
233
234 if hostname == "" {
235 t.Errorf("should set default hostname")
236 }
237
238 w.hostname = "my other hostname"
239
240 _, hostname, err = w.customDialer()
241
242 if err != nil {
243 t.Errorf("failed to dial: %v", err)
244 }
245
246 if hostname != "my other hostname" {
247 t.Errorf("should not interfere with hostname")
248 }
249 }
250
251 type fakeConn struct {
252 net.Conn
253 addr net.Addr
254 }
255
256 func (fc fakeConn) Close() error {
257 return nil
258 }
259
260 func (fc fakeConn) Write(p []byte) (int, error) {
261 return len(p), nil
262 }
263
264 func (fc fakeConn) LocalAddr() net.Addr {
265 return fc.addr
266 }
267
268 type fakeAddr struct {
269 nwork, addr string
270 }
271
272 func (fa *fakeAddr) Network() string {
273 return fa.nwork
274 }
275
276 func (fa *fakeAddr) String() string {
277 return fa.addr
278 }
44 "os"
55 "time"
66 )
7
8 const appNameMaxLength = 48 // limit to 48 chars as per RFC5424
79
810 // Formatter is a type of function that takes the consituent parts of a
911 // syslog message and returns a formatted string. A different Formatter is
3133 // RFC3164Formatter provides an RFC 3164 compliant message.
3234 func RFC3164Formatter(p Priority, hostname, tag, content string) string {
3335 timestamp := time.Now().Format(time.Stamp)
34 msg := fmt.Sprintf("<%d> %s %s %s[%d]: %s",
36 msg := fmt.Sprintf("<%d>%s %s %s[%d]: %s",
3537 p, timestamp, hostname, tag, os.Getpid(), content)
3638 return msg
39 }
40
41 // if string's length is greater than max, then use the last part
42 func truncateStartStr(s string, max int) string {
43 if (len(s) > max) {
44 return s[len(s) - max:]
45 }
46 return s
3747 }
3848
3949 // RFC5424Formatter provides an RFC 5424 compliant message.
4050 func RFC5424Formatter(p Priority, hostname, tag, content string) string {
4151 timestamp := time.Now().Format(time.RFC3339)
4252 pid := os.Getpid()
43 appName := os.Args[0]
44 msg := fmt.Sprintf("<%d>%d %s %s %s %d %s %s",
53 appName := truncateStartStr(os.Args[0], appNameMaxLength)
54 msg := fmt.Sprintf("<%d>%d %s %s %s %d %s - %s",
4555 p, 1, timestamp, hostname, appName, pid, tag, content)
4656 return msg
4757 }
44 "os"
55 "testing"
66 "time"
7 "strings"
78 )
89
910 func TestDefaultFormatter(t *testing.T) {
2627
2728 func TestRFC3164Formatter(t *testing.T) {
2829 out := RFC3164Formatter(LOG_ERR, "hostname", "tag", "content")
29 expected := fmt.Sprintf("<%d> %s %s %s[%d]: %s",
30 expected := fmt.Sprintf("<%d>%s %s %s[%d]: %s",
3031 LOG_ERR, time.Now().Format(time.Stamp), "hostname", "tag", os.Getpid(), "content")
3132 if out != expected {
3233 t.Errorf("expected %v got %v", expected, out)
3536
3637 func TestRFC5424Formatter(t *testing.T) {
3738 out := RFC5424Formatter(LOG_ERR, "hostname", "tag", "content")
38 expected := fmt.Sprintf("<%d>%d %s %s %s %d %s %s",
39 LOG_ERR, 1, time.Now().Format(time.RFC3339), "hostname", os.Args[0], os.Getpid(), "tag", "content")
39 expected := fmt.Sprintf("<%d>%d %s %s %s %d %s - %s",
40 LOG_ERR, 1, time.Now().Format(time.RFC3339), "hostname", truncateStartStr(os.Args[0], appNameMaxLength),
41 os.Getpid(), "tag", "content")
4042 if out != expected {
4143 t.Errorf("expected %v got %v", expected, out)
4244 }
4345 }
46
47 func TestTruncateStartStr(t *testing.T) {
48 out := truncateStartStr("abcde", 3)
49 if strings.Compare(out, "cde" ) != 0 {
50 t.Errorf("expected \"cde\" got %v", out)
51 }
52 out = truncateStartStr("abcde", 5)
53 if strings.Compare(out, "abcde" ) != 0 {
54 t.Errorf("expected \"abcde\" got %v", out)
55 }
56 }
22 import (
33 "crypto/tls"
44 "crypto/x509"
5 "errors"
56 "io/ioutil"
67 "log"
8 "net"
79 "os"
810 )
911
1315 writeString(framer Framer, formatter Formatter, p Priority, hostname, tag, s string) error
1416 close() error
1517 }
18
19 // DialFunc is the function signature to be used for a custom dialer callback
20 // with DialWithCustomDialer
21 type DialFunc func(string, string) (net.Conn, error)
1622
1723 // New establishes a new connection to the system log daemon. Each
1824 // write to the returned Writer sends a log message with the given
2834 // If network is empty, Dial will connect to the local syslog server.
2935 func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
3036 return DialWithTLSConfig(network, raddr, priority, tag, nil)
37 }
38
39 // ErrNilDialFunc is returned from DialWithCustomDialer when a nil DialFunc is passed,
40 // avoiding a nil pointer deference panic.
41 var ErrNilDialFunc = errors.New("srslog: nil DialFunc passed to DialWithCustomDialer")
42
43 // DialWithCustomDialer establishes a connection by calling customDial.
44 // Each write to the returned Writer sends a log message with the given facility, severity and tag.
45 // Network must be "custom" in order for this package to use customDial.
46 // While network and raddr will be passed to customDial, it is allowed for customDial to ignore them.
47 // If customDial is nil, this function returns ErrNilDialFunc.
48 func DialWithCustomDialer(network, raddr string, priority Priority, tag string, customDial DialFunc) (*Writer, error) {
49 if customDial == nil {
50 return nil, ErrNilDialFunc
51 }
52 return dialAllParameters(network, raddr, priority, tag, nil, customDial)
3153 }
3254
3355 // DialWithTLSCertPath establishes a secure connection to a log daemon by connecting to
5880 // DialWithTLSConfig establishes a secure connection to a log daemon by connecting to
5981 // address raddr on the specified network. It uses tlsConfig to configure the secure connection.
6082 func DialWithTLSConfig(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config) (*Writer, error) {
83 return dialAllParameters(network, raddr, priority, tag, tlsConfig, nil)
84 }
85
86 // implementation of the various functions above
87 func dialAllParameters(network, raddr string, priority Priority, tag string, tlsConfig *tls.Config, customDial DialFunc) (*Writer, error) {
6188 if err := validatePriority(priority); err != nil {
6289 return nil, err
6390 }
6895 hostname, _ := os.Hostname()
6996
7097 w := &Writer{
71 priority: priority,
72 tag: tag,
73 hostname: hostname,
74 network: network,
75 raddr: raddr,
76 tlsConfig: tlsConfig,
98 priority: priority,
99 tag: tag,
100 hostname: hostname,
101 network: network,
102 raddr: raddr,
103 tlsConfig: tlsConfig,
104 customDial: customDial,
77105 }
78106
79 w.Lock()
80 defer w.Unlock()
81
82 err := w.connect()
107 _, err := w.connect()
83108 if err != nil {
84109 return nil, err
85110 }
3939 done <- rcvd
4040 }
4141
42 var crashy = false
42 type Crashy struct {
43 sync.RWMutex
44 is bool
45 }
46
47 func (c *Crashy) IsCrashy() bool {
48 c.RLock()
49 defer c.RUnlock()
50 return c.is
51 }
52
53 func (c *Crashy) Set(is bool) {
54 c.Lock()
55 c.is = is
56 c.Unlock()
57 }
58
59 var crashy = Crashy{is: false}
4360
4461 func testableNetwork(network string) bool {
4562 switch network {
6986 defer wg.Done()
7087 c.SetReadDeadline(time.Now().Add(5 * time.Second))
7188 b := bufio.NewReader(c)
72 for ct := 1; !crashy || ct&7 != 0; ct++ {
89 for ct := 1; !crashy.IsCrashy() || ct&7 != 0; ct++ {
7390 s, err := b.ReadString('\n')
7491 if err != nil {
7592 break
452469 }
453470
454471 func TestConcurrentReconnect(t *testing.T) {
455 crashy = true
456 defer func() { crashy = false }()
472 crashy.Set(true)
473 defer func() { crashy.Set(false) }()
457474
458475 const N = 10
459476 const M = 100
77
88 // A Writer is a connection to a syslog server.
99 type Writer struct {
10 sync.Mutex // guards conn
11
1210 priority Priority
1311 tag string
1412 hostname string
1816 framer Framer
1917 formatter Formatter
2018
19 //non-nil if custom dialer set, used in getDialer
20 customDial DialFunc
21
22 mu sync.RWMutex // guards conn
2123 conn serverConn
2224 }
2325
26 // getConn provides access to the internal conn, protected by a mutex. The
27 // conn is threadsafe, so it can be used while unlocked, but we want to avoid
28 // race conditions on grabbing a reference to it.
29 func (w *Writer) getConn() serverConn {
30 w.mu.RLock()
31 conn := w.conn
32 w.mu.RUnlock()
33 return conn
34 }
35
36 // setConn updates the internal conn, protected by a mutex.
37 func (w *Writer) setConn(c serverConn) {
38 w.mu.Lock()
39 w.conn = c
40 w.mu.Unlock()
41 }
42
2443 // connect makes a connection to the syslog server.
25 // It must be called with w.mu held.
26 func (w *Writer) connect() (err error) {
27 if w.conn != nil {
44 func (w *Writer) connect() (serverConn, error) {
45 conn := w.getConn()
46 if conn != nil {
2847 // ignore err from close, it makes sense to continue anyway
29 w.conn.close()
30 w.conn = nil
31 }
32
33 var conn serverConn
48 conn.close()
49 w.setConn(nil)
50 }
51
3452 var hostname string
53 var err error
3554 dialer := w.getDialer()
3655 conn, hostname, err = dialer.Call()
3756 if err == nil {
38 w.conn = conn
57 w.setConn(conn)
3958 w.hostname = hostname
40 }
41
42 return
59
60 return conn, nil
61 } else {
62 return nil, err
63 }
4364 }
4465
4566 // SetFormatter changes the formatter function for subsequent messages.
5071 // SetFramer changes the framer function for subsequent messages.
5172 func (w *Writer) SetFramer(f Framer) {
5273 w.framer = f
74 }
75
76 // SetHostname changes the hostname for syslog messages if needed.
77 func (w *Writer) SetHostname(hostname string) {
78 w.hostname = hostname
5379 }
5480
5581 // Write sends a log message to the syslog daemon using the default priority
5884 return w.writeAndRetry(w.priority, string(b))
5985 }
6086
87 // WriteWithPriority sends a log message with a custom priority.
88 func (w *Writer) WriteWithPriority(p Priority, b []byte) (int, error) {
89 return w.writeAndRetryWithPriority(p, string(b))
90 }
91
6192 // Close closes a connection to the syslog daemon.
6293 func (w *Writer) Close() error {
63 w.Lock()
64 defer w.Unlock()
65
66 if w.conn != nil {
67 err := w.conn.close()
68 w.conn = nil
94 conn := w.getConn()
95 if conn != nil {
96 err := conn.close()
97 w.setConn(nil)
6998 return err
7099 }
71100 return nil
127156 return err
128157 }
129158
130 func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
131 pr := (w.priority & facilityMask) | (p & severityMask)
132
133 w.Lock()
134 defer w.Unlock()
135
136 if w.conn != nil {
137 if n, err := w.write(pr, s); err == nil {
159 // writeAndRetry takes a severity and the string to write. Any facility passed to
160 // it as part of the severity Priority will be ignored.
161 func (w *Writer) writeAndRetry(severity Priority, s string) (int, error) {
162 pr := (w.priority & facilityMask) | (severity & severityMask)
163
164 return w.writeAndRetryWithPriority(pr, s)
165 }
166
167 // writeAndRetryWithPriority differs from writeAndRetry in that it allows setting
168 // of both the facility and the severity.
169 func (w *Writer) writeAndRetryWithPriority(p Priority, s string) (int, error) {
170 conn := w.getConn()
171 if conn != nil {
172 if n, err := w.write(conn, p, s); err == nil {
138173 return n, err
139174 }
140175 }
141 if err := w.connect(); err != nil {
176
177 var err error
178 if conn, err = w.connect(); err != nil {
142179 return 0, err
143180 }
144 return w.write(pr, s)
181 return w.write(conn, p, s)
145182 }
146183
147184 // write generates and writes a syslog formatted string. It formats the
148185 // message based on the current Formatter and Framer.
149 func (w *Writer) write(p Priority, msg string) (int, error) {
186 func (w *Writer) write(conn serverConn, p Priority, msg string) (int, error) {
150187 // ensure it ends in a \n
151188 if !strings.HasSuffix(msg, "\n") {
152189 msg += "\n"
153190 }
154191
155 err := w.conn.writeString(w.framer, w.formatter, p, w.hostname, w.tag, msg)
192 err := conn.writeString(w.framer, w.formatter, p, w.hostname, w.tag, msg)
156193 if err != nil {
157194 return 0, err
158195 }
2323 if n != 0 {
2424 t.Errorf("should not write any bytes")
2525 }
26 }
27
28 func TestSetHostname(t *testing.T) {
29 customHostname := "kubernetesCluster"
30 expected := customHostname
31
32 done := make(chan string)
33 addr, sock, srvWG := startServer("udp", "", done)
34 defer sock.Close()
35 defer srvWG.Wait()
36
37 w := Writer{
38 priority: LOG_ERR,
39 network: "udp",
40 raddr: addr,
41 }
42
43 _, err := w.connect()
44 if err != nil {
45 t.Errorf("failed to connect: %v", err)
46 }
47 defer w.Close()
48
49 if strings.Split(w.hostname, ":")[0] != "127.0.0.1" {
50 t.Errorf("expected hostname: %s, got %s", "127.0.0.1", strings.Split(w.hostname, ":")[0])
51 }
52
53 w.SetHostname(customHostname)
54 if w.hostname != expected {
55 t.Errorf("expected hostname: %s, got %s", expected, w.hostname)
56 }
57 <-done
2658 }
2759
2860 func TestWriteFormatters(t *testing.T) {
5183 raddr: addr,
5284 }
5385
54 w.Lock()
55 err := w.connect()
86 _, err := w.connect()
5687 if err != nil {
5788 t.Errorf("failed to connect: %v", err)
58 w.Unlock()
59 }
60 w.Unlock()
89 }
6190 defer w.Close()
6291
6392 w.SetFormatter(test.f)
103132 raddr: addr,
104133 }
105134
106 w.Lock()
107 err := w.connect()
135 _, err := w.connect()
108136 if err != nil {
109137 t.Errorf("failed to connect: %v", err)
110 w.Unlock()
111 }
112 w.Unlock()
138 }
113139 defer w.Close()
114140
115141 w.SetFramer(test.f)
131157 }
132158 }
133159
160 func TestWriteWithDefaultPriority(t *testing.T) {
161 done := make(chan string)
162 addr, sock, srvWG := startServer("udp", "", done)
163 defer sock.Close()
164 defer srvWG.Wait()
165
166 w := Writer{
167 priority: LOG_ERR,
168 tag: "tag",
169 hostname: "hostname",
170 network: "udp",
171 raddr: addr,
172 }
173
174 _, err := w.connect()
175 if err != nil {
176 t.Errorf("failed to connect: %v", err)
177 }
178 defer w.Close()
179
180 var bytes int
181 bytes, err = w.Write([]byte("this is a test message"))
182 if err != nil {
183 t.Errorf("failed to write: %v", err)
184 }
185 if bytes == 0 {
186 t.Errorf("zero bytes written")
187 }
188
189 checkWithPriorityAndTag(t, LOG_ERR, "tag", "hostname", "this is a test message", <-done)
190 }
191
192 func TestWriteWithPriority(t *testing.T) {
193 done := make(chan string)
194 addr, sock, srvWG := startServer("udp", "", done)
195 defer sock.Close()
196 defer srvWG.Wait()
197
198 w := Writer{
199 priority: LOG_ERR,
200 tag: "tag",
201 hostname: "hostname",
202 network: "udp",
203 raddr: addr,
204 }
205
206 _, err := w.connect()
207 if err != nil {
208 t.Errorf("failed to connect: %v", err)
209 }
210 defer w.Close()
211
212 var bytes int
213 bytes, err = w.WriteWithPriority(LOG_DEBUG, []byte("this is a test message"))
214 if err != nil {
215 t.Errorf("failed to write: %v", err)
216 }
217 if bytes == 0 {
218 t.Errorf("zero bytes written")
219 }
220
221 checkWithPriorityAndTag(t, LOG_DEBUG, "tag", "hostname", "this is a test message", <-done)
222 }
223
224 func TestWriteWithPriorityAndFacility(t *testing.T) {
225 done := make(chan string)
226 addr, sock, srvWG := startServer("udp", "", done)
227 defer sock.Close()
228 defer srvWG.Wait()
229
230 w := Writer{
231 priority: LOG_ERR,
232 tag: "tag",
233 hostname: "hostname",
234 network: "udp",
235 raddr: addr,
236 }
237
238 _, err := w.connect()
239 if err != nil {
240 t.Errorf("failed to connect: %v", err)
241 }
242 defer w.Close()
243
244 var bytes int
245 bytes, err = w.WriteWithPriority(LOG_DEBUG|LOG_LOCAL5, []byte("this is a test message"))
246 if err != nil {
247 t.Errorf("failed to write: %v", err)
248 }
249 if bytes == 0 {
250 t.Errorf("zero bytes written")
251 }
252
253 checkWithPriorityAndTag(t, LOG_DEBUG|LOG_LOCAL5, "tag", "hostname", "this is a test message", <-done)
254 }
255
134256 func TestDebug(t *testing.T) {
135257 done := make(chan string)
136258 addr, sock, srvWG := startServer("udp", "", done)
145267 raddr: addr,
146268 }
147269
148 w.Lock()
149 err := w.connect()
150 if err != nil {
151 t.Errorf("failed to connect: %v", err)
152 w.Unlock()
153 }
154 w.Unlock()
270 _, err := w.connect()
271 if err != nil {
272 t.Errorf("failed to connect: %v", err)
273 }
155274 defer w.Close()
156275
157276 err = w.Debug("this is a test message")
176295 raddr: addr,
177296 }
178297
179 w.Lock()
180 err := w.connect()
181 if err != nil {
182 t.Errorf("failed to connect: %v", err)
183 w.Unlock()
184 }
185 w.Unlock()
298 _, err := w.connect()
299 if err != nil {
300 t.Errorf("failed to connect: %v", err)
301 }
186302 defer w.Close()
187303
188304 err = w.Info("this is a test message")
207323 raddr: addr,
208324 }
209325
210 w.Lock()
211 err := w.connect()
212 if err != nil {
213 t.Errorf("failed to connect: %v", err)
214 w.Unlock()
215 }
216 w.Unlock()
326 _, err := w.connect()
327 if err != nil {
328 t.Errorf("failed to connect: %v", err)
329 }
217330 defer w.Close()
218331
219332 err = w.Notice("this is a test message")
238351 raddr: addr,
239352 }
240353
241 w.Lock()
242 err := w.connect()
243 if err != nil {
244 t.Errorf("failed to connect: %v", err)
245 w.Unlock()
246 }
247 w.Unlock()
354 _, err := w.connect()
355 if err != nil {
356 t.Errorf("failed to connect: %v", err)
357 }
248358 defer w.Close()
249359
250360 err = w.Warning("this is a test message")
269379 raddr: addr,
270380 }
271381
272 w.Lock()
273 err := w.connect()
274 if err != nil {
275 t.Errorf("failed to connect: %v", err)
276 w.Unlock()
277 }
278 w.Unlock()
382 _, err := w.connect()
383 if err != nil {
384 t.Errorf("failed to connect: %v", err)
385 }
279386 defer w.Close()
280387
281388 err = w.Err("this is a test message")
300407 raddr: addr,
301408 }
302409
303 w.Lock()
304 err := w.connect()
305 if err != nil {
306 t.Errorf("failed to connect: %v", err)
307 w.Unlock()
308 }
309 w.Unlock()
410 _, err := w.connect()
411 if err != nil {
412 t.Errorf("failed to connect: %v", err)
413 }
310414 defer w.Close()
311415
312416 err = w.Crit("this is a test message")
331435 raddr: addr,
332436 }
333437
334 w.Lock()
335 err := w.connect()
336 if err != nil {
337 t.Errorf("failed to connect: %v", err)
338 w.Unlock()
339 }
340 w.Unlock()
438 _, err := w.connect()
439 if err != nil {
440 t.Errorf("failed to connect: %v", err)
441 }
341442 defer w.Close()
342443
343444 err = w.Alert("this is a test message")
362463 raddr: addr,
363464 }
364465
365 w.Lock()
366 err := w.connect()
367 if err != nil {
368 t.Errorf("failed to connect: %v", err)
369 w.Unlock()
370 }
371 w.Unlock()
466 _, err := w.connect()
467 if err != nil {
468 t.Errorf("failed to connect: %v", err)
469 }
372470 defer w.Close()
373471
374472 err = w.Emerg("this is a test message")