diff --git a/internal/importexport/importexport.go b/internal/importexport/importexport.go index 761c2992..0825fea9 100644 --- a/internal/importexport/importexport.go +++ b/internal/importexport/importexport.go @@ -120,7 +120,7 @@ func (ie *ImportExport) GetLocalImporter(address, path string) (*transfer.Transf if err != nil { return nil, err } - return transfer.New(ie.panicHandler, ie.config.GetTransferDir(), source, target) + return transfer.New(ie.panicHandler, newImportMetricsManager(ie), ie.config.GetTransferDir(), source, target) } // GetRemoteImporter returns transferrer from remote IMAP to ProtonMail account. @@ -133,7 +133,7 @@ func (ie *ImportExport) GetRemoteImporter(address, username, password, host, por if err != nil { return nil, err } - return transfer.New(ie.panicHandler, ie.config.GetTransferDir(), source, target) + return transfer.New(ie.panicHandler, newImportMetricsManager(ie), ie.config.GetTransferDir(), source, target) } // GetEMLExporter returns transferrer from ProtonMail account to local EML structure. @@ -143,7 +143,7 @@ func (ie *ImportExport) GetEMLExporter(address, path string) (*transfer.Transfer return nil, err } target := transfer.NewEMLProvider(path) - return transfer.New(ie.panicHandler, ie.config.GetTransferDir(), source, target) + return transfer.New(ie.panicHandler, newExportMetricsManager(ie), ie.config.GetTransferDir(), source, target) } // GetMBOXExporter returns transferrer from ProtonMail account to local MBOX structure. @@ -153,7 +153,7 @@ func (ie *ImportExport) GetMBOXExporter(address, path string) (*transfer.Transfe return nil, err } target := transfer.NewMBOXProvider(path) - return transfer.New(ie.panicHandler, ie.config.GetTransferDir(), source, target) + return transfer.New(ie.panicHandler, newExportMetricsManager(ie), ie.config.GetTransferDir(), source, target) } func (ie *ImportExport) getPMAPIProvider(address string) (*transfer.PMAPIProvider, error) { diff --git a/internal/importexport/metrics.go b/internal/importexport/metrics.go new file mode 100644 index 00000000..878c346a --- /dev/null +++ b/internal/importexport/metrics.go @@ -0,0 +1,64 @@ +// Copyright (c) 2020 Proton Technologies AG +// +// This file is part of ProtonMail Bridge. +// +// ProtonMail Bridge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ProtonMail Bridge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with ProtonMail Bridge. If not, see . + +package importexport + +import ( + "strconv" + + "github.com/ProtonMail/proton-bridge/internal/metrics" +) + +type metricsManager struct { + ie *ImportExport + category metrics.Category +} + +func newImportMetricsManager(ie *ImportExport) *metricsManager { + return &metricsManager{ + ie: ie, + category: metrics.Import, + } +} + +func newExportMetricsManager(ie *ImportExport) *metricsManager { + return &metricsManager{ + ie: ie, + category: metrics.Export, + } +} + +func (m *metricsManager) Load(numberOfMailboxes int) { + label := strconv.Itoa(numberOfMailboxes) + m.ie.SendMetric(metrics.New(m.category, metrics.TransferLoad, metrics.Label(label))) +} + +func (m *metricsManager) Start() { + m.ie.SendMetric(metrics.New(m.category, metrics.TransferStart, metrics.NoLabel)) +} + +func (m *metricsManager) Complete() { + m.ie.SendMetric(metrics.New(m.category, metrics.TransferComplete, metrics.NoLabel)) +} + +func (m *metricsManager) Cancel() { + m.ie.SendMetric(metrics.New(m.category, metrics.TransferCancel, metrics.NoLabel)) +} + +func (m *metricsManager) Fail() { + m.ie.SendMetric(metrics.New(m.category, metrics.TransferFail, metrics.NoLabel)) +} diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index b18bf5ac..a14b1154 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -67,4 +67,30 @@ const ( Daily = Action("daily") ) +// Metrics related to import/export (transfer) process. +const ( + // Import is used to group import metrics. + Import = Category("import") + + // Export is used to group export metrics. + Export = Category("export") + + // TransferLoad signifies that the transfer load source. + // It can be IMAP or local files for import, or PM for export. + // With this will be reported also label with number of source mailboxes. + TransferLoad = Action("load") + + // TransferStart signifies started transfer. + TransferStart = Action("start") + + // TransferComplete signifies completed transfer without crash. + TransferComplete = Action("complete") + + // TransferCancel signifies cancelled transfer by an user. + TransferCancel = Action("cancel") + + // TransferFail signifies stopped transfer because of an fatal error. + TransferFail = Action("fail") +) + const NoLabel = Label("") diff --git a/internal/transfer/transfer.go b/internal/transfer/transfer.go index 566dfcb1..6d7ecf68 100644 --- a/internal/transfer/transfer.go +++ b/internal/transfer/transfer.go @@ -32,6 +32,7 @@ var log = logrus.WithField("pkg", "transfer") //nolint[gochecknoglobals] // and target providers. This is the main object which should be used. type Transfer struct { panicHandler PanicHandler + metrics MetricsManager id string dir string rules transferRules @@ -46,11 +47,12 @@ type Transfer struct { // source := transfer.NewEMLProvider(...) // target := transfer.NewPMAPIProvider(...) // transfer.New(source, target, ...) -func New(panicHandler PanicHandler, transferDir string, source SourceProvider, target TargetProvider) (*Transfer, error) { +func New(panicHandler PanicHandler, metrics MetricsManager, transferDir string, source SourceProvider, target TargetProvider) (*Transfer, error) { transferID := fmt.Sprintf("%x", sha256.Sum256([]byte(source.ID()+"-"+target.ID()))) rules := loadRules(transferDir, transferID) transfer := &Transfer{ panicHandler: panicHandler, + metrics: metrics, id: transferID, dir: transferDir, rules: rules, @@ -60,6 +62,7 @@ func New(panicHandler PanicHandler, transferDir string, source SourceProvider, t if err := transfer.setDefaultRules(); err != nil { return nil, err } + metrics.Load(len(transfer.sourceMboxCache)) return transfer, nil } @@ -165,6 +168,8 @@ func (t *Transfer) Start() *Progress { t.rules.save() t.rules.propagateGlobalTime() + t.metrics.Start() + log := log.WithField("id", t.id) reportFile := newFileReport(t.dir, t.id) progress := newProgress(log, reportFile) @@ -184,6 +189,16 @@ func (t *Transfer) Start() *Progress { t.target.TransferFrom(t.rules, &progress, ch) progress.finish() + + if progress.isStopped { + if progress.fatalError != nil { + t.metrics.Fail() + } else { + t.metrics.Cancel() + } + } else { + t.metrics.Complete() + } }() return &progress diff --git a/internal/transfer/types.go b/internal/transfer/types.go index 9b12db49..c9dec426 100644 --- a/internal/transfer/types.go +++ b/internal/transfer/types.go @@ -25,6 +25,14 @@ type PanicHandler interface { HandlePanic() } +type MetricsManager interface { + Load(int) + Start() + Complete() + Cancel() + Fail() +} + type ClientManager interface { GetClient(userID string) pmapi.Client CheckConnection() error