logger.go raw
1 //go:build (darwin && (amd64 || arm64)) || (linux && (amd64 || arm64)) || (windows && amd64)
2
3 package bark
4
5 import (
6 "os"
7 "path/filepath"
8 "sync"
9
10 "github.com/orandin/lumberjackrus"
11 "github.com/sirupsen/logrus"
12 bark "gitlab.com/ark-bitcoin/bark-ffi-bindings/golang/bark"
13
14 "github.com/getAlby/hub/logger"
15 )
16
17 const barkLogFilename = "bark.log"
18 const logDir = "log"
19
20 // setLoggerOnce guards bark.SetLogger, which can only be installed once per
21 // process (calling it again returns an error from the underlying bridge).
22 var setLoggerOnce sync.Once
23
24 // barkLogger forwards bark's internal log records to a dedicated logrus logger
25 // (separate from the main app logger, with its own level and log file) so bark
26 // logs can be tuned independently of the rest of the hub.
27 type barkLogger struct {
28 logger *logrus.Logger
29 }
30
31 var _ bark.BarkLogger = &barkLogger{}
32
33 // Log implements bark.BarkLogger. The record's bark log level is mapped onto the
34 // equivalent logrus level, and the bark target is attached as a structured field.
35 //
36 // IMPORTANT: this must not call back into any bark API that itself emits log
37 // records, or the foreign runtime may stack-overflow or deadlock.
38 func (l *barkLogger) Log(level bark.LogLevel, target string, message string) {
39 l.logger.WithFields(logrus.Fields{
40 "log_type": "bark",
41 "target": target,
42 }).Log(barkLevelToLogrus(level), message)
43 }
44
45 func barkLevelToLogrus(level bark.LogLevel) logrus.Level {
46 switch level {
47 case bark.LogLevelError:
48 return logrus.ErrorLevel
49 case bark.LogLevelWarn:
50 return logrus.WarnLevel
51 case bark.LogLevelInfo:
52 return logrus.InfoLevel
53 case bark.LogLevelDebug:
54 return logrus.DebugLevel
55 case bark.LogLevelTrace:
56 return logrus.TraceLevel
57 }
58 logger.Logger.WithField("log_level", level).Error("Unknown Bark log level")
59 return logrus.ErrorLevel
60 }
61
62 // logrusToBarkLevel maps a logrus level onto the bark log level used to cap
63 // what the FFI bridge emits, so filtered-out records don't cross the boundary.
64 func logrusToBarkLevel(level logrus.Level) bark.LogLevel {
65 switch level {
66 case logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel:
67 return bark.LogLevelError
68 case logrus.WarnLevel:
69 return bark.LogLevelWarn
70 case logrus.InfoLevel:
71 return bark.LogLevelInfo
72 case logrus.DebugLevel:
73 return bark.LogLevelDebug
74 default:
75 return bark.LogLevelTrace
76 }
77 }
78
79 // installBarkLogger wires bark's internal logs into a dedicated logrus logger.
80 // It is safe to call more than once (e.g. when reopening the wallet); only the
81 // first call installs the logger, as the bridge can only be set once per
82 // process.
83 func installBarkLogger(logLevel logrus.Level, logToFile bool, workDir string) {
84 setLoggerOnce.Do(func() {
85 barkLogrus, err := createBarkLogger(logLevel, logToFile, workDir)
86 if err != nil {
87 logger.Logger.WithError(err).Error("Failed to create Bark logger")
88 return
89 }
90 // Cap the FFI bridge at the configured level so records below it are
91 // never produced; the dedicated logger applies the same level again.
92 if err := bark.SetLogger(&barkLogger{logger: barkLogrus}, logrusToBarkLevel(logLevel)); err != nil {
93 logger.Logger.WithError(err).Warn("Failed to install Bark logger")
94 }
95 })
96 }
97
98 func createBarkLogger(logLevel logrus.Level, logToFile bool, workDir string) (*logrus.Logger, error) {
99 barkLogrus := logrus.New()
100 barkLogrus.SetFormatter(&logrus.JSONFormatter{})
101 barkLogrus.SetOutput(os.Stdout)
102 barkLogrus.SetLevel(logLevel)
103
104 if logToFile {
105 parentDir := filepath.Dir(workDir)
106 barkLogFilePath := filepath.Join(parentDir, logDir, barkLogFilename)
107 barkFileLoggerHook, err := lumberjackrus.NewHook(
108 &lumberjackrus.LogFile{
109 Filename: barkLogFilePath,
110 MaxAge: 3,
111 MaxBackups: 3,
112 },
113 logLevel,
114 &logrus.JSONFormatter{},
115 nil,
116 )
117 if err != nil {
118 return nil, err
119 }
120 barkLogrus.AddHook(barkFileLoggerHook)
121 }
122 return barkLogrus, nil
123 }
124