mirror of
https://github.com/muety/wakapi.git
synced 2025-12-05 22:20:24 -08:00
Correct the sentry logs to use the format key:value
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ config*.yml
|
||||
pkged.go
|
||||
package-lock.json
|
||||
node_modules
|
||||
.DS_Store
|
||||
|
||||
@@ -41,7 +41,7 @@ func Log() *SentryWrapperLogger {
|
||||
|
||||
// Create a custom handler that writes to both output and error writers
|
||||
handler := slog.NewTextHandler(io.MultiWriter(ow, ew), &slog.HandlerOptions{
|
||||
Level: slog.LevelDebug, // Adjust this level as needed
|
||||
Level: slog.LevelDebug,
|
||||
})
|
||||
|
||||
return &SentryWrapperLogger{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,6 @@ func RespondJSON(w http.ResponseWriter, r *http.Request, status int, object inte
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
if err := json.NewEncoder(w).Encode(object); err != nil {
|
||||
config.Log().Request(r).Error("error while writing json response: %v", err)
|
||||
config.Log().Request(r).Error("error while writing json response", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func (s *ActivityService) getChartPastYear(user *models.User, darkTheme, hideAtt
|
||||
wp.Submit(func() {
|
||||
summary, err := s.summaryService.Retrieve(interval[0], interval[1], user, nil)
|
||||
if err != nil {
|
||||
config.Log().Warn("failed to retrieve summary for '%s' between %v and %v for activity chart", user.ID, from, to)
|
||||
config.Log().Warn("failed to retrieve summary for activity chart", "userID", user.ID, "from", from, "to", to)
|
||||
summary = models.NewEmptySummary()
|
||||
summary.FromTime = models.CustomTime(from)
|
||||
summary.ToTime = models.CustomTime(to)
|
||||
|
||||
@@ -52,10 +52,10 @@ func (srv *AggregationService) Schedule() {
|
||||
|
||||
if _, err := srv.queueDefault.DispatchCron(func() {
|
||||
if err := srv.AggregateSummaries(datastructure.New[string]()); err != nil {
|
||||
config.Log().Error("failed to generate summaries, %v", err)
|
||||
config.Log().Error("failed to generate summaries", "error", err)
|
||||
}
|
||||
}, srv.config.App.GetAggregationTimeCron()); err != nil {
|
||||
config.Log().Error("failed to schedule summary generation, %v", err)
|
||||
config.Log().Error("failed to schedule summary generation", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,14 +70,14 @@ func (srv *AggregationService) AggregateSummaries(userIds datastructure.Set[stri
|
||||
// Get a map from user ids to the time of their latest summary or nil if none exists yet
|
||||
lastUserSummaryTimes, err := srv.summaryService.GetLatestByUser()
|
||||
if err != nil {
|
||||
config.Log().Error(err.Error())
|
||||
config.Log().Error("error occurred", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get a map from user ids to the time of their earliest heartbeats or nil if none exists yet
|
||||
firstUserHeartbeatTimes, err := srv.heartbeatService.GetFirstByUsers()
|
||||
if err != nil {
|
||||
config.Log().Error(err.Error())
|
||||
config.Log().Error("error occurred", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (srv *AggregationService) AggregateSummaries(userIds datastructure.Set[stri
|
||||
if err := srv.queueWorkers.Dispatch(func() {
|
||||
srv.process(job)
|
||||
}); err != nil {
|
||||
config.Log().Error("failed to dispatch summary generation job for user '%s'", job.User.ID)
|
||||
config.Log().Error("failed to dispatch summary generation job", "userID", job.User.ID)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -138,11 +138,11 @@ func (srv *AggregationService) AggregateSummaries(userIds datastructure.Set[stri
|
||||
|
||||
func (srv *AggregationService) process(job AggregationJob) {
|
||||
if summary, err := srv.summaryService.Summarize(job.From, job.To, job.User, nil); err != nil {
|
||||
config.Log().Error("failed to generate summary (%v, %v, %s) - %v", job.From, job.To, job.User.ID, err)
|
||||
config.Log().Error("failed to generate summary", "from", job.From, "to", job.To, "userID", job.User.ID, "error", err)
|
||||
} else {
|
||||
slog.Info("successfully generated summary", "from", job.From, "to", job.To, "userID", job.User.ID)
|
||||
if err := srv.summaryService.Insert(summary); err != nil {
|
||||
config.Log().Error("failed to save summary (%v, %v, %s) - %v", summary.UserID, summary.FromTime, summary.ToTime, err)
|
||||
config.Log().Error("failed to save summary", "userID", summary.UserID, "fromTime", summary.FromTime, "toTime", summary.ToTime, "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ func (srv *AliasService) InitializeUser(userId string) error {
|
||||
|
||||
func (srv *AliasService) MayInitializeUser(userId string) {
|
||||
if err := srv.InitializeUser(userId); err != nil {
|
||||
config.Log().Error("failed to initialize user alias map for user %s", userId)
|
||||
config.Log().Error("failed to initialize user alias map", "userID", userId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ func (s *HousekeepingService) CleanInactiveUsers(before time.Time) error {
|
||||
|
||||
slog.Warn("deleting user due to inactivity and no data", "userID", u.ID)
|
||||
if err := s.userSrvc.Delete(u); err != nil {
|
||||
config.Log().Error("failed to delete user '%s'", u.ID)
|
||||
config.Log().Error("failed to delete user", "userID", u.ID)
|
||||
} else {
|
||||
i++
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func (s *HousekeepingService) CleanInactiveUsers(before time.Time) error {
|
||||
func (s *HousekeepingService) WarmUserProjectStatsCache(user *models.User) error {
|
||||
slog.Info("pre-warming project stats cache for user", "userID", user.ID)
|
||||
if _, err := s.heartbeatSrvc.GetUserProjectStats(user, time.Time{}, utils.BeginOfToday(time.Local), nil, true); err != nil {
|
||||
config.Log().Error("failed to pre-warm project stats cache for '%s', %v", user.ID, err)
|
||||
config.Log().Error("failed to pre-warm project stats cache", "userID", user.ID, "error", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -94,14 +94,14 @@ func (s *HousekeepingService) runWarmProjectStatsCache() {
|
||||
// fetch active users
|
||||
users, err := s.userSrvc.GetActive(false)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to get active users for project stats cache warming, %v\n", err)
|
||||
config.Log().Error("failed to get active users for project stats cache warming", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// fetch user heartbeat counts
|
||||
userHeartbeatCounts, err := s.heartbeatSrvc.CountByUsers(users)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to count user heartbeats for project stats cache warming, %v\n", err)
|
||||
config.Log().Error("failed to count user heartbeats for project stats cache warming", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ func (s *HousekeepingService) runWarmProjectStatsCache() {
|
||||
})
|
||||
s.queueWorkers.Dispatch(func() {
|
||||
if err := s.WarmUserProjectStatsCache(user); err != nil {
|
||||
config.Log().Error("failed to pre-warm project stats cache for '%s'", user.ID)
|
||||
config.Log().Error("failed to pre-warm project stats cache", "userID", user.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -127,7 +127,7 @@ func (s *HousekeepingService) runCleanData() {
|
||||
// fetch all users
|
||||
users, err := s.userSrvc.GetAll()
|
||||
if err != nil {
|
||||
config.Log().Error("failed to get users for data cleanup, %v", err)
|
||||
config.Log().Error("failed to get users for data cleanup", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ func (s *HousekeepingService) runCleanData() {
|
||||
user := *u
|
||||
s.queueWorkers.Dispatch(func() {
|
||||
if err := s.CleanUserDataBefore(&user, user.MinDataAge()); err != nil {
|
||||
config.Log().Error("failed to clear old user data for '%s'", user.ID)
|
||||
config.Log().Error("failed to clear old user data", "userID", user.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -153,7 +153,7 @@ func (s *HousekeepingService) runCleanInactiveUsers() {
|
||||
return
|
||||
}
|
||||
if err := s.CleanInactiveUsers(time.Now().AddDate(0, -s.config.App.MaxInactiveMonths, 0)); err != nil {
|
||||
config.Log().Error("failed to clean up inactive users, %v", err)
|
||||
config.Log().Error("failed to clean up inactive users", "error", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -169,7 +169,7 @@ func (s *HousekeepingService) scheduleDataCleanups() {
|
||||
|
||||
_, err := s.queueDefault.DispatchCron(s.runCleanData, s.config.App.DataCleanupTime)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to dispatch data cleanup jobs, %v", err)
|
||||
config.Log().Error("failed to dispatch data cleanup jobs", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,7 +182,7 @@ func (s *HousekeepingService) scheduleInactiveUsersCleanup() {
|
||||
|
||||
_, err := s.queueDefault.DispatchCron(s.runCleanInactiveUsers, s.config.App.DataCleanupTime)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to dispatch inactive users cleanup job, %v", err)
|
||||
config.Log().Error("failed to dispatch inactive users cleanup job", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,13 +191,13 @@ func (s *HousekeepingService) scheduleProjectStatsCacheWarming() {
|
||||
|
||||
_, err := s.queueDefault.DispatchEvery(s.runWarmProjectStatsCache, 12*time.Hour)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to dispatch pre-warming project stats cache, %v", err)
|
||||
config.Log().Error("failed to dispatch pre-warming project stats cache", "error", err)
|
||||
}
|
||||
|
||||
// run once initially, 1 min after start
|
||||
if !s.config.QuickStart {
|
||||
if err := s.queueDefault.DispatchIn(s.runWarmProjectStatsCache, 1*time.Minute); err != nil {
|
||||
config.Log().Error("failed to dispatch pre-warming project stats cache, %v", err)
|
||||
config.Log().Error("failed to dispatch pre-warming project stats cache", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,13 +83,13 @@ func (w *WakatimeDumpImporter) Import(user *models.User, minFrom time.Time, maxT
|
||||
}
|
||||
|
||||
onDumpFailed := func(err error, user *models.User) {
|
||||
config.Log().Error("fetching data dump for user '%s' failed - %v", user.ID, err)
|
||||
config.Log().Error("fetching data dump for user failed", "userID", user.ID, "error", err)
|
||||
readyPollTimer.Stop()
|
||||
close(out)
|
||||
}
|
||||
|
||||
onDumpReady := func(dump *wakatime.DataDumpData, user *models.User, out chan *models.Heartbeat) {
|
||||
config.Log().Info("data dump for user '%s' is available for download", user.ID)
|
||||
config.Log().Info("data dump for user is available for download", "userID", user.ID)
|
||||
readyPollTimer.Stop()
|
||||
defer close(out)
|
||||
|
||||
@@ -97,7 +97,7 @@ func (w *WakatimeDumpImporter) Import(user *models.User, minFrom time.Time, maxT
|
||||
req, _ := http.NewRequest(http.MethodGet, dump.DownloadUrl, nil)
|
||||
res, err := utils.RaiseForStatus((&http.Client{Timeout: 5 * time.Minute}).Do(req))
|
||||
if err != nil {
|
||||
config.Log().Error("failed to download %s - %v", dump.DownloadUrl, err)
|
||||
config.Log().Error("failed to download data dump", "url", dump.DownloadUrl, "error", err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
@@ -107,19 +107,19 @@ func (w *WakatimeDumpImporter) Import(user *models.User, minFrom time.Time, maxT
|
||||
// decode
|
||||
var data wakatime.JsonExportViewModel
|
||||
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
|
||||
config.Log().Error("failed to decode data dump for user '%s' ('%s') - %v", user.ID, dump.DownloadUrl, err)
|
||||
config.Log().Error("failed to decode data dump for user", "userID", user.ID, "url", dump.DownloadUrl, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
// fetch user agents and machine names
|
||||
var userAgents map[string]*wakatime.UserAgentEntry
|
||||
if userAgents, err = fetchUserAgents(config.WakatimeApiUrl, w.apiKey); err != nil {
|
||||
config.Log().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", "userID", user.ID, "error", err)
|
||||
return
|
||||
}
|
||||
var machinesNames map[string]*wakatime.MachineEntry
|
||||
if machinesNames, err = fetchMachineNames(config.WakatimeApiUrl, w.apiKey); err != nil {
|
||||
config.Log().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", "userID", user.ID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ func (w *WakatimeHeartbeatsImporter) Import(user *models.User, minFrom time.Time
|
||||
|
||||
startDate, endDate, err := w.fetchRange(baseUrl)
|
||||
if err != nil {
|
||||
config.Log().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", "userID", user.ID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ func (w *WakatimeHeartbeatsImporter) Import(user *models.User, minFrom time.Time
|
||||
userAgents = data
|
||||
} else if strings.Contains(baseUrl, "wakatime.com") {
|
||||
// when importing from wakatime, resolving user agents is mandatorily required
|
||||
config.Log().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", "userID", user.ID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func (w *WakatimeHeartbeatsImporter) Import(user *models.User, minFrom time.Time
|
||||
machinesNames = data
|
||||
} else if strings.Contains(baseUrl, "wakatime.com") {
|
||||
// when importing from wakatime, resolving machine names is mandatorily required
|
||||
config.Log().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", "userID", user.ID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (w *WakatimeHeartbeatsImporter) Import(user *models.User, minFrom time.Time
|
||||
d := d.Format(config.SimpleDateFormat)
|
||||
heartbeats, err := w.fetchHeartbeats(d, baseUrl)
|
||||
if err != nil {
|
||||
config.Log().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 and user", "day", d, "userID", user.ID, "error", err)
|
||||
}
|
||||
|
||||
for _, h := range heartbeats {
|
||||
@@ -124,7 +124,7 @@ func (w *WakatimeHeartbeatsImporter) Import(user *models.User, minFrom time.Time
|
||||
if err := w.queue.Dispatch(func() {
|
||||
process(user, minFrom, maxTo, out)
|
||||
}); err != nil {
|
||||
config.Log().Error("failed to dispatch wakatime import job for user '%s', %v", user.ID, err)
|
||||
config.Log().Error("failed to dispatch wakatime import job for user", "userID", user.ID, "error", err)
|
||||
}
|
||||
|
||||
return out, nil
|
||||
|
||||
@@ -57,7 +57,7 @@ func NewLeaderboardService(leaderboardRepo repositories.ILeaderboardRepository,
|
||||
|
||||
exists, err := srv.ExistsAnyByUser(user.ID)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to check existing leaderboards upon user update - %v", err)
|
||||
config.Log().Error("failed to check existing leaderboards upon user update", "error", err)
|
||||
}
|
||||
|
||||
if user.PublicLeaderboard && !exists {
|
||||
@@ -66,7 +66,7 @@ func NewLeaderboardService(leaderboardRepo repositories.ILeaderboardRepository,
|
||||
} else if !user.PublicLeaderboard && exists {
|
||||
slog.Info("clearing leaderboard after settings update", "userID", user.ID)
|
||||
if err := srv.repository.DeleteByUser(user.ID); err != nil {
|
||||
config.Log().Error("failed to clear leaderboard for user '%s' - %v", user.ID, err)
|
||||
config.Log().Error("failed to clear leaderboard for user", "userID", user.ID, "error", err)
|
||||
}
|
||||
srv.cache.Flush()
|
||||
}
|
||||
@@ -86,7 +86,7 @@ func (srv *LeaderboardService) Schedule() {
|
||||
generate := func() {
|
||||
users, err := srv.userService.GetAllByLeaderboard(true)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to get users for leaderboard generation - %v", err)
|
||||
config.Log().Error("failed to get users for leaderboard generation", "error", err)
|
||||
return
|
||||
}
|
||||
srv.ComputeLeaderboard(users, srv.defaultScope, []uint8{models.SummaryLanguage})
|
||||
@@ -94,7 +94,7 @@ func (srv *LeaderboardService) Schedule() {
|
||||
|
||||
for _, cronExp := range srv.config.App.GetLeaderboardGenerationTimeCron() {
|
||||
if _, err := srv.queueDefault.DispatchCron(generate, cronExp); err != nil {
|
||||
config.Log().Error("failed to schedule leaderboard generation (%s), %v", cronExp, err)
|
||||
config.Log().Error("failed to schedule leaderboard generation", "cronExpression", cronExp, "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,25 +104,25 @@ func (srv *LeaderboardService) ComputeLeaderboard(users []*models.User, interval
|
||||
|
||||
for _, user := range users {
|
||||
if err := srv.repository.DeleteByUserAndInterval(user.ID, interval); err != nil {
|
||||
config.Log().Error("failed to delete leaderboard items for user %s (interval %s) - %v", user.ID, (*interval)[0], err)
|
||||
config.Log().Error("failed to delete leaderboard items for user", "userID", user.ID, "interval", (*interval)[0], "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
item, err := srv.GenerateByUser(user, interval)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to generate general leaderboard for user %s - %v", user.ID, err)
|
||||
config.Log().Error("failed to generate general leaderboard for user", "userID", user.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err := srv.repository.InsertBatch([]*models.LeaderboardItem{item}); err != nil {
|
||||
config.Log().Error("failed to persist general leaderboard for user %s - %v", user.ID, err)
|
||||
config.Log().Error("failed to persist general leaderboard for user", "userID", user.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, by := range by {
|
||||
items, err := srv.GenerateAggregatedByUser(user, interval, by)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to generate aggregated (by %s) leaderboard for user %s - %v", models.GetEntityColumn(by), user.ID, err)
|
||||
config.Log().Error("failed to generate aggregated leaderboard for user", "aggregatedBy", models.GetEntityColumn(by), "userID", user.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ func (srv *LeaderboardService) ComputeLeaderboard(users []*models.User, interval
|
||||
}
|
||||
|
||||
if err := srv.repository.InsertBatch(items); err != nil {
|
||||
config.Log().Error("failed to persist aggregated (by %s) leaderboard for user %s - %v", models.GetEntityColumn(by), user.ID, err)
|
||||
config.Log().Error("failed to persist aggregated leaderboard for user", "aggregatedBy", models.GetEntityColumn(by), "userID", user.ID, "error", err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -184,7 +184,7 @@ func (srv *LeaderboardService) GetAggregatedByInterval(interval *models.Interval
|
||||
if resolveUsers {
|
||||
users, err := srv.userService.GetManyMapped(models.Leaderboard(items).UserIDs())
|
||||
if err != nil {
|
||||
config.Log().Error("failed to resolve users for leaderboard item - %v", err)
|
||||
config.Log().Error("failed to resolve users for leaderboard item", "error", err)
|
||||
} else {
|
||||
for _, item := range items {
|
||||
if u, ok := users[item.UserID]; ok {
|
||||
@@ -213,7 +213,7 @@ func (srv *LeaderboardService) GetAggregatedByIntervalAndUser(interval *models.I
|
||||
if resolveUser {
|
||||
u, err := srv.userService.GetUserById(userId)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to resolve user for leaderboard item - %v", err)
|
||||
config.Log().Error("failed to resolve user for leaderboard item", "error", err)
|
||||
} else {
|
||||
for _, item := range items {
|
||||
item.User = u
|
||||
|
||||
@@ -58,35 +58,35 @@ func NewMiscService(userService IUserService, heartbeatService IHeartbeatService
|
||||
func (srv *MiscService) Schedule() {
|
||||
slog.Info("scheduling total time counting")
|
||||
if _, err := srv.queueDefault.DispatchEvery(srv.CountTotalTime, countUsersEvery); err != nil {
|
||||
config.Log().Error("failed to schedule user counting jobs, %v", err)
|
||||
config.Log().Error("failed to schedule user counting jobs", "error", err)
|
||||
}
|
||||
|
||||
slog.Info("scheduling first data computing")
|
||||
if _, err := srv.queueDefault.DispatchEvery(srv.ComputeOldestHeartbeats, computeOldestDataEvery); err != nil {
|
||||
config.Log().Error("failed to schedule first data computing jobs, %v", err)
|
||||
config.Log().Error("failed to schedule first data computing jobs", "error", err)
|
||||
}
|
||||
|
||||
if srv.config.Subscriptions.Enabled && srv.config.Subscriptions.ExpiryNotifications && srv.config.App.DataRetentionMonths > 0 {
|
||||
slog.Info("scheduling subscription notifications")
|
||||
if _, err := srv.queueDefault.DispatchEvery(srv.NotifyExpiringSubscription, notifyExpiringSubscriptionsEvery); err != nil {
|
||||
config.Log().Error("failed to schedule subscription notification jobs, %v", err)
|
||||
config.Log().Error("failed to schedule subscription notification jobs", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// run once initially for a fresh instance
|
||||
if !srv.existsUsersTotalTime() {
|
||||
if err := srv.queueDefault.Dispatch(srv.CountTotalTime); err != nil {
|
||||
config.Log().Error("failed to dispatch user counting jobs, %v", err)
|
||||
config.Log().Error("failed to dispatch user counting jobs", "error", err)
|
||||
}
|
||||
}
|
||||
if !srv.existsUsersFirstData() {
|
||||
if err := srv.queueDefault.Dispatch(srv.ComputeOldestHeartbeats); err != nil {
|
||||
config.Log().Error("failed to dispatch first data computing jobs, %v", err)
|
||||
config.Log().Error("failed to dispatch first data computing jobs", "error", err)
|
||||
}
|
||||
}
|
||||
if !srv.existsSubscriptionNotifications() && srv.config.Subscriptions.Enabled && srv.config.Subscriptions.ExpiryNotifications && srv.config.App.DataRetentionMonths > 0 {
|
||||
if err := srv.queueDefault.Dispatch(srv.NotifyExpiringSubscription); err != nil {
|
||||
config.Log().Error("failed to schedule subscription notification jobs, %v", err)
|
||||
config.Log().Error("failed to schedule subscription notification jobs", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +100,7 @@ func (srv *MiscService) CountTotalTime() {
|
||||
|
||||
users, err := srv.userService.GetAll()
|
||||
if err != nil {
|
||||
config.Log().Error("failed to fetch users for time counting, %v", err)
|
||||
config.Log().Error("failed to fetch users for time counting", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ func (srv *MiscService) CountTotalTime() {
|
||||
defer pendingJobs.Done()
|
||||
totalTime.Add(srv.countUserTotalTime(user.ID))
|
||||
}); err != nil {
|
||||
config.Log().Error("failed to enqueue counting job for user '%s'", user.ID)
|
||||
config.Log().Error("failed to enqueue counting job for user", "userID", user.ID)
|
||||
pendingJobs.Done()
|
||||
}
|
||||
}
|
||||
@@ -126,14 +126,14 @@ func (srv *MiscService) CountTotalTime() {
|
||||
Key: config.KeyLatestTotalTime,
|
||||
Value: totalTime.Load().String(),
|
||||
}); err != nil {
|
||||
config.Log().Error("failed to save total time count: %v", err)
|
||||
config.Log().Error("failed to save total time count", "error", err)
|
||||
}
|
||||
|
||||
if err := srv.keyValueService.PutString(&models.KeyStringValue{
|
||||
Key: config.KeyLatestTotalUsers,
|
||||
Value: strconv.Itoa(len(users)),
|
||||
}); err != nil {
|
||||
config.Log().Error("failed to save total users count: %v", err)
|
||||
config.Log().Error("failed to save total users count", "error", err)
|
||||
}
|
||||
} else {
|
||||
config.Log().Error("waiting for user counting jobs timed out")
|
||||
@@ -153,7 +153,7 @@ func (srv *MiscService) ComputeOldestHeartbeats() {
|
||||
|
||||
results, err := srv.heartbeatService.GetFirstByUsers()
|
||||
if err != nil {
|
||||
config.Log().Error("failed to compute users' first data, %v", err)
|
||||
config.Log().Error("failed to compute users' first data", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -167,11 +167,11 @@ func (srv *MiscService) ComputeOldestHeartbeats() {
|
||||
Key: kvKey,
|
||||
Value: entry.Time.T().Format(time.RFC822Z),
|
||||
}); err != nil {
|
||||
config.Log().Error("failed to save user's first heartbeat time: %v", err)
|
||||
config.Log().Error("failed to save user's first heartbeat time", "error", err)
|
||||
}
|
||||
}
|
||||
}); err != nil {
|
||||
config.Log().Error("failed to enqueue computing first data for user, %v", err)
|
||||
config.Log().Error("failed to enqueue computing first data for user", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ func (srv *MiscService) NotifyExpiringSubscription() {
|
||||
|
||||
users, err := srv.userService.GetAll()
|
||||
if err != nil {
|
||||
config.Log().Error("failed to fetch users for subscription notifications, %v", err)
|
||||
config.Log().Error("failed to fetch users for subscription notifications", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -203,13 +203,13 @@ func (srv *MiscService) NotifyExpiringSubscription() {
|
||||
return strings.Replace(kv.Key, config.KeySubscriptionNotificationSent+"_", "", 1)
|
||||
})
|
||||
} else {
|
||||
config.Log().Error("failed to fetch key-values for subscription notifications, %v", err)
|
||||
config.Log().Error("failed to fetch key-values for subscription notifications", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, u := range users {
|
||||
if u.HasActiveSubscription() && u.Email == "" {
|
||||
config.Log().Warn("invalid state: user '%s' has active subscription but no e-mail address set", u.ID)
|
||||
config.Log().Warn("invalid state: user has active subscription but no e-mail address set", "userID", u.ID)
|
||||
}
|
||||
|
||||
var alreadySent bool
|
||||
@@ -218,7 +218,7 @@ func (srv *MiscService) NotifyExpiringSubscription() {
|
||||
if sendDate, err := time.Parse(time.RFC822Z, kvs[0].Value); err == nil && now.Sub(sendDate) <= notifyBeforeSubscriptionExpiry {
|
||||
alreadySent = true
|
||||
} else if err != nil {
|
||||
config.Log().Error("failed to parse date for last sent subscription notification mail for user '%s', %v", u.ID, err)
|
||||
config.Log().Error("failed to parse date for last sent subscription notification mail", "userID", u.ID, "error", err)
|
||||
alreadySent = true
|
||||
}
|
||||
}
|
||||
@@ -241,7 +241,7 @@ func (srv *MiscService) NotifyExpiringSubscription() {
|
||||
func (srv *MiscService) countUserTotalTime(userId string) time.Duration {
|
||||
result, err := srv.summaryService.Aliased(time.Time{}, time.Now(), &models.User{ID: userId}, srv.summaryService.Retrieve, nil, false)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to count total for user %s: %v", userId, err)
|
||||
config.Log().Error("failed to count total for user", "userID", userId, "error", err)
|
||||
return 0
|
||||
}
|
||||
return result.TotalTime()
|
||||
@@ -254,7 +254,7 @@ func (srv *MiscService) sendSubscriptionNotificationScheduled(user *models.User,
|
||||
defer time.Sleep(10 * time.Second)
|
||||
|
||||
if err := srv.mailService.SendSubscriptionNotification(&u, hasExpired); err != nil {
|
||||
config.Log().Error("failed to send subscription notification mail to user '%s', %v", u.ID, err)
|
||||
config.Log().Error("failed to send subscription notification mail to user", "userID", u.ID, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -262,7 +262,7 @@ func (srv *MiscService) sendSubscriptionNotificationScheduled(user *models.User,
|
||||
Key: fmt.Sprintf("%s_%s", config.KeySubscriptionNotificationSent, u.ID),
|
||||
Value: time.Now().Format(time.RFC822Z),
|
||||
}); err != nil {
|
||||
config.Log().Error("failed to update subscription notification status key-value for user %s, %v", u.ID, err)
|
||||
config.Log().Error("failed to update subscription notification status key-value for user", "userID", u.ID, "error", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -270,7 +270,7 @@ func (srv *MiscService) sendSubscriptionNotificationScheduled(user *models.User,
|
||||
func (srv *MiscService) existsUsersTotalTime() bool {
|
||||
results, err := srv.keyValueService.GetByPrefix(config.KeyLatestTotalTime)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to fetch latest time key-values, %v", err)
|
||||
config.Log().Error("failed to fetch latest time key-values", "error", err)
|
||||
}
|
||||
return len(results) > 0
|
||||
}
|
||||
@@ -278,7 +278,7 @@ func (srv *MiscService) existsUsersTotalTime() bool {
|
||||
func (srv *MiscService) existsUsersFirstData() bool {
|
||||
results, err := srv.keyValueService.GetByPrefix(config.KeyFirstHeartbeat)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to fetch first heartbeats key-values, %v", err)
|
||||
config.Log().Error("failed to fetch first heartbeats key-values", "error", err)
|
||||
}
|
||||
return len(results) > 0
|
||||
}
|
||||
@@ -286,7 +286,7 @@ func (srv *MiscService) existsUsersFirstData() bool {
|
||||
func (srv *MiscService) existsSubscriptionNotifications() bool {
|
||||
results, err := srv.keyValueService.GetByPrefix(config.KeySubscriptionNotificationSent)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to fetch notifications key-values, %v", err)
|
||||
config.Log().Error("failed to fetch notifications key-values", "error", err)
|
||||
}
|
||||
return len(results) > 0
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func (srv *ReportService) Schedule() {
|
||||
t0 := time.Now()
|
||||
|
||||
if err := srv.SendReport(u, reportRange); err != nil {
|
||||
config.Log().Error("failed to generate report for '%s', %v", u.ID, err)
|
||||
config.Log().Error("failed to generate report", "userID", u.ID, "error", err)
|
||||
}
|
||||
|
||||
// make the job take at least reportDelay seconds
|
||||
@@ -62,7 +62,7 @@ func (srv *ReportService) Schedule() {
|
||||
time.Sleep(diff)
|
||||
}
|
||||
}); err != nil {
|
||||
config.Log().Error("failed to dispatch report generation job for user '%s', %v", u.ID, err)
|
||||
config.Log().Error("failed to dispatch report generation job for user", "userID", u.ID, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ func (srv *ReportService) Schedule() {
|
||||
// fetch all users with reports enabled
|
||||
users, err := srv.userService.GetAllByReports(true)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to get users for report generation, %v", err)
|
||||
config.Log().Error("failed to get users for report generation", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ func (srv *ReportService) Schedule() {
|
||||
}, srv.config.App.GetWeeklyReportCron())
|
||||
|
||||
if err != nil {
|
||||
config.Log().Error("failed to dispatch report generation jobs, %v", err)
|
||||
config.Log().Error("failed to dispatch report generation jobs", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ func (srv *ReportService) SendReport(user *models.User, duration time.Duration)
|
||||
|
||||
fullSummary, err := srv.summaryService.Aliased(start, end, user, srv.summaryService.Retrieve, nil, false)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to generate report for '%s' - %v", user.ID, err)
|
||||
config.Log().Error("failed to generate report", "userID", user.ID, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ func (srv *ReportService) SendReport(user *models.User, duration time.Duration)
|
||||
from, to := datetime.BeginOfDay(interval[0]), interval[1]
|
||||
summary, err := srv.summaryService.Aliased(from, to, user, srv.summaryService.Retrieve, nil, false)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to generate day summary (%v to %v) for report for '%s' - %v", from, to, user.ID, err)
|
||||
config.Log().Error("failed to generate day summary for report", "from", from, "to", to, "userID", user.ID, "error", err)
|
||||
break
|
||||
}
|
||||
summary.FromTime = models.CustomTime(from)
|
||||
@@ -133,7 +133,7 @@ func (srv *ReportService) SendReport(user *models.User, duration time.Duration)
|
||||
}
|
||||
|
||||
if err := srv.mailService.SendReport(user, report); err != nil {
|
||||
config.Log().Error("failed to send report for '%s', %v", user.ID, err)
|
||||
config.Log().Error("failed to send report", "userID", user.ID, "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ func (srv *SummaryService) withProjectLabels(summary *models.Summary) *models.Su
|
||||
|
||||
allLabels, err := srv.projectLabelService.GetByUser(summary.UserID)
|
||||
if err != nil {
|
||||
config.Log().Error("failed to retrieve project labels for user summary ('%s', '%s', '%s')", summary.UserID, summary.FromTime.String(), summary.ToTime.String())
|
||||
config.Log().Error("failed to retrieve project labels for user summary", "userID", summary.UserID, "fromTime", summary.FromTime.String(), "toTime", summary.ToTime.String())
|
||||
return summary
|
||||
}
|
||||
|
||||
|
||||
@@ -42,12 +42,12 @@ func NewUserService(mailService IMailService, userRepo repositories.IUserReposit
|
||||
slog.Warn("resetting wakatime api key for user due to too many failures", "userID", user.ID, "failureCount", n)
|
||||
|
||||
if _, err := srv.SetWakatimeApiCredentials(user, "", ""); err != nil {
|
||||
config.Log().Error("failed to set wakatime api key for user %s", user.ID)
|
||||
config.Log().Error("failed to set wakatime api key for user", "userID", user.ID)
|
||||
}
|
||||
|
||||
if user.Email != "" {
|
||||
if err := mailService.SendWakatimeFailureNotification(user, n); err != nil {
|
||||
config.Log().Error("failed to send wakatime failure notification mail to user %s", user.ID)
|
||||
config.Log().Error("failed to send wakatime failure notification mail to user", "userID", user.ID)
|
||||
} else {
|
||||
slog.Info("sent wakatime connection failure mail", "userID", user.ID)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user