infra: add logging wrapping

Resolves issues with gcp cloud logging quirks such as field names
This commit is contained in:
Arjun Patel
2026-05-27 15:49:27 -07:00
parent e804cee229
commit 563c91e7d5
30 changed files with 347 additions and 250 deletions
+47 -45
View File
@@ -5,12 +5,15 @@ import (
"encoding/json"
"errors"
"fmt"
"log/slog"
"net/http"
"strings"
"time"
"github.com/flowy-live/llink/internal/utils/flog"
"cloud.google.com/go/firestore"
"github.com/livekit/protocol/webhook"
"github.com/flowy-live/llink/internal/auth"
"github.com/flowy-live/llink/internal/billing"
"github.com/flowy-live/llink/internal/depot"
@@ -22,7 +25,6 @@ import (
"github.com/flowy-live/llink/internal/particle"
"github.com/flowy-live/llink/internal/utils"
"github.com/flowy-live/llink/internal/waitlist"
"github.com/livekit/protocol/webhook"
)
type Handler struct {
@@ -187,13 +189,13 @@ func (h *Handler) RequestSignInCode(w http.ResponseWriter, r *http.Request) {
_, err := h.humanSvc.GetOrCreateByEmail(r.Context(), req.Email)
if err != nil {
slog.Error("failed to get or create human", "error", err, "email", req.Email)
flog.Error("failed to get or create human", "error", err, "email", req.Email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
if err := h.authSvc.RequestSignInCode(r.Context(), req.Email); err != nil {
slog.Error("failed to request sign-in code", "error", err, "email", req.Email)
flog.Error("failed to request sign-in code", "error", err, "email", req.Email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -220,7 +222,7 @@ func (h *Handler) SignIn(w http.ResponseWriter, r *http.Request) {
http.Error(w, "human not found", http.StatusNotFound)
return
}
slog.Error("failed to get human for sign-in", "error", err, "email", req.Email)
flog.Error("failed to get human for sign-in", "error", err, "email", req.Email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -231,7 +233,7 @@ func (h *Handler) SignIn(w http.ResponseWriter, r *http.Request) {
http.Error(w, "invalid code", http.StatusUnauthorized)
return
}
slog.Error("failed to verify sign-in code", "error", err, "email", req.Email)
flog.Error("failed to verify sign-in code", "error", err, "email", req.Email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -256,7 +258,7 @@ func (h *Handler) FirebaseToken(w http.ResponseWriter, r *http.Request) {
token, err := h.authSvc.MintFirebaseCustomToken(r.Context(), humanId)
if err != nil {
slog.Error("failed to mint Firebase custom token", "error", err, "humanId", humanId)
flog.Error("failed to mint Firebase custom token", "error", err, "humanId", humanId)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -273,7 +275,7 @@ func (h *Handler) SignOut(w http.ResponseWriter, r *http.Request) {
}
if err := h.authSvc.SignOut(r.Context(), token); err != nil {
slog.Error("failed to sign out", "error", err)
flog.Error("failed to sign out", "error", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -294,7 +296,7 @@ func (h *Handler) GetCurrentHuman(w http.ResponseWriter, r *http.Request) {
http.Error(w, "human not found", http.StatusNotFound)
return
}
slog.Error("failed to get current human", "error", err, "email", email)
flog.Error("failed to get current human", "error", err, "email", email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -323,7 +325,7 @@ func (h *Handler) UpdateSettings(w http.ResponseWriter, r *http.Request) {
if req.EmailNotificationsEnabled != nil {
if err := h.humanSvc.UpdateEmailNotificationsEnabled(r.Context(), humanId, *req.EmailNotificationsEnabled); err != nil {
slog.Error("failed to update email notifications setting", "error", err, "humanId", humanId)
flog.Error("failed to update email notifications setting", "error", err, "humanId", humanId)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -355,14 +357,14 @@ func (h *Handler) CreateNetwork(w http.ResponseWriter, r *http.Request) {
http.Error(w, "name cannot be empty", http.StatusBadRequest)
return
}
slog.Error("failed to create network", "error", err, "humanId", humanId, "name", req.Name)
flog.Error("failed to create network", "error", err, "humanId", humanId, "name", req.Name)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
resp, err := h.networkToDTO(r.Context(), net)
if err != nil {
slog.Error("failed to convert network to DTO", "error", err, "network_id", net.ID)
flog.Error("failed to convert network to DTO", "error", err, "network_id", net.ID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -381,7 +383,7 @@ func (h *Handler) ListNetworks(w http.ResponseWriter, r *http.Request) {
networks, err := h.networkSvc.ListForHuman(r.Context(), humanId)
if err != nil {
slog.Error("failed to list networks", "error", err, "humanId", humanId)
flog.Error("failed to list networks", "error", err, "humanId", humanId)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -390,7 +392,7 @@ func (h *Handler) ListNetworks(w http.ResponseWriter, r *http.Request) {
for _, net := range networks {
dto, err := h.networkToDTO(r.Context(), net)
if err != nil {
slog.Warn("failed to convert network to DTO in list", "error", err, "network_id", net.ID)
flog.Warn("failed to convert network to DTO in list", "error", err, "network_id", net.ID)
continue
}
resp = append(resp, dto)
@@ -415,7 +417,7 @@ func (h *Handler) GetNetwork(w http.ResponseWriter, r *http.Request) {
isMember, err := h.networkSvc.IsMember(r.Context(), networkID, humanId)
if err != nil {
slog.Error("failed to check network membership", "error", err, "network_id", networkID, "humanId", humanId)
flog.Error("failed to check network membership", "error", err, "network_id", networkID, "humanId", humanId)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -430,14 +432,14 @@ func (h *Handler) GetNetwork(w http.ResponseWriter, r *http.Request) {
http.Error(w, "network not found", http.StatusNotFound)
return
}
slog.Error("failed to get network", "error", err, "network_id", networkID)
flog.Error("failed to get network", "error", err, "network_id", networkID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
resp, err := h.networkToDTO(r.Context(), net)
if err != nil {
slog.Error("failed to convert network to DTO", "error", err, "network_id", networkID)
flog.Error("failed to convert network to DTO", "error", err, "network_id", networkID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -463,7 +465,7 @@ func (h *Handler) AddMembersToNetwork(w http.ResponseWriter, r *http.Request) {
isMember, err := h.networkSvc.IsMember(r.Context(), networkID, humanId)
if err != nil {
slog.Error("failed to check network membership", "error", err, "network_id", networkID, "humanId", humanId)
flog.Error("failed to check network membership", "error", err, "network_id", networkID, "humanId", humanId)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -497,7 +499,7 @@ func (h *Handler) AddMembersToNetwork(w http.ResponseWriter, r *http.Request) {
inviteEmails = append(inviteEmails, normalized)
continue
}
slog.Error("failed to look up human by email", "error", err, "email", normalized)
flog.Error("failed to look up human by email", "error", err, "email", normalized)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -506,7 +508,7 @@ func (h *Handler) AddMembersToNetwork(w http.ResponseWriter, r *http.Request) {
if len(memberHumanIds) > 0 {
if err := h.networkSvc.AddMembers(r.Context(), networkID, memberHumanIds); err != nil {
slog.Error("failed to add members to network", "error", err, "network_id", networkID)
flog.Error("failed to add members to network", "error", err, "network_id", networkID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -514,7 +516,7 @@ func (h *Handler) AddMembersToNetwork(w http.ResponseWriter, r *http.Request) {
if len(inviteEmails) > 0 {
if err := h.networkSvc.InviteByEmail(r.Context(), networkID, inviteEmails); err != nil {
slog.Error("failed to invite members to network", "error", err, "network_id", networkID)
flog.Error("failed to invite members to network", "error", err, "network_id", networkID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -522,14 +524,14 @@ func (h *Handler) AddMembersToNetwork(w http.ResponseWriter, r *http.Request) {
net, err := h.networkSvc.GetByID(r.Context(), networkID)
if err != nil {
slog.Error("failed to get network after adding members", "error", err, "network_id", networkID)
flog.Error("failed to get network after adding members", "error", err, "network_id", networkID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
resp, err := h.networkToDTO(r.Context(), net)
if err != nil {
slog.Error("failed to convert network to DTO", "error", err, "network_id", networkID)
flog.Error("failed to convert network to DTO", "error", err, "network_id", networkID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -558,7 +560,7 @@ func (h *Handler) RemoveMemberFromNetwork(w http.ResponseWriter, r *http.Request
}
if err := h.networkSvc.RemoveMember(r.Context(), net.ID, memberHumanId); err != nil {
slog.Error("failed to remove member from network", "error", err, "network_id", net.ID, "memberHumanId", memberHumanId)
flog.Error("failed to remove member from network", "error", err, "network_id", net.ID, "memberHumanId", memberHumanId)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -581,7 +583,7 @@ func (h *Handler) ListInvitationsForNetwork(w http.ResponseWriter, r *http.Reque
isMember, err := h.networkSvc.IsMember(r.Context(), networkID, humanId)
if err != nil {
slog.Error("failed to check network membership", "error", err, "network_id", networkID, "humanId", humanId)
flog.Error("failed to check network membership", "error", err, "network_id", networkID, "humanId", humanId)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -592,7 +594,7 @@ func (h *Handler) ListInvitationsForNetwork(w http.ResponseWriter, r *http.Reque
invitations, err := h.networkSvc.ListInvitationsForNetwork(r.Context(), networkID)
if err != nil {
slog.Error("failed to list invitations", "error", err, "network_id", networkID)
flog.Error("failed to list invitations", "error", err, "network_id", networkID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -620,7 +622,7 @@ func (h *Handler) ListMyInvitations(w http.ResponseWriter, r *http.Request) {
invitations, err := h.networkSvc.ListInvitationsForEmail(r.Context(), email)
if err != nil {
slog.Error("failed to list invitations for email", "error", err, "email", email)
flog.Error("failed to list invitations for email", "error", err, "email", email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -663,7 +665,7 @@ func (h *Handler) AcceptInvitation(w http.ResponseWriter, r *http.Request) {
}
if err := h.networkSvc.AcceptInvitation(r.Context(), req.NetworkId, email, humanId); err != nil {
slog.Error("failed to accept invitation", "error", err, "network_id", req.NetworkId, "email", email)
flog.Error("failed to accept invitation", "error", err, "network_id", req.NetworkId, "email", email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -686,7 +688,7 @@ func (h *Handler) RevokeInvitation(w http.ResponseWriter, r *http.Request) {
isMember, err := h.networkSvc.IsMember(r.Context(), networkID, humanId)
if err != nil {
slog.Error("failed to check network membership", "error", err, "network_id", networkID, "humanId", humanId)
flog.Error("failed to check network membership", "error", err, "network_id", networkID, "humanId", humanId)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -707,7 +709,7 @@ func (h *Handler) RevokeInvitation(w http.ResponseWriter, r *http.Request) {
}
if err := h.networkSvc.RevokeInvitation(r.Context(), networkID, req.Email); err != nil {
slog.Error("failed to revoke invitation", "error", err, "network_id", networkID, "email", req.Email)
flog.Error("failed to revoke invitation", "error", err, "network_id", networkID, "email", req.Email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -729,7 +731,7 @@ func (h *Handler) DownloadParticleMedia(w http.ResponseWriter, r *http.Request)
downloadURL, err := h.depotSvc.GetDownloadURL(r.Context(), objectID)
if err != nil {
slog.Error("failed to get download URL", "error", err, "object_id", objectID)
flog.Error("failed to get download URL", "error", err, "object_id", objectID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -763,7 +765,7 @@ func (h *Handler) PrepareUpload(w http.ResponseWriter, r *http.Request) {
isMember, err := h.networkSvc.IsMember(r.Context(), req.NetworkId, humanId)
if err != nil {
slog.Error("failed to check network membership", "error", err, "network_id", req.NetworkId, "humanId", humanId)
flog.Error("failed to check network membership", "error", err, "network_id", req.NetworkId, "humanId", humanId)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -785,7 +787,7 @@ func (h *Handler) PrepareUpload(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
slog.Error("failed to prepare upload", "error", err, "network_id", req.NetworkId, "name", req.Name)
flog.Error("failed to prepare upload", "error", err, "network_id", req.NetworkId, "name", req.Name)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -823,7 +825,7 @@ func (h *Handler) ConfirmUpload(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
slog.Error("failed to confirm upload", "error", err, "object_id", objectID)
flog.Error("failed to confirm upload", "error", err, "object_id", objectID)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -886,7 +888,7 @@ func (h *Handler) AddToWaitlist(w http.ResponseWriter, r *http.Request) {
http.Error(w, "already in the waitlist", http.StatusConflict)
return
}
slog.Error("failed to add to waitlist", "error", err, "email", req.Email)
flog.Error("failed to add to waitlist", "error", err, "email", req.Email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -912,7 +914,7 @@ func (h *Handler) GetWaitlist(w http.ResponseWriter, r *http.Request) {
entries, err := h.waitlistSvc.GetWaitlist(r.Context(), filter)
if err != nil {
slog.Error("failed to get waitlist", "error", err)
flog.Error("failed to get waitlist", "error", err)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -945,7 +947,7 @@ func (h *Handler) GetWaitlistEntry(w http.ResponseWriter, r *http.Request) {
http.Error(w, "entry not found", http.StatusNotFound)
return
}
slog.Error("failed to get waitlist entry", "error", err, "email", email)
flog.Error("failed to get waitlist entry", "error", err, "email", email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -977,7 +979,7 @@ func (h *Handler) InviteWaitlistEntrant(w http.ResponseWriter, r *http.Request)
http.Error(w, "entry not found", http.StatusNotFound)
return
}
slog.Error("failed to invite waitlist entrant", "error", err, "email", req.Email)
flog.Error("failed to invite waitlist entrant", "error", err, "email", req.Email)
http.Error(w, "internal server error", http.StatusInternalServerError)
return
}
@@ -1019,7 +1021,7 @@ func (h *Handler) networkToDTO(ctx context.Context, n *network.Network) (Network
for _, memberHumanId := range n.MemberHumanIds {
hum, err := h.humanSvc.GetByID(ctx, memberHumanId)
if err != nil {
slog.Warn("failed to look up network member", "humanId", memberHumanId, "error", err)
flog.Warn("failed to look up network member", "humanId", memberHumanId, "error", err)
continue
}
humans = append(humans, humanToDTO(hum))
@@ -1070,7 +1072,7 @@ func (h *Handler) GetLivekitToken(w http.ResponseWriter, r *http.Request) {
token, err := h.livekitClient.GetJoinToken(roomName, humanId, humanEmail)
if err != nil {
slog.Error("failed to generate livekit token", "error", err, "humanId", humanId, "roomName", roomName)
flog.Error("failed to generate livekit token", "error", err, "humanId", humanId, "roomName", roomName)
http.Error(w, "failed to generate token", http.StatusInternalServerError)
return
}
@@ -1084,13 +1086,13 @@ func (h *Handler) GetLivekitToken(w http.ResponseWriter, r *http.Request) {
func (h *Handler) HandleLivekitWebhook(w http.ResponseWriter, r *http.Request) {
event, err := webhook.ReceiveWebhookEvent(r, h.livekitClient.KeyProvider())
if err != nil {
slog.Error("failed to verify livekit webhook", "error", err)
flog.Error("failed to verify livekit webhook", "error", err)
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
eventType := event.GetEvent()
slog.Info("received livekit webhook", "event", eventType, "room", event.GetRoom().GetName())
flog.Info("received livekit webhook", "event", eventType, "room", event.GetRoom().GetName())
switch eventType {
case "participant_joined", "participant_left", "room_finished":
@@ -1103,7 +1105,7 @@ func (h *Handler) HandleLivekitWebhook(w http.ResponseWriter, r *http.Request) {
roomName := event.GetRoom().GetName()
parts := strings.SplitN(roomName, "/", 2)
if len(parts) != 2 {
slog.Error("invalid room name format", "room", roomName)
flog.Error("invalid room name format", "room", roomName)
http.Error(w, "invalid room name", http.StatusBadRequest)
return
}
@@ -1120,7 +1122,7 @@ func (h *Handler) HandleLivekitWebhook(w http.ResponseWriter, r *http.Request) {
// Authoritative list avoids drift from missed/out-of-order webhooks.
participants, err := h.livekitClient.ListParticipants(ctx, roomName)
if err != nil {
slog.Error("failed to list participants", "error", err, "room", roomName)
flog.Error("failed to list participants", "error", err, "room", roomName)
// 200 to suppress LiveKit retries.
w.WriteHeader(http.StatusOK)
return
@@ -1136,7 +1138,7 @@ func (h *Handler) HandleLivekitWebhook(w http.ResponseWriter, r *http.Request) {
{Path: "huddle_active_participants", Value: participantIds},
})
if err != nil {
slog.Error("failed to update huddle participants in firestore", "error", err, "path", docPath)
flog.Error("failed to update huddle participants in firestore", "error", err, "path", docPath)
}
w.WriteHeader(http.StatusOK)