boulder: github.com/letsencrypt/boulder/log Index | Examples | Files

package log

import "github.com/letsencrypt/boulder/log"

Index

Examples

Package Files

log.go mock.go

func LogLineChecksum Uses

func LogLineChecksum(line string) string

func Set Uses

func Set(logger Logger) (err error)

Set configures the singleton Logger. This method must only be called once, and before calling Get the first time.

type Logger Uses

type Logger interface {
    Err(msg string)
    Errf(format string, a ...interface{})
    Warning(msg string)
    Warningf(format string, a ...interface{})
    Info(msg string)
    Infof(format string, a ...interface{})
    Debug(msg string)
    Debugf(format string, a ...interface{})
    AuditPanic()
    AuditInfo(msg string)
    AuditInfof(format string, a ...interface{})
    AuditObject(string, interface{})
    AuditErr(string)
    AuditErrf(format string, a ...interface{})
}

A Logger logs messages with explicit priority levels. It is implemented by a logging back-end as provided by New() or NewMock().

Code:

package main

import (
    "fmt"
    "log"
    "log/syslog"
    "net"
    "os"
    "strings"
    "testing"
    "time"

    "github.com/jmhodges/clock"
    "github.com/letsencrypt/boulder/test"
)

const stdoutLevel = 7
const syslogLevel = 7

func setup(t *testing.T) *impl {
    // Write all logs to UDP on a high port so as to not bother the system
    // which is running the test
    writer, err := syslog.Dial("udp", "127.0.0.1:65530", syslog.LOG_INFO|syslog.LOG_LOCAL0, "")
    test.AssertNotError(t, err, "Could not construct syslog object")

    logger, err := New(writer, stdoutLevel, syslogLevel)
    test.AssertNotError(t, err, "Could not construct syslog object")
    impl, ok := logger.(*impl)
    if !ok {
        t.Fatalf("Wrong type returned from New: %T", logger)
    }
    return impl
}

func TestConstruction(t *testing.T) {
    t.Parallel()
    _ = setup(t)
}

func TestSingleton(t *testing.T) {
    t.Parallel()
    log1 := Get()
    test.AssertNotNil(t, log1, "Logger shouldn't be nil")

    log2 := Get()
    test.AssertEquals(t, log1, log2)

    audit := setup(t)

    // Should not work
    err := Set(audit)
    test.AssertError(t, err, "Can't re-set")

    // Verify no change
    log4 := Get()

    // Verify that log4 != log3
    test.AssertNotEquals(t, log4, audit)

    // Verify that log4 == log2 == log1
    test.AssertEquals(t, log4, log2)
    test.AssertEquals(t, log4, log1)
}

func TestConstructionNil(t *testing.T) {
    t.Parallel()
    _, err := New(nil, stdoutLevel, syslogLevel)
    test.AssertError(t, err, "Nil shouldn't be permitted.")
}

func TestEmit(t *testing.T) {
    t.Parallel()
    log := setup(t)

    log.AuditInfo("test message")
}

func TestEmitEmpty(t *testing.T) {
    t.Parallel()
    log := setup(t)

    log.AuditInfo("")
}

func main() {
    // Write all logs to UDP on a high port so as to not bother the system
    // which is running the test
    writer, err := syslog.Dial("udp", "127.0.0.1:65530", syslog.LOG_INFO|syslog.LOG_LOCAL0, "")
    if err != nil {
        log.Fatal(err)
    }

    logger, err := New(writer, stdoutLevel, syslogLevel)
    if err != nil {
        log.Fatal(err)
    }
    impl, ok := logger.(*impl)
    if !ok {
        log.Fatalf("Wrong type returned from New: %T", logger)
    }

    bw, ok := impl.w.(*bothWriter)
    if !ok {
        log.Fatalf("Wrong type of impl's writer: %T\n", impl.w)
    }
    bw.clk = clock.NewFake()
    impl.AuditErr("Error Audit")
    impl.Warning("Warning Audit")
}

func TestSyslogMethods(t *testing.T) {
    t.Parallel()
    impl := setup(t)

    impl.AuditInfo("audit-logger_test.go: audit-info")
    impl.AuditErr("audit-logger_test.go: audit-err")
    impl.Debug("audit-logger_test.go: debug")
    impl.Err("audit-logger_test.go: err")
    impl.Info("audit-logger_test.go: info")
    impl.Warning("audit-logger_test.go: warning")
    impl.AuditInfof("audit-logger_test.go: %s", "audit-info")
    impl.AuditErrf("audit-logger_test.go: %s", "audit-err")
    impl.Debugf("audit-logger_test.go: %s", "debug")
    impl.Errf("audit-logger_test.go: %s", "err")
    impl.Infof("audit-logger_test.go: %s", "info")
    impl.Warningf("audit-logger_test.go: %s", "warning")
}

