diff --git a/cmd/server.go b/cmd/server.go index 3e43869c5..2cd2aa5a1 100644 --- a/cmd/server.go +++ b/cmd/server.go @@ -17,6 +17,7 @@ package cmd import ( "fmt" "io" + "log" "os" "os/signal" "runtime/pprof" @@ -44,7 +45,12 @@ It will load existing data from the configured directory, and start listening client connections on the configured port.`, RunE: func(cmd *cobra.Command, args []string) error { - fmt.Fprintf(Server.Stderr, "Pilosa %s, build time %s\n", pilosa.Version, pilosa.BuildTime) + logOutput, err := server.GetLogWriter(Server.Config.LogPath, stderr) + if err != nil { + return err + } + logger := log.New(logOutput, "", log.LstdFlags) + logger.Printf("Pilosa %s, build time %s\n", pilosa.Version, pilosa.BuildTime) // Start CPU profiling. if Server.CPUProfile != "" { @@ -73,7 +79,7 @@ on the configured port.`, signal.Notify(c, os.Interrupt) select { case sig := <-c: - fmt.Fprintf(Server.Stderr, "Received %s; gracefully shutting down...\n", sig.String()) + logger.Printf("Received %s; gracefully shutting down...\n", sig.String()) // Second signal causes a hard shutdown. go func() { <-c; os.Exit(1) }() @@ -82,7 +88,7 @@ on the configured port.`, return err } case <-Server.Done: - fmt.Fprintf(Server.Stderr, "Server closed externally") + logger.Printf("Server closed externally") } return nil }, diff --git a/fragment.go b/fragment.go index 4758f18e8..278a23bcf 100644 --- a/fragment.go +++ b/fragment.go @@ -265,7 +265,7 @@ func (f *Fragment) openCache() error { // Unmarshal cache data. var pb internal.Cache if err := proto.Unmarshal(buf, &pb); err != nil { - log.Printf("error unmarshaling cache data, skipping: path=%s, err=%s", path, err) + f.logger().Printf("error unmarshaling cache data, skipping: path=%s, err=%s", path, err) return nil } diff --git a/gossip/gossip.go b/gossip/gossip.go index 1b15522a7..92ec94c97 100644 --- a/gossip/gossip.go +++ b/gossip/gossip.go @@ -18,7 +18,6 @@ import ( "fmt" "io" "log" - "os" "golang.org/x/sync/errgroup" @@ -98,9 +97,9 @@ type gossipConfig struct { } // NewGossipNodeSet returns a new instance of GossipNodeSet. -func NewGossipNodeSet(name string, gossipHost string, gossipPort int, gossipSeed string, sh pilosa.StatusHandler) *GossipNodeSet { +func NewGossipNodeSet(name string, gossipHost string, gossipPort int, gossipSeed string, server *pilosa.Server) *GossipNodeSet { g := &GossipNodeSet{ - LogOutput: os.Stderr, + LogOutput: server.LogOutput, } //TODO: pull memberlist config from pilosa.cfg file @@ -115,7 +114,7 @@ func NewGossipNodeSet(name string, gossipHost string, gossipPort int, gossipSeed g.config.memberlistConfig.AdvertisePort = gossipPort g.config.memberlistConfig.Delegate = g - g.statusHandler = sh + g.statusHandler = server return g } diff --git a/server.go b/server.go index 37fadedd5..1f09119e1 100644 --- a/server.go +++ b/server.go @@ -129,6 +129,7 @@ func (s *Server) Open() error { } // Open holder. + s.Holder.LogOutput = s.LogOutput if err := s.Holder.Open(); err != nil { return fmt.Errorf("opening Holder: %v", err) } @@ -159,7 +160,6 @@ func (s *Server) Open() error { // Initialize Holder. s.Holder.Broadcaster = s.Broadcaster - s.Holder.LogOutput = s.LogOutput // Serve HTTP. go func() { http.Serve(ln, s.Handler) }() @@ -197,14 +197,14 @@ func (s *Server) Addr() net.Addr { return s.ln.Addr() } -func (s *Server) logger() *log.Logger { return log.New(s.LogOutput, "", log.LstdFlags) } +func (s *Server) Logger() *log.Logger { return log.New(s.LogOutput, "", log.LstdFlags) } func (s *Server) monitorAntiEntropy() { t := time.Now() ticker := time.NewTicker(s.AntiEntropyInterval) defer ticker.Stop() - s.logger().Printf("holder sync monitor initializing (%s interval)", s.AntiEntropyInterval) + s.Logger().Printf("holder sync monitor initializing (%s interval)", s.AntiEntropyInterval) for { // Wait for tick or a close. @@ -215,7 +215,7 @@ func (s *Server) monitorAntiEntropy() { s.Holder.Stats.Count("AntiEntropy", 1, 1.0) } - s.logger().Printf("holder sync beginning") + s.Logger().Printf("holder sync beginning") // Initialize syncer with local holder and remote client. var syncer HolderSyncer @@ -226,12 +226,12 @@ func (s *Server) monitorAntiEntropy() { // Sync holders. if err := syncer.SyncHolder(); err != nil { - s.logger().Printf("holder sync error: err=%s", err) + s.Logger().Printf("holder sync error: err=%s", err) continue } // Record successful sync in log. - s.logger().Printf("holder sync complete") + s.Logger().Printf("holder sync complete") } dif := time.Since(t) s.Holder.Stats.Histogram("AntiEntropyDuration", float64(dif), 1.0) @@ -267,7 +267,7 @@ func (s *Server) monitorMaxSlices() { localIndex.SetRemoteMaxSlice(newmax) } } else { - s.logger().Printf("Local Index not found: %s", index) + s.Logger().Printf("Local Index not found: %s", index) } } } @@ -472,7 +472,7 @@ func (s *Server) monitorRuntime() { gcn := gcnotifier.New() defer gcn.Close() - s.logger().Printf("runtime stats initializing (%s interval)", s.MetricInterval) + s.Logger().Printf("runtime stats initializing (%s interval)", s.MetricInterval) for { // Wait for tick or a close. diff --git a/server/server.go b/server/server.go index 7738f513c..f66b88870 100644 --- a/server/server.go +++ b/server/server.go @@ -100,7 +100,8 @@ func (m *Command) Run(args ...string) (err error) { if err = m.Server.Open(); err != nil { return fmt.Errorf("server.Open: %v", err) } - fmt.Fprintf(m.Stderr, "Listening as http://%s\n", m.Server.Host) + + m.Server.Logger().Printf("Listening as http://%s\n", m.Server.Host) return nil } @@ -122,18 +123,13 @@ func (m *Command) SetupServer() error { m.Server.Cluster = cluster // Setup logging output. - if m.Config.LogPath == "" { - m.Server.LogOutput = m.Stderr - } else { - logFile, err := os.OpenFile(m.Config.LogPath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) - if err != nil { - return err - } - m.Server.LogOutput = logFile + m.Server.LogOutput, err = GetLogWriter(m.Config.LogPath, m.Stderr) + if err != nil { + return err } // Configure holder. - fmt.Fprintf(m.Stderr, "Using data from: %s\n", m.Config.DataDir) + m.Server.Logger().Printf("Using data from: %s\n", m.Config.DataDir) m.Server.Holder.Path = m.Config.DataDir m.Server.MetricInterval = time.Duration(m.Config.Metric.PollingInterval) m.Server.Holder.Stats, err = NewStatsClient(m.Config.Metric.Service, m.Config.Metric.Host) @@ -160,7 +156,7 @@ func (m *Command) SetupServer() error { switch m.Config.Cluster.Type { case "http": m.Server.Broadcaster = httpbroadcast.NewHTTPBroadcaster(m.Server, internalPortStr) - m.Server.BroadcastReceiver = httpbroadcast.NewHTTPBroadcastReceiver(internalPortStr, m.Stderr) + m.Server.BroadcastReceiver = httpbroadcast.NewHTTPBroadcastReceiver(internalPortStr, m.Server.LogOutput) m.Server.Cluster.NodeSet = httpbroadcast.NewHTTPNodeSet() err := m.Server.Cluster.NodeSet.(*httpbroadcast.HTTPNodeSet).Join(m.Server.Cluster.Nodes) if err != nil { @@ -202,6 +198,20 @@ func (m *Command) SetupServer() error { return nil } +// GetLogWriter opens a file for logging, or a default io.Writer (such as stderr) for an empty path. +func GetLogWriter(path string, defaultWriter io.Writer) (io.Writer, error) { + // This is split out so it can be used in NewServeCmd as well as SetupServer + if path == "" { + return defaultWriter, nil + } else { + logFile, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0600) + if err != nil { + return nil, err + } + return logFile, nil + } +} + func normalizeHost(host string) (string, error) { if !strings.Contains(host, ":") { host = host + ":"