package srslog import ( "crypto/tls" "strings" "sync" ) // A Writer is a connection to a syslog server. type Writer struct { priority Priority tag string hostname string network string raddr string tlsConfig *tls.Config framer Framer formatter Formatter //non-nil if custom dialer set, used in getDialer customDial DialFunc mu sync.RWMutex // guards conn conn serverConn } // getConn provides access to the internal conn, protected by a mutex. The // conn is threadsafe, so it can be used while unlocked, but we want to avoid // race conditions on grabbing a reference to it. func (w *Writer) getConn() serverConn { w.mu.RLock() conn := w.conn w.mu.RUnlock() return conn } // setConn updates the internal conn, protected by a mutex. func (w *Writer) setConn(c serverConn) { w.mu.Lock() w.conn = c w.mu.Unlock() } // connect makes a connection to the syslog server. func (w *Writer) connect() (serverConn, error) { conn := w.getConn() if conn != nil { // ignore err from close, it makes sense to continue anyway conn.close() w.setConn(nil) } var hostname string var err error dialer := w.getDialer() conn, hostname, err = dialer.Call() if err == nil { w.setConn(conn) w.hostname = hostname return conn, nil } else { return nil, err } } // SetFormatter changes the formatter function for subsequent messages. func (w *Writer) SetFormatter(f Formatter) { w.formatter = f } // SetFramer changes the framer function for subsequent messages. func (w *Writer) SetFramer(f Framer) { w.framer = f } // SetHostname changes the hostname for syslog messages if needed. func (w *Writer) SetHostname(hostname string) { w.hostname = hostname } // Write sends a log message to the syslog daemon using the default priority // passed into `srslog.New` or the `srslog.Dial*` functions. func (w *Writer) Write(b []byte) (int, error) { return w.writeAndRetry(w.priority, string(b)) } // WriteWithPriority sends a log message with a custom priority. func (w *Writer) WriteWithPriority(p Priority, b []byte) (int, error) { return w.writeAndRetryWithPriority(p, string(b)) } // Close closes a connection to the syslog daemon. func (w *Writer) Close() error { conn := w.getConn() if conn != nil { err := conn.close() w.setConn(nil) return err } return nil } // Emerg logs a message with severity LOG_EMERG; this overrides the default // priority passed to `srslog.New` and the `srslog.Dial*` functions. func (w *Writer) Emerg(m string) (err error) { _, err = w.writeAndRetry(LOG_EMERG, m) return err } // Alert logs a message with severity LOG_ALERT; this overrides the default // priority passed to `srslog.New` and the `srslog.Dial*` functions. func (w *Writer) Alert(m string) (err error) { _, err = w.writeAndRetry(LOG_ALERT, m) return err } // Crit logs a message with severity LOG_CRIT; this overrides the default // priority passed to `srslog.New` and the `srslog.Dial*` functions. func (w *Writer) Crit(m string) (err error) { _, err = w.writeAndRetry(LOG_CRIT, m) return err } // Err logs a message with severity LOG_ERR; this overrides the default // priority passed to `srslog.New` and the `srslog.Dial*` functions. func (w *Writer) Err(m string) (err error) { _, err = w.writeAndRetry(LOG_ERR, m) return err } // Warning logs a message with severity LOG_WARNING; this overrides the default // priority passed to `srslog.New` and the `srslog.Dial*` functions. func (w *Writer) Warning(m string) (err error) { _, err = w.writeAndRetry(LOG_WARNING, m) return err } // Notice logs a message with severity LOG_NOTICE; this overrides the default // priority passed to `srslog.New` and the `srslog.Dial*` functions. func (w *Writer) Notice(m string) (err error) { _, err = w.writeAndRetry(LOG_NOTICE, m) return err } // Info logs a message with severity LOG_INFO; this overrides the default // priority passed to `srslog.New` and the `srslog.Dial*` functions. func (w *Writer) Info(m string) (err error) { _, err = w.writeAndRetry(LOG_INFO, m) return err } // Debug logs a message with severity LOG_DEBUG; this overrides the default // priority passed to `srslog.New` and the `srslog.Dial*` functions. func (w *Writer) Debug(m string) (err error) { _, err = w.writeAndRetry(LOG_DEBUG, m) return err } // writeAndRetry takes a severity and the string to write. Any facility passed to // it as part of the severity Priority will be ignored. func (w *Writer) writeAndRetry(severity Priority, s string) (int, error) { pr := (w.priority & facilityMask) | (severity & severityMask) return w.writeAndRetryWithPriority(pr, s) } // writeAndRetryWithPriority differs from writeAndRetry in that it allows setting // of both the facility and the severity. func (w *Writer) writeAndRetryWithPriority(p Priority, s string) (int, error) { conn := w.getConn() if conn != nil { if n, err := w.write(conn, p, s); err == nil { return n, err } } var err error if conn, err = w.connect(); err != nil { return 0, err } return w.write(conn, p, s) } // write generates and writes a syslog formatted string. It formats the // message based on the current Formatter and Framer. func (w *Writer) write(conn serverConn, p Priority, msg string) (int, error) { // ensure it ends in a \n if !strings.HasSuffix(msg, "\n") { msg += "\n" } err := conn.writeString(w.framer, w.formatter, p, w.hostname, w.tag, msg) if err != nil { return 0, err } // Note: return the length of the input, not the number of // bytes printed by Fprintf, because this must behave like // an io.Writer. return len(msg), nil }