LEFT | RIGHT |
1 // Copyright 2012 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // +build !windows,!plan9 | 5 // +build !windows,!plan9 |
6 | 6 |
7 // Package syslog provides a simple interface to the system log | 7 // Package syslog provides a simple interface to the system log |
8 // service. It can send messages to the syslog daemon using UNIX | 8 // service. It can send messages to the syslog daemon using UNIX |
9 // domain sockets, UDP, or TCP connections. | 9 // domain sockets, UDP or TCP. |
| 10 // |
| 11 // Only one call to Dial is necessary. On write failures, |
| 12 // the syslog client will attempt to reconnect to the server |
| 13 // and write again. |
10 package syslog | 14 package syslog |
11 | 15 |
12 import ( | 16 import ( |
13 "errors" | 17 "errors" |
14 "fmt" | 18 "fmt" |
15 "log" | 19 "log" |
16 "net" | 20 "net" |
17 "os" | 21 "os" |
| 22 "strings" |
| 23 "sync" |
18 "time" | 24 "time" |
19 ) | 25 ) |
20 | 26 |
21 // The Priority is a combination of the syslog facility and | 27 // The Priority is a combination of the syslog facility and |
22 // severity. For example, LOG_ALERT | LOG_FTP sends an alert severity | 28 // severity. For example, LOG_ALERT | LOG_FTP sends an alert severity |
23 // message from the FTP facility. The default severity is LOG_EMERG; | 29 // message from the FTP facility. The default severity is LOG_EMERG; |
24 // the default facility is LOG_KERN. | 30 // the default facility is LOG_KERN. |
25 type Priority int | 31 type Priority int |
26 | 32 |
27 const severityMask = 0x07 | 33 const severityMask = 0x07 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 LOG_LOCAL5 | 77 LOG_LOCAL5 |
72 LOG_LOCAL6 | 78 LOG_LOCAL6 |
73 LOG_LOCAL7 | 79 LOG_LOCAL7 |
74 ) | 80 ) |
75 | 81 |
76 // A Writer is a connection to a syslog server. | 82 // A Writer is a connection to a syslog server. |
77 type Writer struct { | 83 type Writer struct { |
78 priority Priority | 84 priority Priority |
79 tag string | 85 tag string |
80 hostname string | 86 hostname string |
81 conn serverConn | |
82 network string | 87 network string |
83 raddr string | 88 raddr string |
84 } | 89 |
85 | 90 » mu sync.Mutex // guards conn |
86 type serverConn interface { | |
87 » writeString(p Priority, hostname, tag, s string) (int, error) | |
88 » close() error | |
89 } | |
90 | |
91 type netConn struct { | |
92 conn net.Conn | 91 conn net.Conn |
93 } | 92 } |
94 | 93 |
95 // New establishes a new connection to the system log daemon. Each | 94 // New establishes a new connection to the system log daemon. Each |
96 // write to the returned writer sends a log message with the given | 95 // write to the returned writer sends a log message with the given |
97 // priority and prefix. | 96 // priority and prefix. |
98 func New(priority Priority, tag string) (w *Writer, err error) { | 97 func New(priority Priority, tag string) (w *Writer, err error) { |
99 return Dial("", "", priority, tag) | 98 return Dial("", "", priority, tag) |
100 } | 99 } |
101 | 100 |
102 // Dial establishes a connection to a log daemon by connecting to | 101 // Dial establishes a connection to a log daemon by connecting to |
103 // address raddr on the network net. Each write to the returned | 102 // address raddr on the network net. Each write to the returned |
104 // writer sends a log message with the given facility, severity and | 103 // writer sends a log message with the given facility, severity and |
105 // tag. | 104 // tag. |
106 func Dial(network, raddr string, priority Priority, tag string) (w *Writer, err
error) { | 105 func Dial(network, raddr string, priority Priority, tag string) (*Writer, error)
{ |
107 if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG { | 106 if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG { |
108 return nil, errors.New("log/syslog: invalid priority") | 107 return nil, errors.New("log/syslog: invalid priority") |
109 } | 108 } |
110 | 109 |
111 if tag == "" { | 110 if tag == "" { |
112 tag = os.Args[0] | 111 tag = os.Args[0] |
113 } | 112 } |
114 | |
115 hostname, _ := os.Hostname() | 113 hostname, _ := os.Hostname() |
116 | 114 |
117 » return &Writer{priority: priority, tag: tag, hostname: hostname, conn: n
il, network: network, raddr: raddr}, nil | 115 » w := &Writer{ |
118 } | 116 » » priority: priority, |
119 | 117 » » tag: tag, |
120 func (w *Writer) reconnect() (err error) { | 118 » » hostname: hostname, |
| 119 » » network: network, |
| 120 » » raddr: raddr, |
| 121 » } |
| 122 |
| 123 » w.mu.Lock() |
| 124 » defer w.mu.Unlock() |
| 125 |
| 126 » err := w.connect() |
| 127 » if err != nil { |
| 128 » » return nil, err |
| 129 » } |
| 130 » return w, err |
| 131 } |
| 132 |
| 133 // connect makes a connection to the syslog server. |
| 134 // It must be called with w.mu held. |
| 135 func (w *Writer) connect() (err error) { |
121 if w.conn != nil { | 136 if w.conn != nil { |
122 // ignore err from close, it makes sense to continue anyway | 137 // ignore err from close, it makes sense to continue anyway |
123 » » w.conn.close() | 138 » » w.conn.Close() |
| 139 » » w.conn = nil |
124 } | 140 } |
125 | 141 |
126 if w.network == "" { | 142 if w.network == "" { |
127 w.conn, err = unixSyslog() | 143 w.conn, err = unixSyslog() |
128 if w.hostname == "" { | 144 if w.hostname == "" { |
129 w.hostname = "localhost" | 145 w.hostname = "localhost" |
130 } | 146 } |
131 } else { | 147 } else { |
132 var c net.Conn | 148 var c net.Conn |
133 c, err = net.Dial(w.network, w.raddr) | 149 c, err = net.Dial(w.network, w.raddr) |
134 if err == nil { | 150 if err == nil { |
135 » » » w.conn = netConn{c} | 151 » » » w.conn = c |
136 if w.hostname == "" { | 152 if w.hostname == "" { |
137 w.hostname = c.LocalAddr().String() | 153 w.hostname = c.LocalAddr().String() |
138 } | 154 } |
139 } | 155 } |
140 } | 156 } |
141 return | 157 return |
142 } | 158 } |
143 | 159 |
144 func shouldRetry(err error) bool { | |
145 if oe, ok := err.(*net.OpError); ok { | |
146 return oe.Refused() | |
147 } | |
148 return false | |
149 } | |
150 | |
151 // Write sends a log message to the syslog daemon. | 160 // Write sends a log message to the syslog daemon. |
152 func (w *Writer) Write(b []byte) (int, error) { | 161 func (w *Writer) Write(b []byte) (int, error) { |
153 » return w.writeString(w.priority, string(b)) | 162 » return w.writeAndRetry(w.priority, string(b)) |
154 } | 163 } |
155 | 164 |
| 165 // Close closes a connection to the syslog daemon. |
156 func (w *Writer) Close() error { | 166 func (w *Writer) Close() error { |
| 167 w.mu.Lock() |
| 168 defer w.mu.Unlock() |
| 169 |
157 if w.conn != nil { | 170 if w.conn != nil { |
158 » » return w.conn.close() | 171 » » err := w.conn.Close() |
| 172 » » w.conn = nil |
| 173 » » return err |
159 } | 174 } |
160 return nil | 175 return nil |
161 } | 176 } |
162 | 177 |
163 // Emerg logs a message with severity LOG_EMERG, ignoring the severity | 178 // Emerg logs a message with severity LOG_EMERG, ignoring the severity |
164 // passed to New. | 179 // passed to New. |
165 func (w *Writer) Emerg(m string) (err error) { | 180 func (w *Writer) Emerg(m string) (err error) { |
166 » _, err = w.writeString(LOG_EMERG, m) | 181 » _, err = w.writeAndRetry(LOG_EMERG, m) |
167 return err | 182 return err |
168 } | 183 } |
169 | 184 |
170 // Alert logs a message with severity LOG_ALERT, ignoring the severity | 185 // Alert logs a message with severity LOG_ALERT, ignoring the severity |
171 // passed to New. | 186 // passed to New. |
172 func (w *Writer) Alert(m string) (err error) { | 187 func (w *Writer) Alert(m string) (err error) { |
173 » _, err = w.writeString(LOG_ALERT, m) | 188 » _, err = w.writeAndRetry(LOG_ALERT, m) |
174 return err | 189 return err |
175 } | 190 } |
176 | 191 |
177 // Crit logs a message with severity LOG_CRIT, ignoring the severity | 192 // Crit logs a message with severity LOG_CRIT, ignoring the severity |
178 // passed to New. | 193 // passed to New. |
179 func (w *Writer) Crit(m string) (err error) { | 194 func (w *Writer) Crit(m string) (err error) { |
180 » _, err = w.writeString(LOG_CRIT, m) | 195 » _, err = w.writeAndRetry(LOG_CRIT, m) |
181 return err | 196 return err |
182 } | 197 } |
183 | 198 |
184 // Err logs a message with severity LOG_ERR, ignoring the severity | 199 // Err logs a message with severity LOG_ERR, ignoring the severity |
185 // passed to New. | 200 // passed to New. |
186 func (w *Writer) Err(m string) (err error) { | 201 func (w *Writer) Err(m string) (err error) { |
187 » _, err = w.writeString(LOG_ERR, m) | 202 » _, err = w.writeAndRetry(LOG_ERR, m) |
188 return err | 203 return err |
189 } | 204 } |
190 | 205 |
191 // Wanring logs a message with severity LOG_WARNING, ignoring the | 206 // Wanring logs a message with severity LOG_WARNING, ignoring the |
192 // severity passed to New. | 207 // severity passed to New. |
193 func (w *Writer) Warning(m string) (err error) { | 208 func (w *Writer) Warning(m string) (err error) { |
194 » _, err = w.writeString(LOG_WARNING, m) | 209 » _, err = w.writeAndRetry(LOG_WARNING, m) |
195 return err | 210 return err |
196 } | 211 } |
197 | 212 |
198 // Notice logs a message with severity LOG_NOTICE, ignoring the | 213 // Notice logs a message with severity LOG_NOTICE, ignoring the |
199 // severity passed to New. | 214 // severity passed to New. |
200 func (w *Writer) Notice(m string) (err error) { | 215 func (w *Writer) Notice(m string) (err error) { |
201 » _, err = w.writeString(LOG_NOTICE, m) | 216 » _, err = w.writeAndRetry(LOG_NOTICE, m) |
202 return err | 217 return err |
203 } | 218 } |
204 | 219 |
205 // Info logs a message with severity LOG_INFO, ignoring the severity | 220 // Info logs a message with severity LOG_INFO, ignoring the severity |
206 // passed to New. | 221 // passed to New. |
207 func (w *Writer) Info(m string) (err error) { | 222 func (w *Writer) Info(m string) (err error) { |
208 » _, err = w.writeString(LOG_INFO, m) | 223 » _, err = w.writeAndRetry(LOG_INFO, m) |
209 return err | 224 return err |
210 } | 225 } |
211 | 226 |
212 // Debug logs a message with severity LOG_DEBUG, ignoring the severity | 227 // Debug logs a message with severity LOG_DEBUG, ignoring the severity |
213 // passed to New. | 228 // passed to New. |
214 func (w *Writer) Debug(m string) (err error) { | 229 func (w *Writer) Debug(m string) (err error) { |
215 » _, err = w.writeString(LOG_DEBUG, m) | 230 » _, err = w.writeAndRetry(LOG_DEBUG, m) |
216 » return err | 231 » return err |
217 } | 232 } |
218 | 233 |
219 func (w *Writer) writeString(p Priority, s string) (int, error) { | 234 func (w *Writer) writeAndRetry(p Priority, s string) (int, error) { |
220 » if w.conn == nil { | 235 » pr := (w.priority & facilityMask) | (p & severityMask) |
221 » » err := w.reconnect() | 236 |
222 » » if err != nil { | 237 » w.mu.Lock() |
223 » » » return 0, err | 238 » defer w.mu.Unlock() |
| 239 |
| 240 » if w.conn != nil { |
| 241 » » if n, err := w.write(pr, s); err == nil { |
| 242 » » » return n, err |
224 } | 243 } |
225 } | 244 } |
226 » n, err := w.conn.writeString((w.priority&facilityMask)|(p&severityMask), | 245 » if err := w.connect(); err != nil { |
227 » » w.hostname, w.tag, s) | |
228 » if err == nil { | |
229 » » // normal exit | |
230 » » return n, err | |
231 » } | |
232 | |
233 » // error recovery: maybe try to reopen connection | |
234 | |
235 » if !shouldRetry(err) { | |
236 » » return n, err | |
237 » } | |
238 | |
239 » err = w.reconnect() | |
240 » if err != nil { | |
241 return 0, err | 246 return 0, err |
242 } | 247 } |
243 » return w.conn.writeString((w.priority&facilityMask)|(p&severityMask), | 248 » return w.write(pr, s) |
244 » » w.hostname, w.tag, s) | 249 } |
245 } | 250 |
246 | 251 // write generates and writes a syslog formatted string. The |
247 // writeString: generates and writes a syslog formatted string. The | 252 // format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG |
248 // format is as follows: <PRI>1 TIMESTAMP HOSTNAME TAG[PID]: MSG | 253 func (w *Writer) write(p Priority, msg string) (int, error) { |
249 func (n netConn) writeString(p Priority, hostname, tag, msg string) (int, error)
{ | 254 » // ensure it ends in a \n |
250 nl := "" | 255 nl := "" |
251 » if len(msg) == 0 || msg[len(msg)-1] != '\n' { | 256 » if !strings.HasSuffix(msg, "\n") { |
252 nl = "\n" | 257 nl = "\n" |
253 } | 258 } |
| 259 |
254 timestamp := time.Now().Format(time.RFC3339) | 260 timestamp := time.Now().Format(time.RFC3339) |
255 » if _, err := fmt.Fprintf(n.conn, "<%d>1 %s %s %s[%d]: %s%s", p, timestam
p, hostname, | 261 » fmt.Fprintf(w.conn, "<%d>%s %s %s[%d]: %s%s", |
256 » » tag, os.Getpid(), msg, nl); err != nil { | 262 » » p, timestamp, w.hostname, |
257 » » return 0, err | 263 » » w.tag, os.Getpid(), msg, nl) |
258 » } | |
259 return len(msg), nil | 264 return len(msg), nil |
260 } | |
261 | |
262 func (n netConn) close() error { | |
263 return n.conn.Close() | |
264 } | 265 } |
265 | 266 |
266 // NewLogger creates a log.Logger whose output is written to | 267 // NewLogger creates a log.Logger whose output is written to |
267 // the system log service with the specified priority. The logFlag | 268 // the system log service with the specified priority. The logFlag |
268 // argument is the flag set passed through to log.New to create | 269 // argument is the flag set passed through to log.New to create |
269 // the Logger. | 270 // the Logger. |
270 func NewLogger(p Priority, logFlag int) (*log.Logger, error) { | 271 func NewLogger(p Priority, logFlag int) (*log.Logger, error) { |
271 s, err := New(p, "") | 272 s, err := New(p, "") |
272 if err != nil { | 273 if err != nil { |
273 return nil, err | 274 return nil, err |
274 } | 275 } |
275 return log.New(s, "", logFlag), nil | 276 return log.New(s, "", logFlag), nil |
276 } | 277 } |
LEFT | RIGHT |