aboutsummaryrefslogtreecommitdiff
path: root/src/log
diff options
context:
space:
mode:
authorRuss Cox <rsc@golang.org>2014-09-08 00:08:51 -0400
committerRuss Cox <rsc@golang.org>2014-09-08 00:08:51 -0400
commitc007ce824d9a4fccb148f9204e04c23ed2984b71 (patch)
tree7dcac257114ef5c446be5b7b68c27dea230b7c09 /src/log
parent220a6de47eced55956eb8af8d643d4f5b67fd634 (diff)
downloadgo-c007ce824d9a4fccb148f9204e04c23ed2984b71.tar.gz
go-c007ce824d9a4fccb148f9204e04c23ed2984b71.zip
build: move package sources from src/pkg to src
Preparation was in CL 134570043. This CL contains only the effect of 'hg mv src/pkg/* src'. For more about the move, see golang.org/s/go14nopkg.
Diffstat (limited to 'src/log')
-rw-r--r--src/log/example_test.go21
-rw-r--r--src/log/log.go322
-rw-r--r--src/log/log_test.go119
-rw-r--r--src/log/syslog/syslog.go319
-rw-r--r--src/log/syslog/syslog_plan9.go8
-rw-r--r--src/log/syslog/syslog_test.go358
-rw-r--r--src/log/syslog/syslog_unix.go31
-rw-r--r--src/log/syslog/syslog_windows.go8
8 files changed, 1186 insertions, 0 deletions
diff --git a/src/log/example_test.go b/src/log/example_test.go
new file mode 100644
index 0000000000..74385a3a0a
--- /dev/null
+++ b/src/log/example_test.go
@@ -0,0 +1,21 @@
+// Copyright 2013 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.
+
+package log_test
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+)
+
+func ExampleLogger() {
+ var buf bytes.Buffer
+ logger := log.New(&buf, "logger: ", log.Lshortfile)
+ logger.Print("Hello, log file!")
+
+ fmt.Print(&buf)
+ // Output:
+ // logger: example_test.go:16: Hello, log file!
+}
diff --git a/src/log/log.go b/src/log/log.go
new file mode 100644
index 0000000000..d37e4375e4
--- /dev/null
+++ b/src/log/log.go
@@ -0,0 +1,322 @@
+// 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.
+
+// Package log implements a simple logging package. It defines a type, Logger,
+// with methods for formatting output. It also has a predefined 'standard'
+// Logger accessible through helper functions Print[f|ln], Fatal[f|ln], and
+// Panic[f|ln], which are easier to use than creating a Logger manually.
+// That logger writes to standard error and prints the date and time
+// of each logged message.
+// The Fatal functions call os.Exit(1) after writing the log message.
+// The Panic functions call panic after writing the log message.
+package log
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "runtime"
+ "sync"
+ "time"
+)
+
+// These flags define which text to prefix to each log entry generated by the Logger.
+const (
+ // Bits or'ed together to control what's printed. There is no control over the
+ // order they appear (the order listed here) or the format they present (as
+ // described in the comments). A colon appears after these items:
+ // 2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
+ Ldate = 1 << iota // the date: 2009/01/23
+ Ltime // the time: 01:23:23
+ Lmicroseconds // microsecond resolution: 01:23:23.123123. assumes Ltime.
+ Llongfile // full file name and line number: /a/b/c/d.go:23
+ Lshortfile // final file name element and line number: d.go:23. overrides Llongfile
+ LstdFlags = Ldate | Ltime // initial values for the standard logger
+)
+
+// A Logger represents an active logging object that generates lines of
+// output to an io.Writer. Each logging operation makes a single call to
+// the Writer's Write method. A Logger can be used simultaneously from
+// multiple goroutines; it guarantees to serialize access to the Writer.
+type Logger struct {
+ mu sync.Mutex // ensures atomic writes; protects the following fields
+ prefix string // prefix to write at beginning of each line
+ flag int // properties
+ out io.Writer // destination for output
+ buf []byte // for accumulating text to write
+}
+
+// New creates a new Logger. The out variable sets the
+// destination to which log data will be written.
+// The prefix appears at the beginning of each generated log line.
+// The flag argument defines the logging properties.
+func New(out io.Writer, prefix string, flag int) *Logger {
+ return &Logger{out: out, prefix: prefix, flag: flag}
+}
+
+var std = New(os.Stderr, "", LstdFlags)
+
+// Cheap integer to fixed-width decimal ASCII. Give a negative width to avoid zero-padding.
+// Knows the buffer has capacity.
+func itoa(buf *[]byte, i int, wid int) {
+ var u uint = uint(i)
+ if u == 0 && wid <= 1 {
+ *buf = append(*buf, '0')
+ return
+ }
+
+ // Assemble decimal in reverse order.
+ var b [32]byte
+ bp := len(b)
+ for ; u > 0 || wid > 0; u /= 10 {
+ bp--
+ wid--
+ b[bp] = byte(u%10) + '0'
+ }
+ *buf = append(*buf, b[bp:]...)
+}
+
+func (l *Logger) formatHeader(buf *[]byte, t time.Time, file string, line int) {
+ *buf = append(*buf, l.prefix...)
+ if l.flag&(Ldate|Ltime|Lmicroseconds) != 0 {
+ if l.flag&Ldate != 0 {
+ year, month, day := t.Date()
+ itoa(buf, year, 4)
+ *buf = append(*buf, '/')
+ itoa(buf, int(month), 2)
+ *buf = append(*buf, '/')
+ itoa(buf, day, 2)
+ *buf = append(*buf, ' ')
+ }
+ if l.flag&(Ltime|Lmicroseconds) != 0 {
+ hour, min, sec := t.Clock()
+ itoa(buf, hour, 2)
+ *buf = append(*buf, ':')
+ itoa(buf, min, 2)
+ *buf = append(*buf, ':')
+ itoa(buf, sec, 2)
+ if l.flag&Lmicroseconds != 0 {
+ *buf = append(*buf, '.')
+ itoa(buf, t.Nanosecond()/1e3, 6)
+ }
+ *buf = append(*buf, ' ')
+ }
+ }
+ if l.flag&(Lshortfile|Llongfile) != 0 {
+ if l.flag&Lshortfile != 0 {
+ short := file
+ for i := len(file) - 1; i > 0; i-- {
+ if file[i] == '/' {
+ short = file[i+1:]
+ break
+ }
+ }
+ file = short
+ }
+ *buf = append(*buf, file...)
+ *buf = append(*buf, ':')
+ itoa(buf, line, -1)
+ *buf = append(*buf, ": "...)
+ }
+}
+
+// Output writes the output for a logging event. The string s contains
+// the text to print after the prefix specified by the flags of the
+// Logger. A newline is appended if the last character of s is not
+// already a newline. Calldepth is used to recover the PC and is
+// provided for generality, although at the moment on all pre-defined
+// paths it will be 2.
+func (l *Logger) Output(calldepth int, s string) error {
+ now := time.Now() // get this early.
+ var file string
+ var line int
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ if l.flag&(Lshortfile|Llongfile) != 0 {
+ // release lock while getting caller info - it's expensive.
+ l.mu.Unlock()
+ var ok bool
+ _, file, line, ok = runtime.Caller(calldepth)
+ if !ok {
+ file = "???"
+ line = 0
+ }
+ l.mu.Lock()
+ }
+ l.buf = l.buf[:0]
+ l.formatHeader(&l.buf, now, file, line)
+ l.buf = append(l.buf, s...)
+ if len(s) > 0 && s[len(s)-1] != '\n' {
+ l.buf = append(l.buf, '\n')
+ }
+ _, err := l.out.Write(l.buf)
+ return err
+}
+
+// Printf calls l.Output to print to the logger.
+// Arguments are handled in the manner of fmt.Printf.
+func (l *Logger) Printf(format string, v ...interface{}) {
+ l.Output(2, fmt.Sprintf(format, v...))
+}
+
+// Print calls l.Output to print to the logger.
+// Arguments are handled in the manner of fmt.Print.
+func (l *Logger) Print(v ...interface{}) { l.Output(2, fmt.Sprint(v...)) }
+
+// Println calls l.Output to print to the logger.
+// Arguments are handled in the manner of fmt.Println.
+func (l *Logger) Println(v ...interface{}) { l.Output(2, fmt.Sprintln(v...)) }
+
+// Fatal is equivalent to l.Print() followed by a call to os.Exit(1).
+func (l *Logger) Fatal(v ...interface{}) {
+ l.Output(2, fmt.Sprint(v...))
+ os.Exit(1)
+}
+
+// Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1).
+func (l *Logger) Fatalf(format string, v ...interface{}) {
+ l.Output(2, fmt.Sprintf(format, v...))
+ os.Exit(1)
+}
+
+// Fatalln is equivalent to l.Println() followed by a call to os.Exit(1).
+func (l *Logger) Fatalln(v ...interface{}) {
+ l.Output(2, fmt.Sprintln(v...))
+ os.Exit(1)
+}
+
+// Panic is equivalent to l.Print() followed by a call to panic().
+func (l *Logger) Panic(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ l.Output(2, s)
+ panic(s)
+}
+
+// Panicf is equivalent to l.Printf() followed by a call to panic().
+func (l *Logger) Panicf(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ l.Output(2, s)
+ panic(s)
+}
+
+// Panicln is equivalent to l.Println() followed by a call to panic().
+func (l *Logger) Panicln(v ...interface{}) {
+ s := fmt.Sprintln(v...)
+ l.Output(2, s)
+ panic(s)
+}
+
+// Flags returns the output flags for the logger.
+func (l *Logger) Flags() int {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ return l.flag
+}
+
+// SetFlags sets the output flags for the logger.
+func (l *Logger) SetFlags(flag int) {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.flag = flag
+}
+
+// Prefix returns the output prefix for the logger.
+func (l *Logger) Prefix() string {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ return l.prefix
+}
+
+// SetPrefix sets the output prefix for the logger.
+func (l *Logger) SetPrefix(prefix string) {
+ l.mu.Lock()
+ defer l.mu.Unlock()
+ l.prefix = prefix
+}
+
+// SetOutput sets the output destination for the standard logger.
+func SetOutput(w io.Writer) {
+ std.mu.Lock()
+ defer std.mu.Unlock()
+ std.out = w
+}
+
+// Flags returns the output flags for the standard logger.
+func Flags() int {
+ return std.Flags()
+}
+
+// SetFlags sets the output flags for the standard logger.
+func SetFlags(flag int) {
+ std.SetFlags(flag)
+}
+
+// Prefix returns the output prefix for the standard logger.
+func Prefix() string {
+ return std.Prefix()
+}
+
+// SetPrefix sets the output prefix for the standard logger.
+func SetPrefix(prefix string) {
+ std.SetPrefix(prefix)
+}
+
+// These functions write to the standard logger.
+
+// Print calls Output to print to the standard logger.
+// Arguments are handled in the manner of fmt.Print.
+func Print(v ...interface{}) {
+ std.Output(2, fmt.Sprint(v...))
+}
+
+// Printf calls Output to print to the standard logger.
+// Arguments are handled in the manner of fmt.Printf.
+func Printf(format string, v ...interface{}) {
+ std.Output(2, fmt.Sprintf(format, v...))
+}
+
+// Println calls Output to print to the standard logger.
+// Arguments are handled in the manner of fmt.Println.
+func Println(v ...interface{}) {
+ std.Output(2, fmt.Sprintln(v...))
+}
+
+// Fatal is equivalent to Print() followed by a call to os.Exit(1).
+func Fatal(v ...interface{}) {
+ std.Output(2, fmt.Sprint(v...))
+ os.Exit(1)
+}
+
+// Fatalf is equivalent to Printf() followed by a call to os.Exit(1).
+func Fatalf(format string, v ...interface{}) {
+ std.Output(2, fmt.Sprintf(format, v...))
+ os.Exit(1)
+}
+
+// Fatalln is equivalent to Println() followed by a call to os.Exit(1).
+func Fatalln(v ...interface{}) {
+ std.Output(2, fmt.Sprintln(v...))
+ os.Exit(1)
+}
+
+// Panic is equivalent to Print() followed by a call to panic().
+func Panic(v ...interface{}) {
+ s := fmt.Sprint(v...)
+ std.Output(2, s)
+ panic(s)
+}
+
+// Panicf is equivalent to Printf() followed by a call to panic().
+func Panicf(format string, v ...interface{}) {
+ s := fmt.Sprintf(format, v...)
+ std.Output(2, s)
+ panic(s)
+}
+
+// Panicln is equivalent to Println() followed by a call to panic().
+func Panicln(v ...interface{}) {
+ s := fmt.Sprintln(v...)
+ std.Output(2, s)
+ panic(s)
+}
diff --git a/src/log/log_test.go b/src/log/log_test.go
new file mode 100644
index 0000000000..158c3d93c7
--- /dev/null
+++ b/src/log/log_test.go
@@ -0,0 +1,119 @@
+// 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.
+
+package log
+
+// These tests are too simple.
+
+import (
+ "bytes"
+ "os"
+ "regexp"
+ "testing"
+)
+
+const (
+ Rdate = `[0-9][0-9][0-9][0-9]/[0-9][0-9]/[0-9][0-9]`
+ Rtime = `[0-9][0-9]:[0-9][0-9]:[0-9][0-9]`
+ Rmicroseconds = `\.[0-9][0-9][0-9][0-9][0-9][0-9]`
+ Rline = `(54|56):` // must update if the calls to l.Printf / l.Print below move
+ Rlongfile = `.*/[A-Za-z0-9_\-]+\.go:` + Rline
+ Rshortfile = `[A-Za-z0-9_\-]+\.go:` + Rline
+)
+
+type tester struct {
+ flag int
+ prefix string
+ pattern string // regexp that log output must match; we add ^ and expected_text$ always
+}
+
+var tests = []tester{
+ // individual pieces:
+ {0, "", ""},
+ {0, "XXX", "XXX"},
+ {Ldate, "", Rdate + " "},
+ {Ltime, "", Rtime + " "},
+ {Ltime | Lmicroseconds, "", Rtime + Rmicroseconds + " "},
+ {Lmicroseconds, "", Rtime + Rmicroseconds + " "}, // microsec implies time
+ {Llongfile, "", Rlongfile + " "},
+ {Lshortfile, "", Rshortfile + " "},
+ {Llongfile | Lshortfile, "", Rshortfile + " "}, // shortfile overrides longfile
+ // everything at once:
+ {Ldate | Ltime | Lmicroseconds | Llongfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rlongfile + " "},
+ {Ldate | Ltime | Lmicroseconds | Lshortfile, "XXX", "XXX" + Rdate + " " + Rtime + Rmicroseconds + " " + Rshortfile + " "},
+}
+
+// Test using Println("hello", 23, "world") or using Printf("hello %d world", 23)
+func testPrint(t *testing.T, flag int, prefix string, pattern string, useFormat bool) {
+ buf := new(bytes.Buffer)
+ SetOutput(buf)
+ SetFlags(flag)
+ SetPrefix(prefix)
+ if useFormat {
+ Printf("hello %d world", 23)
+ } else {
+ Println("hello", 23, "world")
+ }
+ line := buf.String()
+ line = line[0 : len(line)-1]
+ pattern = "^" + pattern + "hello 23 world$"
+ matched, err4 := regexp.MatchString(pattern, line)
+ if err4 != nil {
+ t.Fatal("pattern did not compile:", err4)
+ }
+ if !matched {
+ t.Errorf("log output should match %q is %q", pattern, line)
+ }
+ SetOutput(os.Stderr)
+}
+
+func TestAll(t *testing.T) {
+ for _, testcase := range tests {
+ testPrint(t, testcase.flag, testcase.prefix, testcase.pattern, false)
+ testPrint(t, testcase.flag, testcase.prefix, testcase.pattern, true)
+ }
+}
+
+func TestOutput(t *testing.T) {
+ const testString = "test"
+ var b bytes.Buffer
+ l := New(&b, "", 0)
+ l.Println(testString)
+ if expect := testString + "\n"; b.String() != expect {
+ t.Errorf("log output should match %q is %q", expect, b.String())
+ }
+}
+
+func TestFlagAndPrefixSetting(t *testing.T) {
+ var b bytes.Buffer
+ l := New(&b, "Test:", LstdFlags)
+ f := l.Flags()
+ if f != LstdFlags {
+ t.Errorf("Flags 1: expected %x got %x", LstdFlags, f)
+ }
+ l.SetFlags(f | Lmicroseconds)
+ f = l.Flags()
+ if f != LstdFlags|Lmicroseconds {
+ t.Errorf("Flags 2: expected %x got %x", LstdFlags|Lmicroseconds, f)
+ }
+ p := l.Prefix()
+ if p != "Test:" {
+ t.Errorf(`Prefix: expected "Test:" got %q`, p)
+ }
+ l.SetPrefix("Reality:")
+ p = l.Prefix()
+ if p != "Reality:" {
+ t.Errorf(`Prefix: expected "Reality:" got %q`, p)
+ }
+ // Verify a log message looks right, with our prefix and microseconds present.
+ l.Print("hello")
+ pattern := "^Reality:" + Rdate + " " + Rtime + Rmicroseconds + " hello\n"
+ matched, err := regexp.Match(pattern, b.Bytes())
+ if err != nil {
+ t.Fatalf("pattern %q did not compile: %s", pattern, err)
+ }
+ if !matched {
+ t.Error("message did not match pattern")
+ }
+}
diff --git a/src/log/syslog/syslog.go b/src/log/syslog/syslog.go
new file mode 100644
index 0000000000..5e09599162
--- /dev/null
+++ b/src/log/syslog/syslog.go
@@ -0,0 +1,319 @@
+// 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.
+
+// +build !windows,!nacl,!plan9
+
+// Package syslog provides a simple interface to the system log
+// service. It can send messages to the syslog daemon using UNIX
+// domain sockets, UDP or TCP.
+//
+// Only one call to Dial is necessary. On write failures,
+// the syslog client will attempt to reconnect to the server
+// and write again.
+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 can not
+// 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 and prefix.
+func New(priority Priority, tag string) (w *Writer, err 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 given facility, severity and
+// tag.
+// If network is empty, Dial will connect to the local syslog server.
+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}
+ 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) (err 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) (err 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) (err 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) (err 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) (err 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) (err 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) (err 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) (err 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. 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
+}
diff --git a/src/log/syslog/syslog_plan9.go b/src/log/syslog/syslog_plan9.go
new file mode 100644
index 0000000000..0c05f6f83c
--- /dev/null
+++ b/src/log/syslog/syslog_plan9.go
@@ -0,0 +1,8 @@
+// Copyright 2012 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.
+
+// Package syslog provides a simple interface to the system log service.
+package syslog
+
+// BUG(akumar): This package is not implemented on Plan 9 yet.
diff --git a/src/log/syslog/syslog_test.go b/src/log/syslog/syslog_test.go
new file mode 100644
index 0000000000..6a863fed31
--- /dev/null
+++ b/src/log/syslog/syslog_test.go
@@ -0,0 +1,358 @@
+// 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.
+
+// +build !windows,!nacl,!plan9
+
+package syslog
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "log"
+ "net"
+ "os"
+ "sync"
+ "testing"
+ "time"
+)
+
+func runPktSyslog(c net.PacketConn, done chan<- string) {
+ var buf [4096]byte
+ var rcvd string
+ ct := 0
+ for {
+ var n int
+ var err error
+
+ c.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
+ n, _, err = c.ReadFrom(buf[:])
+ rcvd += string(buf[:n])
+ if err != nil {
+ if oe, ok := err.(*net.OpError); ok {
+ if ct < 3 && oe.Temporary() {
+ ct++
+ continue
+ }
+ }
+ break
+ }
+ }
+ c.Close()
+ done <- rcvd
+}
+
+var crashy = false
+
+func runStreamSyslog(l net.Listener, done chan<- string, wg *sync.WaitGroup) {
+ for {
+ var c net.Conn
+ var err error
+ if c, err = l.Accept(); err != nil {
+ return
+ }
+ wg.Add(1)
+ go func(c net.Conn) {
+ defer wg.Done()
+ c.SetReadDeadline(time.Now().Add(5 * time.Second))
+ b := bufio.NewReader(c)
+ for ct := 1; !crashy || ct&7 != 0; ct++ {
+ s, err := b.ReadString('\n')
+ if err != nil {
+ break
+ }
+ done <- s
+ }
+ c.Close()
+ }(c)
+ }
+}
+
+func startServer(n, la string, done chan<- string) (addr string, sock io.Closer, wg *sync.WaitGroup) {
+ if n == "udp" || n == "tcp" {
+ la = "127.0.0.1:0"
+ } else {
+ // unix and unixgram: choose an address if none given
+ if la == "" {
+ // use ioutil.TempFile to get a name that is unique
+ f, err := ioutil.TempFile("", "syslogtest")
+ if err != nil {
+ log.Fatal("TempFile: ", err)
+ }
+ f.Close()
+ la = f.Name()
+ }
+ os.Remove(la)
+ }
+
+ wg = new(sync.WaitGroup)
+ if n == "udp" || n == "unixgram" {
+ l, e := net.ListenPacket(n, la)
+ if e != nil {
+ log.Fatalf("startServer failed: %v", e)
+ }
+ addr = l.LocalAddr().String()
+ sock = l
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ runPktSyslog(l, done)
+ }()
+ } else {
+ l, e := net.Listen(n, la)
+ if e != nil {
+ log.Fatalf("startServer failed: %v", e)
+ }
+ addr = l.Addr().String()
+ sock = l
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ runStreamSyslog(l, done, wg)
+ }()
+ }
+ return
+}
+
+func TestWithSimulated(t *testing.T) {
+ msg := "Test 123"
+ transport := []string{"unix", "unixgram", "udp", "tcp"}
+
+ for _, tr := range transport {
+ done := make(chan string)
+ addr, sock, srvWG := startServer(tr, "", done)
+ defer srvWG.Wait()
+ defer sock.Close()
+ if tr == "unix" || tr == "unixgram" {
+ defer os.Remove(addr)
+ }
+ s, err := Dial(tr, addr, LOG_INFO|LOG_USER, "syslog_test")
+ if err != nil {
+ t.Fatalf("Dial() failed: %v", err)
+ }
+ err = s.Info(msg)
+ if err != nil {
+ t.Fatalf("log failed: %v", err)
+ }
+ check(t, msg, <-done)
+ s.Close()
+ }
+}
+
+func TestFlap(t *testing.T) {
+ net := "unix"
+ done := make(chan string)
+ addr, sock, srvWG := startServer(net, "", done)
+ defer srvWG.Wait()
+ defer os.Remove(addr)
+ defer sock.Close()
+
+ s, err := Dial(net, addr, LOG_INFO|LOG_USER, "syslog_test")
+ if err != nil {
+ t.Fatalf("Dial() failed: %v", err)
+ }
+ msg := "Moo 2"
+ err = s.Info(msg)
+ if err != nil {
+ t.Fatalf("log failed: %v", err)
+ }
+ check(t, msg, <-done)
+
+ // restart the server
+ _, sock2, srvWG2 := startServer(net, addr, done)
+ defer srvWG2.Wait()
+ defer sock2.Close()
+
+ // and try retransmitting
+ msg = "Moo 3"
+ err = s.Info(msg)
+ if err != nil {
+ t.Fatalf("log failed: %v", err)
+ }
+ check(t, msg, <-done)
+
+ s.Close()
+}
+
+func TestNew(t *testing.T) {
+ if LOG_LOCAL7 != 23<<3 {
+ t.Fatalf("LOG_LOCAL7 has wrong value")
+ }
+ if testing.Short() {
+ // Depends on syslog daemon running, and sometimes it's not.
+ t.Skip("skipping syslog test during -short")
+ }
+
+ s, err := New(LOG_INFO|LOG_USER, "the_tag")
+ if err != nil {
+ t.Fatalf("New() failed: %s", err)
+ }
+ // Don't send any messages.
+ s.Close()
+}
+
+func TestNewLogger(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping syslog test during -short")
+ }
+ f, err := NewLogger(LOG_USER|LOG_INFO, 0)
+ if f == nil {
+ t.Error(err)
+ }
+}
+
+func TestDial(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping syslog test during -short")
+ }
+ f, err := Dial("", "", (LOG_LOCAL7|LOG_DEBUG)+1, "syslog_test")
+ if f != nil {
+ t.Fatalf("Should have trapped bad priority")
+ }
+ f, err = Dial("", "", -1, "syslog_test")
+ if f != nil {
+ t.Fatalf("Should have trapped bad priority")
+ }
+ l, err := Dial("", "", LOG_USER|LOG_ERR, "syslog_test")
+ if err != nil {
+ t.Fatalf("Dial() failed: %s", err)
+ }
+ l.Close()
+}
+
+func check(t *testing.T, in, out string) {
+ tmpl := fmt.Sprintf("<%d>%%s %%s syslog_test[%%d]: %s\n", LOG_USER+LOG_INFO, in)
+ if hostname, err := os.Hostname(); err != nil {
+ t.Error("Error retrieving hostname")
+ } else {
+ var parsedHostname, timestamp string
+ var pid int
+ if n, err := fmt.Sscanf(out, tmpl, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
+ t.Errorf("Got %q, does not match template %q (%d %s)", out, tmpl, n, err)
+ }
+ }
+}
+
+func TestWrite(t *testing.T) {
+ tests := []struct {
+ pri Priority
+ pre string
+ msg string
+ exp string
+ }{
+ {LOG_USER | LOG_ERR, "syslog_test", "", "%s %s syslog_test[%d]: \n"},
+ {LOG_USER | LOG_ERR, "syslog_test", "write test", "%s %s syslog_test[%d]: write test\n"},
+ // Write should not add \n if there already is one
+ {LOG_USER | LOG_ERR, "syslog_test", "write test 2\n", "%s %s syslog_test[%d]: write test 2\n"},
+ }
+
+ if hostname, err := os.Hostname(); err != nil {
+ t.Fatalf("Error retrieving hostname")
+ } else {
+ for _, test := range tests {
+ done := make(chan string)
+ addr, sock, srvWG := startServer("udp", "", done)
+ defer srvWG.Wait()
+ defer sock.Close()
+ l, err := Dial("udp", addr, test.pri, test.pre)
+ if err != nil {
+ t.Fatalf("syslog.Dial() failed: %v", err)
+ }
+ defer l.Close()
+ _, err = io.WriteString(l, test.msg)
+ if err != nil {
+ t.Fatalf("WriteString() failed: %v", err)
+ }
+ rcvd := <-done
+ test.exp = fmt.Sprintf("<%d>", test.pri) + test.exp
+ var parsedHostname, timestamp string
+ var pid int
+ if n, err := fmt.Sscanf(rcvd, test.exp, &timestamp, &parsedHostname, &pid); n != 3 || err != nil || hostname != parsedHostname {
+ t.Errorf("s.Info() = '%q', didn't match '%q' (%d %s)", rcvd, test.exp, n, err)
+ }
+ }
+ }
+}
+
+func TestConcurrentWrite(t *testing.T) {
+ addr, sock, srvWG := startServer("udp", "", make(chan string, 1))
+ defer srvWG.Wait()
+ defer sock.Close()
+ w, err := Dial("udp", addr, LOG_USER|LOG_ERR, "how's it going?")
+ if err != nil {
+ t.Fatalf("syslog.Dial() failed: %v", err)
+ }
+ var wg sync.WaitGroup
+ for i := 0; i < 10; i++ {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ err := w.Info("test")
+ if err != nil {
+ t.Errorf("Info() failed: %v", err)
+ return
+ }
+ }()
+ }
+ wg.Wait()
+}
+
+func TestConcurrentReconnect(t *testing.T) {
+ crashy = true
+ defer func() { crashy = false }()
+
+ const N = 10
+ const M = 100
+ net := "unix"
+ done := make(chan string, N*M)
+ addr, sock, srvWG := startServer(net, "", done)
+ defer os.Remove(addr)
+
+ // count all the messages arriving
+ count := make(chan int)
+ go func() {
+ ct := 0
+ for range done {
+ ct++
+ // we are looking for 500 out of 1000 events
+ // here because lots of log messages are lost
+ // in buffers (kernel and/or bufio)
+ if ct > N*M/2 {
+ break
+ }
+ }
+ count <- ct
+ }()
+
+ var wg sync.WaitGroup
+ wg.Add(N)
+ for i := 0; i < N; i++ {
+ go func() {
+ defer wg.Done()
+ w, err := Dial(net, addr, LOG_USER|LOG_ERR, "tag")
+ if err != nil {
+ t.Fatalf("syslog.Dial() failed: %v", err)
+ }
+ defer w.Close()
+ for i := 0; i < M; i++ {
+ err := w.Info("test")
+ if err != nil {
+ t.Errorf("Info() failed: %v", err)
+ return
+ }
+ }
+ }()
+ }
+ wg.Wait()
+ sock.Close()
+ srvWG.Wait()
+ close(done)
+
+ select {
+ case <-count:
+ case <-time.After(100 * time.Millisecond):
+ t.Error("timeout in concurrent reconnect")
+ }
+}
diff --git a/src/log/syslog/syslog_unix.go b/src/log/syslog/syslog_unix.go
new file mode 100644
index 0000000000..f6d2f1b7a3
--- /dev/null
+++ b/src/log/syslog/syslog_unix.go
@@ -0,0 +1,31 @@
+// 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.
+
+// +build !windows,!nacl,!plan9
+
+package syslog
+
+import (
+ "errors"
+ "net"
+)
+
+// unixSyslog opens a connection to the syslog daemon running on the
+// local machine using a Unix domain socket.
+
+func unixSyslog() (conn serverConn, err error) {
+ logTypes := []string{"unixgram", "unix"}
+ logPaths := []string{"/dev/log", "/var/run/syslog"}
+ for _, network := range logTypes {
+ for _, path := range logPaths {
+ conn, err := net.Dial(network, path)
+ if err != nil {
+ continue
+ } else {
+ return &netConn{conn: conn, local: true}, nil
+ }
+ }
+ }
+ return nil, errors.New("Unix syslog delivery error")
+}
diff --git a/src/log/syslog/syslog_windows.go b/src/log/syslog/syslog_windows.go
new file mode 100644
index 0000000000..8d99e2e594
--- /dev/null
+++ b/src/log/syslog/syslog_windows.go
@@ -0,0 +1,8 @@
+// Copyright 2012 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.
+
+// Package syslog provides a simple interface to the system log service.
+package syslog
+
+// BUG(brainman): This package is not implemented on Windows yet.