mirror of
https://github.com/muety/wakapi.git
synced 2023-08-10 21:12:56 +03:00
refactor: logging (resolve #169)
This commit is contained in:
parent
337b39481b
commit
dca736752e
111
config/sentry.go
111
config/sentry.go
@ -4,22 +4,100 @@ import (
|
|||||||
"github.com/emvi/logbuch"
|
"github.com/emvi/logbuch"
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
"github.com/muety/wakapi/models"
|
"github.com/muety/wakapi/models"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SentryErrorWriter struct{}
|
// How to: Logging
|
||||||
|
// Use logbuch.[Debug|Info|Warn|Error|Fatal]() by default
|
||||||
|
// Use config.Log().[Debug|Info|Warn|Error|Fatal]() when wanting the log to appear in Sentry as well
|
||||||
|
|
||||||
// TODO: extend sentry error logging to include context and stacktrace
|
type capturingWriter struct {
|
||||||
// see https://github.com/muety/wakapi/issues/169
|
Writer io.Writer
|
||||||
func (s *SentryErrorWriter) Write(p []byte) (n int, err error) {
|
Message string
|
||||||
sentry.CaptureMessage(string(p))
|
|
||||||
return os.Stderr.Write(p)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func (c *capturingWriter) Clear() {
|
||||||
logbuch.SetOutput(os.Stdout, &SentryErrorWriter{})
|
c.Message = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *capturingWriter) Write(p []byte) (n int, err error) {
|
||||||
|
c.Message = string(p)
|
||||||
|
return c.Writer.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SentryWrapperLogger is a wrapper around a logbuch.Logger that forwards events to Sentry in addition and optionally allows to attach a request context
|
||||||
|
type SentryWrapperLogger struct {
|
||||||
|
*logbuch.Logger
|
||||||
|
req *http.Request
|
||||||
|
outWriter *capturingWriter
|
||||||
|
errWriter *capturingWriter
|
||||||
|
}
|
||||||
|
|
||||||
|
func Log() *SentryWrapperLogger {
|
||||||
|
ow, ew := &capturingWriter{Writer: os.Stdout}, &capturingWriter{Writer: os.Stderr}
|
||||||
|
return &SentryWrapperLogger{
|
||||||
|
Logger: logbuch.NewLogger(ow, ew),
|
||||||
|
outWriter: ow,
|
||||||
|
errWriter: ew,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SentryWrapperLogger) Request(req *http.Request) *SentryWrapperLogger {
|
||||||
|
l.req = req
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SentryWrapperLogger) Debug(msg string, params ...interface{}) {
|
||||||
|
l.outWriter.Clear()
|
||||||
|
l.Logger.Debug(msg, params...)
|
||||||
|
l.log(l.errWriter.Message, sentry.LevelDebug)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SentryWrapperLogger) Info(msg string, params ...interface{}) {
|
||||||
|
l.outWriter.Clear()
|
||||||
|
l.Logger.Info(msg, params...)
|
||||||
|
l.log(l.errWriter.Message, sentry.LevelInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SentryWrapperLogger) Warn(msg string, params ...interface{}) {
|
||||||
|
l.outWriter.Clear()
|
||||||
|
l.Logger.Warn(msg, params...)
|
||||||
|
l.log(l.errWriter.Message, sentry.LevelWarning)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SentryWrapperLogger) Error(msg string, params ...interface{}) {
|
||||||
|
l.errWriter.Clear()
|
||||||
|
l.Logger.Error(msg, params...)
|
||||||
|
l.log(l.errWriter.Message, sentry.LevelError)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SentryWrapperLogger) Fatal(msg string, params ...interface{}) {
|
||||||
|
l.errWriter.Clear()
|
||||||
|
l.Logger.Fatal(msg, params...)
|
||||||
|
l.log(l.errWriter.Message, sentry.LevelFatal)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *SentryWrapperLogger) log(msg string, level sentry.Level) {
|
||||||
|
event := sentry.NewEvent()
|
||||||
|
event.Level = level
|
||||||
|
event.Message = msg
|
||||||
|
|
||||||
|
if l.req != nil {
|
||||||
|
if h := l.req.Context().Value(sentry.HubContextKey); h != nil {
|
||||||
|
hub := h.(*sentry.Hub)
|
||||||
|
hub.Scope().SetRequest(l.req)
|
||||||
|
if u := getPrincipal(l.req); u != nil {
|
||||||
|
hub.Scope().SetUser(sentry.User{ID: u.ID})
|
||||||
|
}
|
||||||
|
hub.CaptureEvent(event)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sentry.CaptureEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initSentry(config sentryConfig, debug bool) {
|
func initSentry(config sentryConfig, debug bool) {
|
||||||
@ -43,13 +121,10 @@ func initSentry(config sentryConfig, debug bool) {
|
|||||||
return sentry.UniformTracesSampler(config.SampleRate).Sample(ctx)
|
return sentry.UniformTracesSampler(config.SampleRate).Sample(ctx)
|
||||||
}),
|
}),
|
||||||
BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
|
BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event {
|
||||||
type principalGetter interface {
|
|
||||||
GetPrincipal() *models.User
|
|
||||||
}
|
|
||||||
if hint.Context != nil {
|
if hint.Context != nil {
|
||||||
if req, ok := hint.Context.Value(sentry.RequestContextKey).(*http.Request); ok {
|
if req, ok := hint.Context.Value(sentry.RequestContextKey).(*http.Request); ok {
|
||||||
if p := req.Context().Value("principal"); p != nil {
|
if u := getPrincipal(req); u != nil {
|
||||||
event.User.ID = p.(principalGetter).GetPrincipal().ID
|
event.User.ID = u.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,3 +134,13 @@ func initSentry(config sentryConfig, debug bool) {
|
|||||||
logbuch.Fatal("failed to initialized sentry – %v", err)
|
logbuch.Fatal("failed to initialized sentry – %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getPrincipal(r *http.Request) *models.User {
|
||||||
|
type principalGetter interface {
|
||||||
|
GetPrincipal() *models.User
|
||||||
|
}
|
||||||
|
if p := r.Context().Value("principal"); p != nil {
|
||||||
|
return p.(principalGetter).GetPrincipal()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
3
main.go
3
main.go
@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -183,7 +184,7 @@ func main() {
|
|||||||
router.Use(middlewares.NewLoggingMiddleware(logbuch.Info, []string{"/assets"}))
|
router.Use(middlewares.NewLoggingMiddleware(logbuch.Info, []string{"/assets"}))
|
||||||
router.Use(handlers.RecoveryHandler())
|
router.Use(handlers.RecoveryHandler())
|
||||||
if config.Sentry.Dsn != "" {
|
if config.Sentry.Dsn != "" {
|
||||||
router.Use(middlewares.NewSentryMiddleware())
|
router.Use(sentryhttp.New(sentryhttp.Options{Repanic: true}).Handle)
|
||||||
}
|
}
|
||||||
rootRouter.Use(middlewares.NewSecurityMiddleware())
|
rootRouter.Use(middlewares.NewSecurityMiddleware())
|
||||||
|
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
package middlewares
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"github.com/getsentry/sentry-go"
|
|
||||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SentryMiddleware struct {
|
|
||||||
handler http.Handler
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSentryMiddleware() func(http.Handler) http.Handler {
|
|
||||||
return func(h http.Handler) http.Handler {
|
|
||||||
return sentryhttp.New(sentryhttp.Options{
|
|
||||||
Repanic: true,
|
|
||||||
}).Handle(&SentryMiddleware{handler: h})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *SentryMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
||||||
ctx := context.WithValue(r.Context(), "-", "-")
|
|
||||||
h.handler.ServeHTTP(w, r.WithContext(ctx))
|
|
||||||
if hub := sentry.GetHubFromContext(ctx); hub != nil {
|
|
||||||
if user := GetPrincipal(r); user != nil {
|
|
||||||
hub.Scope().SetUser(sentry.User{ID: user.ID})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,7 +2,6 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/emvi/logbuch"
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
conf "github.com/muety/wakapi/config"
|
conf "github.com/muety/wakapi/config"
|
||||||
"github.com/muety/wakapi/middlewares"
|
"github.com/muety/wakapi/middlewares"
|
||||||
@ -83,7 +82,7 @@ func (h *HeartbeatApiHandler) Post(w http.ResponseWriter, r *http.Request) {
|
|||||||
if err := h.heartbeatSrvc.InsertBatch(heartbeats); err != nil {
|
if err := h.heartbeatSrvc.InsertBatch(heartbeats); err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(conf.ErrInternalServerError))
|
w.Write([]byte(conf.ErrInternalServerError))
|
||||||
logbuch.Error("failed to batch-insert heartbeats – %v", err)
|
conf.Log().Request(r).Error("failed to batch-insert heartbeats – %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +91,7 @@ func (h *HeartbeatApiHandler) Post(w http.ResponseWriter, r *http.Request) {
|
|||||||
if _, err := h.userSrvc.Update(user); err != nil {
|
if _, err := h.userSrvc.Update(user); err != nil {
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(conf.ErrInternalServerError))
|
w.Write([]byte(conf.ErrInternalServerError))
|
||||||
logbuch.Error("failed to update user – %v", err)
|
conf.Log().Request(r).Error("failed to update user – %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +78,7 @@ func (h *MetricsHandler) Get(w http.ResponseWriter, r *http.Request) {
|
|||||||
var metrics mm.Metrics
|
var metrics mm.Metrics
|
||||||
|
|
||||||
if userMetrics, err := h.getUserMetrics(reqUser); err != nil {
|
if userMetrics, err := h.getUserMetrics(reqUser); err != nil {
|
||||||
|
conf.Log().Request(r).Error("%v", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(conf.ErrInternalServerError))
|
w.Write([]byte(conf.ErrInternalServerError))
|
||||||
return
|
return
|
||||||
@ -89,6 +90,7 @@ func (h *MetricsHandler) Get(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if reqUser.IsAdmin {
|
if reqUser.IsAdmin {
|
||||||
if adminMetrics, err := h.getAdminMetrics(reqUser); err != nil {
|
if adminMetrics, err := h.getAdminMetrics(reqUser); err != nil {
|
||||||
|
conf.Log().Request(r).Error("%v", err)
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
w.Write([]byte(conf.ErrInternalServerError))
|
w.Write([]byte(conf.ErrInternalServerError))
|
||||||
return
|
return
|
||||||
|
@ -284,14 +284,14 @@ func (h *LoginHandler) PostResetPassword(w http.ResponseWriter, r *http.Request)
|
|||||||
go func(user *models.User) {
|
go func(user *models.User) {
|
||||||
link := fmt.Sprintf("%s/set-password?token=%s", h.config.Server.GetPublicUrl(), user.ResetToken)
|
link := fmt.Sprintf("%s/set-password?token=%s", h.config.Server.GetPublicUrl(), user.ResetToken)
|
||||||
if err := h.mailSrvc.SendPasswordReset(user, link); err != nil {
|
if err := h.mailSrvc.SendPasswordReset(user, link); err != nil {
|
||||||
logbuch.Error("failed to send password reset mail to %s – %v", user.ID, err)
|
conf.Log().Request(r).Error("failed to send password reset mail to %s – %v", user.ID, err)
|
||||||
} else {
|
} else {
|
||||||
logbuch.Info("sent password reset mail to %s", user.ID)
|
logbuch.Info("sent password reset mail to %s", user.ID)
|
||||||
}
|
}
|
||||||
}(u)
|
}(u)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logbuch.Warn("password reset requested for unregistered address '%s'", resetRequest.Email)
|
conf.Log().Request(r).Warn("password reset requested for unregistered address '%s'", resetRequest.Email)
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Redirect(w, r, fmt.Sprintf("%s/?success=%s", h.config.Server.BasePath, "an e-mail was sent to you in case your e-mail address was registered"), http.StatusFound)
|
http.Redirect(w, r, fmt.Sprintf("%s/?success=%s", h.config.Server.BasePath, "an e-mail was sent to you in case your e-mail address was registered"), http.StatusFound)
|
||||||
|
@ -450,7 +450,7 @@ func (h *SettingsHandler) actionImportWaktime(w http.ResponseWriter, r *http.Req
|
|||||||
|
|
||||||
if user.Email != "" {
|
if user.Email != "" {
|
||||||
if err := h.mailSrvc.SendImportNotification(user, time.Now().Sub(start), int(countAfter-countBefore)); err != nil {
|
if err := h.mailSrvc.SendImportNotification(user, time.Now().Sub(start), int(countAfter-countBefore)); err != nil {
|
||||||
logbuch.Error("failed to send import notification mail to %s – %v", user.ID, err)
|
conf.Log().Request(r).Error("failed to send import notification mail to %s – %v", user.ID, err)
|
||||||
} else {
|
} else {
|
||||||
logbuch.Info("sent import notification mail to %s", user.ID)
|
logbuch.Info("sent import notification mail to %s", user.ID)
|
||||||
}
|
}
|
||||||
@ -472,7 +472,7 @@ func (h *SettingsHandler) actionRegenerateSummaries(w http.ResponseWriter, r *ht
|
|||||||
|
|
||||||
go func(user *models.User) {
|
go func(user *models.User) {
|
||||||
if err := h.regenerateSummaries(user); err != nil {
|
if err := h.regenerateSummaries(user); err != nil {
|
||||||
logbuch.Error("failed to regenerate summaries for user '%s' – %v", user.ID, err)
|
conf.Log().Request(r).Error("failed to regenerate summaries for user '%s' – %v", user.ID, err)
|
||||||
}
|
}
|
||||||
}(middlewares.GetPrincipal(r))
|
}(middlewares.GetPrincipal(r))
|
||||||
|
|
||||||
@ -489,7 +489,7 @@ func (h *SettingsHandler) actionDeleteUser(w http.ResponseWriter, r *http.Reques
|
|||||||
logbuch.Info("deleting user '%s' shortly", user.ID)
|
logbuch.Info("deleting user '%s' shortly", user.ID)
|
||||||
time.Sleep(5 * time.Minute)
|
time.Sleep(5 * time.Minute)
|
||||||
if err := h.userSrvc.Delete(user); err != nil {
|
if err := h.userSrvc.Delete(user); err != nil {
|
||||||
logbuch.Error("failed to delete user '%s' – %v", user.ID, err)
|
conf.Log().Request(r).Error("failed to delete user '%s' – %v", user.ID, err)
|
||||||
} else {
|
} else {
|
||||||
logbuch.Info("successfully deleted user '%s'", user.ID)
|
logbuch.Info("successfully deleted user '%s'", user.ID)
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ func (srv *AggregationService) Run(userIds map[string]bool) error {
|
|||||||
func (srv *AggregationService) summaryWorker(jobs <-chan *AggregationJob, summaries chan<- *models.Summary) {
|
func (srv *AggregationService) summaryWorker(jobs <-chan *AggregationJob, summaries chan<- *models.Summary) {
|
||||||
for job := range jobs {
|
for job := range jobs {
|
||||||
if summary, err := srv.summaryService.Summarize(job.From, job.To, &models.User{ID: job.UserID}); err != nil {
|
if summary, err := srv.summaryService.Summarize(job.From, job.To, &models.User{ID: job.UserID}); err != nil {
|
||||||
logbuch.Error("failed to generate summary (%v, %v, %s) – %v", job.From, job.To, job.UserID, err)
|
config.Log().Error("failed to generate summary (%v, %v, %s) – %v", job.From, job.To, job.UserID, err)
|
||||||
} else {
|
} else {
|
||||||
logbuch.Info("successfully generated summary (%v, %v, %s)", job.From, job.To, job.UserID)
|
logbuch.Info("successfully generated summary (%v, %v, %s)", job.From, job.To, job.UserID)
|
||||||
summaries <- summary
|
summaries <- summary
|
||||||
@ -84,7 +84,7 @@ func (srv *AggregationService) summaryWorker(jobs <-chan *AggregationJob, summar
|
|||||||
func (srv *AggregationService) persistWorker(summaries <-chan *models.Summary) {
|
func (srv *AggregationService) persistWorker(summaries <-chan *models.Summary) {
|
||||||
for summary := range summaries {
|
for summary := range summaries {
|
||||||
if err := srv.summaryService.Insert(summary); err != nil {
|
if err := srv.summaryService.Insert(summary); err != nil {
|
||||||
logbuch.Error("failed to save summary (%v, %v, %s) – %v", summary.UserID, summary.FromTime, summary.ToTime, err)
|
config.Log().Error("failed to save summary (%v, %v, %s) – %v", summary.UserID, summary.FromTime, summary.ToTime, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ func (srv *AggregationService) trigger(jobs chan<- *AggregationJob, userIds map[
|
|||||||
|
|
||||||
var users []*models.User
|
var users []*models.User
|
||||||
if allUsers, err := srv.userService.GetAll(); err != nil {
|
if allUsers, err := srv.userService.GetAll(); err != nil {
|
||||||
logbuch.Error(err.Error())
|
config.Log().Error(err.Error())
|
||||||
return err
|
return err
|
||||||
} else if userIds != nil && len(userIds) > 0 {
|
} else if userIds != nil && len(userIds) > 0 {
|
||||||
users = make([]*models.User, 0)
|
users = make([]*models.User, 0)
|
||||||
@ -110,14 +110,14 @@ func (srv *AggregationService) trigger(jobs chan<- *AggregationJob, userIds map[
|
|||||||
// Get a map from user ids to the time of their latest summary or nil if none exists yet
|
// Get a map from user ids to the time of their latest summary or nil if none exists yet
|
||||||
lastUserSummaryTimes, err := srv.summaryService.GetLatestByUser()
|
lastUserSummaryTimes, err := srv.summaryService.GetLatestByUser()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logbuch.Error(err.Error())
|
config.Log().Error(err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a map from user ids to the time of their earliest heartbeats or nil if none exists yet
|
// Get a map from user ids to the time of their earliest heartbeats or nil if none exists yet
|
||||||
firstUserHeartbeatTimes, err := srv.heartbeatService.GetFirstByUsers()
|
firstUserHeartbeatTimes, err := srv.heartbeatService.GetFirstByUsers()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logbuch.Error(err.Error())
|
config.Log().Error(err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ func (w *WakatimeHeartbeatImporter) Import(user *models.User, minFrom time.Time,
|
|||||||
go func(user *models.User, out chan *models.Heartbeat) {
|
go func(user *models.User, out chan *models.Heartbeat) {
|
||||||
startDate, endDate, err := w.fetchRange()
|
startDate, endDate, err := w.fetchRange()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logbuch.Error("failed to fetch date range while importing wakatime heartbeats for user '%s' – %v", user.ID, err)
|
config.Log().Error("failed to fetch date range while importing wakatime heartbeats for user '%s' – %v", user.ID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,13 +55,13 @@ func (w *WakatimeHeartbeatImporter) Import(user *models.User, minFrom time.Time,
|
|||||||
|
|
||||||
userAgents, err := w.fetchUserAgents()
|
userAgents, err := w.fetchUserAgents()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logbuch.Error("failed to fetch user agents while importing wakatime heartbeats for user '%s' – %v", user.ID, err)
|
config.Log().Error("failed to fetch user agents while importing wakatime heartbeats for user '%s' – %v", user.ID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
machinesNames, err := w.fetchMachineNames()
|
machinesNames, err := w.fetchMachineNames()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logbuch.Error("failed to fetch machine names while importing wakatime heartbeats for user '%s' – %v", user.ID, err)
|
config.Log().Error("failed to fetch machine names while importing wakatime heartbeats for user '%s' – %v", user.ID, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ func (w *WakatimeHeartbeatImporter) Import(user *models.User, minFrom time.Time,
|
|||||||
d := day.Format(config.SimpleDateFormat)
|
d := day.Format(config.SimpleDateFormat)
|
||||||
heartbeats, err := w.fetchHeartbeats(d)
|
heartbeats, err := w.fetchHeartbeats(d)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logbuch.Error("failed to fetch heartbeats for day '%s' and user '%s' – &v", d, user.ID, err)
|
config.Log().Error("failed to fetch heartbeats for day '%s' and user '%s' – &v", d, user.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, h := range heartbeats {
|
for _, h := range heartbeats {
|
||||||
|
@ -42,7 +42,7 @@ type CountTotalTimeResult struct {
|
|||||||
func (srv *MiscService) ScheduleCountTotalTime() {
|
func (srv *MiscService) ScheduleCountTotalTime() {
|
||||||
// Run once initially
|
// Run once initially
|
||||||
if err := srv.runCountTotalTime(); err != nil {
|
if err := srv.runCountTotalTime(); err != nil {
|
||||||
logbuch.Error("failed to run CountTotalTimeJob: %v", err)
|
logbuch.Fatal("failed to run CountTotalTimeJob: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s := gocron.NewScheduler(time.Local)
|
s := gocron.NewScheduler(time.Local)
|
||||||
@ -80,7 +80,7 @@ func (srv *MiscService) runCountTotalTime() error {
|
|||||||
func (srv *MiscService) countTotalTimeWorker(jobs <-chan *CountTotalTimeJob, results chan<- *CountTotalTimeResult) {
|
func (srv *MiscService) countTotalTimeWorker(jobs <-chan *CountTotalTimeJob, results chan<- *CountTotalTimeResult) {
|
||||||
for job := range jobs {
|
for job := range jobs {
|
||||||
if result, err := srv.summaryService.Aliased(time.Time{}, time.Now(), &models.User{ID: job.UserID}, srv.summaryService.Retrieve, false); err != nil {
|
if result, err := srv.summaryService.Aliased(time.Time{}, time.Now(), &models.User{ID: job.UserID}, srv.summaryService.Retrieve, false); err != nil {
|
||||||
logbuch.Error("failed to count total for user %s: %v", job.UserID, err)
|
config.Log().Error("failed to count total for user %s: %v", job.UserID, err)
|
||||||
} else {
|
} else {
|
||||||
logbuch.Info("successfully counted total for user %s", job.UserID)
|
logbuch.Info("successfully counted total for user %s", job.UserID)
|
||||||
results <- &CountTotalTimeResult{
|
results <- &CountTotalTimeResult{
|
||||||
|
@ -2,7 +2,7 @@ package utils
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/emvi/logbuch"
|
"github.com/muety/wakapi/config"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,6 +10,6 @@ func RespondJSON(w http.ResponseWriter, status int, object interface{}) {
|
|||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(status)
|
w.WriteHeader(status)
|
||||||
if err := json.NewEncoder(w).Encode(object); err != nil {
|
if err := json.NewEncoder(w).Encode(object); err != nil {
|
||||||
logbuch.Error("error while writing json response: %v", err)
|
config.Log().Error("error while writing json response: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user