Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for logging #583

Merged
merged 12 commits into from
May 24, 2022
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
* Support user profile data. ([#570](https://github.com/realm/realm-dart/pull/570))
* Support flexible synchronization. ([#496](https://github.com/realm/realm-dart/pull/496))
* Added support for DateTime properties. ([#569](https://github.com/realm/realm-dart/pull/569))
* Support setting logger on AppConfiguration. ([#583](https://github.com/realm/realm-dart/pull/583))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't appear to be correct anymore now that we have a single logger.

* Support setting logger on Realm class. Default is to print info message or worse to the console. ([#583](https://github.com/realm/realm-dart/pull/583))

### Fixed
* Fixed an issue that would result in the wrong transaction being rolled back if you start a write transaction inside a write transaction. ([#442](https://github.com/realm/realm-dart/issues/442))
Expand Down
2 changes: 2 additions & 0 deletions ffigen/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ headers:
- 'realm_android_platform.h'
- 'subscription_set.h'
- 'sync_session.h'
- 'sync_client_config.h'
include-directives: #generate only for these headers
- 'realm.h'
- 'realm_dart.h'
- 'realm_dart_scheduler.h'
- 'realm_android_platform.h'
- 'subscription_set.h'
- 'sync_session.h'
- 'sync_client_config.h'
compiler-opts:
- '-DRLM_NO_ANON_UNIONS'
- '-DFFI_GEN'
Expand Down
1 change: 1 addition & 0 deletions ffigen/sync_client_config.h
49 changes: 6 additions & 43 deletions lib/src/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,49 +15,16 @@
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

import 'dart:io';

import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'native/realm_core.dart';

import '../realm.dart';
import 'configuration.dart';
import 'credentials.dart';
import 'native/realm_core.dart';
import 'user.dart';
import 'configuration.dart';

/// Specifies the criticality level above which messages will be logged
/// by the default sync client logger.
/// {@category Application}
enum LogLevel {
/// Log everything. This will seriously harm the performance of the
/// sync client and should never be used in production scenarios.
all,

/// A version of 'debug' that allows for very high volume output.
/// This may seriously affect the performance of the sync client.
trace,

/// Reveal information that can aid debugging, no longer paying
/// attention to efficiency.
debug,

/// Same as 'Info', but prioritize completeness over minimalism.
detail,

/// Log operational sync client messages, but in a minimalistic fashion to
/// avoid general overhead from logging and to keep volume down.
info,

/// Log errors and warnings.
warn,

/// Log errors only.
error,

/// Log only fatal errors.
fatal,

/// Log nothing.
off,
}

/// A class exposing configuration options for an [App]
/// {@category Application}
Expand Down Expand Up @@ -111,9 +78,6 @@ class AppConfiguration {
/// Setting this will not change the encryption key for individual Realms, which is set in the [Configuration].
final List<int>? metadataEncryptionKey;

/// The [LogLevel] for sync operations.
final LogLevel logLevel;

/// The [HttpClient] that will be used for HTTP requests during authentication.
///
/// You can use this to override the default http client handler and configure settings like proxies,
Expand All @@ -132,7 +96,6 @@ class AppConfiguration {
this.localAppVersion,
this.metadataEncryptionKey,
this.metadataPersistenceMode = MetadataPersistenceMode.plaintext,
this.logLevel = LogLevel.error,
this.maxConnectionTimeout = const Duration(minutes: 2),
HttpClient? httpClient,
}) : baseUrl = baseUrl ?? Uri.parse('https://realm.mongodb.com'),
Expand Down
54 changes: 48 additions & 6 deletions lib/src/native/realm_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3134,6 +3134,40 @@ class RealmLibrary {
late final _realm_dart_scheduler_invoke = _realm_dart_scheduler_invokePtr
.asFunction<void Function(int, ffi.Pointer<ffi.Void>)>();

void realm_dart_sync_client_config_set_log_callback(
ffi.Pointer<realm_sync_client_config_t> config,
realm_log_func_t callback,
ffi.Pointer<ffi.Void> userdata,
realm_free_userdata_func_t userdata_free,
ffi.Pointer<realm_scheduler_t> scheduler,
) {
return _realm_dart_sync_client_config_set_log_callback(
config,
callback,
userdata,
userdata_free,
scheduler,
);
}

late final _realm_dart_sync_client_config_set_log_callbackPtr = _lookup<
ffi.NativeFunction<
ffi.Void Function(
ffi.Pointer<realm_sync_client_config_t>,
realm_log_func_t,
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t,
ffi.Pointer<realm_scheduler_t>)>>(
'realm_dart_sync_client_config_set_log_callback');
late final _realm_dart_sync_client_config_set_log_callback =
_realm_dart_sync_client_config_set_log_callbackPtr.asFunction<
void Function(
ffi.Pointer<realm_sync_client_config_t>,
realm_log_func_t,
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t,
ffi.Pointer<realm_scheduler_t>)>();

/// Register a handler in order to be notified when subscription set is equal to the one passed as parameter
/// This is an asynchronous operation.
///
Expand All @@ -3145,7 +3179,7 @@ class RealmLibrary {
bool realm_dart_sync_on_subscription_set_state_change_async(
ffi.Pointer<realm_flx_sync_subscription_set_t> subscription_set,
int notify_when,
realm_dart_sync_on_subscription_state_changed callback,
realm_sync_on_subscription_state_changed_t callback,
ffi.Pointer<ffi.Void> userdata,
realm_free_userdata_func_t userdata_free,
ffi.Pointer<realm_scheduler_t> scheduler,
Expand All @@ -3167,7 +3201,7 @@ class RealmLibrary {
ffi.Uint8 Function(
ffi.Pointer<realm_flx_sync_subscription_set_t>,
ffi.Int32,
realm_dart_sync_on_subscription_state_changed,
realm_sync_on_subscription_state_changed_t,
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t,
ffi.Pointer<realm_scheduler_t>)>>(
Expand All @@ -3177,7 +3211,7 @@ class RealmLibrary {
int Function(
ffi.Pointer<realm_flx_sync_subscription_set_t>,
int,
realm_dart_sync_on_subscription_state_changed,
realm_sync_on_subscription_state_changed_t,
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t,
ffi.Pointer<realm_scheduler_t>)>();
Expand Down Expand Up @@ -9097,12 +9131,22 @@ class _SymbolAddresses {
ffi.Void Function(ffi.Uint64, ffi.Pointer<ffi.Void>)>>
get realm_dart_scheduler_invoke =>
_library._realm_dart_scheduler_invokePtr;
ffi.Pointer<
ffi.NativeFunction<
ffi.Void Function(
ffi.Pointer<realm_sync_client_config_t>,
realm_log_func_t,
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t,
ffi.Pointer<realm_scheduler_t>)>>
get realm_dart_sync_client_config_set_log_callback =>
_library._realm_dart_sync_client_config_set_log_callbackPtr;
ffi.Pointer<
ffi.NativeFunction<
ffi.Uint8 Function(
ffi.Pointer<realm_flx_sync_subscription_set_t>,
ffi.Int32,
realm_dart_sync_on_subscription_state_changed,
realm_sync_on_subscription_state_changed_t,
ffi.Pointer<ffi.Void>,
realm_free_userdata_func_t,
ffi.Pointer<realm_scheduler_t>)>>
Expand Down Expand Up @@ -9470,8 +9514,6 @@ abstract class realm_column_attr {
class realm_config extends ffi.Opaque {}

typedef realm_config_t = realm_config;
typedef realm_dart_sync_on_subscription_state_changed = ffi.Pointer<
ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Int32)>>;
typedef realm_data_initialization_func_t = ffi.Pointer<
ffi.NativeFunction<
ffi.Uint8 Function(ffi.Pointer<ffi.Void>, ffi.Pointer<realm_t>)>>;
Expand Down
49 changes: 48 additions & 1 deletion lib/src/native/realm_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import 'dart:typed_data';

// Hide StringUtf8Pointer.toNativeUtf8 and StringUtf16Pointer since these allows silently allocating memory. Use toUtf8Ptr instead
import 'package:ffi/ffi.dart' hide StringUtf8Pointer, StringUtf16Pointer;
import 'package:logging/logging.dart';

import '../app.dart';
import '../collections.dart';
Expand Down Expand Up @@ -1026,13 +1027,36 @@ class _RealmCore {
});
}

static void _logCallback(Pointer<Void> userdata, int levelAsInt, Pointer<Int8> message) {
try {
final logger = Realm.logger;
final level = _LogLevel.values[levelAsInt].loggerLevel;

// Don't do expensive utf8 to utf16 conversion unless we have to..
if (logger.isLoggable(level)) {
nielsenko marked this conversation as resolved.
Show resolved Hide resolved
logger.log(level, message.cast<Utf8>().toDartString());
}
} finally {
_realmLib.realm_free(message.cast()); // .. but always free the message
}
}

SyncClientConfigHandle _createSyncClientConfig(AppConfiguration configuration) {
return using((arena) {
final handle = SyncClientConfigHandle._(_realmLib.realm_sync_client_config_new());

_realmLib.realm_sync_client_config_set_base_file_path(handle._pointer, configuration.baseFilePath.path.toUtf8Ptr(arena));
_realmLib.realm_sync_client_config_set_metadata_mode(handle._pointer, configuration.metadataPersistenceMode.index);
_realmLib.realm_sync_client_config_set_log_level(handle._pointer, configuration.logLevel.index);

_realmLib.realm_sync_client_config_set_log_level(handle._pointer, _LogLevel.fromLevel(Realm.logger.level).index);
_realmLib.realm_dart_sync_client_config_set_log_callback(
handle._pointer,
Pointer.fromFunction(_logCallback),
nullptr,
nullptr,
scheduler.handle._pointer,
);

_realmLib.realm_sync_client_config_set_connect_timeout(handle._pointer, configuration.maxConnectionTimeout.inMicroseconds);
if (configuration.metadataEncryptionKey != null && configuration.metadataPersistenceMode == MetadataPersistenceMode.encrypted) {
_realmLib.realm_sync_client_config_set_metadata_encryption_key(handle._pointer, configuration.metadataEncryptionKey!.toUint8Ptr(arena));
Expand Down Expand Up @@ -1970,3 +1994,26 @@ extension on realm_object_id {
return ObjectId.fromBytes(buffer);
}
}

// Helper enum for converting Level
enum _LogLevel {
all(RealmLogLevel.all),
trace(RealmLogLevel.trace),
debug(RealmLogLevel.debug),
detail(RealmLogLevel.detail),
info(RealmLogLevel.info),
warn(RealmLogLevel.warn),
error(RealmLogLevel.error),
fatal(RealmLogLevel.fatal),
off(RealmLogLevel.off);

final Level loggerLevel;
const _LogLevel(this.loggerLevel);

factory _LogLevel.fromLevel(Level level) {
for (final candidate in _LogLevel.values) {
if (level.value > candidate.loggerLevel.value) return candidate;
}
return _LogLevel.off;
}
}
76 changes: 76 additions & 0 deletions lib/src/realm_class.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';

import 'package:logging/logging.dart';
import 'package:realm_common/realm_common.dart';

import 'configuration.dart';
Expand Down Expand Up @@ -70,6 +71,72 @@ export 'subscription.dart' show Subscription, SubscriptionSet, SubscriptionSetSt
export 'user.dart' show User, UserState, UserIdentity;
export 'session.dart' show Session, SessionState, ConnectionState, ProgressDirection, ProgressMode, SyncProgress;

/// Specifies the criticality level above which messages will be logged
/// by the default sync client logger.
/// {@category Realm}
class RealmLogLevel {
/// Log everything. This will seriously harm the performance of the
/// sync client and should never be used in production scenarios.
///
/// Same as [Level.ALL]
static const all = Level.ALL;

/// A version of [debug] that allows for very high volume output.
/// This may seriously affect the performance of the sync client.
///
/// Same as [Level.FINEST]
static const trace = Level('TRACE', 300);

/// Reveal information that can aid debugging, no longer paying
/// attention to efficiency.
///
/// Same as [Level.FINER]
static const debug = Level('DEBUG', 400);

/// Same as [info], but prioritize completeness over minimalism.
///
/// Same as [Level.FINE];
static const detail = Level('DETAIL', 500);

/// Log operational sync client messages, but in a minimalist fashion to
/// avoid general overhead from logging and to keep volume down.
///
/// Same as [Level.INFO];
static const info = Level.INFO;

/// Log errors and warnings.
///
/// Same as [Level.WARNING];
static const warn = Level.WARNING;

/// Log errors only.
///
/// Same as [Level.SEVERE];
static const error = Level('ERROR', 1000);

/// Log only fatal errors.
///
/// Same as [Level.SHOUT];
static const fatal = Level('FATAL', 1200);

/// Log nothing.
///
/// Same as [Level.OFF];
static const off = Level.OFF;

static const levels = [
all,
trace,
debug,
detail,
info,
warn,
error,
fatal,
off,
];
}

/// A [Realm] instance represents a `Realm` database.
///
/// {@category Realm}
Expand All @@ -79,6 +146,13 @@ class Realm {
late final RealmHandle _handle;
late final Scheduler _scheduler;

/// The logger to use.
///
/// Defaults to printing info or worse to the console
static late var logger = Logger.detached('Realm')
..level = RealmLogLevel.info
..onRecord.listen((event) => print(event));

/// The [Configuration] object used to open this [Realm]
Configuration get config => _config;

Expand Down Expand Up @@ -342,6 +416,8 @@ class Scheduler {
}
}

late final scheduler = Scheduler(() {});
nielsenko marked this conversation as resolved.
Show resolved Hide resolved
nirinchev marked this conversation as resolved.
Show resolved Hide resolved

/// @nodoc
class Transaction {
Realm? _realm;
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ set(SOURCES
realm_dart_scheduler.cpp
subscription_set.cpp
sync_session.cpp
sync_client_config.cpp
)

set(HEADERS
Expand All @@ -20,6 +21,7 @@ set(HEADERS
realm-core/src/realm.h
subscription_set.h
sync_session.h
sync_client_config.h
)

add_library(realm_dart SHARED ${SOURCES} ${HEADERS})
Expand Down
Loading