func TestPanic(t *testing.T) {
    t.Parallel()
    impl := setup(t)
    defer impl.AuditPanic()
    panic("Test panic")
    // Can't assert anything here or golint gets angry
}

func TestAuditObject(t *testing.T) {
    t.Parallel()

    log := NewMock()

    // Test a simple object
    log.AuditObject("Prefix", "String")
    if len(log.GetAllMatching("[AUDIT]")) != 1 {
        t.Errorf("Failed to audit log simple object")
    }

    // Test a system object
    log.Clear()
    log.AuditObject("Prefix", t)
    if len(log.GetAllMatching("[AUDIT]")) != 1 {
        t.Errorf("Failed to audit log system object")
    }

    // Test a complex object
    log.Clear()
    type validObj struct {
        A   string
        B   string
    }
    var valid = validObj{A: "B", B: "C"}
    log.AuditObject("Prefix", valid)
    if len(log.GetAllMatching("[AUDIT]")) != 1 {
        t.Errorf("Failed to audit log complex object")
    }

    // Test logging an unserializable object
    log.Clear()
    type invalidObj struct {
        A chan string
    }

    var invalid = invalidObj{A: make(chan string)}
    log.AuditObject("Prefix", invalid)
    if len(log.GetAllMatching("[AUDIT]")) != 1 {
        t.Errorf("Failed to audit log unserializable object %v", log.GetAllMatching("[AUDIT]"))
    }
}

func TestTransmission(t *testing.T) {
    t.Parallel()

    l, err := newUDPListener("127.0.0.1:0")
    test.AssertNotError(t, err, "Failed to open log server")
    defer func() {
        err = l.Close()
        test.AssertNotError(t, err, "listener.Close returned error")
    }()

    fmt.Printf("Going to %s\n", l.LocalAddr().String())
    writer, err := syslog.Dial("udp", l.LocalAddr().String(), syslog.LOG_INFO|syslog.LOG_LOCAL0, "")
    test.AssertNotError(t, err, "Failed to find connect to log server")

    impl, err := New(writer, stdoutLevel, syslogLevel)
    test.AssertNotError(t, err, "Failed to construct audit logger")

    data := make([]byte, 128)

    impl.AuditInfo("audit-logger_test.go: audit-info")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.AuditErr("audit-logger_test.go: audit-err")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.Debug("audit-logger_test.go: debug")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.Err("audit-logger_test.go: err")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.Info("audit-logger_test.go: info")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.Warning("audit-logger_test.go: warning")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.AuditInfof("audit-logger_test.go: %s", "audit-info")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.AuditErrf("audit-logger_test.go: %s", "audit-err")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.Debugf("audit-logger_test.go: %s", "debug")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.Errf("audit-logger_test.go: %s", "err")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.Infof("audit-logger_test.go: %s", "info")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")

    impl.Warningf("audit-logger_test.go: %s", "warning")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")
}

func TestSyslogLevels(t *testing.T) {
    t.Parallel()

    l, err := newUDPListener("127.0.0.1:0")
    test.AssertNotError(t, err, "Failed to open log server")
    defer func() {
        err = l.Close()
        test.AssertNotError(t, err, "listener.Close returned error")
    }()

    fmt.Printf("Going to %s\n", l.LocalAddr().String())
    writer, err := syslog.Dial("udp", l.LocalAddr().String(), syslog.LOG_INFO|syslog.LOG_LOCAL0, "")
    test.AssertNotError(t, err, "Failed to find connect to log server")

    // create a logger with syslog level debug
    impl, err := New(writer, stdoutLevel, int(syslog.LOG_DEBUG))
    test.AssertNotError(t, err, "Failed to construct audit logger")

    data := make([]byte, 512)

    // debug messages should be sent to the logger
    impl.Debug("log_test.go: debug")
    _, _, err = l.ReadFrom(data)
    test.AssertNotError(t, err, "Failed to find packet")
    test.Assert(t, strings.Contains(string(data), "log_test.go: debug"), "Failed to find log message")

    // create a logger with syslog level info
    impl, err = New(writer, stdoutLevel, int(syslog.LOG_INFO))
    test.AssertNotError(t, err, "Failed to construct audit logger")

    // debug messages should not be sent to the logger
    impl.Debug("log_test.go: debug")
    n, _, err := l.ReadFrom(data)
    if n != 0 && err == nil {
        t.Error("Failed to withhold debug log message")
    }
}

func newUDPListener(addr string) (*net.UDPConn, error) {
    l, err := net.ListenPacket("udp", addr)
    if err != nil {
        return nil, err
    }
    err = l.SetDeadline(time.Now().Add(100 * time.Millisecond))
    if err != nil {
        return nil, err
    }
    err = l.SetReadDeadline(time.Now().Add(100 * time.Millisecond))
    if err != nil {
        return nil, err
    }
    err = l.SetWriteDeadline(time.Now().Add(100 * time.Millisecond))
    if err != nil {
        return nil, err
    }
    return l.(*net.UDPConn), nil
}

