소스 검색

Implement a logger for requests

Tone 3 년 전
부모
커밋
1e97772ce2
4개의 변경된 파일89개의 추가작업 그리고 42개의 파일을 삭제
  1. 12 10
      cmd/root.go
  2. 58 32
      cmd/serve.go
  3. 5 0
      go.mod
  4. 14 0
      go.sum

+ 12 - 10
cmd/root.go

@@ -17,20 +17,22 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
 package cmd
 
 import (
-	"fmt"
 	"os"
 
+	"github.com/rs/zerolog"
 	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
 )
 
 var cfgFile string
 
+var log zerolog.Logger
+
 // rootCmd represents the base command when called without any subcommands
 var rootCmd = &cobra.Command{
 	Use:   "sakuin",
 	Short: "Simple but effective fileserver",
-	Long: `An HTTP fileserver in golang with a simple WebUI`,
+	Long:  `An HTTP fileserver in golang with a simple WebUI`,
 }
 
 // Execute adds all child commands to the root command and sets flags appropriately.
@@ -45,15 +47,14 @@ func Execute() {
 func init() {
 	cobra.OnInitialize(initConfig)
 
-	// Here you will define your flags and configuration settings.
-	// Cobra supports persistent flags, which, if defined here,
-	// will be global for your application.
-
 	rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.sakuin.yaml)")
 
-	// Cobra also supports local flags, which will only run
-	// when this action is called directly.
-	rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
+	host, _ := os.Hostname()
+	log = zerolog.New(os.Stdout).With().
+		Timestamp().
+		Str("role", "sakuin").
+		Str("host", host).
+		Logger()
 }
 
 // initConfig reads in config file and ENV variables if set.
@@ -72,10 +73,11 @@ func initConfig() {
 		viper.SetConfigName(".sakuin")
 	}
 
+	viper.SetEnvPrefix("sakuin")
 	viper.AutomaticEnv() // read in environment variables that match
 
 	// If a config file is found, read it in.
 	if err := viper.ReadInConfig(); err == nil {
-		fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
+		log.Info().Msgf("Using config file: %s", viper.ConfigFileUsed())
 	}
 }

+ 58 - 32
cmd/serve.go

