From cd25e6179ef04a3db50a2cfd24d189cb4593fe63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20M=C3=BCller?= Date: Mon, 18 Sep 2017 23:19:04 +0200 Subject: [PATCH] logs added --- server.go | 28 +++++++++++++++++++++++++++- storage.go | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/server.go b/server.go index dca785e..6fc9edd 100644 --- a/server.go +++ b/server.go @@ -7,14 +7,18 @@ import ( "io/ioutil" "net/http" "net/url" + "sync" "database/sql" _ "github.com/mattn/go-sqlite3" "github.com/labstack/echo" + "github.com/labstack/gommon/log" ) +var stats = &sync.Map{} + type Template struct{ templates *template.Template } func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { @@ -23,6 +27,8 @@ func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Con func main() { e := echo.New() + e.Logger.SetLevel(log.DEBUG) + db, err := sql.Open("sqlite3", "./database.sqlite") if err != nil { e.Logger.Error(err) @@ -37,6 +43,8 @@ func main() { e.File("/index.html", "assets/public/index.html") e.File("/", "assets/public/index.html") + go persistStats(e.Logger, db, stats) + e.GET("/TOS.md", func(c echo.Context) error { n, code := md2html(c, "TOS") return c.Render(code, "Page", n) @@ -45,11 +53,21 @@ func main() { e.GET("/:id", func(c echo.Context) error { n, code := load(c, db) n.prepare() + views := n.Views + if val, ok := stats.Load(n.ID); ok { + intVal, ok := val.(int) + if ok { + views = intVal + } + } + defer stats.Store(n.ID, views+1) + c.Logger().Debugf("/%q requested; response code: %d", n.ID, code) return c.Render(code, "Note", n) }) e.GET("/:id/export", func(c echo.Context) error { n, code := load(c, db) + c.Logger().Debugf("/%q/export requested; response code: %d", n.ID, code) return c.String(code, n.Text) }) @@ -59,30 +77,36 @@ func main() { buf := bytes.NewBuffer([]byte{}) e.Renderer.Render(buf, "Stats", n, c) n.Content = template.HTML(buf.String()) + c.Logger().Debugf("/%q/stats requested; response code: %d", n.ID, code) return c.Render(code, "Note", n) }) e.GET("/:id/edit", func(c echo.Context) error { n, code := load(c, db) + c.Logger().Debugf("/%q/edit requested; response code: %d", n.ID, code) return c.Render(code, "Form", n) }) e.GET("/new", func(c echo.Context) error { + c.Logger().Debug("/new requested") return c.Render(http.StatusOK, "Form", nil) }) e.POST("/note", func(c echo.Context) error { + c.Logger().Debug("POST /note requested") vals, err := c.FormParams() if err != nil { return err } if get(vals, "tos") != "on" { code := http.StatusPreconditionFailed + c.Logger().Errorf("POST /note error: %d", code) return c.Render(code, "Note", errPage(code)) } text := get(vals, "text") if 10 > len(text) || len(text) > 50000 { code := http.StatusBadRequest + c.Logger().Errorf("POST /note error: %d", code) return c.Render(code, "Note", errPage(code, "note length not accepted")) } @@ -101,9 +125,10 @@ func main() { } else if err == errorBadRequest { code = http.StatusBadRequest } + c.Logger().Errorf("POST /note error: %d", code) return c.Render(code, "Note", errPage(code)) } - c.Logger().Infof("note %q saved", n.ID) + c.Logger().Debugf("note %q saved", n.ID) return c.Redirect(http.StatusMovedPermanently, "/"+n.ID) }) @@ -127,5 +152,6 @@ func md2html(c echo.Context, name string) (Note, int) { code := http.StatusServiceUnavailable return errPage(code), code } + c.Logger().Debugf("rendering markdown page %q", name) return Note{Title: name, Content: mdTmplHTML(mdContent)}, http.StatusOK } diff --git a/storage.go b/storage.go index c4fd231..f9c1176 100644 --- a/storage.go +++ b/storage.go @@ -11,6 +11,7 @@ import ( "net/http" "regexp" "strings" + "sync" "time" "github.com/golang-commonmark/markdown" @@ -21,7 +22,10 @@ func init() { rand.Seed(time.Now().UnixNano()) } -const idLength = 5 +const ( + idLength = 5 + statsSavingInterval = 1 * time.Minute +) var ( errorCodes = map[int]string{ @@ -31,6 +35,7 @@ var ( 412: "Precondition failed", 503: "Service unavailable", } + rexpNewLine = regexp.MustCompile("[\n\r]") rexpNonAlphaNum = regexp.MustCompile("[`~!@#$%^&*_|+=?;:'\",.<>{}\\/]") rexpNoScriptIframe = regexp.MustCompile("<.*?(iframe|script).*?>") @@ -68,6 +73,32 @@ func (n *Note) prepare() { n.Content = mdTmplHTML([]byte(n.Text)) } +func persistStats(logger echo.Logger, db *sql.DB, stats *sync.Map) { + for { + time.Sleep(statsSavingInterval) + tx, err := db.Begin() + if err != nil { + logger.Error(err) + return + } + c := 0 + stats.Range(func(id, views interface{}) bool { + stmt, _ := tx.Prepare("update notes set views = ? where id = ?") + _, err := stmt.Exec(views, id) + if err != nil { + tx.Rollback() + return false + } + stmt.Close() + defer stats.Delete(id) + c++ + return true + }) + tx.Commit() + logger.Infof("successfully persisted %d values", c) + } +} + func save(c echo.Context, db *sql.DB, n *Note) (*Note, error) { if n.Password != "" { n.Password = fmt.Sprintf("%x", sha256.Sum256([]byte(n.Password))) @@ -79,6 +110,7 @@ func save(c echo.Context, db *sql.DB, n *Note) (*Note, error) { } func update(c echo.Context, db *sql.DB, n *Note) (*Note, error) { + c.Logger().Debugf("updating note %q", n.ID) if n.Password == "" { return nil, errorBadRequest } @@ -98,10 +130,12 @@ func update(c echo.Context, db *sql.DB, n *Note) (*Note, error) { tx.Rollback() return nil, errorUnathorised } + c.Logger().Debugf("updating note %q; committing transaction", n.ID) return n, tx.Commit() } func insert(c echo.Context, db *sql.DB, n *Note) (*Note, error) { + c.Logger().Debug("inserting new note") tx, err := db.Begin() if err != nil { return nil, err @@ -119,6 +153,7 @@ func insert(c echo.Context, db *sql.DB, n *Note) (*Note, error) { return nil, err } n.ID = id + c.Logger().Debugf("inserting new note %q; commiting transaction", n.ID) return n, tx.Commit() } @@ -137,9 +172,11 @@ func randId() string { } func load(c echo.Context, db *sql.DB) (Note, int) { + q := c.Param("id") + c.Logger().Debugf("loading note %q", q) stmt, _ := db.Prepare("select * from notes where id = ?") defer stmt.Close() - row := stmt.QueryRow(c.Param("id")) + row := stmt.QueryRow(q) var id, text, password string var published time.Time var editedVal interface{}