feat: add bark logger
Some checks failed
Multiplatform Docker build & push / build (push) Has been cancelled
Code quality - linting and typechecking / linting (push) Has been cancelled
Backend testing with Postgres / test-postgres (push) Has been cancelled

This commit is contained in:
Roland Bewick 2026-06-05 16:40:43 +07:00
parent a4dec48322
commit 5c23375c08
7 changed files with 146 additions and 0 deletions

View file

@ -6,6 +6,8 @@ AUTO_LINK_ALBY_ACCOUNT=false
# Optionally set LDK debug log level to get more info # Optionally set LDK debug log level to get more info
#LDK_LOG_LEVEL=5 #LDK_LOG_LEVEL=5
# Optionally set Bark debug log level to get more info
#BARK_LOG_LEVEL=5
# Optionally set Main application debug log level to get more info # Optionally set Main application debug log level to get more info
#LOG_LEVEL=5 #LOG_LEVEL=5

View file

@ -292,6 +292,7 @@ Bark connects to an [Ark](https://second.tech/) server. It can be configured via
- `BARK_SERVER`: the Ark server URL. For signet use `https://ark.signet.2nd.dev` - `BARK_SERVER`: the Ark server URL. For signet use `https://ark.signet.2nd.dev`
- `BARK_ESPLORA_SERVER`: the Esplora server URL used for chain data. For signet use `https://esplora.signet.2nd.dev`. - `BARK_ESPLORA_SERVER`: the Esplora server URL used for chain data. For signet use `https://esplora.signet.2nd.dev`.
- `BARK_SERVER_ACCESS_TOKEN`: an optional access token required by the Ark server (pre-public mainnet launch). - `BARK_SERVER_ACCESS_TOKEN`: an optional access token required by the Ark server (pre-public mainnet launch).
- `BARK_LOG_LEVEL`: Log level for Bark. Higher is more verbose. Default: 3. This is separate from the main application log level, allowing you to enable more verbose Bark logging (e.g., level 4 or 5) without enabling verbose logging for the entire application.
### Alby OAuth ### Alby OAuth

View file

@ -67,6 +67,7 @@ type AppConfig struct {
BarkServer string `envconfig:"BARK_SERVER" default:"https://ark.second.tech"` BarkServer string `envconfig:"BARK_SERVER" default:"https://ark.second.tech"`
BarkEsploraServer string `envconfig:"BARK_ESPLORA_SERVER" default:"https://mempool.second.tech/api"` BarkEsploraServer string `envconfig:"BARK_ESPLORA_SERVER" default:"https://mempool.second.tech/api"`
BarkServerAccessToken string `envconfig:"BARK_SERVER_ACCESS_TOKEN"` BarkServerAccessToken string `envconfig:"BARK_SERVER_ACCESS_TOKEN"`
BarkLogLevel string `envconfig:"BARK_LOG_LEVEL" default:"3"`
} }
func (c *AppConfig) IsDefaultClientId() bool { func (c *AppConfig) IsDefaultClientId() bool {

View file

@ -8,6 +8,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"strconv"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -50,6 +51,12 @@ type Config struct {
// ServerAccessToken is an optional access token required by some Ark // ServerAccessToken is an optional access token required by some Ark
// servers (currently used to gate mainnet access ahead of a public launch). // servers (currently used to gate mainnet access ahead of a public launch).
ServerAccessToken string ServerAccessToken string
// LogLevel is the logrus level (as an int string, e.g. "3" for Info) used
// for bark's own internal logs. Defaults to Info if empty/unparseable.
LogLevel string
// LogToFile controls whether bark's logs are also written to a dedicated
// bark.log file alongside the other backend logs.
LogToFile bool
} }
type BarkService struct { type BarkService struct {
@ -103,6 +110,14 @@ func NewBarkService(ctx context.Context, eventPublisher events.EventPublisher, w
return nil, err return nil, err
} }
// Forward bark's internal logs into a dedicated logger. Done before opening
// the wallet so any logs emitted during open are captured.
logLevel, err := strconv.Atoi(config.LogLevel)
if err != nil {
logLevel = int(logrus.InfoLevel)
}
installBarkLogger(logrus.Level(logLevel), config.LogToFile, workDir)
// Usually, you have two wait 2 blocks. You can set nb_min_round_confirmations=0 to make it go faster. // Usually, you have two wait 2 blocks. You can set nb_min_round_confirmations=0 to make it go faster.
roundTxRequiredConfirmations := uint32(0) roundTxRequiredConfirmations := uint32(0)

View file

@ -22,6 +22,8 @@ type Config struct {
ServerAddress string ServerAddress string
EsploraAddress string EsploraAddress string
ServerAccessToken string ServerAccessToken string
LogLevel string
LogToFile bool
} }
func NewBarkService(ctx context.Context, eventPublisher events.EventPublisher, workDir, mnemonic string, config Config) (lnclient.LNClient, error) { func NewBarkService(ctx context.Context, eventPublisher events.EventPublisher, workDir, mnemonic string, config Config) (lnclient.LNClient, error) {

123
lnclient/bark/logger.go Normal file
View file

@ -0,0 +1,123 @@
//go:build (darwin && (amd64 || arm64)) || (linux && (amd64 || arm64)) || (windows && amd64)
package bark
import (
"os"
"path/filepath"
"sync"
"github.com/orandin/lumberjackrus"
"github.com/sirupsen/logrus"
bark "gitlab.com/ark-bitcoin/bark-ffi-bindings/golang/bark"
"github.com/getAlby/hub/logger"
)
const barkLogFilename = "bark.log"
const logDir = "log"
// setLoggerOnce guards bark.SetLogger, which can only be installed once per
// process (calling it again returns an error from the underlying bridge).
var setLoggerOnce sync.Once
// barkLogger forwards bark's internal log records to a dedicated logrus logger
// (separate from the main app logger, with its own level and log file) so bark
// logs can be tuned independently of the rest of the hub.
type barkLogger struct {
logger *logrus.Logger
}
var _ bark.BarkLogger = &barkLogger{}
// Log implements bark.BarkLogger. The record's bark log level is mapped onto the
// equivalent logrus level, and the bark target is attached as a structured field.
//
// IMPORTANT: this must not call back into any bark API that itself emits log
// records, or the foreign runtime may stack-overflow or deadlock.
func (l *barkLogger) Log(level bark.LogLevel, target string, message string) {
l.logger.WithFields(logrus.Fields{
"log_type": "bark",
"target": target,
}).Log(barkLevelToLogrus(level), message)
}
func barkLevelToLogrus(level bark.LogLevel) logrus.Level {
switch level {
case bark.LogLevelError:
return logrus.ErrorLevel
case bark.LogLevelWarn:
return logrus.WarnLevel
case bark.LogLevelInfo:
return logrus.InfoLevel
case bark.LogLevelDebug:
return logrus.DebugLevel
case bark.LogLevelTrace:
return logrus.TraceLevel
}
logger.Logger.WithField("log_level", level).Error("Unknown Bark log level")
return logrus.ErrorLevel
}
// logrusToBarkLevel maps a logrus level onto the bark log level used to cap
// what the FFI bridge emits, so filtered-out records don't cross the boundary.
func logrusToBarkLevel(level logrus.Level) bark.LogLevel {
switch level {
case logrus.PanicLevel, logrus.FatalLevel, logrus.ErrorLevel:
return bark.LogLevelError
case logrus.WarnLevel:
return bark.LogLevelWarn
case logrus.InfoLevel:
return bark.LogLevelInfo
case logrus.DebugLevel:
return bark.LogLevelDebug
default:
return bark.LogLevelTrace
}
}
// installBarkLogger wires bark's internal logs into a dedicated logrus logger.
// It is safe to call more than once (e.g. when reopening the wallet); only the
// first call installs the logger, as the bridge can only be set once per
// process.
func installBarkLogger(logLevel logrus.Level, logToFile bool, workDir string) {
setLoggerOnce.Do(func() {
barkLogrus, err := createBarkLogger(logLevel, logToFile, workDir)
if err != nil {
logger.Logger.WithError(err).Error("Failed to create Bark logger")
return
}
// Cap the FFI bridge at the configured level so records below it are
// never produced; the dedicated logger applies the same level again.
if err := bark.SetLogger(&barkLogger{logger: barkLogrus}, logrusToBarkLevel(logLevel)); err != nil {
logger.Logger.WithError(err).Warn("Failed to install Bark logger")
}
})
}
func createBarkLogger(logLevel logrus.Level, logToFile bool, workDir string) (*logrus.Logger, error) {
barkLogrus := logrus.New()
barkLogrus.SetFormatter(&logrus.JSONFormatter{})
barkLogrus.SetOutput(os.Stdout)
barkLogrus.SetLevel(logLevel)
if logToFile {
parentDir := filepath.Dir(workDir)
barkLogFilePath := filepath.Join(parentDir, logDir, barkLogFilename)
barkFileLoggerHook, err := lumberjackrus.NewHook(
&lumberjackrus.LogFile{
Filename: barkLogFilePath,
MaxAge: 3,
MaxBackups: 3,
},
logLevel,
&logrus.JSONFormatter{},
nil,
)
if err != nil {
return nil, err
}
barkLogrus.AddHook(barkFileLoggerHook)
}
return barkLogrus, nil
}

View file

@ -384,6 +384,8 @@ func (svc *service) launchLNBackend(ctx context.Context, encryptionKey string) e
ServerAddress: env.BarkServer, ServerAddress: env.BarkServer,
EsploraAddress: env.BarkEsploraServer, EsploraAddress: env.BarkEsploraServer,
ServerAccessToken: env.BarkServerAccessToken, ServerAccessToken: env.BarkServerAccessToken,
LogLevel: env.BarkLogLevel,
LogToFile: env.LogToFile,
}) })
case config.CLNBackendType: case config.CLNBackendType:
CLNAddress, _ := svc.cfg.Get("CLNAddress", encryptionKey) CLNAddress, _ := svc.cfg.Get("CLNAddress", encryptionKey)