11import 'dart:async' ;
22import 'dart:isolate' ;
33
4+ import 'package:flutter/foundation.dart' ;
45import 'package:logging/logging.dart' ;
56import 'package:sqlite_async/sqlite3.dart' as sqlite;
67import 'package:sqlite_async/sqlite_async.dart' ;
@@ -68,6 +69,9 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
6869 /// null when disconnected, present when connecting or connected
6970 AbortController ? _disconnecter;
7071
72+ /// The Logger internally used by this PowerSyncDatabase
73+ late final Logger logger;
74+
7175 /// Open a [PowerSyncDatabase] .
7276 ///
7377 /// Only a single [PowerSyncDatabase] per [path] should be opened at a time.
@@ -84,12 +88,13 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
8488 {required Schema schema,
8589 required String path,
8690 int maxReaders = SqliteDatabase .defaultMaxReaders,
91+ LogType log = LogType .auto,
8792 @Deprecated ("Use [PowerSyncDatabase.withFactory] instead" )
8893 // ignore: deprecated_member_use_from_same_package
8994 SqliteConnectionSetup ? sqliteSetup}) {
9095 // ignore: deprecated_member_use_from_same_package
9196 var factory = PowerSyncOpenFactory (path: path, sqliteSetup: sqliteSetup);
92- return PowerSyncDatabase .withFactory (factory , schema: schema);
97+ return PowerSyncDatabase .withFactory (factory , schema: schema, log : log );
9398 }
9499
95100 /// Open a [PowerSyncDatabase] with a [PowerSyncOpenFactory] .
@@ -98,18 +103,54 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
98103 /// additional logic to run inside the database isolate before or after opening.
99104 ///
100105 /// Subclass [PowerSyncOpenFactory] to add custom logic to this process.
101- factory PowerSyncDatabase .withFactory (PowerSyncOpenFactory openFactory,
102- {required Schema schema,
103- int maxReaders = SqliteDatabase .defaultMaxReaders}) {
106+ factory PowerSyncDatabase .withFactory (
107+ PowerSyncOpenFactory openFactory, {
108+ required Schema schema,
109+ int maxReaders = SqliteDatabase .defaultMaxReaders,
110+ LogType log = LogType .auto,
111+ }) {
104112 final db = SqliteDatabase .withFactory (openFactory, maxReaders: maxReaders);
105- return PowerSyncDatabase .withDatabase (schema: schema, database: db);
113+ return PowerSyncDatabase .withDatabase (
114+ schema: schema, database: db, log: log);
106115 }
107116
108117 /// Open a PowerSyncDatabase on an existing [SqliteDatabase] .
109118 ///
110119 /// Migrations are run on the database when this constructor is called.
111- PowerSyncDatabase .withDatabase (
112- {required this .schema, required this .database}) {
120+ PowerSyncDatabase .withDatabase ({
121+ required this .schema,
122+ required this .database,
123+ LogType log = LogType .auto,
124+ }) {
125+ if (log == LogType .debug || log == LogType .auto) {
126+ // Use a detached logger to log directly to the console
127+ logger = Logger .detached ('PowerSync' );
128+ final debug = log == LogType .debug || kDebugMode;
129+ if (debug) {
130+ logger.level = Level .FINE ;
131+ logger.onRecord.listen ((record) {
132+ print (
133+ '[${record .loggerName }] ${record .level .name }: ${record .time }: ${record .message }' );
134+
135+ if (record.error != null ) {
136+ print (record.error);
137+ }
138+ if (record.stackTrace != null ) {
139+ print (record.stackTrace);
140+ }
141+ });
142+ } else {
143+ logger.level = Level .OFF ;
144+ }
145+ } else if (log == LogType .logger) {
146+ // Standard logger. The app is responsible for adding an onRecord listener
147+ // on the root logger.
148+ logger = Logger ('PowerSync' );
149+ } else {
150+ // Should not happen
151+ logger = Logger .detached ('PowerSync' );
152+ }
153+
113154 updates = database.updates
114155 .map ((update) =>
115156 PowerSyncUpdateNotification .fromUpdateNotification (update))
@@ -173,11 +214,11 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
173214 if (action == "getCredentials" ) {
174215 await (data[1 ] as PortCompleter ).handle (() async {
175216 final token = await connector.getCredentialsCached ();
176- log .fine ('Credentials: $token ' );
217+ logger .fine ('Credentials: $token ' );
177218 return token;
178219 });
179220 } else if (action == "invalidateCredentials" ) {
180- log .fine ('Refreshing credentials' );
221+ logger .fine ('Refreshing credentials' );
181222 await (data[1 ] as PortCompleter ).handle (() async {
182223 await connector.prefetchCredentials ();
183224 });
@@ -205,7 +246,7 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
205246 updateSubscription? .cancel ();
206247 } else if (action == 'log' ) {
207248 LogRecord record = data[1 ];
208- log .log (
249+ logger .log (
209250 record.level, record.message, record.error, record.stackTrace);
210251 }
211252 }
@@ -221,7 +262,7 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
221262 // #3 _ForwardingStreamSubscription._handleError (dart:async/stream_pipe.dart:157:13)
222263 // #4 _HttpClientResponse.listen.<anonymous closure> (dart:_http/http_impl.dart:707:16)
223264 // ...
224- log .severe ('Sync Isolate error' , message);
265+ logger .severe ('Sync Isolate error' , message);
225266
226267 // Reconnect
227268 connect (connector: connector);
@@ -237,7 +278,7 @@ class PowerSyncDatabase with SqliteQueries implements SqliteConnection {
237278
238279 var exitPort = ReceivePort ();
239280 exitPort.listen ((message) {
240- log .fine ('Sync Isolate exit' );
281+ logger .fine ('Sync Isolate exit' );
241282 disconnected ();
242283 });
243284
@@ -532,8 +573,8 @@ Future<void> _powerSyncDatabaseIsolate(
532573
533574 // Is there a way to avoid the overhead if logging is not enabled?
534575 // This only takes effect in this isolate.
535- Logger .root .level = Level .ALL ;
536- log .onRecord.listen ((record) {
576+ isolateLogger .level = Level .ALL ;
577+ isolateLogger .onRecord.listen ((record) {
537578 var copy = LogRecord (record.level, record.message, record.loggerName,
538579 record.error, record.stackTrace);
539580 sPort.send (["log" , copy]);
@@ -602,3 +643,15 @@ Future<void> _powerSyncDatabaseIsolate(
602643 throw error;
603644 });
604645}
646+
647+ enum LogType {
648+ /// Log to the console, with FINE level in debug mode, no logs in release mode
649+ auto,
650+
651+ /// Always log to the console with FINE level
652+ debug,
653+
654+ /// Uses a Logger instance.
655+ /// Use Logger.root.onRecord to handle log messages
656+ logger,
657+ }
0 commit comments