diff --git a/libs/Cargo.lock b/libs/Cargo.lock index 7803ea9a1..088569b96 100644 --- a/libs/Cargo.lock +++ b/libs/Cargo.lock @@ -671,6 +671,7 @@ dependencies = [ "anyhow", "breez-sdk-core", "camino", + "env_logger 0.10.1", "flutter_rust_bridge", "glob", "log", diff --git a/libs/Cargo.toml b/libs/Cargo.toml index c27f40f3e..0f24429d1 100644 --- a/libs/Cargo.toml +++ b/libs/Cargo.toml @@ -20,6 +20,7 @@ version = "0.6.2" aes = "0.8" anyhow = { version = "1.0.79", features = ["backtrace"] } base64 = "0.13.0" +env_logger = "0.10" bitcoin = "=0.29.2" # Same version as used in gl-client # Pin the reqwest dependency until macOS linker issue is fixed: https://github.com/seanmonstar/reqwest/issues/2006 hex = "0.4" diff --git a/libs/sdk-bindings/Cargo.toml b/libs/sdk-bindings/Cargo.toml index a83d22391..5c204a563 100644 --- a/libs/sdk-bindings/Cargo.toml +++ b/libs/sdk-bindings/Cargo.toml @@ -17,6 +17,7 @@ crate-type = ["staticlib", "cdylib", "lib"] anyhow = { workspace = true } breez-sdk-core = { path = "../sdk-core" } sdk-common = { path = "../sdk-common" } +env_logger = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } uniffi = { version = "0.23.0", features = ["bindgen-tests", "cli"] } diff --git a/libs/sdk-bindings/src/uniffi_binding.rs b/libs/sdk-bindings/src/uniffi_binding.rs index cb50c61e6..bca846a15 100644 --- a/libs/sdk-bindings/src/uniffi_binding.rs +++ b/libs/sdk-bindings/src/uniffi_binding.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use anyhow::Result; use breez_sdk_core::lnurl::pay::{LnUrlPayResult, LnUrlPaySuccessData}; -use breez_sdk_core::logger::init_uniffi_logger; +use breez_sdk_core::logger::{get_filter_level, init_env_logger}; use breez_sdk_core::{ error::*, mnemonic_to_seed as sdk_mnemonic_to_seed, parse as sdk_parse_input, parse_invoice as sdk_parse_invoice, AesSuccessActionDataDecrypted, AesSuccessActionDataResult, @@ -32,10 +32,76 @@ use breez_sdk_core::{ SwapAmountType, SwapInfo, SwapStatus, Symbol, TlvEntry, UnspentTransactionOutput, UrlSuccessActionData, }; +use env_logger::{Logger, Target}; +use log::{ + error, max_level, set_boxed_logger, set_max_level, Log, Metadata, Record, STATIC_MAX_LEVEL, +}; use once_cell::sync::Lazy; +use std::sync::Once; static RT: Lazy = Lazy::new(|| tokio::runtime::Runtime::new().unwrap()); +/* UniFFI Logger */ + +static INIT_UNIFFI_LOGGER: Once = Once::new(); + +fn init_uniffi_logger(log_stream: Box, filter_level: Option) { + INIT_UNIFFI_LOGGER.call_once(|| { + UniFFILogger::set_log_stream(log_stream, filter_level); + }); +} + +struct UniFFILogger { + env_logger: Logger, + log_stream: Box, +} + +impl UniFFILogger { + fn set_log_stream(log_stream: Box, filter_level: Option) { + let filter_level = get_filter_level(filter_level); + assert!( + filter_level <= STATIC_MAX_LEVEL, + "Should respect STATIC_MAX_LEVEL={:?}, which is done in compile time. level{:?}", + STATIC_MAX_LEVEL, + filter_level + ); + + let env_logger = init_env_logger(Some(Target::Stdout), Some(filter_level)); + + let uniffi_logger = UniFFILogger { + env_logger, + log_stream, + }; + set_boxed_logger(Box::new(uniffi_logger)) + .unwrap_or_else(|_| error!("Log stream already created.")); + set_max_level(filter_level); + } + + fn record_to_entry(record: &Record) -> LogEntry { + LogEntry { + line: format!("{}", record.args()), + level: format!("{}", record.level()), + } + } +} + +impl Log for UniFFILogger { + fn enabled(&self, metadata: &Metadata) -> bool { + // ignore the internal uniffi log to prevent infinite loop. + return metadata.level() <= max_level() + && *metadata.target() != *"breez_sdk_bindings::uniffi_binding"; + } + + fn log(&self, record: &Record) { + let metadata = record.metadata(); + if self.enabled(metadata) && self.env_logger.enabled(metadata) { + let entry = Self::record_to_entry(record); + self.log_stream.log(entry); + } + } + fn flush(&self) {} +} + /// Create a new SDK config with default values pub fn default_config( env_type: EnvironmentType, diff --git a/libs/sdk-core/Cargo.toml b/libs/sdk-core/Cargo.toml index cb33c7c8f..5e40b2200 100644 --- a/libs/sdk-core/Cargo.toml +++ b/libs/sdk-core/Cargo.toml @@ -19,7 +19,7 @@ zbase32 = "0.1.2" base64 = { workspace = true } chrono = "0.4" ecies = { version = "0.2.6", default-features = false, features = ["pure"] } -env_logger = "0.10" +env_logger = { workspace = true } futures = "0.3.30" ripemd = "0.1" rand = "0.8" diff --git a/libs/sdk-core/src/binding.rs b/libs/sdk-core/src/binding.rs index cec947040..04738a71e 100644 --- a/libs/sdk-core/src/binding.rs +++ b/libs/sdk-core/src/binding.rs @@ -33,17 +33,17 @@ use crate::error::{ ConnectError, ReceiveOnchainError, ReceivePaymentError, RedeemOnchainError, SdkError, SendOnchainError, SendPaymentError, }; -use crate::logger::{init_dart_logger, DartLogger}; +use crate::logger::{get_filter_level, init_env_logger}; use crate::lsp::LspInformation; -use crate::models::{Config, LogEntry, NodeState, Payment, SwapInfo}; +use crate::models::{Config, LevelFilter, LogEntry, NodeState, Payment, SwapInfo}; use crate::{ BackupStatus, BuyBitcoinRequest, BuyBitcoinResponse, CheckMessageRequest, CheckMessageResponse, - ConfigureNodeRequest, ConnectRequest, EnvironmentType, LevelFilter, ListPaymentsRequest, - LnUrlAuthError, MaxReverseSwapAmountResponse, NodeConfig, NodeCredentials, - OnchainPaymentLimitsResponse, OpenChannelFeeRequest, OpenChannelFeeResponse, PayOnchainRequest, - PayOnchainResponse, PrepareOnchainPaymentRequest, PrepareOnchainPaymentResponse, - PrepareRedeemOnchainFundsRequest, PrepareRedeemOnchainFundsResponse, PrepareRefundRequest, - PrepareRefundResponse, ReceiveOnchainRequest, ReceivePaymentRequest, ReceivePaymentResponse, + ConfigureNodeRequest, ConnectRequest, EnvironmentType, ListPaymentsRequest, LnUrlAuthError, + MaxReverseSwapAmountResponse, NodeConfig, NodeCredentials, OnchainPaymentLimitsResponse, + OpenChannelFeeRequest, OpenChannelFeeResponse, PayOnchainRequest, PayOnchainResponse, + PrepareOnchainPaymentRequest, PrepareOnchainPaymentResponse, PrepareRedeemOnchainFundsRequest, + PrepareRedeemOnchainFundsResponse, PrepareRefundRequest, PrepareRefundResponse, + ReceiveOnchainRequest, ReceivePaymentRequest, ReceivePaymentResponse, RedeemOnchainFundsRequest, RedeemOnchainFundsResponse, RefundRequest, RefundResponse, ReportIssueRequest, ReverseSwapFeesRequest, ReverseSwapInfo, ReverseSwapPairInfo, SendOnchainRequest, SendOnchainResponse, SendPaymentRequest, SendPaymentResponse, @@ -51,6 +51,85 @@ use crate::{ SignMessageResponse, StaticBackupRequest, StaticBackupResponse, }; +use lazy_static::lazy_static; +use log::{ + max_level, set_boxed_logger, set_max_level, warn, Log, Metadata, Record, STATIC_MAX_LEVEL, +}; +use parking_lot::RwLock; +use std::sync::Once; + +use env_logger::{Logger, Target}; + +/* Dart Logger */ + +static INIT_DART_LOGGER: Once = Once::new(); + +fn init_dart_logger(filter_level: Option) { + INIT_DART_LOGGER.call_once(|| { + let filter_level = get_filter_level(filter_level); + + assert!( + filter_level <= STATIC_MAX_LEVEL, + "Should respect STATIC_MAX_LEVEL={:?}, which is done in compile time. level{:?}", + STATIC_MAX_LEVEL, + filter_level + ); + + let env_logger = init_env_logger(Some(Target::Stdout), Some(filter_level)); + + let dart_logger = DartLogger { env_logger }; + set_boxed_logger(Box::new(dart_logger)) + .unwrap_or_else(|_| error!("Log stream already created.")); + set_max_level(filter_level); + }); +} + +lazy_static! { + static ref DART_LOGGER_STREAM_SINK: RwLock>> = RwLock::new(None); +} + +struct DartLogger { + env_logger: Logger, +} + +impl DartLogger { + fn set_stream_sink(stream_sink: StreamSink) { + let mut guard = DART_LOGGER_STREAM_SINK.write(); + if guard.is_some() { + warn!( + "BindingLogger::set_stream_sink but already exist a sink, thus overriding. \ + (This may or may not be a problem. It will happen normally if hot-reload Flutter app.)" + ); + } + *guard = Some(stream_sink); + drop(guard) + } + + fn record_to_entry(record: &Record) -> LogEntry { + LogEntry { + line: format!("{}", record.args()), + level: format!("{}", record.level()), + } + } +} + +impl Log for DartLogger { + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= max_level() + } + + fn log(&self, record: &Record) { + if self.env_logger.enabled(record.metadata()) { + let entry = Self::record_to_entry(record); + if let Some(sink) = &*DART_LOGGER_STREAM_SINK.read() { + sink.add(entry); + } + } + } + + fn flush(&self) {} +} + // === FRB mirroring // // This section contains frb "mirroring" structs and enums. diff --git a/libs/sdk-core/src/logger.rs b/libs/sdk-core/src/logger.rs index d1400bce1..bb2cb94cc 100644 --- a/libs/sdk-core/src/logger.rs +++ b/libs/sdk-core/src/logger.rs @@ -1,19 +1,16 @@ use crate::models::LevelFilter as BindingLevelFilter; -use crate::{LogEntry, LogStream}; use anyhow::{anyhow, Result}; use env_logger::{Builder, Logger, Target}; -use flutter_rust_bridge::StreamSink; -use lazy_static::lazy_static; + use log::{ - max_level, set_boxed_logger, set_max_level, warn, LevelFilter, Log, Metadata, Record, + max_level, set_boxed_logger, set_max_level, LevelFilter, Log, Metadata, Record, STATIC_MAX_LEVEL, }; -use parking_lot::RwLock; + use std::fs::OpenOptions; use std::io::Write; use chrono::Utc; -use std::sync::Once; /* env_logger */ @@ -57,7 +54,7 @@ vls_protocol_signer=warn, vls_protocol_signer::handler::HandlerBuilder::do_build=warn "#; -fn init_env_logger(target: Option, filter_level: Option) -> Logger { +pub fn init_env_logger(target: Option, filter_level: Option) -> Logger { let mut binding = Builder::new(); let builder = binding .parse_filters(ENV_LOGGER_FILTER) @@ -81,140 +78,6 @@ fn init_env_logger(target: Option, filter_level: Option) -> builder.build() } -/* Dart */ - -static INIT_DART_LOGGER: Once = Once::new(); - -pub fn init_dart_logger(filter_level: Option) { - INIT_DART_LOGGER.call_once(|| { - let filter_level = get_filter_level(filter_level); - - assert!( - filter_level <= STATIC_MAX_LEVEL, - "Should respect STATIC_MAX_LEVEL={:?}, which is done in compile time. level{:?}", - STATIC_MAX_LEVEL, - filter_level - ); - - let env_logger = init_env_logger(Some(Target::Stdout), Some(filter_level)); - - let dart_logger = DartLogger { env_logger }; - set_boxed_logger(Box::new(dart_logger)) - .unwrap_or_else(|_| error!("Log stream already created.")); - set_max_level(filter_level); - }); -} - -lazy_static! { - static ref DART_LOGGER_STREAM_SINK: RwLock>> = RwLock::new(None); -} - -pub struct DartLogger { - env_logger: Logger, -} - -impl DartLogger { - pub fn set_stream_sink(stream_sink: StreamSink) { - let mut guard = DART_LOGGER_STREAM_SINK.write(); - if guard.is_some() { - warn!( - "BindingLogger::set_stream_sink but already exist a sink, thus overriding. \ - (This may or may not be a problem. It will happen normally if hot-reload Flutter app.)" - ); - } - *guard = Some(stream_sink); - drop(guard) - } - - fn record_to_entry(record: &Record) -> LogEntry { - LogEntry { - line: format!("{}", record.args()), - level: format!("{}", record.level()), - } - } -} - -impl Log for DartLogger { - fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= max_level() - } - - fn log(&self, record: &Record) { - if self.env_logger.enabled(record.metadata()) { - let entry = Self::record_to_entry(record); - if let Some(sink) = &*DART_LOGGER_STREAM_SINK.read() { - sink.add(entry); - } - } - } - - fn flush(&self) {} -} - -/* UniFFI */ - -static INIT_UNIFFI_LOGGER: Once = Once::new(); - -pub fn init_uniffi_logger( - log_stream: Box, - filter_level: Option, -) { - INIT_UNIFFI_LOGGER.call_once(|| { - UniFFILogger::set_log_stream(log_stream, filter_level); - }); -} - -pub struct UniFFILogger { - env_logger: env_logger::Logger, - log_stream: Box, -} - -impl UniFFILogger { - fn set_log_stream(log_stream: Box, filter_level: Option) { - let filter_level = get_filter_level(filter_level); - assert!( - filter_level <= STATIC_MAX_LEVEL, - "Should respect STATIC_MAX_LEVEL={:?}, which is done in compile time. level{:?}", - STATIC_MAX_LEVEL, - filter_level - ); - - let env_logger = init_env_logger(Some(Target::Stdout), Some(filter_level)); - - let uniffi_logger = UniFFILogger { - env_logger, - log_stream, - }; - set_boxed_logger(Box::new(uniffi_logger)) - .unwrap_or_else(|_| error!("Log stream already created.")); - set_max_level(filter_level); - } - - fn record_to_entry(record: &Record) -> LogEntry { - LogEntry { - line: format!("{}", record.args()), - level: format!("{}", record.level()), - } - } -} - -impl Log for UniFFILogger { - fn enabled(&self, metadata: &Metadata) -> bool { - // ignore the internal uniffi log to prevent infinite loop. - return metadata.level() <= max_level() - && *metadata.target() != *"breez_sdk_bindings::uniffi_binding"; - } - - fn log(&self, record: &Record) { - let metadata = record.metadata(); - if self.enabled(metadata) && self.env_logger.enabled(metadata) { - let entry = Self::record_to_entry(record); - self.log_stream.log(entry); - } - } - fn flush(&self) {} -} - /* Rust */ /// Configures a global SDK logger that will log to file and will forward log events to @@ -310,7 +173,7 @@ impl Log for GlobalSdkLogger { /* Binding LevelFilter */ -fn get_filter_level(filter_level: Option) -> LevelFilter { +pub fn get_filter_level(filter_level: Option) -> LevelFilter { match filter_level.unwrap_or(BindingLevelFilter::Trace) { BindingLevelFilter::Off => LevelFilter::Off, BindingLevelFilter::Error => LevelFilter::Error,