318 lines
7.5 KiB
Go
318 lines
7.5 KiB
Go
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
//go:build !windows && !plan9
|
|
|
|
package syslog
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"net"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// The Priority is a combination of the syslog facility and
|
|
// severity. For example, LOG_ALERT | LOG_FTP sends an alert severity
|
|
// message from the FTP facility. The default severity is LOG_EMERG;
|
|
// the default facility is LOG_KERN.
|
|
type Priority int
|
|
|
|
const severityMask = 0x07
|
|
const facilityMask = 0xf8
|
|
|
|
const (
|
|
// Severity.
|
|
|
|
// From /usr/include/sys/syslog.h.
|
|
// These are the same on Linux, BSD, and OS X.
|
|
LOG_EMERG Priority = iota
|
|
LOG_ALERT
|
|
LOG_CRIT
|
|
LOG_ERR
|
|
LOG_WARNING
|
|
LOG_NOTICE
|
|
LOG_INFO
|
|
LOG_DEBUG
|
|
)
|
|
|
|
const (
|
|
// Facility.
|
|
|
|
// From /usr/include/sys/syslog.h.
|
|
// These are the same up to LOG_FTP on Linux, BSD, and OS X.
|
|
LOG_KERN Priority = iota << 3
|
|
LOG_USER
|
|
LOG_MAIL
|
|
LOG_DAEMON
|
|
LOG_AUTH
|
|
LOG_SYSLOG
|
|
LOG_LPR
|
|
LOG_NEWS
|
|
LOG_UUCP
|
|
LOG_CRON
|
|
LOG_AUTHPRIV
|
|
LOG_FTP
|
|
_ // unused
|
|
_ // unused
|
|
_ // unused
|
|
_ // unused
|
|
LOG_LOCAL0
|
|
LOG_LOCAL1
|
|
LOG_LOCAL2
|
|
LOG_LOCAL3
|
|
LOG_LOCAL4
|
|
LOG_LOCAL5
|
|
LOG_LOCAL6
|
|
LOG_LOCAL7
|
|
)
|
|
|
|
// A Writer is a connection to a syslog server.
|
|
type Writer struct {
|
|
priority Priority
|
|
tag string
|
|
hostname string
|
|
network string
|
|
raddr string
|
|
|
|
mu sync.Mutex // guards conn
|
|
conn serverConn
|
|
}
|
|
|
|
// This interface and the separate syslog_unix.go file exist for
|
|
// Solaris support as implemented by gccgo. On Solaris you cannot
|
|
// simply open a TCP connection to the syslog daemon. The gccgo
|
|
// sources have a syslog_solaris.go file that implements unixSyslog to
|
|
// return a type that satisfies this interface and simply calls the C
|
|
// library syslog function.
|
|
type serverConn interface {
|
|
writeString(p Priority, hostname, tag, s, nl string) error
|
|
close() error
|
|
}
|
|
|
|
type netConn struct {
|
|
local bool
|
|
conn net.Conn
|
|
}
|
|
|
|
// New establishes a new connection to the system log daemon. Each
|
|
// write to the returned writer sends a log message with the given
|
|
// priority (a combination of the syslog facility and severity) and
|
|
// prefix tag. If tag is empty, the os.Args[0] is used.
|
|
func New(priority Priority, tag string) (*Writer, error) {
|
|
return Dial("", "", priority, tag)
|
|
}
|
|
|
|
// Dial establishes a connection to a log daemon by connecting to
|
|
// address raddr on the specified network. Each write to the returned
|
|
// writer sends a log message with the facility and severity
|
|
// (from priority) and tag. If tag is empty, the os.Args[0] is used.
|
|
// If network is empty, Dial will connect to the local syslog server.
|
|
// Otherwise, see the documentation for net.Dial for valid values
|
|
// of network and raddr.
|
|
func Dial(network, raddr string, priority Priority, tag string) (*Writer, error) {
|
|
if priority < 0 || priority > LOG_LOCAL7|LOG_DEBUG {
|
|
return nil, errors.New("log/syslog: invalid priority")
|
|
}
|
|
|
|
if tag == "" {
|
|
tag = os.Args[0]
|
|
}
|
|
hostname, _ := os.Hostname()
|
|
|
|
w := &Writer{
|
|
priority: priority,
|
|
tag: tag,
|
|
hostname: hostname,
|
|
network: network,
|
|
raddr: raddr,
|
|
}
|
|
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
err := w.connect()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return w, err
|
|
}
|
|
|
|
// connect makes a connection to the syslog server.
|
|
// It must be called with w.mu held.
|
|
func (w *Writer) connect() (err error) {
|
|
if w.conn != nil {
|
|
// ignore err from close, it makes sense to continue anyway
|
|
w.conn.close()
|
|
w.conn = nil
|
|
}
|
|
|
|
if w.network == "" {
|
|
w.conn, err = unixSyslog()
|
|
if w.hostname == "" {
|
|
w.hostname = "localhost"
|
|
}
|
|
} else {
|
|
var c net.Conn
|
|
c, err = net.Dial(w.network, w.raddr)
|
|
if err == nil {
|
|
w.conn = &netConn{
|
|
conn: c,
|
|
local: w.network == "unixgram" || w.network == "unix",
|
|
}
|
|
if w.hostname == "" {
|
|
w.hostname = c.LocalAddr().String()
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Write sends a log message to the syslog daemon.
|
|
func (w *Writer) Write(b []byte) (int, error) {
|
|
return w.writeAndRetry(w.priority, string(b))
|
|
}
|
|
|
|
// Close closes a connection to the syslog daemon.
|
|
func (w *Writer) Close() error {
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
if w.conn != nil {
|
|
err := w.conn.close()
|
|
w.conn = nil
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Emerg logs a message with severity LOG_EMERG, ignoring the severity
|
|
// passed to New.
|
|
func (w *Writer) Emerg(m string) error {
|
|
_, err := w.writeAndRetry(LOG_EMERG, m)
|
|
return err
|
|
}
|
|
|
|
// Alert logs a message with severity LOG_ALERT, ignoring the severity
|
|
// passed to New.
|
|
func (w *Writer) Alert(m string) error {
|
|
_, err := w.writeAndRetry(LOG_ALERT, m)
|
|
return err
|
|
}
|
|
|
|
// Crit logs a message with severity LOG_CRIT, ignoring the severity
|
|
// passed to New.
|
|
func (w *Writer) Crit(m string) error {
|
|
_, err := w.writeAndRetry(LOG_CRIT, m)
|
|
return err
|
|
}
|
|
|
|
// Err logs a message with severity LOG_ERR, ignoring the severity
|
|
// passed to New.
|
|
func (w *Writer) Err(m string) error {
|
|
_, err := w.writeAndRetry(LOG_ERR, m)
|
|
return err
|
|
}
|
|
|
|
// Warning logs a message with severity LOG_WARNING, ignoring the
|
|
// severity passed to New.
|
|
func (w *Writer) Warning(m string) error {
|
|
_, err := w.writeAndRetry(LOG_WARNING, m)
|
|
return err
|
|
}
|
|
|
|
// Notice logs a message with severity LOG_NOTICE, ignoring the
|
|
// severity passed to New.
|
|
func (w *Writer) Notice(m string) error {
|
|
_, err := w.writeAndRetry(LOG_NOTICE, m)
|
|
return err
|
|
}
|
|
|
|
// Info logs a message with severity LOG_INFO, ignoring the severity
|
|
// passed to New.
|
|
func (w *Writer) Info(m string) error {
|
|
_, err := w.writeAndRetry(LOG_INFO, m)
|
|
return err
|
|
}
|
|
|
|
// Debug logs a message with severity LOG_DEBUG, ignoring the severity
|
|
// passed to New.
|
|
func (w *Writer) Debug(m string) error {
|
|
_, err := w.writeAndRetry(LOG_DEBUG, m)
|
|
return err
|
|
}
|
|
|
|
func (w *Writer) writeAndRetry(p Priority, s string) (int, error) {
|
|
pr := (w.priority & facilityMask) | (p & severityMask)
|
|
|
|
w.mu.Lock()
|
|
defer w.mu.Unlock()
|
|
|
|
if w.conn != nil {
|
|
if n, err := w.write(pr, s); err == nil {
|
|
return n, err
|
|
}
|
|
}
|
|
if err := w.connect(); err != nil {
|
|
return 0, err
|
|
}
|
|
return w.write(pr, s)
|
|
}
|
|
|
|
// write generates and writes a syslog formatted string. The
|
|
// format is as follows: <PRI>TIMESTAMP HOSTNAME TAG[PID]: MSG
|
|
func (w *Writer) write(p Priority, msg string) (int, error) {
|
|
// ensure it ends in a \n
|
|
nl := ""
|
|
if !strings.HasSuffix(msg, "\n") {
|
|
nl = "\n"
|
|
}
|
|
|
|
err := w.conn.writeString(p, w.hostname, w.tag, msg, nl)
|
|
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
|
|
}
|
|
|
|
func (n *netConn) writeString(p Priority, hostname, tag, msg, nl string) error {
|
|
if n.local {
|
|
// Compared to the network form below, the changes are:
|
|
// 1. Use time.Stamp instead of time.RFC3339.
|
|
// 2. Drop the hostname field from the Fprintf.
|
|
timestamp := time.Now().Format(time.Stamp)
|
|
_, err := fmt.Fprintf(n.conn, "<%d>%s %s[%d]: %s%s",
|
|
p, timestamp,
|
|
tag, os.Getpid(), msg, nl)
|
|
return err
|
|
}
|
|
timestamp := time.Now().Format(time.RFC3339)
|
|
_, err := fmt.Fprintf(n.conn, "<%d>%s %s %s[%d]: %s%s",
|
|
p, timestamp, hostname,
|
|
tag, os.Getpid(), msg, nl)
|
|
return err
|
|
}
|
|
|
|
func (n *netConn) close() error {
|
|
return n.conn.Close()
|
|
}
|
|
|
|
// NewLogger creates a log.Logger whose output is written to the
|
|
// system log service with the specified priority, a combination of
|
|
// the syslog facility and severity. The logFlag argument is the flag
|
|
// set passed through to log.New to create the Logger.
|
|
func NewLogger(p Priority, logFlag int) (*log.Logger, error) {
|
|
s, err := New(p, "")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return log.New(s, "", logFlag), nil
|
|
}
|