improve logging
This commit is contained in:
49
main.go
49
main.go
@@ -17,6 +17,7 @@ import (
|
|||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"mime"
|
"mime"
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -145,6 +146,8 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slog.Info("starting 7am...")
|
||||||
|
|
||||||
_ = godotenv.Load()
|
_ = godotenv.Load()
|
||||||
err := checkEnv()
|
err := checkEnv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -225,6 +228,8 @@ func main() {
|
|||||||
go listenForSummaryUpdates(&state, locKey)
|
go listenForSummaryUpdates(&state, locKey)
|
||||||
|
|
||||||
s.Start()
|
s.Start()
|
||||||
|
|
||||||
|
slog.Info("update job scheduled", "location", locKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = loadSubscriptions(&state)
|
err = loadSubscriptions(&state)
|
||||||
@@ -234,10 +239,9 @@ func main() {
|
|||||||
|
|
||||||
http.HandleFunc("/", handleHTTPRequest(&state))
|
http.HandleFunc("/", handleHTTPRequest(&state))
|
||||||
|
|
||||||
log.Printf("server listening on %d...", *port)
|
slog.Info("server starting", "port", *port)
|
||||||
|
|
||||||
err = http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
|
err = http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to start http server: %e\n", err)
|
log.Printf("failed to start http server: %e\n", err)
|
||||||
}
|
}
|
||||||
@@ -245,6 +249,8 @@ func main() {
|
|||||||
for _, s := range schedulers {
|
for _, s := range schedulers {
|
||||||
s.Shutdown()
|
s.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
slog.Info("7am shut down")
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateKeys() {
|
func generateKeys() {
|
||||||
@@ -302,6 +308,7 @@ func handleHTTPRequest(state *state) http.HandlerFunc {
|
|||||||
|
|
||||||
reg, err := registerSubscription(state, &update)
|
reg, err := registerSubscription(state, &update)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
slog.Error("web push subscription registration failed", "error", err)
|
||||||
writer.WriteHeader(http.StatusBadRequest)
|
writer.WriteHeader(http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -309,6 +316,8 @@ func handleHTTPRequest(state *state) http.HandlerFunc {
|
|||||||
err = json.NewEncoder(writer).Encode(reg)
|
err = json.NewEncoder(writer).Encode(reg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writer.WriteHeader(http.StatusBadRequest)
|
writer.WriteHeader(http.StatusBadRequest)
|
||||||
|
} else {
|
||||||
|
slog.Info("new web push registration", "id", reg.ID)
|
||||||
}
|
}
|
||||||
} else if request.Method == "PATCH" || request.Method == "DELETE" {
|
} else if request.Method == "PATCH" || request.Method == "DELETE" {
|
||||||
parts := strings.Split(path, "/")
|
parts := strings.Split(path, "/")
|
||||||
@@ -343,6 +352,7 @@ func handleHTTPRequest(state *state) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
json.NewEncoder(writer).Encode(reg)
|
json.NewEncoder(writer).Encode(reg)
|
||||||
|
slog.Info("web push registration updated", "id", reg.ID, "locations", strings.Join(reg.Locations, ","))
|
||||||
}
|
}
|
||||||
|
|
||||||
case "DELETE":
|
case "DELETE":
|
||||||
@@ -355,6 +365,7 @@ func handleHTTPRequest(state *state) http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
writer.WriteHeader(http.StatusNoContent)
|
writer.WriteHeader(http.StatusNoContent)
|
||||||
|
slog.Info("web push registration deleted", "id", regID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,12 +439,14 @@ func loadSubscriptions(state *state) error {
|
|||||||
|
|
||||||
err := rows.Scan(&id, &locations, &j)
|
err := rows.Scan(&id, &locations, &j)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
slog.Warn("unable to load a subscription", "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
s := webpush.Subscription{}
|
s := webpush.Subscription{}
|
||||||
err = json.Unmarshal([]byte(j), &s)
|
err = json.Unmarshal([]byte(j), &s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
slog.Warn("invalid web push subscription json encountered", "id", id, "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -514,12 +527,12 @@ func updateRegisteredSubscription(state *state, id uuid.UUID, update *updateSubs
|
|||||||
func registerSubscription(state *state, sub *updateSubscription) (*registeredSubscription, error) {
|
func registerSubscription(state *state, sub *updateSubscription) (*registeredSubscription, error) {
|
||||||
j, err := json.Marshal(sub.Subscription)
|
j, err := json.Marshal(sub.Subscription)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("invalid web push subscription object: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := uuid.NewV7()
|
id, err := uuid.NewV7()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unable to generate id for subscription: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
locs := slices.Compact(sub.Locations)
|
locs := slices.Compact(sub.Locations)
|
||||||
@@ -529,7 +542,7 @@ func registerSubscription(state *state, sub *updateSubscription) (*registeredSub
|
|||||||
id, strings.Join(locs, ","), string(j),
|
id, strings.Join(locs, ","), string(j),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("unable to insert into subscriptions table: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reg := registeredSubscription{
|
reg := registeredSubscription{
|
||||||
@@ -553,7 +566,7 @@ func deleteSubscription(state *state, regID uuid.UUID) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateSummary(state *state, locKey string, loc *location) {
|
func updateSummary(state *state, locKey string, loc *location) {
|
||||||
log.Printf("updating summary for %v...\n", locKey)
|
slog.Info("updating weather summary", "location", locKey)
|
||||||
|
|
||||||
var weatherJSON string
|
var weatherJSON string
|
||||||
if state.usePlaceholder {
|
if state.usePlaceholder {
|
||||||
@@ -561,21 +574,21 @@ func updateSummary(state *state, locKey string, loc *location) {
|
|||||||
} else {
|
} else {
|
||||||
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=%v&lon=%v", loc.lat, loc.lon), nil)
|
req, err := http.NewRequest("GET", fmt.Sprintf("https://api.met.no/weatherapi/locationforecast/2.0/compact?lat=%v&lon=%v", loc.lat, loc.lon), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error querying weather data for %s: %e\n", locKey, err)
|
slog.Error("failed to query weather data", "location", locKey, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
req.Header.Set("User-Agent", state.metAPIUserAgent)
|
req.Header.Set("User-Agent", state.metAPIUserAgent)
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error updating summaries for %s: %e\n", locKey, err)
|
slog.Error("failed to query weather data", "location", locKey, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := io.ReadAll(resp.Body)
|
b, err := io.ReadAll(resp.Body)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error updating summaries for %s: %e\n", locKey, err)
|
slog.Error("failed to query weather data", "location", locKey, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -591,7 +604,7 @@ func updateSummary(state *state, locKey string, loc *location) {
|
|||||||
},
|
},
|
||||||
}}, nil)
|
}}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error updating summaries for %s: %e\n", locKey, err)
|
slog.Error("failed to generate weather summary", "location", locKey, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,7 +616,7 @@ func updateSummary(state *state, locKey string, loc *location) {
|
|||||||
c <- summary
|
c <- summary
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("updated summary for %v successfully\n", locKey)
|
slog.Info("updated weather summary", "location", locKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenForSummaryUpdates(state *state, locKey string) {
|
func listenForSummaryUpdates(state *state, locKey string) {
|
||||||
@@ -619,32 +632,36 @@ func listenForSummaryUpdates(state *state, locKey string) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case summary := <-c:
|
case summary := <-c:
|
||||||
log.Printf("sending summary for %v to subscribers...\n", locKey)
|
|
||||||
|
|
||||||
payload := webpushNotificationPayload{
|
payload := webpushNotificationPayload{
|
||||||
Summary: summary,
|
Summary: summary,
|
||||||
Location: locKey,
|
Location: locKey,
|
||||||
}
|
}
|
||||||
b, err := json.Marshal(&payload)
|
b, err := json.Marshal(&payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("error creating notification payload: %e\n", err)
|
slog.Error("failed to create web push notification payload", "location", locKey, "error", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
subs := state.subscriptions[locKey]
|
||||||
|
|
||||||
|
slog.Info("pushing weather summary to subscribers", "count", len(subs), "location", locKey)
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
for _, sub := range state.subscriptions[locKey] {
|
for _, sub := range subs {
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
_, err := webpush.SendNotificationWithContext(state.ctx, b, sub.Subscription, &opts)
|
_, err := webpush.SendNotificationWithContext(state.ctx, b, sub.Subscription, &opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to send summary for %v to sub id %v: %e\n", locKey, sub.ID, err)
|
slog.Warn("unable to send web push to subscription", "id", sub.ID, "location", locKey, "error", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
|
slog.Info("pushed weather summary to subscribers", "count", len(subs), "location", locKey)
|
||||||
|
|
||||||
case <-state.ctx.Done():
|
case <-state.ctx.Done():
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user