feat(BRIDGE-396): Observability metrics for vault issues; Extension to observability service to support caching

This commit is contained in:
Atanas Janeshliev
2025-07-11 13:20:49 +02:00
parent de3fd34998
commit 7faf32d0ff
19 changed files with 452 additions and 79 deletions

View File

@ -41,6 +41,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/logging"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
"github.com/ProtonMail/proton-bridge/v3/internal/services/observability"
"github.com/ProtonMail/proton-bridge/v3/internal/unleash"
"github.com/ProtonMail/proton-bridge/v3/internal/useragent"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
@ -292,53 +293,56 @@ func run(c *cli.Context) error {
return withSingleInstance(settings, locations.GetLockFile(), version, func() error {
// Look for available keychains
return WithKeychainList(crashHandler, func(keychains *keychain.List) error {
// Unlock the encrypted vault.
return WithVault(reporter, locations, keychains, featureFlags, crashHandler, func(v *vault.Vault, insecure, corrupt bool) error {
if !v.Migrated() {
// Migrate old settings into the vault.
if err := migrateOldSettings(v); err != nil {
logrus.WithError(err).Error("Failed to migrate old settings")
}
// Migrate old accounts into the vault.
if err := migrateOldAccounts(locations, keychains, v); err != nil {
logrus.WithError(err).Error("Failed to migrate old accounts")
}
// The vault has been migrated.
if err := v.SetMigrated(); err != nil {
logrus.WithError(err).Error("Failed to mark vault as migrated")
}
}
logrus.WithFields(logrus.Fields{
"lastVersion": v.GetLastVersion().String(),
"showAllMail": v.GetShowAllMail(),
"updateCh": v.GetUpdateChannel(),
"autoUpdate": v.GetAutoUpdate(),
"rollout": v.GetUpdateRollout(),
"DoH": v.GetProxyAllowed(),
}).Info("Vault loaded")
// Load the cookies from the vault.
return withCookieJar(v, func(cookieJar http.CookieJar) error {
// Create a new bridge instance.
return withBridge(c, exe, locations, version, identifier, crashHandler, reporter, v, cookieJar, keychains, func(b *bridge.Bridge, eventCh <-chan events.Event) error {
if insecure {
logrus.Warn("The vault key could not be retrieved; the vault will not be encrypted")
b.PushError(bridge.ErrVaultInsecure)
// Pre-init the observability service, load the cached metrics.
return observability.WithObservability(locations, func(obsService *observability.Service) error {
// Unlock the encrypted vault.
return WithVault(reporter, locations, keychains, obsService, featureFlags, crashHandler, func(v *vault.Vault, insecure, corrupt bool) error {
if !v.Migrated() {
// Migrate old settings into the vault.
if err := migrateOldSettings(v); err != nil {
logrus.WithError(err).Error("Failed to migrate old settings")
}
if corrupt {
logrus.Warn("The vault is corrupt and has been wiped")
b.PushError(bridge.ErrVaultCorrupt)
// Migrate old accounts into the vault.
if err := migrateOldAccounts(locations, keychains, v); err != nil {
logrus.WithError(err).Error("Failed to migrate old accounts")
}
// Remove old updates files
b.RemoveOldUpdates()
// The vault has been migrated.
if err := v.SetMigrated(); err != nil {
logrus.WithError(err).Error("Failed to mark vault as migrated")
}
}
// Run the frontend.
return runFrontend(c, crashHandler, restarter, locations, b, eventCh, quitCh, c.Int(flagParentPID))
logrus.WithFields(logrus.Fields{
"lastVersion": v.GetLastVersion().String(),
"showAllMail": v.GetShowAllMail(),
"updateCh": v.GetUpdateChannel(),
"autoUpdate": v.GetAutoUpdate(),
"rollout": v.GetUpdateRollout(),
"DoH": v.GetProxyAllowed(),
}).Info("Vault loaded")
// Load the cookies from the vault.
return withCookieJar(v, func(cookieJar http.CookieJar) error {
// Create a new bridge instance.
return withBridge(c, exe, locations, version, identifier, obsService, crashHandler, reporter, v, cookieJar, keychains, func(b *bridge.Bridge, eventCh <-chan events.Event) error {
if insecure {
logrus.Warn("The vault key could not be retrieved; the vault will not be encrypted")
b.PushError(bridge.ErrVaultInsecure)
}
if corrupt {
logrus.Warn("The vault is corrupt and has been wiped")
b.PushError(bridge.ErrVaultCorrupt)
}
// Remove old updates files
b.RemoveOldUpdates()
// Run the frontend.
return runFrontend(c, crashHandler, restarter, locations, b, eventCh, quitCh, c.Int(flagParentPID))
})
})
})
})

View File

@ -33,6 +33,7 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/events"
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
"github.com/ProtonMail/proton-bridge/v3/internal/services/observability"
"github.com/ProtonMail/proton-bridge/v3/internal/updater"
"github.com/ProtonMail/proton-bridge/v3/internal/useragent"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
@ -52,6 +53,7 @@ func withBridge(
locations *locations.Locations,
version *semver.Version,
identifier *useragent.UserAgent,
obsService *observability.Service,
crashHandler *crash.Handler,
reporter *sentry.Reporter,
vault *vault.Vault,
@ -100,6 +102,7 @@ func withBridge(
updater,
version,
keychains,
obsService,
// The API stuff.
constants.APIHost,

View File

@ -31,18 +31,20 @@ import (
"github.com/ProtonMail/proton-bridge/v3/internal/locations"
"github.com/ProtonMail/proton-bridge/v3/internal/platform"
"github.com/ProtonMail/proton-bridge/v3/internal/sentry"
"github.com/ProtonMail/proton-bridge/v3/internal/services/observability"
"github.com/ProtonMail/proton-bridge/v3/internal/unleash"
"github.com/ProtonMail/proton-bridge/v3/internal/vault"
"github.com/ProtonMail/proton-bridge/v3/internal/vault/observabilitymetrics"
"github.com/ProtonMail/proton-bridge/v3/pkg/keychain"
"github.com/sirupsen/logrus"
)
func WithVault(reporter *sentry.Reporter, locations *locations.Locations, keychains *keychain.List, featureFlags unleash.FeatureFlagStartupStore, panicHandler async.PanicHandler, fn func(*vault.Vault, bool, bool) error) error {
func WithVault(reporter *sentry.Reporter, locations *locations.Locations, keychains *keychain.List, obsSender observability.BasicSender, featureFlags unleash.FeatureFlagStartupStore, panicHandler async.PanicHandler, fn func(*vault.Vault, bool, bool) error) error {
logrus.Debug("Creating vault")
defer logrus.Debug("Vault stopped")
// Create the encVault.
encVault, insecure, corrupt, err := newVault(reporter, locations, keychains, featureFlags, panicHandler)
encVault, insecure, corrupt, err := newVault(reporter, locations, keychains, obsSender, featureFlags, panicHandler)
if err != nil {
return fmt.Errorf("could not create vault: %w", err)
}
@ -64,7 +66,7 @@ func WithVault(reporter *sentry.Reporter, locations *locations.Locations, keycha
return fn(encVault, insecure, corrupt != nil)
}
func newVault(reporter *sentry.Reporter, locations *locations.Locations, keychains *keychain.List, featureFlags unleash.FeatureFlagStartupStore, panicHandler async.PanicHandler) (*vault.Vault, bool, error, error) {
func newVault(reporter *sentry.Reporter, locations *locations.Locations, keychains *keychain.List, obsSender observability.BasicSender, featureFlags unleash.FeatureFlagStartupStore, panicHandler async.PanicHandler) (*vault.Vault, bool, error, error) {
vaultDir, err := locations.ProvideSettingsPath()
if err != nil {
return nil, false, nil, fmt.Errorf("could not get vault dir: %w", err)
@ -101,6 +103,9 @@ func newVault(reporter *sentry.Reporter, locations *locations.Locations, keychai
// We store the insecure vault in a separate directory
vaultDir = path.Join(vaultDir, "insecure")
// Schedule the relevant observability metric for sending.
obsSender.AddMetrics(observabilitymetrics.GenerateVaultKeyFetchGenericErrorMetric())
} else {
vaultKey = key
lastUsedHelper = helper
@ -114,9 +119,14 @@ func newVault(reporter *sentry.Reporter, locations *locations.Locations, keychai
userVault, corrupt, err := vault.New(vaultDir, gluonCacheDir, vaultKey, panicHandler)
if err != nil {
obsSender.AddMetrics(observabilitymetrics.GenerateVaultCreationGenericErrorMetric())
return nil, false, corrupt, fmt.Errorf("could not create vault: %w", err)
}
if corrupt != nil {
obsSender.AddMetrics(observabilitymetrics.GenerateVaultCreationCorruptErrorMetric())
}
// Remember the last successfully used keychain on Linux and store that as the user preference.
if runtime.GOOS == platform.LINUX {
if err := vault.SetHelper(vaultDir, lastUsedHelper); err != nil {