Moves logger setup into its own package. (#2471)

* Moves logger setup into its own package.

* Removes a stray regex mark in the test locator.
This commit is contained in:
James Phillips 2016-11-03 21:14:56 -07:00 committed by GitHub
parent f4e34397ac
commit ea95e8f40d
15 changed files with 135 additions and 86 deletions

View File

@ -25,9 +25,9 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/hashicorp/consul/lib"
"github.com/hashicorp/consul/logger"
"github.com/hashicorp/consul/watch"
"github.com/hashicorp/go-checkpoint"
"github.com/hashicorp/go-syslog"
"github.com/hashicorp/logutils"
scada "github.com/hashicorp/scada-client/scada"
"github.com/mitchellh/cli"
@ -452,63 +452,8 @@ func (c *Config) discoverEc2Hosts(logger *log.Logger) ([]string, error) {
return servers, nil
}
// setupLoggers is used to setup the logGate, logWriter, and our logOutput
func (c *Command) setupLoggers(config *Config) (*GatedWriter, *logWriter, io.Writer) {
// Setup logging. First create the gated log writer, which will
// store logs until we're ready to show them. Then create the level
// filter, filtering logs of the specified level.
logGate := &GatedWriter{
Writer: &cli.UiWriter{Ui: c.Ui},
}
c.logFilter = LevelFilter()
c.logFilter.MinLevel = logutils.LogLevel(strings.ToUpper(config.LogLevel))
c.logFilter.Writer = logGate
if !ValidateLevelFilter(c.logFilter.MinLevel, c.logFilter) {
c.Ui.Error(fmt.Sprintf(
"Invalid log level: %s. Valid log levels are: %v",
c.logFilter.MinLevel, c.logFilter.Levels))
return nil, nil, nil
}
// Check if syslog is enabled
var syslog io.Writer
retries := 12
delay := 5 * time.Second
if config.EnableSyslog {
for i := 0; i <= retries; i++ {
l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, "consul")
if err != nil {
c.Ui.Error(fmt.Sprintf("Syslog setup error: %v", err))
if i == retries {
timeout := time.Duration(retries) * delay
c.Ui.Error(fmt.Sprintf("Syslog setup did not succeed within timeout (%s).", timeout.String()))
return nil, nil, nil
} else {
c.Ui.Error(fmt.Sprintf("Retrying syslog setup in %s...", delay.String()))
time.Sleep(delay)
}
} else {
syslog = &SyslogWrapper{l, c.logFilter}
break
}
}
}
// Create a log writer, and wrap a logOutput around it
logWriter := NewLogWriter(512)
var logOutput io.Writer
if syslog != nil {
logOutput = io.MultiWriter(c.logFilter, logWriter, syslog)
} else {
logOutput = io.MultiWriter(c.logFilter, logWriter)
}
c.logOutput = logOutput
return logGate, logWriter, logOutput
}
// setupAgent is used to start the agent and various interfaces
func (c *Command) setupAgent(config *Config, logOutput io.Writer, logWriter *logWriter) error {
func (c *Command) setupAgent(config *Config, logOutput io.Writer, logWriter *logger.LogWriter) error {
c.Ui.Output("Starting Consul agent...")
agent, err := Create(config, logOutput)
if err != nil {
@ -779,10 +724,17 @@ func (c *Command) Run(args []string) int {
}
// Setup the log outputs
logGate, logWriter, logOutput := c.setupLoggers(config)
if logWriter == nil {
logConfig := &logger.Config{
LogLevel: config.LogLevel,
EnableSyslog: config.EnableSyslog,
SyslogFacility: config.SyslogFacility,
}
logFilter, logGate, logWriter, logOutput, ok := logger.Setup(logConfig, c.Ui)
if !ok {
return 1
}
c.logFilter = logFilter
c.logOutput = logOutput
/* Setup telemetry
Aggregate on 10 second intervals for 1 minute. Expose the
@ -1055,7 +1007,7 @@ func (c *Command) handleReload(config *Config) *Config {
// Change the log level
minLevel := logutils.LogLevel(strings.ToUpper(newConf.LogLevel))
if ValidateLevelFilter(minLevel, c.logFilter) {
if logger.ValidateLevelFilter(minLevel, c.logFilter) {
c.logFilter.SetMinLevel(minLevel)
} else {
c.Ui.Error(fmt.Sprintf(

View File

@ -10,6 +10,7 @@ import (
"strings"
"testing"
"github.com/hashicorp/consul/logger"
"github.com/hashicorp/consul/testutil"
"github.com/mitchellh/cli"
)
@ -337,7 +338,7 @@ func TestSetupAgent_RPCUnixSocket_FileExists(t *testing.T) {
Ui: new(cli.MockUi),
}
logWriter := NewLogWriter(512)
logWriter := logger.NewLogWriter(512)
logOutput := new(bytes.Buffer)
// Ensure the server is created

View File

@ -32,6 +32,7 @@ import (
"sync"
"github.com/hashicorp/consul/consul/structs"
"github.com/hashicorp/consul/logger"
"github.com/hashicorp/go-msgpack/codec"
"github.com/hashicorp/logutils"
"github.com/hashicorp/serf/serf"
@ -171,7 +172,7 @@ type AgentRPC struct {
clients map[string]*rpcClient
listener net.Listener
logger *log.Logger
logWriter *logWriter
logWriter *logger.LogWriter
reloadCh chan struct{}
stop bool
stopCh chan struct{}
@ -218,7 +219,7 @@ func (c *rpcClient) String() string {
// NewAgentRPC is used to create a new Agent RPC handler
func NewAgentRPC(agent *Agent, listener net.Listener,
logOutput io.Writer, logWriter *logWriter) *AgentRPC {
logOutput io.Writer, logWriter *logger.LogWriter) *AgentRPC {
if logOutput == nil {
logOutput = os.Stderr
}
@ -513,9 +514,9 @@ func (i *AgentRPC) handleMonitor(client *rpcClient, seq uint64) error {
req.LogLevel = strings.ToUpper(req.LogLevel)
// Create a level filter
filter := LevelFilter()
filter := logger.LevelFilter()
filter.MinLevel = logutils.LogLevel(req.LogLevel)
if !ValidateLevelFilter(filter.MinLevel, filter) {
if !logger.ValidateLevelFilter(filter.MinLevel, filter) {
resp.Error = fmt.Sprintf("Unknown log level: %s", filter.MinLevel)
goto SEND
}

View File

@ -13,6 +13,7 @@ import (
"testing"
"time"
"github.com/hashicorp/consul/logger"
"github.com/hashicorp/consul/testutil"
"github.com/hashicorp/serf/serf"
)
@ -38,7 +39,7 @@ func testRPCClient(t *testing.T) *rpcParts {
}
func testRPCClientWithConfig(t *testing.T, cb func(c *Config)) *rpcParts {
lw := NewLogWriter(512)
lw := logger.NewLogWriter(512)
mult := io.MultiWriter(os.Stderr, lw)
configTry := 0

View File

@ -1,11 +1,13 @@
package agent
import (
"github.com/hashicorp/logutils"
"log"
"os"
"testing"
"time"
"github.com/hashicorp/consul/logger"
"github.com/hashicorp/logutils"
)
type MockStreamClient struct {
@ -22,7 +24,7 @@ func (m *MockStreamClient) Send(h *responseHeader, o interface{}) error {
func TestRPCLogStream(t *testing.T) {
sc := &MockStreamClient{}
filter := LevelFilter()
filter := logger.LevelFilter()
filter.MinLevel = logutils.LogLevel("INFO")
ls := newLogStream(sc, filter, 42, log.New(os.Stderr, "", log.LstdFlags))

View File

@ -15,6 +15,7 @@ import (
"github.com/hashicorp/consul/api"
"github.com/hashicorp/consul/command/agent"
"github.com/hashicorp/consul/consul"
"github.com/hashicorp/consul/logger"
"github.com/mitchellh/cli"
)
@ -61,7 +62,7 @@ func testAgentWithConfig(t *testing.T, cb func(c *agent.Config)) *agentWrapper {
t.Fatalf("err: %s", err)
}
lw := agent.NewLogWriter(512)
lw := logger.NewLogWriter(512)
mult := io.MultiWriter(os.Stderr, lw)
conf := nextConfig()

View File

@ -1,4 +1,4 @@
package agent
package logger
import (
"io"

View File

@ -1,4 +1,4 @@
package agent
package logger
import (
"bytes"

View File

@ -1,8 +1,9 @@
package agent
package logger
import (
"github.com/hashicorp/logutils"
"io/ioutil"
"github.com/hashicorp/logutils"
)
// LevelFilter returns a LevelFilter that is configured with the log

View File

@ -1,4 +1,4 @@
package agent
package logger
import (
"sync"
@ -10,19 +10,19 @@ type LogHandler interface {
HandleLog(string)
}
// logWriter implements io.Writer so it can be used as a log sink.
// LogWriter implements io.Writer so it can be used as a log sink.
// It maintains a circular buffer of logs, and a set of handlers to
// which it can stream the logs to.
type logWriter struct {
type LogWriter struct {
sync.Mutex
logs []string
index int
handlers map[LogHandler]struct{}
}
// NewLogWriter creates a logWriter with the given buffer capacity
func NewLogWriter(buf int) *logWriter {
return &logWriter{
// NewLogWriter creates a LogWriter with the given buffer capacity
func NewLogWriter(buf int) *LogWriter {
return &LogWriter{
logs: make([]string, buf),
index: 0,
handlers: make(map[LogHandler]struct{}),
@ -31,7 +31,7 @@ func NewLogWriter(buf int) *logWriter {
// RegisterHandler adds a log handler to receive logs, and sends
// the last buffered logs to the handler
func (l *logWriter) RegisterHandler(lh LogHandler) {
func (l *LogWriter) RegisterHandler(lh LogHandler) {
l.Lock()
defer l.Unlock()
@ -55,14 +55,14 @@ func (l *logWriter) RegisterHandler(lh LogHandler) {
}
// DeregisterHandler removes a LogHandler and prevents more invocations
func (l *logWriter) DeregisterHandler(lh LogHandler) {
func (l *LogWriter) DeregisterHandler(lh LogHandler) {
l.Lock()
defer l.Unlock()
delete(l.handlers, lh)
}
// Write is used to accumulate new logs
func (l *logWriter) Write(p []byte) (n int, err error) {
func (l *LogWriter) Write(p []byte) (n int, err error) {
l.Lock()
defer l.Unlock()

View File

@ -1,4 +1,4 @@
package agent
package logger
import (
"testing"

89
logger/logger.go Normal file
View File

@ -0,0 +1,89 @@
package logger
import (
"fmt"
"io"
"strings"
"time"
"github.com/hashicorp/go-syslog"
"github.com/hashicorp/logutils"
"github.com/mitchellh/cli"
)
// Config is used to set up logging.
type Config struct {
// LogLevel is the minimum level to be logged.
LogLevel string
// EnableSyslog controls forwarding to syslog.
EnableSyslog bool
// SyslogFacility is the destination for syslog forwarding.
SyslogFacility string
}
// Setup is used to perform setup of several logging objects:
//
// * A LevelFilter is used to perform filtering by log level.
// * A GatedWriter is used to buffer logs until startup UI operations are
// complete. After this is flushed then logs flow directly to output
// destinations.
// * A LogWriter provides a mean to temporarily hook logs, such as for running
// a command like "consul monitor".
// * An io.Writer is provided as the sink for all logs to flow to.
//
// The provided ui object will get any log messages related to setting up
// logging itself, and will also be hooked up to the gated logger. The final bool
// parameter indicates if logging was set up successfully.
func Setup(config *Config, ui cli.Ui) (*logutils.LevelFilter, *GatedWriter, *LogWriter, io.Writer, bool) {
// The gated writer buffers logs at startup and holds until it's flushed.
logGate := &GatedWriter{
Writer: &cli.UiWriter{ui},
}
// Set up the level filter.
logFilter := LevelFilter()
logFilter.MinLevel = logutils.LogLevel(strings.ToUpper(config.LogLevel))
logFilter.Writer = logGate
if !ValidateLevelFilter(logFilter.MinLevel, logFilter) {
ui.Error(fmt.Sprintf(
"Invalid log level: %s. Valid log levels are: %v",
logFilter.MinLevel, logFilter.Levels))
return nil, nil, nil, nil, false
}
// Set up syslog if it's enabled.
var syslog io.Writer
if config.EnableSyslog {
retries := 12
delay := 5 * time.Second
for i := 0; i <= retries; i++ {
l, err := gsyslog.NewLogger(gsyslog.LOG_NOTICE, config.SyslogFacility, "consul")
if err != nil {
ui.Error(fmt.Sprintf("Syslog setup error: %v", err))
if i == retries {
timeout := time.Duration(retries) * delay
ui.Error(fmt.Sprintf("Syslog setup did not succeed within timeout (%s).", timeout.String()))
return nil, nil, nil, nil, false
} else {
ui.Error(fmt.Sprintf("Retrying syslog setup in %s...", delay.String()))
time.Sleep(delay)
}
} else {
syslog = &SyslogWrapper{l, logFilter}
break
}
}
}
// Create a log writer, and wrap a logOutput around it
logWriter := NewLogWriter(512)
var logOutput io.Writer
if syslog != nil {
logOutput = io.MultiWriter(logFilter, logWriter, syslog)
} else {
logOutput = io.MultiWriter(logFilter, logWriter)
}
return logFilter, logGate, logWriter, logOutput, true
}

View File

@ -1,7 +1,8 @@
package agent
package logger
import (
"bytes"
"github.com/hashicorp/go-syslog"
"github.com/hashicorp/logutils"
)

View File

@ -1,4 +1,4 @@
package agent
package logger
import (
"os"

View File

@ -11,4 +11,4 @@ go build -tags="${BUILD_TAGS}" -o $TEMPDIR/consul || exit 1
# Run the tests
echo "--> Running tests"
go list ./... | grep -v '^/vendor/' | PATH=$TEMPDIR:$PATH xargs -n1 go test -tags="${BUILD_TAGS}" ${GOTEST_FLAGS:--cover -timeout=360s}
go list ./... | grep -v '/vendor/' | PATH=$TEMPDIR:$PATH xargs -n1 go test -tags="${BUILD_TAGS}" ${GOTEST_FLAGS:--cover -timeout=360s}