Skip to content

Commit 191d750

Browse files
committed
common: route library logging through a caller-supplied Logger
Add a Logger interface (and matching Config.Logger field) whose method set lines up with resty.Logger and go-proton-api's WithLogger option, so callers can plug in a single adapter that captures all bridge log output along with HTTP-layer warnings emitted by go-proton-api. Convert the active stdlib log.Println / log.Printf call sites in cache.go, drive.go, file_download.go, folder_recursive.go, mail.go and common/user.go to use the configured logger via Config.GetLogger, which falls back to the stdlib log package when no Logger is set so existing callers see the same behaviour. Downgrade the four log.Fatalln sites in cache.go and mail.go: a library has no business calling os.Exit. The cache assertions become logged errors (the offending branches were already unreachable in practice), and the unsupported-recipient case in sendDraft now returns a new ErrUnsupportedRecipientType.
1 parent e435f93 commit 191d750

8 files changed

Lines changed: 69 additions & 21 deletions

File tree

cache.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ package proton_api_bridge
22

33
import (
44
"context"
5-
"log"
65
"sync"
76

87
"github.com/ProtonMail/gopenpgp/v2/crypto"
8+
"github.com/rclone/Proton-API-Bridge/common"
99
"github.com/rclone/go-proton-api"
1010
)
1111

@@ -18,15 +18,17 @@ type cache struct {
1818
data map[string]*cacheEntry
1919
children map[string]map[string]interface{}
2020
enableCaching bool
21+
logger common.Logger
2122

2223
sync.RWMutex
2324
}
2425

25-
func newCache(enableCaching bool) *cache {
26+
func newCache(enableCaching bool, logger common.Logger) *cache {
2627
return &cache{
2728
data: make(map[string]*cacheEntry),
2829
children: make(map[string]map[string]interface{}),
2930
enableCaching: enableCaching,
31+
logger: logger,
3032
}
3133
}
3234

@@ -84,8 +86,10 @@ func (cache *cache) _insert(linkID string, link *proton.Link, kr *crypto.KeyRing
8486

8587
}
8688
} else {
87-
// TODO: we should never have missing link though
88-
log.Fatalln("we should never have missing link though")
89+
// Defensive guard: every caller of _insert passes a non-nil link,
90+
// so this branch is unreachable in practice. Log a warning rather
91+
// than crashing the process if the invariant is ever violated.
92+
cache.logger.Errorf("cache._insert called with nil link for linkID %s", linkID)
8993
}
9094
}
9195

@@ -106,10 +110,10 @@ func (cache *cache) _remove_nolock(linkID string, includingChildren bool) {
106110
delete(data, link.LinkID)
107111
cache.children[link.ParentLinkID] = data
108112
} else {
109-
log.Fatalln("we have an issue for cache inconsistency where link is not found in the parent's map")
113+
cache.logger.Errorf("cache inconsistency: link %s not found in parent %s's map", link.LinkID, link.ParentLinkID)
110114
}
111115
} else {
112-
log.Fatalln("we have an issue for cache inconsistency where link's parent map is missing")
116+
cache.logger.Errorf("cache inconsistency: parent map for %s is missing", link.ParentLinkID)
113117
}
114118

115119
// we don't recursively go upward to clean up the parent's map

common/config.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,30 @@
11
package common
22

33
import (
4+
"log"
45
"net/http"
56
"os"
67
"runtime"
78
)
89

