Skip to content

Commit

Permalink
Merge afdb72b into 2c2b359
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsenko committed Mar 18, 2024
2 parents 2c2b359 + afdb72b commit f211592
Show file tree
Hide file tree
Showing 21 changed files with 510 additions and 532 deletions.
34 changes: 34 additions & 0 deletions CHANGELOG.md
Expand Up @@ -45,6 +45,40 @@
```
* Removed `SchemaObject.properties` - instead, `SchemaObject` is now an iterable collection of `Property`. (Issue [#1449](https://github.com/realm/realm-dart/issues/1449))
* `SyncProgress.transferredBytes` and `SyncProgress.transferableBytes` have been consolidated into `SyncProgress.progressEstimate`. The values reported previously were incorrect and did not accurately represent bytes either. The new field better conveys the uncertainty around the progress being reported. With this release, we're reporting accurate estimates for upload progress, but estimating downloads is still unreliable. A future server and SDK release will add better estimations for download progress. (Issue [#1562](https://github.com/realm/realm-dart/issues/1562))
* `Realm.logger` is no longer settable, and no longer implements `Logger` from package `logging`. In particular you can no longer call `Realm.logger.level =`. Instead you should call `Realm.logger.setLogLevel(RealmLogLevel level, {RealmLogCategory? category})` that takes an optional category. If no category is exlicitly given, then `RealmLogCategory.realm` is assumed.

Also, note that setting a level is no longer local to the current isolate, but shared between accross all isolates. Maintaining the illution that there exists a logger per isolate, became untennable with the new API. At the core level there is just one process wide logger.

Categories form a hirarchy and setting the log level of a parent category will override the level of the current children. The hierarchy is exposed in a type safe manner with:
```dart
sealed class RealmLogCategory {
/// All possible log categories.
static final values = [
realm,
realm.app,
realm.sdk,
realm.storage,
realm.storage.notification,
realm.storage.object,
realm.storage.query,
realm.storage.transaction,
realm.sync,
realm.sync.client,
realm.sync.client.changeset,
realm.sync.client.network,
realm.sync.client.reset,
realm.sync.client.session,
realm.sync.server,
...
```
the `onRecord` stream now pumps `RealmLogRecord`s that includes the category the message was logged to.

If you want to hook up realm logging with conventional dart logging you can do:
```dart
Realm.logger.onRecord.forEach((r) => Logger(r.category.toString()).log(r.level.level, r.message));
```
If no isolate subscribes to `Realm.logger.onRecord` then the logs will by default be send to stdout.


### Enhancements
* Realm objects can now be serialized as [EJSON](https://www.mongodb.com/docs/manual/reference/mongodb-extended-json/)
Expand Down
1 change: 1 addition & 0 deletions packages/realm_dart/lib/src/app.dart
Expand Up @@ -11,6 +11,7 @@ import 'package:path/path.dart' as _path;

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

Expand Down
1 change: 1 addition & 0 deletions packages/realm_dart/lib/src/configuration.dart
Expand Up @@ -7,6 +7,7 @@ import 'dart:io';

// ignore: no_leading_underscores_for_library_prefixes
import 'package:path/path.dart' as _path;
import 'logging.dart';
import 'native/realm_core.dart';
import 'realm_class.dart';
import 'init.dart';
Expand Down
182 changes: 182 additions & 0 deletions packages/realm_dart/lib/src/logging.dart
@@ -0,0 +1,182 @@
// Copyright 2024 MongoDB, Inc.
// SPDX-License-Identifier: Apache-2.0

import 'dart:async';

import 'package:logging/logging.dart';

import 'native/realm_core.dart';

// Using classes to make a fancy hierarchical enum
sealed class RealmLogCategory {
/// All possible log categories.
static final values = [
realm,
realm.app,
realm.sdk,
realm.storage,
realm.storage.notification,
realm.storage.object,
realm.storage.query,
realm.storage.transaction,
realm.sync,
realm.sync.client,
realm.sync.client.changeset,
realm.sync.client.network,
realm.sync.client.reset,
realm.sync.client.session,
realm.sync.server,
];

/// The root category for all log messages from the Realm SDK.
static final realm = _RealmLogCategory();

final String _name;
final RealmLogCategory? _parent;

RealmLogCategory._(this._name, this._parent);

bool contains(RealmLogCategory category) {
var current = category;
while (current != this) {
if (current == realm) { // root
return false;
}
current = current._parent!;
}
return true;
}

// cache the toString result
late final String _toString = (_parent == null ? _name : '$_parent.$_name');
@override
String toString() => _toString;

static final _map = {for (final category in RealmLogCategory.values) category.toString(): category};
factory RealmLogCategory.fromString(String message) => _map[message]!;
}

class _LeafLogCategory extends RealmLogCategory {
_LeafLogCategory(super.name, RealmLogCategory super.parent) : super._();
}

class _RealmLogCategory extends RealmLogCategory {
_RealmLogCategory() : super._('Realm', null);

late final app = _LeafLogCategory('App', this);
late final sdk = _LeafLogCategory('SDK', this);
late final storage = _StorageLogCategory(this);
late final sync = _SyncLogCategory(this);
}

class _SyncLogCategory extends RealmLogCategory {
_SyncLogCategory(RealmLogCategory parent) : super._('Sync', parent);

late final client = _ClientLogCategory(this);
late final server = _LeafLogCategory('Server', this);
}

class _ClientLogCategory extends RealmLogCategory {
_ClientLogCategory(RealmLogCategory parent) : super._('Client', parent);

late final changeset = _LeafLogCategory('Changeset', this);
late final network = _LeafLogCategory('Network', this);
late final reset = _LeafLogCategory('Reset', this);
late final session = _LeafLogCategory('Session', this);
}

class _StorageLogCategory extends RealmLogCategory {
_StorageLogCategory(RealmLogCategory parent) : super._('Storage', parent);

late final notification = _LeafLogCategory('Notification', this);
late final object = _LeafLogCategory('Object', this);
late final query = _LeafLogCategory('Query', this);
late final transaction = _LeafLogCategory('Transaction', this);
}

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

/// A version of [debug] that allows for very high volume output.
/// This may seriously affect the performance of the sync client.
trace(Level.FINEST),

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

/// Same as [info], but prioritize completeness over minimalism.
detail(Level.FINE),

/// Log operational sync client messages, but in a minimalist fashion to
/// avoid general overhead from logging and to keep volume down.
info(Level.INFO),

/// Log errors and warnings.
warn(Level.WARNING),

/// Log errors only.
error(Level.SEVERE),

/// Log only fatal errors.
fatal(Level.SHOUT),

/// Turn off logging.
off(Level.OFF),
;

static const logToValues = [
RealmLogLevel.trace,
RealmLogLevel.debug,
RealmLogLevel.detail,
RealmLogLevel.info,
RealmLogLevel.warn,
RealmLogLevel.error,
RealmLogLevel.fatal,
];

final Level level;
const RealmLogLevel(this.level);

static final _valuesMap = {for (var value in RealmLogLevel.values) value.index: value};
factory RealmLogLevel.fromInt(int code) => _valuesMap[code]!;
}

/// A record of a log message from the Realm SDK.
typedef RealmLogRecord = ({RealmLogCategory category, RealmLogLevel level, String message});

/// A logger that logs messages from the Realm SDK.
class RealmLogger {
static final _controller = StreamController<RealmLogRecord>.broadcast(
onListen: () => realmCore.loggerAttach(),
onCancel: () => realmCore.loggerDetach(),
);

const RealmLogger();

void setLogLevel(RealmLogLevel level, {RealmLogCategory? category}) {
category ??= RealmLogCategory.realm;
realmCore.setLogLevel(level, category: category);
}

Stream<RealmLogRecord> get onRecord => _controller.stream;

void _raise(RealmLogRecord record) {
_controller.add(record);
}

void _log(RealmLogLevel level, Object message, {RealmLogCategory? category}) {
category ??= RealmLogCategory.realm.sdk;
realmCore.logMessage(RealmLogCategory.realm.sdk, level, message.toString());
}
}

extension RealmLoggerInternal on RealmLogger {
void raise(RealmLogRecord record) => _raise(record);
void log(RealmLogLevel level, Object message, {RealmLogCategory? category}) => _log(level, message, category: category);
}

0 comments on commit f211592

Please sign in to comment.