// TestStdoutFailure tests that audit logging with a bothWriter panics if stdout
// becomes unavailable.
func TestStdoutFailure(t *testing.T) {
    // Save the stdout fd so we can restore it later
    saved := os.Stdout

    // Create a throw-away pipe FD to replace stdout with
    _, w, err := os.Pipe()
    test.AssertNotError(t, err, "failed to create pipe")
    os.Stdout = w

    // Setup the logger
    log := setup(t)

    // Close Stdout so that the fmt.Printf in bothWriter's logAtLevel
    // function will return an err on next log.
    err = os.Stdout.Close()
    test.AssertNotError(t, err, "failed to close stdout")

    // Defer a function that will check if there was a panic to recover from. If
    // there wasn't then the test should fail, we were able to AuditInfo when
    // Stdout was inoperable.
    defer func() {
        if recovered := recover(); recovered == nil {
            t.Errorf("log.AuditInfo with Stdout closed did not panic")
        }

        // Restore stdout so that subsequent tests don't fail
        os.Stdout = saved
    }()

    // Try to audit log something
    log.AuditInfo("This should cause a panic, stdout is closed!")
}

func Get Uses

func Get() Logger

Get obtains the singleton Logger. If Set has not been called first, this method initializes with basic defaults. The basic defaults cannot error, and subsequent access to an already-set Logger also cannot error, so this method is error-safe.

func New Uses

func New(log *syslog.Writer, stdoutLogLevel int, syslogLogLevel int) (Logger, error)

New returns a new Logger that uses the given syslog.Writer as a backend.

type Mock Uses

type Mock struct {
    // contains filtered or unexported fields
}

Mock is a logger that stores all log messages in memory to be examined by a test.

func NewMock Uses

func NewMock() *Mock

NewMock creates a mock logger.

func UseMock Uses

func UseMock() *Mock

UseMock sets a mock logger as the default logger, and returns it.

func (*Mock) AuditErr Uses

func (log *Mock) AuditErr(msg string)

AuditErr can format an error for auditing; it does so at ERR level.

func (*Mock) AuditErrf Uses

func (log *Mock) AuditErrf(format string, a ...interface{})

AuditErrf can format an error for auditing; it does so at ERR level.

func (*Mock) AuditInfo Uses

func (log *Mock) AuditInfo(msg string)

AuditInfo sends an INFO-severity message that is prefixed with the audit tag, for special handling at the upstream system logger.

func (*Mock) AuditInfof Uses

func (log *Mock) AuditInfof(format string, a ...interface{})

AuditInfof sends an INFO-severity message that is prefixed with the audit tag, for special handling at the upstream system logger.

func (*Mock) AuditObject Uses

func (log *Mock) AuditObject(msg string, obj interface{})

AuditObject sends an INFO-severity JSON-serialized object message that is prefixed with the audit tag, for special handling at the upstream system logger.

func (*Mock) AuditPanic Uses

func (log *Mock) AuditPanic()

AuditPanic catches panicking executables. This method should be added in a defer statement as early as possible

func (*Mock) Clear Uses

func (m *Mock) Clear()

Clear resets the log buffer.

func (*Mock) Debug Uses

func (log *Mock) Debug(msg string)

Debug level messages pass through normally.

func (*Mock) Debugf Uses

func (log *Mock) Debugf(format string, a ...interface{})

Debugf level messages pass through normally.

func (*Mock) Err Uses

func (log *Mock) Err(msg string)

Err level messages are always marked with the audit tag, for special handling at the upstream system logger.

func (*Mock) Errf Uses

func (log *Mock) Errf(format string, a ...interface{})

Errf level messages are always marked with the audit tag, for special handling at the upstream system logger.

func (*Mock) GetAll Uses

func (m *Mock) GetAll() []string

GetAll returns all messages logged since instantiation or the last call to Clear().

The caller must not modify the returned slice or its elements.

func (*Mock) GetAllMatching Uses

func (m *Mock) GetAllMatching(reString string) []string

GetAllMatching returns all messages logged since instantiation or the last Clear() whose text matches the given regexp. The regexp is accepted as a string and compiled on the fly, because convenience is more important than performance.

The caller must not modify the elements of the returned slice.

func (*Mock) Info Uses

func (log *Mock) Info(msg string)

Info level messages pass through normally.

func (*Mock) Infof Uses

func (log *Mock) Infof(format string, a ...interface{})

Infof level messages pass through normally.

func (*Mock) Warning Uses

func (log *Mock) Warning(msg string)

Warning level messages pass through normally.

func (*Mock) Warningf Uses

func (log *Mock) Warningf(format string, a ...interface{})

Warningf level messages pass through normally.

Package log imports 14 packages (graph) and is imported by 1204 packages. Updated 2020-04-17. Refresh now. Tools for package owners.