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
+19 -17
View File
@@ -3,11 +3,15 @@ package main
import (
"context"
"fmt"
"log/slog"
"os"
"time"
"github.com/flowy-live/llink/internal/utils/flog"
"cloud.google.com/go/firestore"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pbaero "github.com/flowy-live/llink/genproto/aero"
pbpusher "github.com/flowy-live/llink/genproto/llink/pusher"
"github.com/flowy-live/llink/internal/db"
@@ -15,12 +19,10 @@ import (
"github.com/flowy-live/llink/internal/network"
"github.com/flowy-live/llink/internal/particle"
"github.com/flowy-live/llink/internal/utils"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
const (
maxActivityAge = 24 * time.Hour // ignore streams idle longer than this
maxActivityAge = 24 * time.Hour // ignore streams idle longer than this
unreadThreshold = 10 * time.Minute // grace window before a message is "unread"
emailCooldown = 12 * time.Hour // min gap between emails to the same user
)
@@ -34,7 +36,7 @@ func main() {
gcpProject := utils.MustGetEnv("GCP_PROJECT")
firestoreClient, err := firestore.NewClient(ctx, gcpProject)
if err != nil {
slog.Error("failed to create Firestore client", "error", err)
flog.Error("failed to create Firestore client", "error", err)
os.Exit(1)
}
defer firestoreClient.Close()
@@ -42,7 +44,7 @@ func main() {
aeroAddr := utils.MustGetEnv("AERO_ADDR")
aeroConn, err := grpc.NewClient(aeroAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
slog.Error("failed to connect to aero", "error", err)
flog.Error("failed to connect to aero", "error", err)
os.Exit(1)
}
defer aeroConn.Close()
@@ -51,7 +53,7 @@ func main() {
pusherAddr := utils.MustGetEnv("PUSHER_GRPC_ADDR")
pusherConn, err := grpc.NewClient(pusherAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
slog.Error("failed to connect to pusher", "error", err)
flog.Error("failed to connect to pusher", "error", err)
os.Exit(1)
}
defer pusherConn.Close()
@@ -60,12 +62,12 @@ func main() {
humanSvc := human.NewService(db.Pool())
networkSvc := network.NewReader(db.Pool())
slog.Info("starting email notification cycle")
flog.Info("starting email notification cycle")
if err := runNotificationCycle(ctx, firestoreClient, aeroSvc, pusherSvc, humanSvc, networkSvc); err != nil {
slog.Error("notification cycle failed", "error", err)
flog.Error("notification cycle failed", "error", err)
os.Exit(1)
}
slog.Info("email notification cycle complete")
flog.Info("email notification cycle complete")
}
func runNotificationCycle(
@@ -100,7 +102,7 @@ func runNotificationCycle(
if err != nil {
return fmt.Errorf("failed to get online humans: %w", err)
}
slog.Info("gathered online presence", "onlineCount", len(onlineResp.HumanIds), "humanIds", onlineResp.HumanIds)
flog.Info("gathered online presence", "onlineCount", len(onlineResp.HumanIds), "humanIds", onlineResp.HumanIds)
for _, id := range onlineResp.HumanIds {
allOnline[id] = true
}
@@ -108,7 +110,7 @@ func runNotificationCycle(
for _, net := range networks {
streams, err := getOpenStreams(ctx, fsClient, net.ID)
if err != nil {
slog.Error("failed to query streams", "networkId", net.ID, "error", err)
flog.Error("failed to query streams", "networkId", net.ID, "error", err)
continue
}
@@ -141,7 +143,7 @@ func runNotificationCycle(
sentCount := 0
for humanId, count := range behindCounts {
if allOnline[humanId] {
slog.Info("human online...skipping email", "humanId", humanId)
flog.Info("human online...skipping email", "humanId", humanId)
continue
}
@@ -164,18 +166,18 @@ func runNotificationCycle(
}
if err := sendNotificationEmail(ctx, aeroSvc, h, count); err != nil {
slog.Error("failed to send email", "humanId", humanId, "error", err)
flog.Error("failed to send email", "humanId", humanId, "error", err)
continue
}
if err := humanSvc.UpdateLastEmailNotificationSentAt(ctx, humanId, now); err != nil {
slog.Error("failed to update last_email_notification_sent_at", "humanId", humanId, "error", err)
flog.Error("failed to update last_email_notification_sent_at", "humanId", humanId, "error", err)
}
sentCount++
}
slog.Info("notification cycle summary",
flog.Info("notification cycle summary",
"networks", len(networks),
"humansBehind", len(behindCounts),
"emailsSent", sentCount,
@@ -198,7 +200,7 @@ func getOpenStreams(ctx context.Context, client *firestore.Client, networkId str
for _, doc := range docs {
var s particle.FirestoreStreamParticle
if err := doc.DataTo(&s); err != nil {
slog.Warn("failed to unmarshal stream particle", "docId", doc.Ref.ID, "error", err)
flog.Warn("failed to unmarshal stream particle", "docId", doc.Ref.ID, "error", err)
continue
}
streams = append(streams, s)
+8 -6
View File
@@ -6,17 +6,19 @@ package main
import (
"context"
"log/slog"
"os"
"slices"
"time"
"github.com/flowy-live/llink/internal/utils/flog"
"cloud.google.com/go/firestore"
"google.golang.org/api/iterator"
"github.com/flowy-live/llink/internal/db"
"github.com/flowy-live/llink/internal/human"
"github.com/flowy-live/llink/internal/network"
"github.com/flowy-live/llink/internal/utils"
"google.golang.org/api/iterator"
)
func main() {
@@ -28,7 +30,7 @@ func main() {
gcpProject := utils.MustGetEnv("GCP_PROJECT")
fs, err := firestore.NewClient(ctx, gcpProject)
if err != nil {
slog.Error("failed to create Firestore client", "error", err)
flog.Error("failed to create Firestore client", "error", err)
os.Exit(1)
}
defer fs.Close()
@@ -39,10 +41,10 @@ func main() {
started := time.Now()
written, scanned, err := reconcile(ctx, fs, humanSvc, networkSvc)
if err != nil {
slog.Error("reconciliation failed", "error", err, "elapsed", time.Since(started))
flog.Error("reconciliation failed", "error", err, "elapsed", time.Since(started))
os.Exit(1)
}
slog.Info("reconciliation complete",
flog.Info("reconciliation complete",
"humans_scanned", scanned,
"humans_written", written,
"elapsed", time.Since(started),
@@ -109,7 +111,7 @@ func snapshotMirror(ctx context.Context, fs *firestore.Client) (map[string][]str
Networks []string `firestore:"networks"`
}
if err := doc.DataTo(&data); err != nil {
slog.Warn("skipping malformed mirror doc", "id", doc.Ref.ID, "error", err)
flog.Warn("skipping malformed mirror doc", "id", doc.Ref.ID, "error", err)
continue
}
out[doc.Ref.ID] = data.Networks
+15 -13
View File
@@ -3,14 +3,19 @@ package main
import (
"context"
"fmt"
"log/slog"
"net/http"
"os"
"strings"
"github.com/flowy-live/llink/internal/utils/flog"
"cloud.google.com/go/firestore"
"cloud.google.com/go/storage"
firebase "firebase.google.com/go/v4"
"github.com/redis/go-redis/v9"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pbaero "github.com/flowy-live/llink/genproto/aero"
"github.com/flowy-live/llink/internal"
"github.com/flowy-live/llink/internal/auth"
@@ -27,9 +32,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/redis/go-redis/v9"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
func redisForAuth() *redis.Client {
@@ -48,19 +50,19 @@ func main() {
ctx := context.Background()
storageClient, err := storage.NewClient(ctx)
if err != nil {
slog.Error("failed to create GCS client", "error", err)
flog.Error("failed to create GCS client", "error", err)
os.Exit(1)
}
defer storageClient.Close()
aeroAddr := utils.MustGetEnv("AERO_ADDR")
if aeroAddr == "" {
slog.Error("must provide AERO_ADDR")
flog.Error("must provide AERO_ADDR")
os.Exit(1)
}
aeroServer, err := grpc.NewClient(aeroAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
slog.Error("connection to aero server invalid", "error", err)
flog.Error("connection to aero server invalid", "error", err)
os.Exit(1)
}
defer aeroServer.Close()
@@ -69,12 +71,12 @@ func main() {
gcpProject := utils.MustGetEnv("GCP_PROJECT")
fbApp, err := firebase.NewApp(ctx, &firebase.Config{ProjectID: gcpProject})
if err != nil {
slog.Error("failed to init Firebase Admin app", "error", err)
flog.Error("failed to init Firebase Admin app", "error", err)
os.Exit(1)
}
fbAuth, err := fbApp.Auth(ctx)
if err != nil {
slog.Error("failed to create Firebase auth client", "error", err)
flog.Error("failed to create Firebase auth client", "error", err)
os.Exit(1)
}
@@ -90,13 +92,13 @@ func main() {
CancelURL: utils.MustGetEnv("BILLING_CANCEL_URL"),
})
if err != nil {
slog.Error("failed to initialize billing service", "error", err)
flog.Error("failed to initialize billing service", "error", err)
os.Exit(1)
}
firestoreClient, err := firestore.NewClient(ctx, gcpProject)
if err != nil {
slog.Error("failed to create Firestore client", "error", err)
flog.Error("failed to create Firestore client", "error", err)
os.Exit(1)
}
defer firestoreClient.Close()
@@ -202,9 +204,9 @@ func main() {
muxWithCors := middleware.CORS(allowedOrigins)(mux)
addr := fmt.Sprintf("0.0.0.0:%s", port)
slog.Info("running server", "addr", addr)
flog.Info("running server", "addr", addr)
if err := http.ListenAndServe(addr, muxWithCors); err != nil {
slog.Error("server failed", "error", err)
flog.Error("server failed", "error", err)
os.Exit(1)
}
}
+37 -35
View File
@@ -4,11 +4,15 @@ import (
"context"
"fmt"
"log"
"log/slog"
"os"
"strings"
"time"
"github.com/flowy-live/llink/internal/utils/flog"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"github.com/flowy-live/llink/internal/billing"
"github.com/flowy-live/llink/internal/db"
"github.com/flowy-live/llink/internal/depot"
@@ -18,8 +22,6 @@ import (
"github.com/flowy-live/llink/internal/particle"
"github.com/flowy-live/llink/internal/speech"
"github.com/flowy-live/llink/internal/utils"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"cloud.google.com/go/firestore"
"cloud.google.com/go/storage"
@@ -48,7 +50,7 @@ func main() {
storageClient, err := storage.NewClient(ctx)
if err != nil {
slog.Error("failed to create GCS client", "error", err)
flog.Error("failed to create GCS client", "error", err)
os.Exit(1)
}
defer storageClient.Close()
@@ -83,7 +85,7 @@ func main() {
panic(fmt.Errorf("error: %w", err))
}
if err != nil {
slog.Error("error in processing snapshot", "error", err)
flog.Error("error in processing snapshot", "error", err)
continue
}
@@ -100,15 +102,15 @@ func main() {
processed, err := processingRepo.IsProcessed(ctx, particleID)
if err != nil {
slog.Error("failed to check processing status", "particleID", particleID, "error", err)
flog.Error("failed to check processing status", "particleID", particleID, "error", err)
continue
}
if processed {
slog.Debug("skipping already processed particle", "particleID", particleID)
flog.Debug("skipping already processed particle", "particleID", particleID)
continue
}
slog.Debug("processing particle", "particleID", particleID, "data", change.Doc.Data())
flog.Debug("processing particle", "particleID", particleID, "data", change.Doc.Data())
// Side effects below are best-effort — failures don't prevent
// marking the particle as processed.
@@ -120,7 +122,7 @@ func main() {
notifyForParticle(ctx, notifier, humanSvc, change.Doc, parentDoc, transcript)
if err := processingRepo.MarkProcessed(ctx, particleID); err != nil {
slog.Error("failed to mark particle as processed", "particleID", particleID, "error", err)
flog.Error("failed to mark particle as processed", "particleID", particleID, "error", err)
}
}
}
@@ -132,30 +134,30 @@ func transcribeMediaParticle(ctx context.Context, depotSvc depot.Service, speech
var mediaParticle particle.FirestoreMediaParticle
err := doc.DataTo(&mediaParticle)
if err != nil {
slog.Error("unable to marshal particle data", "error", err)
flog.Error("unable to marshal particle data", "error", err)
return ""
}
particleType, err := particle.ParseParticleType(mediaParticle.Type)
if err != nil {
slog.Error("invalid particle type", "error", err)
flog.Error("invalid particle type", "error", err)
return ""
}
if particleType != particle.TypeMedia {
slog.Info("received a particle of type", "particle type", particleType)
flog.Info("received a particle of type", "particle type", particleType)
return ""
}
downloadURL, err := depotSvc.GetDownloadURL(ctx, mediaParticle.Properties.ObjectId)
if err != nil {
slog.Error("failed to get download URL", "error", err, "object_id", mediaParticle.Properties.ObjectId)
flog.Error("failed to get download URL", "error", err, "object_id", mediaParticle.Properties.ObjectId)
return ""
}
result, err := speechSvc.Transcribe(ctx, downloadURL)
if err != nil {
slog.Error("failed to transcribe media", "error", err, "particleID", doc.Ref.ID)
flog.Error("failed to transcribe media", "error", err, "particleID", doc.Ref.ID)
return ""
}
@@ -167,11 +169,11 @@ func transcribeMediaParticle(ctx context.Context, depotSvc depot.Service, speech
},
}, firestore.MergeAll)
if err != nil {
slog.Error("failed to update transcript in firestore", "error", err, "particleID", doc.Ref.ID)
flog.Error("failed to update transcript in firestore", "error", err, "particleID", doc.Ref.ID)
return ""
}
slog.Info("transcribed media particle", "particleID", doc.Ref.ID)
flog.Info("transcribed media particle", "particleID", doc.Ref.ID)
return transcript.Transcript
}
@@ -215,17 +217,17 @@ func toFirestoreTranscript(result *speech.TranscriptResult) particle.FirestoreTr
func recordFreemiumUsage(ctx context.Context, billingSvc billing.Service, doc *firestore.DocumentSnapshot) {
rawType, err := doc.DataAt("type")
if err != nil {
slog.Error("failed to read particle type", "error", err, "particleID", doc.Ref.ID)
flog.Error("failed to read particle type", "error", err, "particleID", doc.Ref.ID)
return
}
typeStr, ok := rawType.(string)
if !ok {
slog.Error("particle type is not a string", "particleID", doc.Ref.ID, "type", rawType)
flog.Error("particle type is not a string", "particleID", doc.Ref.ID, "type", rawType)
return
}
particleType, err := particle.ParseParticleType(typeStr)
if err != nil {
slog.Error("invalid particle type", "error", err, "particleID", doc.Ref.ID)
flog.Error("invalid particle type", "error", err, "particleID", doc.Ref.ID)
return
}
// Containers don't count toward the daily message cap.
@@ -235,12 +237,12 @@ func recordFreemiumUsage(ctx context.Context, billingSvc billing.Service, doc *f
networkID, err := particle.NetworkIDFromParticlePath(doc.Ref.Path)
if err != nil {
slog.Error("failed to derive network id", "error", err, "path", doc.Ref.Path)
flog.Error("failed to derive network id", "error", err, "path", doc.Ref.Path)
return
}
if err := billingSvc.IncrementDailyUsage(ctx, networkID, doc.CreateTime); err != nil {
slog.Error("failed to increment daily usage", "error", err, "networkID", networkID, "particleID", doc.Ref.ID)
flog.Error("failed to increment daily usage", "error", err, "networkID", networkID, "particleID", doc.Ref.ID)
}
}
@@ -252,12 +254,12 @@ func loadParentParticle(ctx context.Context, doc *firestore.DocumentSnapshot) *f
}
parentParticleDocRef := parentChildrenCollectionRef.Parent
if parentParticleDocRef == nil {
slog.Error("particle has no parent document", "particleID", doc.Ref.ID)
flog.Error("particle has no parent document", "particleID", doc.Ref.ID)
return nil
}
parentParticleDoc, err := parentParticleDocRef.Get(ctx)
if err != nil {
slog.Error("failed to get parent particle", "error", err, "particleID", doc.Ref.ID)
flog.Error("failed to get parent particle", "error", err, "particleID", doc.Ref.ID)
return nil
}
return parentParticleDoc
@@ -272,13 +274,13 @@ func updateParentLastChildCreatedAt(ctx context.Context, doc *firestore.Document
var streamParticle particle.FirestoreStreamParticle
if err := parent.DataTo(&streamParticle); err != nil {
slog.Error("failed to parse stream particle", "error", err)
flog.Error("failed to parse stream particle", "error", err)
return
}
particleType, err := particle.ParseParticleType(streamParticle.Type)
if err != nil {
slog.Error("invalid particle type", "error", err)
flog.Error("invalid particle type", "error", err)
return
}
@@ -288,7 +290,7 @@ func updateParentLastChildCreatedAt(ctx context.Context, doc *firestore.Document
childCreatedAt, err := doc.DataAt("created_at")
if err != nil {
slog.Error("failed to read child created_at", "error", err, "particleID", doc.Ref.ID)
flog.Error("failed to read child created_at", "error", err, "particleID", doc.Ref.ID)
return
}
@@ -299,7 +301,7 @@ func updateParentLastChildCreatedAt(ctx context.Context, doc *firestore.Document
},
})
if err != nil {
slog.Error("unable to update parent particle `last_child_created_at`", "error", err)
flog.Error("unable to update parent particle `last_child_created_at`", "error", err)
}
}
@@ -315,7 +317,7 @@ func notifyForParticle(
transcript string,
) {
if parent == nil {
slog.Info("notify: skip — no parent", "particleID", doc.Ref.ID)
flog.Info("notify: skip — no parent", "particleID", doc.Ref.ID)
return
}
@@ -323,31 +325,31 @@ func notifyForParticle(
typeName, _ := typeStr.(string)
pType, err := particle.ParseParticleType(typeName)
if err != nil {
slog.Info("notify: skip — unparseable particle type",
flog.Info("notify: skip — unparseable particle type",
"particleID", doc.Ref.ID, "type", typeName, "error", err)
return
}
if pType == particle.TypeStream || pType == particle.TypeFolder {
slog.Info("notify: skip — container particle",
flog.Info("notify: skip — container particle",
"particleID", doc.Ref.ID, "type", pType)
return
}
var parentStream particle.FirestoreStreamParticle
if err := parent.DataTo(&parentStream); err != nil {
slog.Error("notify: failed to parse parent stream", "error", err)
flog.Error("notify: failed to parse parent stream", "error", err)
return
}
parentType, err := particle.ParseParticleType(parentStream.Type)
if err != nil || parentType != particle.TypeStream {
slog.Info("notify: skip — parent isn't a stream",
flog.Info("notify: skip — parent isn't a stream",
"particleID", doc.Ref.ID, "parentType", parentType, "parseErr", err)
return
}
networkID, err := particle.NetworkIDFromParticlePath(doc.Ref.Path)
if err != nil {
slog.Error("notify: failed to derive network id", "error", err, "path", doc.Ref.Path)
flog.Error("notify: failed to derive network id", "error", err, "path", doc.Ref.Path)
return
}
@@ -370,7 +372,7 @@ func notifyForParticle(
if sender, err := humanSvc.GetByID(ctx, senderHumanID); err == nil {
senderEmailPrefix = sender.EmailPrefix
} else {
slog.Warn("notify: failed to look up sender", "error", err, "humanID", senderHumanID)
flog.Warn("notify: failed to look up sender", "error", err, "humanID", senderHumanID)
}
}
@@ -385,7 +387,7 @@ func notifyForParticle(
StreamVisibleTo: parentStream.VisibleTo,
Body: previewForParticle(pType, doc, transcript),
}); err != nil {
slog.Error("notify: dispatch failed", "error", err, "particleID", doc.Ref.ID, "networkID", networkID)
flog.Error("notify: dispatch failed", "error", err, "particleID", doc.Ref.ID, "networkID", networkID)
}
}
+11 -9
View File
@@ -3,13 +3,14 @@ package main
import (
"context"
"fmt"
"log/slog"
"net"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/flowy-live/llink/internal/utils/flog"
"github.com/flowy-live/llink/internal"
"github.com/flowy-live/llink/internal/auth"
"github.com/flowy-live/llink/internal/db"
@@ -17,8 +18,9 @@ import (
"github.com/flowy-live/llink/internal/pusher"
"github.com/flowy-live/llink/internal/utils"
pbpusher "github.com/flowy-live/llink/genproto/llink/pusher"
"google.golang.org/grpc"
pbpusher "github.com/flowy-live/llink/genproto/llink/pusher"
)
func main() {
@@ -56,15 +58,15 @@ func main() {
// --- gRPC server (internal presence queries) ---
grpcListener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%s", grpcPort))
if err != nil {
slog.Error("failed to listen for gRPC", "port", grpcPort, "error", err)
flog.Error("failed to listen for gRPC", "port", grpcPort, "error", err)
os.Exit(1)
}
grpcServer := grpc.NewServer()
pbpusher.RegisterPusherServiceServer(grpcServer, server)
go func() {
slog.Info("gRPC server listening", "port", grpcPort)
flog.Info("gRPC server listening", "port", grpcPort)
if err := grpcServer.Serve(grpcListener); err != nil {
slog.Error("gRPC server failed", "error", err)
flog.Error("gRPC server failed", "error", err)
}
}()
@@ -79,9 +81,9 @@ func main() {
httpServer := &http.Server{Addr: httpAddr, Handler: mux}
go func() {
slog.Info("HTTP server listening", "addr", httpAddr)
flog.Info("HTTP server listening", "addr", httpAddr)
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
slog.Error("HTTP server failed", "error", err)
flog.Error("HTTP server failed", "error", err)
os.Exit(1)
}
}()
@@ -91,10 +93,10 @@ func main() {
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
<-sigCh
slog.Info("shutting down...")
flog.Info("shutting down...")
cancel()
grpcServer.GracefulStop()
httpServer.Shutdown(context.Background())
slog.Info("shutdown complete")
flog.Info("shutdown complete")
}
+8 -7
View File
@@ -5,10 +5,11 @@ package main
import (
"context"
"log/slog"
"os"
"time"
"github.com/flowy-live/llink/internal/utils/flog"
"cloud.google.com/go/firestore"
"cloud.google.com/go/storage"
"google.golang.org/api/iterator"
@@ -27,7 +28,7 @@ func main() {
storageClient, err := storage.NewClient(ctx)
if err != nil {
slog.Error("failed to create GCS client", "error", err)
flog.Error("failed to create GCS client", "error", err)
os.Exit(1)
}
defer storageClient.Close()
@@ -40,7 +41,7 @@ func main() {
gcpProject := utils.MustGetEnv("GCP_PROJECT")
fs, err := firestore.NewClient(ctx, gcpProject)
if err != nil {
slog.Error("failed to create Firestore client", "error", err)
flog.Error("failed to create Firestore client", "error", err)
os.Exit(1)
}
defer fs.Close()
@@ -49,7 +50,7 @@ func main() {
stats, err := run(ctx, fs, depotSvc)
elapsed := time.Since(started)
slog.Info("transcode_backfill_summary",
flog.Info("transcode_backfill_summary",
"scanned", stats.scanned,
"media", stats.media,
"transcoded", stats.transcoded,
@@ -61,7 +62,7 @@ func main() {
)
if err != nil {
slog.Error("transcode backfill aborted", "error", err)
flog.Error("transcode backfill aborted", "error", err)
os.Exit(1)
}
if stats.failures > 0 {
@@ -116,7 +117,7 @@ func run(ctx context.Context, fs *firestore.Client, depotSvc depot.Service) (sta
switch {
case result.Err != nil:
s.failures++
slog.Error("transcode_backfill_failure",
flog.Error("transcode_backfill_failure",
"particleID", doc.Ref.ID,
"path", doc.Ref.Path,
"error", result.Err,
@@ -129,7 +130,7 @@ func run(ctx context.Context, fs *firestore.Client, depotSvc depot.Service) (sta
s.skippedNonMedia++
default:
s.transcoded++
slog.Info("transcode_backfill_progress",
flog.Info("transcode_backfill_progress",
"particleID", doc.Ref.ID,
"transcodedObjectID", result.TranscodedObjectID,
"outputMime", result.OutputMimeType,