From b40bbb6412acae7d3caed3e599117dc5b1661fc6 Mon Sep 17 00:00:00 2001 From: Graham Plata Date: Wed, 29 Jan 2025 15:45:44 -0500 Subject: [PATCH] add logs --- admin/billing.go | 38 ++++++++++++++++++++--- admin/database/database.go | 1 + admin/database/postgres/postgres.go | 23 +++++++++----- admin/jobs/river/subscription_handlers.go | 9 +++++- admin/jobs/river/trial_checks.go | 21 ++++++++++++- admin/projects.go | 13 ++++++-- admin/server/billing.go | 23 ++++++++++++-- 7 files changed, 110 insertions(+), 18 deletions(-) diff --git a/admin/billing.go b/admin/billing.go index cca8d395c2e..5add739cadd 100644 --- a/admin/billing.go +++ b/admin/billing.go @@ -19,7 +19,13 @@ func (s *Service) InitOrganizationBilling(ctx context.Context, org *database.Org if err != nil { return nil, err } - s.Logger.Info("created payment customer", zap.String("org_id", org.ID), zap.String("org_name", org.Name), zap.String("payment_customer_id", pc.ID)) + s.Logger.Info("created payment customer", + zap.String("org_id", org.ID), + zap.String("org_name", org.Name), + zap.String("payment_customer_id", pc.ID), + zap.String("user_email", org.BillingEmail), + ) + org.PaymentCustomerID = pc.ID // create billing customer @@ -27,7 +33,13 @@ func (s *Service) InitOrganizationBilling(ctx context.Context, org *database.Org if err != nil { return nil, err } - s.Logger.Info("created billing customer", zap.String("org", org.Name), zap.String("billing_customer_id", bc.ID)) + s.Logger.Info("created billing customer", + zap.String("org", org.Name), + zap.String("billing_customer_id", bc.ID), + zap.String("payment_customer_id", pc.ID), + zap.String("user_email", org.BillingEmail), + ) + org.BillingCustomerID = bc.ID org, err = s.DB.UpdateOrganization(ctx, org.ID, &database.UpdateOrganizationOptions{ @@ -90,7 +102,12 @@ func (s *Service) RepairOrganizationBilling(ctx context.Context, org *database.O if err != nil { return nil, nil, fmt.Errorf("failed to create payment customer: %w", err) } - s.Logger.Info("created payment customer", zap.String("org_id", org.ID), zap.String("org_name", org.Name), zap.String("payment_customer_id", pc.ID)) + s.Logger.Info("created payment customer", + zap.String("org_id", org.ID), + zap.String("org_name", org.Name), + zap.String("payment_customer_id", pc.ID), + zap.String("user_email", pc.Email), + ) } else { return nil, nil, fmt.Errorf("error finding payment customer: %w", err) } @@ -110,7 +127,13 @@ func (s *Service) RepairOrganizationBilling(ctx context.Context, org *database.O if err != nil { return nil, nil, fmt.Errorf("failed to create billing customer: %w", err) } - s.Logger.Info("created billing customer", zap.String("org_id", org.ID), zap.String("org_name", org.Name), zap.String("billing_customer_id", bc.ID)) + s.Logger.Info("created billing customer", + zap.String("org_id", org.ID), + zap.String("org_name", org.Name), + zap.String("billing_customer_id", bc.Email), + zap.String("payment_customer_id", org.PaymentCustomerID), + zap.String("user_email", org.BillingEmail), + ) org.BillingCustomerID = bc.ID } else if bc.PaymentProviderID == "" { // update payment customer id in billing system @@ -244,7 +267,12 @@ func (s *Service) StartTrial(ctx context.Context, org *database.Organization) (* return org, sub, nil } - s.Logger.Named("billing").Info("started trial for organization", zap.String("org_name", org.Name), zap.String("org_id", org.ID), zap.String("trial_end_date", sub.TrialEndDate.String())) + s.Logger.Named("billing").Info("started trial for organization", + zap.String("org_name", org.Name), + zap.String("org_id", org.ID), + zap.String("trial_end_date", sub.TrialEndDate.String()), + zap.String("email", *org.CreatedByUserID), + ) org, err = s.DB.UpdateOrganization(ctx, org.ID, &database.UpdateOrganizationOptions{ Name: org.Name, diff --git a/admin/database/database.go b/admin/database/database.go index 3259c551990..93541b007af 100644 --- a/admin/database/database.go +++ b/admin/database/database.go @@ -89,6 +89,7 @@ type DB interface { InsertProject(ctx context.Context, opts *InsertProjectOptions) (*Project, error) DeleteProject(ctx context.Context, id string) error UpdateProject(ctx context.Context, id string, opts *UpdateProjectOptions) (*Project, error) + CountProjectsForOrganization(ctx context.Context, orgID string) (int, error) CountProjectsQuotaUsage(ctx context.Context, orgID string) (*ProjectsQuotaUsage, error) FindProjectWhitelistedDomain(ctx context.Context, projectID, domain string) (*ProjectWhitelistedDomain, error) FindProjectWhitelistedDomainForProjectWithJoinedRoleNames(ctx context.Context, projectID string) ([]*ProjectWhitelistedDomainWithJoinedRoleNames, error) diff --git a/admin/database/postgres/postgres.go b/admin/database/postgres/postgres.go index dffa325650a..948bc83183f 100644 --- a/admin/database/postgres/postgres.go +++ b/admin/database/postgres/postgres.go @@ -103,6 +103,15 @@ func (c *connection) FindOrganizationByName(ctx context.Context, name string) (* return res, nil } +func (c *connection) CountProjectsForOrganization(ctx context.Context, orgID string) (int, error) { + var count int + err := c.getDB(ctx).SelectContext(ctx, &count, "SELECT COUNT(*) FROM projects WHERE org_id=$1", orgID) + if err != nil { + return 0, parseErr("projects", err) + } + return count, nil +} + func (c *connection) FindOrganizationByCustomDomain(ctx context.Context, domain string) (*database.Organization, error) { res := &database.Organization{} err := c.getDB(ctx).QueryRowxContext(ctx, "SELECT * FROM orgs WHERE lower(custom_domain)=lower($1)", domain).StructScan(res) @@ -1951,7 +1960,7 @@ func (c *connection) FindUnusedAssets(ctx context.Context, limit int) ([]*databa // We skip unused assets created in last 6 hours to prevent race condition // where somebody just created an asset but is yet to use it err := c.getDB(ctx).SelectContext(ctx, &res, ` - SELECT a.* FROM assets a + SELECT a.* FROM assets a WHERE a.created_on < now() - INTERVAL '6 hours' AND NOT EXISTS (SELECT 1 FROM projects p WHERE p.archive_asset_id = a.id) AND NOT EXISTS (SELECT 1 FROM orgs o WHERE o.logo_asset_id = a.id) @@ -2131,14 +2140,14 @@ func (c *connection) FindProjectVariables(ctx context.Context, projectID string, // Also include variables that are not environment specific and not set for the given environment q += ` AND ( - p.environment = $2 + p.environment = $2 OR ( - p.environment = '' + p.environment = '' AND NOT EXISTS ( - SELECT 1 - FROM project_variables p2 - WHERE p2.project_id = p.project_id - AND p2.environment = $2 + SELECT 1 + FROM project_variables p2 + WHERE p2.project_id = p.project_id + AND p2.environment = $2 AND lower(p2.name) = lower(p.name) ) ) diff --git a/admin/jobs/river/subscription_handlers.go b/admin/jobs/river/subscription_handlers.go index d26e93bab46..6040c302e18 100644 --- a/admin/jobs/river/subscription_handlers.go +++ b/admin/jobs/river/subscription_handlers.go @@ -89,11 +89,13 @@ func (w *SubscriptionCancellationCheckWorker) subscriptionCancellationCheck(ctx // hibernate projects limit := 10 afterProjectName := "" + projectCount := 0 for { projs, err := w.admin.DB.FindProjectsForOrganization(ctx, org.ID, afterProjectName, limit) if err != nil { return err } + projectCount += len(projs) for _, proj := range projs { _, err = w.admin.HibernateProject(ctx, proj) @@ -118,7 +120,12 @@ func (w *SubscriptionCancellationCheckWorker) subscriptionCancellationCheck(ctx w.logger.Error("failed to send subscription ended email", zap.String("org_id", org.ID), zap.String("org_name", org.Name), zap.String("billing_email", org.BillingEmail), zap.Error(err)) } - w.logger.Warn("projects hibernated due to subscription cancellation", zap.String("org_id", org.ID), zap.String("org_name", org.Name)) + w.logger.Warn("projects hibernated due to subscription cancellation", + zap.String("org_id", org.ID), + zap.String("org_name", org.Name), + zap.Int("count_of_projects", projectCount), + zap.String("user_email", org.BillingEmail), + ) // mark the billing issue as processed err = w.admin.DB.UpdateBillingIssueOverdueAsProcessed(ctx, issue.ID) diff --git a/admin/jobs/river/trial_checks.go b/admin/jobs/river/trial_checks.go index 1cc387a698d..db65b96c2ea 100644 --- a/admin/jobs/river/trial_checks.go +++ b/admin/jobs/river/trial_checks.go @@ -51,7 +51,26 @@ func (w *TrialEndingSoonWorker) trialEndingSoon(ctx context.Context) error { return fmt.Errorf("failed to find organization: %w", err) } - w.logger.Warn("trial ending soon", zap.String("org_id", org.ID), zap.String("org_name", org.Name), zap.Time("trial_end_date", m.EndDate)) + // remaining days in the trial period + daysRemaining := int(m.EndDate.Sub(time.Now().UTC()).Hours() / 24) + if daysRemaining < 0 { + daysRemaining = 0 + } + + // number of projects for the org + projects, err := w.admin.DB.CountProjectsForOrganization(ctx, org.ID) + if err != nil { + return fmt.Errorf("failed to count projects for org %q: %w", org.Name, err) + } + + w.logger.Warn("trial ending soon", + zap.String("org_id", org.ID), + zap.String("org_name", org.Name), + zap.Time("trial_end_date", m.EndDate), + zap.String("user_email", org.BillingEmail), + zap.Int("count_of_projects", projects), + zap.Int("count_of_days_remaining", daysRemaining), + ) err = w.admin.Email.SendTrialEndingSoon(&email.TrialEndingSoon{ ToEmail: org.BillingEmail, diff --git a/admin/projects.go b/admin/projects.go index a1026229e39..0547469b7ce 100644 --- a/admin/projects.go +++ b/admin/projects.go @@ -108,8 +108,17 @@ func (s *Service) CreateProject(ctx context.Context, org *database.Organization, return nil, multierr.Combine(err, err2, err3) } - // Log project creation - s.Logger.Info("created project", zap.String("id", proj.ID), zap.String("name", proj.Name), zap.String("org", org.Name), zap.Any("user_id", opts.CreatedByUserID)) + user, err := s.DB.FindUser(ctx, *proj.CreatedByUserID) + if err != nil { + return nil, err + } + + plan, err := s.Biller.GetDefaultPlan(ctx) + if err != nil { + return nil, err + } + + s.Logger.Info("created project", zap.String("id", proj.ID), zap.String("name", proj.Name), zap.String("org", org.Name), zap.Any("user_id", opts.CreatedByUserID), zap.String("user_email", user.Email), zap.String("billing_plan", plan.Name)) return res, nil } diff --git a/admin/server/billing.go b/admin/server/billing.go index 02dd225ae8c..f360ad1bb5f 100644 --- a/admin/server/billing.go +++ b/admin/server/billing.go @@ -173,7 +173,12 @@ func (s *Server) UpdateBillingSubscription(ctx context.Context, req *adminv1.Upd return nil, err } planChange = true - s.logger.Named("billing").Info("new subscription created", zap.String("org_id", org.ID), zap.String("org_name", org.Name), zap.String("plan_id", sub.Plan.ID), zap.String("plan_name", sub.Plan.Name)) + s.logger.Named("billing").Info("new subscription created", + zap.String("org_id", org.ID), + zap.String("org_name", org.Name), + zap.String("plan_id", sub.Plan.ID), + zap.String("plan_name", sub.Plan.Name), + ) } else { // schedule plan change oldPlan := sub.Plan @@ -183,7 +188,14 @@ func (s *Server) UpdateBillingSubscription(ctx context.Context, req *adminv1.Upd return nil, err } planChange = true - s.logger.Named("billing").Info("plan changed", zap.String("org_id", org.ID), zap.String("org_name", org.Name), zap.String("old_plan_id", oldPlan.ID), zap.String("old_plan_name", oldPlan.Name), zap.String("new_plan_id", sub.Plan.ID), zap.String("new_plan_name", sub.Plan.Name)) + s.logger.Named("billing").Info("plan changed", + zap.String("org_id", org.ID), + zap.String("org_name", org.Name), + zap.String("old_plan_id", oldPlan.ID), + zap.String("old_plan_name", oldPlan.Name), + zap.String("new_plan_id", sub.Plan.ID), + zap.String("new_plan_name", sub.Plan.Name), + ) } } @@ -196,6 +208,13 @@ func (s *Server) UpdateBillingSubscription(ctx context.Context, req *adminv1.Upd // send plan changed email if plan.PlanType == billing.TeamPlanType { + s.logger.Named("billing").Info("upgraded to team plan", + zap.String("org_id", org.ID), + zap.String("org_name", org.Name), + zap.String("plan_id", sub.Plan.ID), + zap.String("plan_name", sub.Plan.Name), + ) + // special handling for team plan to send custom email err = s.admin.Email.SendTeamPlanStarted(&email.TeamPlan{ ToEmail: org.BillingEmail,