github.com/mattn/go@v0.0.0-20171011075504-07f7db3ea99f/src/log/syslog/syslog.go (about) 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 (a combination of the syslog facility and severity) and 106 // prefix tag. If tag is empty, the os.Args[0] is used. 107 func New(priority Priority, tag string) (*Writer, error) { 108 return Dial("", "", priority, tag) 109 } 110 111 // Dial establishes a connection to a log daemon by connecting to 112 // address raddr on the specified network. Each write to the returned 113 // writer sends a log message with the facility and severity 114 // (from priority) and tag. If tag is empty, the os.Args[0] is used. 115 // If network is empty, Dial will connect to the local syslog server. 116 // Otherwise, see the documentation for net.Dial for valid values 117 // of network and raddr. 118 func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) { 119 if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG { 120 return nil, errors.New("log/syslog: invalid priority") 121 } 122 123 if tag == "" { 124 tag = os.Args[0] 125 } 126 hostname, _ := os.Hostname() 127 128 w := &Writer{ 129 priority: priority, 130 tag: tag, 131 hostname: hostname, 132 network: network, 133 raddr: raddr, 134 } 135 136 w.mu.Lock() 137 defer w.mu.Unlock() 138 139 err := w.connect() 140 if err != nil { 141 return nil, err 142 } 143 return w, err 144 } 145 146 // connect makes a connection to the syslog server. 147 // It must be called with w.mu held. 148 func (w *Writer) connect() (err error) { 149 if w.conn != nil { 150 // ignore err from close, it makes sense to continue anyway 151 w.conn.close() 152 w.conn = nil 153 } 154 155 if w.network == "" { 156 w.conn, err = unixSyslog() 157 if w.hostname == "" { 158 w.hostname = "localhost" 159 } 160 } else { 161 var c net.Conn 162 c, err = net.Dial(w.network, w.raddr) 163 if err == nil { 164 w.conn = &netConn{conn: c} 165 if w.hostname == "" { 166 w.hostname = c.LocalAddr().String() 167 } 168 } 169 } 170 return 171 } 172 173 // Write sends a log message to the syslog daemon. 174 func (w *Writer) Write(b []byte) (int, error) { 175 return w.writeAndRetry(w.priority, string(b)) 176 } 177 178 // Close closes a connection to the syslog daemon. 179 func (w *Writer) Close() error { 180 w.mu.Lock() 181 defer w.mu.Unlock() 182 183 if w.conn != nil { 184 err := w.conn.close() 185 w.conn = nil 186 return err 187 } 188 return nil 189 } 190 191 // Emerg logs a message with severity LOG_EMERG, ignoring the severity 192 // passed to New. 193 func (w *Writer) Emerg(m string) error { 194 _, err := w.writeAndRetry(LOG_EMERG, m) 195 return err 196 } 197 198 // Alert logs a message with severity LOG_ALERT, ignoring the severity 199 // passed to New. 200 func (w *Writer) Alert(m string) error { 201 _, err := w.writeAndRetry(LOG_ALERT, m) 202 return err 203 } 204 205 // Crit logs a message with severity LOG_CRIT, ignoring the severity 206 // passed to New. 207 func (w *Writer) Crit(m string) error { 208 _, err := w.writeAndRetry(LOG_CRIT, m) 209 return err 210 } 211 212 // Err logs a message with severity LOG_ERR, ignoring the severity 213 // passed to New. 214 func (w *Writer) Err(m string) error { 215 _, err := w.writeAndRetry(LOG_ERR, m) 216 return err 217 } 218 219 // Warning logs a message with severity LOG_WARNING, ignoring the 220 // severity passed to New. 221 func (w *Writer) Warning(m string) error { 222 _, err := w.writeAndRetry(LOG_WARNING, m) 223 return err 224 } 225 226 // Notice logs a message with severity LOG_NOTICE, ignoring the 227 // severity passed to New. 228 func (w *Writer) Notice(m string) error { 229 _, err := w.writeAndRetry(LOG_NOTICE, m) 230 return err 231 } 232 233 // Info logs a message with severity LOG_INFO, ignoring the severity 234 // passed to New. 235 func (w *Writer) Info(m string) error { 236 _, err := w.writeAndRetry(LOG_INFO, m) 237 return err 238 } 239 240 // Debug logs a message with severity LOG_DEBUG, ignoring the severity 241 // passed to New. 242 func (w *Writer) Debug(m string) error { 243 _, err := w.writeAndRetry(LOG_DEBUG, m) 244 return err 245 } 246 247 func (w *Writer) writeAndRetry(p Priority, s string) (int, error) { 248 pr := (w.priority & facilityMask) | (p & severityMask) 249 250 w.mu.Lock() 251 defer w.mu.Unlock() 252 253 if w.conn != nil { 254 if n, err := w.write(pr, s); err == nil { 255 return n, err 256 } 257 } 258 if err := w.connect(); err != nil { 259 return 0, err 260 } 261 return w.write(pr, s) 262 } 263 264 // write generates and writes a syslog formatted string. The 265 // format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG 266 func (w *Writer) write(p Priority, msg string) (int, error) { 267 // ensure it ends in a \n 268 nl := "" 269 if !strings.HasSuffix(msg, "\n") { 270 nl = "\n" 271 } 272 273 err := w.conn.writeString(p, w.hostname, w.tag, msg, nl) 274 if err != nil { 275 return 0, err 276 } 277 // Note: return the length of the input, not the number of 278 // bytes printed by Fprintf, because this must behave like 279 // an io.Writer. 280 return len(msg), nil 281 } 282 283 func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error { 284 if n.local { 285 // Compared to the network form below, the changes are: 286 // 1. Use time.Stamp instead of time.RFC3339. 287 // 2. Drop the hostname field from the Fprintf. 288 timestamp := time.Now().Format(time.Stamp) 289 _, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s", 290 p, timestamp, 291 tag, os.Getpid(), msg, nl) 292 return err 293 } 294 timestamp := time.Now().Format(time.RFC3339) 295 _, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s", 296 p, timestamp, hostname, 297 tag, os.Getpid(), msg, nl) 298 return err 299 } 300 301 func (n *netConn) close() error { 302 return n.conn.Close() 303 } 304 305 // NewLogger creates a log.Logger whose output is written to the 306 // system log service with the specified priority, a combination of 307 // the syslog facility and severity. The logFlag argument is the flag 308 // set passed through to log.New to create the Logger. 309 func NewLogger(p Priority, logFlag int) (*log.Logger, error) { 310 s, err := New(p, "") 311 if err != nil { 312 return nil, err 313 } 314 return log.New(s, "", logFlag), nil 315 }