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 "errors" 11 "fmt" 12 "log" 13 "net" 14 "os" 15 "strings" 16 "sync" 17 "time" 18 ) 19 20 // The Priority is a combination of the syslog facility and 21 // severity. For example, LOG_ALERT | LOG_FTP sends an alert severity 22 // message from the FTP facility. The default severity is LOG_EMERG; 23 // the default facility is LOG_KERN. 24 type Priority int 25 26 const severityMask = 0x07 27 const facilityMask = 0xf8 28 29 const ( 30 // Severity. 31 32 // From /usr/include/sys/syslog.h. 33 // These are the same on Linux, BSD, and OS X. 34 LOG_EMERG Priority = iota 35 LOG_ALERT 36 LOG_CRIT 37 LOG_ERR 38 LOG_WARNING 39 LOG_NOTICE 40 LOG_INFO 41 LOG_DEBUG 42 ) 43 44 const ( 45 // Facility. 46 47 // From /usr/include/sys/syslog.h. 48 // These are the same up to LOG_FTP on Linux, BSD, and OS X. 49 LOG_KERN Priority = iota << 3 50 LOG_USER 51 LOG_MAIL 52 LOG_DAEMON 53 LOG_AUTH 54 LOG_SYSLOG 55 LOG_LPR 56 LOG_NEWS 57 LOG_UUCP 58 LOG_CRON 59 LOG_AUTHPRIV 60 LOG_FTP 61 _ // unused 62 _ // unused 63 _ // unused 64 _ // unused 65 LOG_LOCAL0 66 LOG_LOCAL1 67 LOG_LOCAL2 68 LOG_LOCAL3 69 LOG_LOCAL4 70 LOG_LOCAL5 71 LOG_LOCAL6 72 LOG_LOCAL7 73 ) 74 75 // A Writer is a connection to a syslog server. 76 type Writer struct { 77 priority Priority 78 tag string 79 hostname string 80 network string 81 raddr string 82 83 mu sync.Mutex // guards conn 84 conn serverConn 85 } 86 87 // This interface and the separate syslog_unix.go file exist for 88 // Solaris support as implemented by gccgo. On Solaris you cannot 89 // simply open a TCP connection to the syslog daemon. The gccgo 90 // sources have a syslog_solaris.go file that implements unixSyslog to 91 // return a type that satisfies this interface and simply calls the C 92 // library syslog function. 93 type serverConn interface { 94 writeString(p Priority, hostname, tag, s, nl string) error 95 close() error 96 } 97 98 type netConn struct { 99 local bool 100 conn net.Conn 101 } 102 103 // New establishes a new connection to the system log daemon. Each 104 // write to the returned writer sends a log message with the given 105 // priority and prefix. 106 func New(priority Priority, tag string) (*Writer, error) { 107 return Dial("", "", priority, tag) 108 } 109 110 // Dial establishes a connection to a log daemon by connecting to 111 // address raddr on the specified network. Each write to the returned 112 // writer sends a log message with the given facility, severity and 113 // tag. 114 // If network is empty, Dial will connect to the local syslog server. 115 // Otherwise, see the documentation for net.Dial for valid values 116 // of network and raddr. 117 func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) { 118 if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG { 119 return nil, errors.New("log/syslog: invalid priority") 120 } 121 122 if tag == "" { 123 tag = os.Args[0] 124 } 125 hostname, _ := os.Hostname() 126 127 w := &Writer{ 128 priority: priority, 129 tag: tag, 130 hostname: hostname, 131 network: network, 132 raddr: raddr, 133 } 134 135 w.mu.Lock() 136 defer w.mu.Unlock() 137 138 err := w.connect() 139 if err != nil { 140 return nil, err 141 } 142 return w, err 143 } 144 145 // connect makes a connection to the syslog server. 146 // It must be called with w.mu held. 147 func (w *Writer) connect() (err error) { 148 if w.conn != nil { 149 // ignore err from close, it makes sense to continue anyway 150 w.conn.close() 151 w.conn = nil 152 } 153 154 if w.network == "" { 155 w.conn, err = unixSyslog() 156 if w.hostname == "" { 157 w.hostname = "localhost" 158 } 159 } else { 160 var c net.Conn 161 c, err = net.Dial(w.network, w.raddr) 162 if err == nil { 163 w.conn = &netConn{conn: c} 164 if w.hostname == "" { 165 w.hostname = c.LocalAddr().String() 166 } 167 } 168 } 169 return 170 } 171 172 // Write sends a log message to the syslog daemon. 173 func (w *Writer) Write(b []byte) (int, error) { 174 return w.writeAndRetry(w.priority, string(b)) 175 } 176 177 // Close closes a connection to the syslog daemon. 178 func (w *Writer) Close() error { 179 w.mu.Lock() 180 defer w.mu.Unlock() 181 182 if w.conn != nil { 183 err := w.conn.close() 184 w.conn = nil 185 return err 186 } 187 return nil 188 } 189 190 // Emerg logs a message with severity LOG_EMERG, ignoring the severity 191 // passed to New. 192 func (w *Writer) Emerg(m string) error { 193 _, err := w.writeAndRetry(LOG_EMERG, m) 194 return err 195 } 196 197 // Alert logs a message with severity LOG_ALERT, ignoring the severity 198 // passed to New. 199 func (w *Writer) Alert(m string) error { 200 _, err := w.writeAndRetry(LOG_ALERT, m) 201 return err 202 } 203 204 // Crit logs a message with severity LOG_CRIT, ignoring the severity 205 // passed to New. 206 func (w *Writer) Crit(m string) error { 207 _, err := w.writeAndRetry(LOG_CRIT, m) 208 return err 209 } 210 211 // Err logs a message with severity LOG_ERR, ignoring the severity 212 // passed to New. 213 func (w *Writer) Err(m string) error { 214 _, err := w.writeAndRetry(LOG_ERR, m) 215 return err 216 } 217 218 // Warning logs a message with severity LOG_WARNING, ignoring the 219 // severity passed to New. 220 func (w *Writer) Warning(m string) error { 221 _, err := w.writeAndRetry(LOG_WARNING, m) 222 return err 223 } 224 225 // Notice logs a message with severity LOG_NOTICE, ignoring the 226 // severity passed to New. 227 func (w *Writer) Notice(m string) error { 228 _, err := w.writeAndRetry(LOG_NOTICE, m) 229 return err 230 } 231 232 // Info logs a message with severity LOG_INFO, ignoring the severity 233 // passed to New. 234 func (w *Writer) Info(m string) error { 235 _, err := w.writeAndRetry(LOG_INFO, m) 236 return err 237 } 238 239 // Debug logs a message with severity LOG_DEBUG, ignoring the severity 240 // passed to New. 241 func (w *Writer) Debug(m string) error { 242 _, err := w.writeAndRetry(LOG_DEBUG, m) 243 return err 244 } 245 246 func (w *Writer) writeAndRetry(p Priority, s string) (int, error) { 247 pr := (w.priority & facilityMask) | (p & severityMask) 248 249 w.mu.Lock() 250 defer w.mu.Unlock() 251 252 if w.conn != nil { 253 if n, err := w.write(pr, s); err == nil { 254 return n, err 255 } 256 } 257 if err := w.connect(); err != nil { 258 return 0, err 259 } 260 return w.write(pr, s) 261 } 262 263 // write generates and writes a syslog formatted string. The 264 // format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG 265 func (w *Writer) write(p Priority, msg string) (int, error) { 266 // ensure it ends in a \n 267 nl := "" 268 if !strings.HasSuffix(msg, "\n") { 269 nl = "\n" 270 } 271 272 err := w.conn.writeString(p, w.hostname, w.tag, msg, nl) 273 if err != nil { 274 return 0, err 275 } 276 // Note: return the length of the input, not the number of 277 // bytes printed by Fprintf, because this must behave like 278 // an io.Writer. 279 return len(msg), nil 280 } 281 282 func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error { 283 if n.local { 284 // Compared to the network form below, the changes are: 285 // 1. Use time.Stamp instead of time.RFC3339. 286 // 2. Drop the hostname field from the Fprintf. 287 timestamp := time.Now().Format(time.Stamp) 288 _, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s", 289 p, timestamp, 290 tag, os.Getpid(), msg, nl) 291 return err 292 } 293 timestamp := time.Now().Format(time.RFC3339) 294 _, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s", 295 p, timestamp, hostname, 296 tag, os.Getpid(), msg, nl) 297 return err 298 } 299 300 func (n *netConn) close() error { 301 return n.conn.Close() 302 } 303 304 // NewLogger creates a log.Logger whose output is written to 305 // the system log service with the specified priority. The logFlag 306 // argument is the flag set passed through to log.New to create 307 // the Logger. 308 func NewLogger(p Priority, logFlag int) (*log.Logger, error) { 309 s, err := New(p, "") 310 if err != nil { 311 return nil, err 312 } 313 return log.New(s, "", logFlag), nil 314 } 315