@@ -17,16 +17,19 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
 package cmd
 
 import (
+	"errors"
 	"fmt"
 	"html/template"
 	"io/ioutil"
-	"log"
 	"net/http"
 	"os"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"github.com/dustin/go-humanize"
+	"github.com/justinas/alice"
+	"github.com/rs/zerolog/hlog"
 	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
 	"github.com/t0n3/sakuin/web"
@@ -56,30 +59,7 @@ var dataDir string
 var serveCmd = &cobra.Command{
 	Use:   "serve",
 	Short: "Start the HTTP server",
-	Run: func(cmd *cobra.Command, args []string) {
-		dataDir = viper.GetString("data-dir")
-
-		if dataDir == "" {
-			log.Fatalln("Error: please specify a data directory, can't be empty")
-		}
-
-		_, err := os.Stat(dataDir)
-		if err != nil {
-			if os.IsNotExist(err) {
-				log.Fatalln("Error: please specify a valid data directory")
-			}
-		}
-
-		port := viper.GetInt("port")
-		address := viper.GetString("listen-addr")
-
-		mux := http.NewServeMux()
-		mux.Handle("/assets/", web.AssetsHandler("/assets/", "dist"))
-		mux.HandleFunc("/", serve)
-
-		log.Printf("Starting Sakuin HTTP Server on %s:%d\n", address, port)
-		http.ListenAndServe(fmt.Sprintf("%s:%d", address, port), mux)
-	},
+	Run:   serve,
 }
 
 func init() {
@@ -93,7 +73,56 @@ func init() {
 	viper.BindPFlag("listen-addr", serveCmd.Flags().Lookup("listen-addr"))
 }
 
-func serve(w http.ResponseWriter, r *http.Request) {
+func serve(cmd *cobra.Command, args []string) {
+	dataDir = viper.GetString("data-dir")
+
+	if dataDir == "" {
+		log.Fatal().Err(errors.New("please specify a data directory, can't be empty"))
+	}
+
+	_, err := os.Stat(dataDir)
+	if err != nil {
+		if os.IsNotExist(err) {
+			log.Fatal().Err(errors.New("please specify a valid data directory"))
+		}
+	}
+
+	middleware := alice.New()
+
+	// Install the logger handler with default output on the console
+	middleware = middleware.Append(hlog.NewHandler(log))
+
+	// Install some provided extra handler to set some request's context fields.
+	// Thanks to that handler, all our logs will come with some prepopulated fields.
+	middleware = middleware.Append(hlog.AccessHandler(func(r *http.Request, status, size int, duration time.Duration) {
+		hlog.FromRequest(r).Info().
+			Str("method", r.Method).
+			Stringer("url", r.URL).
+			Int("status", status).
+			Int("size", size).
+			Dur("duration", duration).
+			Msg("")
+	}))
+	middleware = middleware.Append(hlog.RemoteAddrHandler("ip"))
+	middleware = middleware.Append(hlog.UserAgentHandler("user_agent"))
+	middleware = middleware.Append(hlog.RefererHandler("referer"))
+	middleware = middleware.Append(hlog.RequestIDHandler("req_id", "Request-Id"))
+
+	handler := middleware.Then(http.HandlerFunc(serverHandler))
+	assetsHandler := middleware.Then(web.AssetsHandler("/assets/", "dist"))
+
+	port := viper.GetInt("port")
+	address := viper.GetString("listen-addr")
+
+	mux := http.NewServeMux()
+	mux.Handle("/assets/", assetsHandler)
+	mux.Handle("/", handler)
+
+	log.Info().Msgf("Starting Sakuin HTTP Server on %s:%d", address, port)
+	http.ListenAndServe(fmt.Sprintf("%s:%d", address, port), mux)
+}
+
+func serverHandler(w http.ResponseWriter, r *http.Request) {
 	// Filepath, from the root data dir
 	fp := filepath.Join(dataDir, filepath.Clean(r.URL.Path))
 	// Cleaned filepath, without the root data dir, used for template rendering purpose
@@ -106,7 +135,6 @@ func serve(w http.ResponseWriter, r *http.Request) {
 			notFound, _ := template.ParseFS(web.NotFound, "404.html")
 			w.WriteHeader(http.StatusNotFound)
 			notFound.ExecuteTemplate(w, "404.html", nil)
-			log.Printf("404 - %s\n", cfp)
 			return
 		}
 	}
@@ -115,7 +143,7 @@ func serve(w http.ResponseWriter, r *http.Request) {
 	if info.IsDir() {
 		files, err := ioutil.ReadDir(fp)
 		if err != nil {
-			log.Fatal(err)
+			log.Error().Err(err)
 		}
 
 		// Init template variables
@@ -152,7 +180,7 @@ func serve(w http.ResponseWriter, r *http.Request) {
 		tmpl, err := template.ParseFS(web.Index, "index.html")
 		if err != nil {
 			// Log the detailed error
-			log.Println(err.Error())
+			log.Error().Err(err)
 			// Return a generic "Internal Server Error" message
 			http.Error(w, http.StatusText(500), 500)
 			return
@@ -160,10 +188,9 @@ func serve(w http.ResponseWriter, r *http.Request) {
 
 		// Return file listing in the template
 		if err := tmpl.ExecuteTemplate(w, "index.html", templateVars); err != nil {
-			log.Println(err.Error())
+			log.Error().Err(err)
 			http.Error(w, http.StatusText(500), 500)
 		}
-		log.Printf("200 - DIR %s\n", "/")
 		return
 	}
 
@@ -171,7 +198,6 @@ func serve(w http.ResponseWriter, r *http.Request) {
 		content, _ := os.Open(fp)
 		w.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", info.Name()))
 		http.ServeContent(w, r, fp, info.ModTime(), content)
-		log.Printf("200 - FILE %s\n", cfp)
 		return
 	}
 }

+ 5 - 0
go.mod

@@ -4,6 +4,8 @@ go 1.18
 
 require (
 	github.com/dustin/go-humanize v1.0.0
+	github.com/justinas/alice v1.2.0
+	github.com/rs/zerolog v1.27.0
 	github.com/spf13/cobra v1.5.0
 	github.com/spf13/viper v1.12.0
 )
@@ -13,9 +15,12 @@ require (
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/inconshreveable/mousetrap v1.0.0 // indirect
 	github.com/magiconair/properties v1.8.6 // indirect
+	github.com/mattn/go-colorable v0.1.12 // indirect
+	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/pelletier/go-toml v1.9.5 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.2 // indirect
+	github.com/rs/xid v1.3.0 // indirect
 	github.com/spf13/afero v1.8.2 // indirect
 	github.com/spf13/cast v1.5.0 // indirect
 	github.com/spf13/jwalterweatherman v1.1.0 // indirect

+ 14 - 0
go.sum

@@ -46,6 +46,7 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -64,6 +65,7 @@ github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmV
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -129,6 +131,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
+github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -138,6 +142,10 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
 github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
+github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
+github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
+github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
+github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
@@ -151,6 +159,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
+github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
+github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
+github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
 github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
@@ -307,6 +319,8 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220624220833-87e55d714810 h1:rHZQSjJdAI4Xf5Qzeh2bBc5YJIkPFVM6oDtMFYmgws0=
 golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=