Skip to content

Commit

Permalink
[RKOTLIN-1038] Implement new log category for RealmLogger (#1692)
Browse files Browse the repository at this point in the history
  • Loading branch information
clementetb authored May 17, 2024
1 parent 52ad45b commit fd5a5ca
Show file tree
Hide file tree
Showing 45 changed files with 787 additions and 699 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
## 1.17.0-SNAPSHOT (YYYY-MM-DD)
## 2.0.0-SNAPSHOT (YYYY-MM-DD)

[!NOTE]
This release will bump the Realm file format from version 23 to 24. Opening a file with an older format will automatically upgrade it from file format v10. If you want to upgrade from an earlier file format version you will have to use Realm Kotlin v1.13.1 or earlier. Downgrading to a previous file format is not possible.

### Breaking changes
* None.
* Removed property `RealmLog.level`. Log levels can be set with `RealmLog.setLevel`. (Issue [#1691](https://github.com/realm/realm-kotlin/issues/1691) [JIRA](https://jira.mongodb.org/browse/RKOTLIN-1038))
* Removed `LogConfiguration`. Log levels and custom loggers can be set with `RealmLog`. (Issue [#1691](https://github.com/realm/realm-kotlin/issues/1691) [JIRA](https://jira.mongodb.org/browse/RKOTLIN-1038))

### Enhancements
* Support for RealmLists and RealmDictionaries in `RealmAny`. (Issue [#1434](https://github.com/realm/realm-kotlin/issues/1434))
* Optimized `RealmList.indexOf()` and `RealmList.contains()` using Core implementation of operations instead of iterating elements and comparing them in Kotlin. (Issue [#1625](https://github.com/realm/realm-kotlin/pull/1666) [RKOTLIN-995](https://jira.mongodb.org/browse/RKOTLIN-995)).
* Add support for filtering logs by category. (Issue [#1691](https://github.com/realm/realm-kotlin/issues/1691) [JIRA](https://jira.mongodb.org/browse/RKOTLIN-1038))

### Fixed
* None.
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ val HOST_OS: OperatingSystem = findHostOs()

object Realm {
val ciBuild = (System.getenv("CI") != null)
const val version = "1.17.0-SNAPSHOT"
const val version = "2.0.0-SNAPSHOT"
const val group = "io.realm.kotlin"
const val projectUrl = "https://realm.io"
const val pluginPortalId = "io.realm.kotlin"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ interface SyncSessionTransferCompletionCallback {

interface LogCallback {
// Passes core log levels as shorts to avoid unnecessary jumping between the SDK and JNI
fun log(logLevel: Short, category: String?, message: String?)
fun log(logLevel: Short, categoryValue: String, message: String?)
}

interface SyncBeforeClientResetHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,12 @@ expect object RealmInterop {

fun realm_set_log_level(level: CoreLogLevel)

fun realm_set_log_level_category(category: String, level: CoreLogLevel)

fun realm_get_log_level_category(category: String): CoreLogLevel

fun realm_get_category_names(): List<String>

fun realm_app_config_set_metadata_mode(
appConfig: RealmAppConfigurationPointer,
metadataMode: MetadataMode
Expand Down
7 changes: 7 additions & 0 deletions packages/cinterop/src/jvm/jni/java_class_global_def.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class JavaClassGlobalDef {
JavaClassGlobalDef(JNIEnv* env)
: m_java_util_hashmap(env, "java/util/HashMap", false)
, m_java_lang_int(env, "java/lang/Integer", false)
, m_java_lang_string(env, "java/lang/String", false)
, m_kotlin_jvm_functions_function0(env, "kotlin/jvm/functions/Function0", false)
, m_kotlin_jvm_functions_function1(env, "kotlin/jvm/functions/Function1", false)
, m_io_realm_kotlin_internal_interop_sync_network_transport(env, "io/realm/kotlin/internal/interop/sync/NetworkTransport", false)
Expand Down Expand Up @@ -74,6 +75,7 @@ class JavaClassGlobalDef {

jni_util::JavaClass m_java_util_hashmap;
jni_util::JavaClass m_java_lang_int;
jni_util::JavaClass m_java_lang_string;
jni_util::JavaClass m_kotlin_jvm_functions_function0;
jni_util::JavaClass m_kotlin_jvm_functions_function1;
jni_util::JavaClass m_io_realm_kotlin_internal_interop_sync_network_transport;
Expand Down Expand Up @@ -133,6 +135,11 @@ class JavaClassGlobalDef {
return env->NewObject(instance()->m_java_lang_int, init, value);
}

inline static const jni_util::JavaClass& java_lang_string()
{
return instance()->m_java_lang_string;
}

inline static const jni_util::JavaClass& network_transport_class()
{
return instance()->m_io_realm_kotlin_internal_interop_sync_network_transport;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1365,9 +1365,21 @@ actual object RealmInterop {
realmc.realm_set_log_level(level.priority)
}

actual fun realm_set_log_level_category(category: String, level: CoreLogLevel) {
realmc.realm_set_log_level_category(category, level.priority)
}

actual fun realm_get_log_level_category(category: String): CoreLogLevel =
CoreLogLevel.valueFromPriority(realmc.realm_get_log_level_category(category).toShort())

actual fun realm_get_category_names(): List<String> {
val names: Array<String> = realmc.realm_get_log_category_names() as Array<String>
return names.asList()
}

actual fun realm_app_config_set_metadata_mode(
appConfig: RealmAppConfigurationPointer,
metadataMode: MetadataMode
metadataMode: MetadataMode,
) {
realmc.realm_app_config_set_metadata_mode(
appConfig.cptr(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
// TODO https://github.com/realm/realm-kotlin/issues/889
@file:Suppress("TooGenericExceptionThrown", "TooGenericExceptionCaught")
@file:OptIn(ExperimentalForeignApi::class)

package io.realm.kotlin.internal.interop

Expand Down Expand Up @@ -54,6 +55,7 @@ import kotlinx.cinterop.CPointerVar
import kotlinx.cinterop.CPointerVarOf
import kotlinx.cinterop.CValue
import kotlinx.cinterop.CVariable
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.LongVar
import kotlinx.cinterop.MemScope
import kotlinx.cinterop.StableRef
Expand Down Expand Up @@ -2511,7 +2513,7 @@ actual object RealmInterop {
realm_wrapper.realm_set_log_callback(
staticCFunction { userData, category, logLevel, message ->
val userDataLogCallback = safeUserData<LogCallback>(userData)
userDataLogCallback.log(logLevel.toShort(), category?.toKString(), message?.toKString())
userDataLogCallback.log(logLevel.toShort(), category!!.toKString(), message?.toKString())
},
StableRef.create(callback).asCPointer(),
staticCFunction { userData -> disposeUserData<() -> LogCallback>(userData) }
Expand All @@ -2522,9 +2524,28 @@ actual object RealmInterop {
realm_wrapper.realm_set_log_level(level.priority.toUInt())
}

actual fun realm_set_log_level_category(category: String, level: CoreLogLevel) {
realm_wrapper.realm_set_log_level_category(category, level.priority.toUInt())
}

actual fun realm_get_log_level_category(category: String): CoreLogLevel =
CoreLogLevel.valueFromPriority(realm_wrapper.realm_get_log_level_category(category).toShort())

actual fun realm_get_category_names(): List<String> {
memScoped {
val namesCount = realm_wrapper.realm_get_category_names(0u, null)
val namesBuffer = allocArray<CPointerVar<ByteVar>>(namesCount.toInt())
realm_wrapper.realm_get_category_names(namesCount, namesBuffer)

return List(namesCount.toInt()) {
namesBuffer[it].safeKString()
}
}
}

actual fun realm_app_config_set_metadata_mode(
appConfig: RealmAppConfigurationPointer,
metadataMode: MetadataMode
metadataMode: MetadataMode,
) {
realm_wrapper.realm_app_config_set_metadata_mode(
appConfig.cptr(),
Expand Down
26 changes: 23 additions & 3 deletions packages/jni-swig-stub/src/main/jni/realm_api_helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -960,8 +960,8 @@ realm_sync_socket_t* realm_sync_websocket_new(int64_t sync_client_config_ptr, jo
// *** END - WebSocket Client (Platform Networking) *** //

void set_log_callback(jobject log_callback) {
auto jenv = get_env(true);
realm_set_log_callback([](void *userdata, const char* category, realm_log_level_e level, const char *message) {
auto jenv = get_env(false);
realm_set_log_callback([](void *userdata, const char *category, realm_log_level_e level, const char *message) {
auto log_callback = static_cast<jobject>(userdata);
auto jenv = get_env(true);

Expand All @@ -970,7 +970,7 @@ realm_set_log_callback([](void *userdata, const char* category, realm_log_level_
static JavaMethod log_method(jenv,
JavaClassGlobalDef::log_callback(),
"log",
"(SLjava/lang/String;Ljava/lang/String;)V");
"(SLjava/lang/String;Ljava/lang/String;)V");

push_local_frame(jenv, 2);
jenv->CallVoidMethod(log_callback, log_method, java_level, to_jstring(jenv, category), to_jstring(jenv, message));
Expand Down Expand Up @@ -1370,3 +1370,23 @@ realm_class_info_t_cleanup(realm_class_info_t * value) {
delete[] value->primary_key;
delete[] value->name;
}

jobjectArray realm_get_log_category_names() {
JNIEnv* env = get_env(true);

size_t namesCount = realm_get_category_names(0, nullptr);

const char** category_names = new const char*[namesCount];
realm_get_category_names(namesCount, category_names);

auto array = env->NewObjectArray(namesCount, JavaClassGlobalDef::java_lang_string(), nullptr);

for(size_t i = 0; i < namesCount; i++) {
jstring string = env->NewStringUTF(category_names[i]);
env->SetObjectArrayElement(array, i, string);
}

delete[] category_names;

return array;
}
4 changes: 3 additions & 1 deletion packages/jni-swig-stub/src/main/jni/realm_api_helpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ realm_http_transport_t*
realm_network_transport_new(jobject network_transport);

void
set_log_callback(jobject log_callbac);
set_log_callback(jobject log_callback);

realm_scheduler_t*
realm_create_scheduler(jobject dispatchScheduler);
Expand Down Expand Up @@ -161,4 +161,6 @@ bool realm_sync_websocket_message(int64_t observer_ptr, jbyteArray data, size_t

void realm_sync_websocket_closed(int64_t observer_ptr, bool was_clean, int error_code, const char* reason);

jobjectArray realm_get_log_category_names();

#endif //TEST_REALM_API_HELPERS_H
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
package io.realm.kotlin.internal.platform

import android.util.Log
import io.realm.kotlin.internal.messageWithCategory
import io.realm.kotlin.log.LogCategory
import io.realm.kotlin.log.LogLevel
import io.realm.kotlin.log.RealmLogger
import java.io.PrintWriter
import java.io.StringWriter
import java.util.Locale
import kotlin.math.min

/**
* Create a logger that outputs to Android LogCat.
Expand All @@ -29,13 +32,22 @@ import java.util.Locale
* for message creation and formatting
*/
internal class LogCatLogger(
override val tag: String = "REALM",
override val level: LogLevel
private val tag: String,
) : RealmLogger {

override fun log(level: LogLevel, throwable: Throwable?, message: String?, vararg args: Any?) {
override fun log(
category: LogCategory,
level: LogLevel,
throwable: Throwable?,
message: String?,
vararg args: Any?,
) {
val priority: Int = level.priority
val logMessage: String = prepareLogMessage(throwable, message, *args)
val logMessage: String = prepareLogMessage(
throwable = throwable,
message = messageWithCategory(category, message),
args = *args
)

// Short circuit if message can fit into a single line in LogCat
if (logMessage.length < MAX_LOG_LENGTH) {
Expand All @@ -50,7 +62,7 @@ internal class LogCatLogger(
var newline = logMessage.indexOf('\n', i)
newline = if (newline != -1) newline else length
do {
val end = Math.min(newline, i + MAX_LOG_LENGTH)
val end = min(newline, i + MAX_LOG_LENGTH)
val part = logMessage.substring(i, end)
printMessage(priority, part)
i = end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import io.realm.kotlin.internal.RealmInitializer
import io.realm.kotlin.internal.RealmInstantImpl
import io.realm.kotlin.internal.interop.SyncConnectionParams
import io.realm.kotlin.internal.util.Exceptions
import io.realm.kotlin.log.LogLevel
import io.realm.kotlin.log.RealmLogger
import io.realm.kotlin.types.RealmInstant
import java.io.FileNotFoundException
Expand Down Expand Up @@ -37,8 +36,8 @@ public actual fun assetFileAsStream(assetFilename: String): InputStream = try {
}

// Returns the default logger for the platform
public actual fun createDefaultSystemLogger(tag: String, logLevel: LogLevel): RealmLogger =
LogCatLogger(tag, logLevel)
public actual fun createDefaultSystemLogger(tag: String): RealmLogger =
LogCatLogger(tag)

public actual fun currentTime(): RealmInstant {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ import io.realm.kotlin.internal.MISSING_PLUGIN_MESSAGE
import io.realm.kotlin.internal.REALM_FILE_EXTENSION
import io.realm.kotlin.internal.platform.PATH_SEPARATOR
import io.realm.kotlin.internal.realmObjectCompanionOrNull
import io.realm.kotlin.log.LogLevel
import io.realm.kotlin.log.RealmLog
import io.realm.kotlin.log.RealmLogger
import io.realm.kotlin.types.BaseRealmObject
import kotlinx.coroutines.CoroutineDispatcher
import kotlin.reflect.KClass
Expand Down Expand Up @@ -74,23 +71,6 @@ public fun interface InitialDataCallback {
public fun MutableRealm.write()
}

/**
* Configuration for log events created by a Realm instance.
*/
@Deprecated("Use io.realm.kotlin.log.RealmLog instead.")
public data class LogConfiguration(
/**
* The [LogLevel] for which all log events of equal or higher priority will be reported.
*/
public val level: LogLevel,

/**
* Any loggers to install. They will receive all log events with a priority equal to or higher
* than the value defined in [LogConfiguration.level].
*/
public val loggers: List<RealmLogger>
)

/**
* Configuration for pre-bundled asset files used as initial state of the realm file.
*/
Expand Down Expand Up @@ -126,12 +106,6 @@ public interface Configuration {
*/
public val schema: Set<KClass<out BaseRealmObject>>

/**
* The log configuration used for the realm instance.
*/
@Deprecated("Use io.realm.kotlin.log.RealmLog instead.")
public val log: LogConfiguration

/**
* Maximum number of active versions.
*
Expand Down Expand Up @@ -226,9 +200,6 @@ public interface Configuration {

// 'name' must be nullable as it is optional when getting SyncClient's default path!
protected abstract var name: String?
protected var logLevel: LogLevel = RealmLog.level
protected var appConfigLoggers: List<RealmLogger> = listOf()
protected var realmConfigLoggers: List<RealmLogger> = listOf()
protected var maxNumberOfActiveVersions: Long = Long.MAX_VALUE
protected var notificationDispatcher: CoroutineDispatcher? = null
protected var writeDispatcher: CoroutineDispatcher? = null
Expand Down Expand Up @@ -282,23 +253,6 @@ public interface Configuration {
this.maxNumberOfActiveVersions = maxVersions
} as S

/**
* Configure how Realm will report log events.
*
* @param level all events at this level or higher will be reported.
* @param customLoggers any custom loggers to send log events to. A default system logger is
* installed by default that will redirect to the common logging framework on the platform,
* i.e. LogCat on Android and NSLog on iOS.
*/
@Deprecated("Use io.realm.kotlin.log.RealmLog instead.")
public open fun log(
level: LogLevel = LogLevel.WARN,
customLoggers: List<RealmLogger> = emptyList()
): S = apply {
this.logLevel = level
this.realmConfigLoggers = customLoggers
} as S

/**
* Dispatcher on which Realm notifications are run. It is possible to listen for changes to
* Realm objects from any thread, but the underlying logic will run on this dispatcher
Expand Down
Loading

0 comments on commit fd5a5ca

Please sign in to comment.