10+
// Logger is the minimum interface the bridge needs to emit log lines.
11+
// Its method set matches resty.Logger and go-proton-api's WithLogger
12+
// option, so a single value can be forwarded straight through to the
13+
// underlying API client without conversion.
14+
type Logger interface {
15+
Errorf(format string, v ...interface{})
16+
Warnf(format string, v ...interface{})
17+
Debugf(format string, v ...interface{})
18+
}
19+
20+
// stdlibLogger preserves the bridge's historical "print to stdlib log"
21+
// behaviour for callers that don't supply a Logger via Config.
22+
type stdlibLogger struct{}
23+
24+
func (stdlibLogger) Errorf(format string, v ...interface{}) { log.Printf("ERROR: "+format, v...) }
25+
func (stdlibLogger) Warnf(format string, v ...interface{}) { log.Printf("WARN: "+format, v...) }
26+
func (stdlibLogger) Debugf(format string, v ...interface{}) { log.Printf("DEBUG: "+format, v...) }
27+
928
type Config struct {
1029
/* Constants */
1130
AppVersion string
@@ -17,6 +36,14 @@ type Config struct {
1736
// transport.
1837
Transport http.RoundTripper
1938

39+
/* Logging */
40+
// Logger, if non-nil, receives all log output produced by the bridge
41+
// and is also forwarded to go-proton-api via proton.WithLogger so
42+
// that HTTP-layer warnings/errors emitted by resty go through the
43+
// same sink. When nil, the bridge falls back to printing through the
44+
// stdlib log package.
45+
Logger Logger
46+
2047
/* Login */
2148
FirstLoginCredential *FirstLoginCredentialData
2249
ReusableCredential *ReusableCredentialData
@@ -35,6 +62,16 @@ type Config struct {
3562
DataFolderName string
3663
}
3764

65+
// GetLogger returns the configured Logger or a stdlib-backed default
66+
// when Config.Logger is nil. Callers should use this rather than reading
67+
// Config.Logger directly so they never need to nil-check.
68+
func (c *Config) GetLogger() Logger {
69+
if c.Logger != nil {
70+
return c.Logger
71+
}
72+
return stdlibLogger{}
73+
}
74+
3875
type FirstLoginCredentialData struct {
3976
Username string
4077
Password string

common/proton_manager.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ func getProtonManager(config *Config) *proton.Manager {
1313
if config.Transport != nil {
1414
options = append(options, proton.WithTransport(config.Transport))
1515
}
16+
if config.Logger != nil {
17+
options = append(options, proton.WithLogger(config.Logger))
18+
}
1619
m := proton.New(options...)
1720

1821
return m

common/user.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"context"
55
"encoding/base64"
66
"encoding/json"
7-
"log"
87
"os"
98

109
"github.com/ProtonMail/gopenpgp/v2/crypto"
@@ -148,7 +147,7 @@ func Logout(ctx context.Context, config *Config, m *proton.Manager, c *proton.Cl
148147
defer c.Close()
149148

150149
if config.CredentialCacheFile == "" {
151-
log.Println("Logging out user")
150+
config.GetLogger().Debugf("Logging out user")
152151

153152
// log out
154153
err := c.AuthDelete(ctx)

drive.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package proton_api_bridge
22

33
import (
44
"context"
5-
"log"
65

76
"github.com/rclone/Proton-API-Bridge/common"
87
"golang.org/x/sync/semaphore"
@@ -98,8 +97,9 @@ func NewProtonDrive(ctx context.Context, config *common.Config, authHandler prot
9897
}
9998

10099
if !mainShareCheck {
101-
log.Printf("mainShare %#v", mainShare)
102-
log.Printf("shares %#v", shares)
100+
logger := config.GetLogger()
101+
logger.Errorf("mainShare %#v", mainShare)
102+
logger.Errorf("shares %#v", shares)
103103
return nil, nil, ErrMainSharePreconditionsFailed
104104
}
105105
}
@@ -146,7 +146,7 @@ func NewProtonDrive(ctx context.Context, config *common.Config, authHandler prot
146146
addrData: addrData,
147147
signatureAddress: mainShare.Creator,
148148

149-
cache: newCache(config.EnableCaching),
149+
cache: newCache(config.EnableCaching, config.GetLogger()),
150150
blockUploadSemaphore: semaphore.NewWeighted(int64(config.ConcurrentBlockUploadCount)),
151151
blockCryptoSemaphore: semaphore.NewWeighted(int64(config.ConcurrentFileCryptoCount)),
152152
}, credentials, nil

file_download.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"context"
66
"io"
7-
"log"
87

98
"github.com/ProtonMail/gopenpgp/v2/crypto"
109
"github.com/rclone/Proton-API-Bridge/utility"
@@ -171,7 +170,7 @@ func (protonDrive *ProtonDrive) DownloadFile(ctx context.Context, link *proton.L
171170
}
172171

173172
if useFallbackDownload {
174-
log.Println("Performing inefficient seek as metadata of encrypted file is missing")
173+
protonDrive.Config.GetLogger().Warnf("Performing inefficient seek as metadata of encrypted file is missing")
175174
n, err := io.CopyN(io.Discard, reader, offset)
176175
if err != nil {
177176
return nil, 0, nil, err

folder_recursive.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package proton_api_bridge
33
import (
44
"context"
55
"io"
6-
"log"
76
"os"
87

98
"github.com/ProtonMail/gopenpgp/v2/crypto"
@@ -53,8 +52,9 @@ func (protonDrive *ProtonDrive) listDirectoriesRecursively(
5352
}
5453

5554
if link.Type == proton.LinkTypeFile {
56-
log.Println("Downloading", currentPath)
57-
defer log.Println("Completes downloading", currentPath)
55+
logger := protonDrive.Config.GetLogger()
56+
logger.Debugf("Downloading %s", currentPath)
57+
defer logger.Debugf("Completes downloading %s", currentPath)
5858

5959
reader, _, _, err := protonDrive.DownloadFile(ctx, link, 0)
6060
if err != nil {

mail.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package proton_api_bridge
33
import (
44
"context"
55
"encoding/base64"
6-
"log"
6+
"errors"
77
"net/mail"
88
"os"
99
"path/filepath"
@@ -13,6 +13,11 @@ import (
1313
"github.com/rclone/go-proton-api"
1414
)
1515

16+
// ErrUnsupportedRecipientType is returned when the bridge is asked to
17+
// send mail to a recipient type it does not yet handle (currently
18+
// anything other than internal Proton accounts).
19+
var ErrUnsupportedRecipientType = errors.New("currently only support internal email sending")
20+
1621
type MailSendingParameters struct {
1722
TemplateFile string
1823
EmailSubject string
@@ -22,8 +27,9 @@ type MailSendingParameters struct {
2227
}
2328

2429
func (protonDrive *ProtonDrive) SendEmail(ctx context.Context, i int, errChan chan error, config *MailSendingParameters) {
25-
log.Println("SendEmail in", i)
26-
defer log.Println("SendEmail out", i)
30+
logger := protonDrive.Config.GetLogger()
31+
logger.Debugf("SendEmail in %d", i)
32+
defer logger.Debugf("SendEmail out %d", i)
2733

2834
createDraftResp, err := protonDrive.createDraft(ctx, config)
2935
if err != nil {
@@ -149,7 +155,7 @@ func (protonDrive *ProtonDrive) sendDraft(ctx context.Context, messageID string,
149155
return err
150156
}
151157
if recipientType != proton.RecipientTypeInternal {
152-
log.Fatalln("Currently only support internal email sending")
158+
return ErrUnsupportedRecipientType
153159
}
154160
recipientKR, err := recipientPublicKeys.GetKeyRing()
155161
if err != nil {

0 commit comments

Comments
 (0)