Remove the rc_log crate.
This commit is contained in:
Родитель
47ef7c8c98
Коммит
a5e715b286
|
@ -49,13 +49,6 @@ projects:
|
||||||
- name: remotesettings
|
- name: remotesettings
|
||||||
type: aar
|
type: aar
|
||||||
description: A Remote Settings client intended for the application layer.
|
description: A Remote Settings client intended for the application layer.
|
||||||
rustlog:
|
|
||||||
path: components/rc_log/android
|
|
||||||
artifactId: rustlog
|
|
||||||
publications:
|
|
||||||
- name: rustlog
|
|
||||||
type: aar
|
|
||||||
description: Android hook into the log crate.
|
|
||||||
rust-log-forwarder:
|
rust-log-forwarder:
|
||||||
path: components/support/rust-log-forwarder/android
|
path: components/support/rust-log-forwarder/android
|
||||||
artifactId: rust-log-forwarder
|
artifactId: rust-log-forwarder
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
## 🦊 What's Changed 🦊
|
## 🦊 What's Changed 🦊
|
||||||
|
|
||||||
|
- The long-deprecated `rc_log` crate has been removed.
|
||||||
|
|
||||||
### Android
|
### Android
|
||||||
- Upgraded NDK from r25c to r26c. ([#6134](https://github.com/mozilla/application-services/pull/6134))
|
- Upgraded NDK from r25c to r26c. ([#6134](https://github.com/mozilla/application-services/pull/6134))
|
||||||
|
|
||||||
|
|
|
@ -2352,7 +2352,6 @@ dependencies = [
|
||||||
"nimbus-sdk",
|
"nimbus-sdk",
|
||||||
"places",
|
"places",
|
||||||
"push",
|
"push",
|
||||||
"rc_log_ffi",
|
|
||||||
"remote_settings",
|
"remote_settings",
|
||||||
"rust-log-forwarder",
|
"rust-log-forwarder",
|
||||||
"suggest",
|
"suggest",
|
||||||
|
@ -2367,7 +2366,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"error-support",
|
"error-support",
|
||||||
"nimbus-sdk",
|
"nimbus-sdk",
|
||||||
"rc_log_ffi",
|
|
||||||
"remote_settings",
|
"remote_settings",
|
||||||
"rust-log-forwarder",
|
"rust-log-forwarder",
|
||||||
"viaduct",
|
"viaduct",
|
||||||
|
@ -2387,7 +2385,6 @@ dependencies = [
|
||||||
"nimbus-sdk",
|
"nimbus-sdk",
|
||||||
"places",
|
"places",
|
||||||
"push",
|
"push",
|
||||||
"rc_log_ffi",
|
|
||||||
"remote_settings",
|
"remote_settings",
|
||||||
"rust-log-forwarder",
|
"rust-log-forwarder",
|
||||||
"suggest",
|
"suggest",
|
||||||
|
@ -3484,16 +3481,6 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rc_log_ffi"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 0.1.10",
|
|
||||||
"ffi-support",
|
|
||||||
"lazy_static",
|
|
||||||
"log",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.2.13"
|
version = "0.2.13"
|
||||||
|
|
|
@ -12,7 +12,6 @@ members = [
|
||||||
"components/places",
|
"components/places",
|
||||||
"components/push",
|
"components/push",
|
||||||
"components/remote_settings",
|
"components/remote_settings",
|
||||||
"components/rc_log",
|
|
||||||
"components/suggest",
|
"components/suggest",
|
||||||
"components/support/error",
|
"components/support/error",
|
||||||
"components/support/error/tests",
|
"components/support/error/tests",
|
||||||
|
@ -91,7 +90,6 @@ default-members = [
|
||||||
"components/places",
|
"components/places",
|
||||||
"components/push",
|
"components/push",
|
||||||
"components/remote_settings",
|
"components/remote_settings",
|
||||||
"components/rc_log",
|
|
||||||
"components/support/error",
|
"components/support/error",
|
||||||
"components/support/error/macros",
|
"components/support/error/macros",
|
||||||
"components/support/error/tests",
|
"components/support/error/tests",
|
||||||
|
|
11
README.md
11
README.md
|
@ -57,14 +57,11 @@ The application-services library primary consumers are Fenix (Firefox on Android
|
||||||
|
|
||||||
# Rust Components
|
# Rust Components
|
||||||
|
|
||||||
[./components/](components) contains the source for each component, and its
|
[./components/](components) contains the source for each component. Note that most components have their FFI generated
|
||||||
FFI bindings.
|
by the [uniffi](https://github.com/mozilla/uniffi-rs/) library.
|
||||||
|
|
||||||
> Please note that we are in the process of moving away from hand-written ffi code and instead favouring the use of the [uniffi](https://github.com/mozilla/uniffi-rs/) library.
|
|
||||||
* See [./components/places/](components/places) for an example, where you can
|
* See [./components/places/](components/places) for an example, where you can
|
||||||
find:
|
find:
|
||||||
* The shared [rust code](components/places/src).
|
* The shared [rust code](components/places/src).
|
||||||
* The mapping into a [C FFI](components/places/ffi).
|
|
||||||
* The [Kotlin bindings](components/places/android) for use by Android
|
* The [Kotlin bindings](components/places/android) for use by Android
|
||||||
applications.
|
applications.
|
||||||
* The [Swift bindings](components/places/ios) for use by iOS applications.
|
* The [Swift bindings](components/places/ios) for use by iOS applications.
|
||||||
|
@ -85,8 +82,6 @@ The application-services library primary consumers are Fenix (Firefox on Android
|
||||||
browsing history
|
browsing history
|
||||||
* [push](components/push) - for applications to receive real-time updates via
|
* [push](components/push) - for applications to receive real-time updates via
|
||||||
WebPush
|
WebPush
|
||||||
* [rc_log](components/rc_log) - for connecting component log output to the
|
|
||||||
application's log stream
|
|
||||||
* [support](components/support) - low-level utility libraries
|
* [support](components/support) - low-level utility libraries
|
||||||
* [support/rc_crypto](components/rc_crypto) - handles cryptographic needs backed by Mozilla's
|
* [support/rc_crypto](components/rc_crypto) - handles cryptographic needs backed by Mozilla's
|
||||||
[NSS](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS) library
|
[NSS](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS) library
|
||||||
|
@ -100,3 +95,5 @@ The application-services library primary consumers are Fenix (Firefox on Android
|
||||||
* [viaduct](components/viaduct) - an HTTP request library
|
* [viaduct](components/viaduct) - an HTTP request library
|
||||||
* [webext-storage](components/webext-storage) - powers an implementation of the
|
* [webext-storage](components/webext-storage) - powers an implementation of the
|
||||||
chrome.storage.sync WebExtension API
|
chrome.storage.sync WebExtension API
|
||||||
|
|
||||||
|
Note the above list is partial; see the actual list under the `components` directory.
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "rc_log_ffi"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
authors = ["Thom Chiovoloni <tchiovoloni@mozilla.com>"]
|
|
||||||
license = "MPL-2.0"
|
|
||||||
exclude = ["/android", "/ios"]
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "rc_log_ffi"
|
|
||||||
crate-type = ["lib"]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = []
|
|
||||||
# Required for gradle in robolectric
|
|
||||||
force_android = []
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
log = "0.4"
|
|
||||||
ffi-support = "0.4"
|
|
||||||
lazy_static = "1.4"
|
|
||||||
cfg-if = "0.1"
|
|
|
@ -1,10 +0,0 @@
|
||||||
apply from: "$rootDir/build-scripts/component-common.gradle"
|
|
||||||
apply from: "$rootDir/build-scripts/protobuf-common.gradle"
|
|
||||||
apply from: "$rootDir/publish.gradle"
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace 'org.mozilla.appservices.rustlog'
|
|
||||||
}
|
|
||||||
|
|
||||||
ext.dependsOnTheMegazord()
|
|
||||||
ext.configurePublish()
|
|
|
@ -1,21 +0,0 @@
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
|
@ -1 +0,0 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"/>
|
|
|
@ -1,49 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
package mozilla.appservices.rustlog
|
|
||||||
|
|
||||||
import com.sun.jna.Callback
|
|
||||||
import com.sun.jna.Library
|
|
||||||
import com.sun.jna.Pointer
|
|
||||||
import com.sun.jna.PointerType
|
|
||||||
import mozilla.appservices.support.native.loadIndirect
|
|
||||||
import org.mozilla.appservices.rustlog.BuildConfig
|
|
||||||
|
|
||||||
@Suppress("FunctionNaming", "TooGenericExceptionThrown")
|
|
||||||
internal interface LibRustLogAdapter : Library {
|
|
||||||
companion object {
|
|
||||||
// XXX this should be direct binding...
|
|
||||||
internal var INSTANCE: LibRustLogAdapter =
|
|
||||||
loadIndirect(componentName = "rustlog", componentVersion = BuildConfig.LIBRARY_VERSION)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun rc_log_adapter_create(
|
|
||||||
callback: RawLogCallback,
|
|
||||||
outErr: RustError.ByReference,
|
|
||||||
): RawLogAdapter?
|
|
||||||
|
|
||||||
fun rc_log_adapter_set_max_level(
|
|
||||||
level: Int,
|
|
||||||
outErr: RustError.ByReference,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun rc_log_adapter_destroy(
|
|
||||||
adapter: RawLogAdapter,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun rc_log_adapter_destroy_string(
|
|
||||||
stringPtr: Pointer,
|
|
||||||
)
|
|
||||||
|
|
||||||
fun rc_log_adapter_test__log_msg(
|
|
||||||
string: String,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal interface RawLogCallback : Callback {
|
|
||||||
fun invoke(level: Int, tag: Pointer?, message: Pointer): Byte
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class RawLogAdapter : PointerType()
|
|
|
@ -1,38 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
package mozilla.appservices.rustlog
|
|
||||||
|
|
||||||
import com.sun.jna.Pointer
|
|
||||||
import com.sun.jna.Structure
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This should be considered private, but it needs to be public for JNA.
|
|
||||||
*/
|
|
||||||
@Structure.FieldOrder("code", "message")
|
|
||||||
open class RustError : Structure() {
|
|
||||||
|
|
||||||
class ByReference : RustError(), Structure.ByReference
|
|
||||||
|
|
||||||
@JvmField var code: Int = 0
|
|
||||||
|
|
||||||
@JvmField var message: Pointer? = null
|
|
||||||
|
|
||||||
fun isFailure(): Boolean {
|
|
||||||
return code != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get and consume the error message, or null if there is none.
|
|
||||||
*/
|
|
||||||
@Synchronized
|
|
||||||
fun consumeErrorMessage(): String {
|
|
||||||
val result = this.message?.getAndConsumeRustString()
|
|
||||||
this.message = null
|
|
||||||
if (result == null) {
|
|
||||||
throw NullPointerException("consumeErrorMessage called with null message!")
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
package mozilla.appservices.rustlog
|
|
||||||
|
|
||||||
import com.sun.jna.CallbackThreadInitializer
|
|
||||||
import com.sun.jna.Native
|
|
||||||
import com.sun.jna.Pointer
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
|
||||||
|
|
||||||
typealias OnLog = (Int, String?, String) -> Boolean
|
|
||||||
class RustLogAdapter private constructor(
|
|
||||||
// IMPORTANT: This must not be GCed while the adapter is alive!
|
|
||||||
@Suppress("Unused")
|
|
||||||
private val callbackImpl: RawLogCallbackImpl,
|
|
||||||
private val adapter: RawLogAdapter,
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
@Volatile
|
|
||||||
private var instance: RustLogAdapter? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* true if the log is enabled.
|
|
||||||
*/
|
|
||||||
val isEnabled get() = getEnabled()
|
|
||||||
|
|
||||||
// Used to signal from the log callback that we should disable
|
|
||||||
// the adapter because the callback returned false. Note that
|
|
||||||
// Rust handles this too.
|
|
||||||
internal val disabledRemotely = AtomicBoolean(false)
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
private fun getEnabled(): Boolean {
|
|
||||||
if (instance == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (disabledRemotely.getAndSet(false)) {
|
|
||||||
this.disable()
|
|
||||||
}
|
|
||||||
return instance != null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable the logger and use the provided logging callback.
|
|
||||||
*
|
|
||||||
* @throws [LogAdapterCannotEnable] if it is already enabled.
|
|
||||||
*/
|
|
||||||
@Synchronized
|
|
||||||
fun enable(onLog: OnLog) {
|
|
||||||
if (isEnabled) {
|
|
||||||
throw LogAdapterCannotEnable("Adapter is already enabled")
|
|
||||||
}
|
|
||||||
// Tell JNA to reuse the callback thread.
|
|
||||||
val initializer = CallbackThreadInitializer(
|
|
||||||
// Don't block JVM shutdown waiting for this thread to exit.
|
|
||||||
true, // daemon
|
|
||||||
// Don't detach the JVM from this thread after invoking the callback.
|
|
||||||
false, // detach
|
|
||||||
"RustLogThread", // name
|
|
||||||
)
|
|
||||||
val callbackImpl = RawLogCallbackImpl(onLog)
|
|
||||||
Native.setCallbackThreadInitializer(callbackImpl, initializer)
|
|
||||||
// Hopefully there is no way to half-initialize the logger such that where the callback
|
|
||||||
// could still get called despite an error/null being returned? If there is, we need to
|
|
||||||
// make callbackImpl isn't GCed here, or very bad things will happen. (Should the logger
|
|
||||||
// init code abort on panic?)
|
|
||||||
val adapter = rustCall { err ->
|
|
||||||
LibRustLogAdapter.INSTANCE.rc_log_adapter_create(callbackImpl, err)
|
|
||||||
}
|
|
||||||
// For example, it would be *extremely bad* if somehow adapter were actually null here.
|
|
||||||
instance = RustLogAdapter(callbackImpl, adapter!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper to enable the logger if it can be enabled. Returns true if
|
|
||||||
* the logger was enabled by this call.
|
|
||||||
*/
|
|
||||||
@Synchronized
|
|
||||||
fun tryEnable(onLog: OnLog): Boolean {
|
|
||||||
if (isEnabled) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
enable(onLog)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disable the logger, allowing the logging callback to be garbage collected.
|
|
||||||
*/
|
|
||||||
@Synchronized
|
|
||||||
fun disable() {
|
|
||||||
val state = instance ?: return
|
|
||||||
LibRustLogAdapter.INSTANCE.rc_log_adapter_destroy(state.adapter)
|
|
||||||
// XXX Letting that callback get GCed still makes me extremely uneasy...
|
|
||||||
// Maybe we should just null out the callback provided by the user so that
|
|
||||||
// it can be GCed (while letting the RawLogCallbackImpl which actually is
|
|
||||||
// called by Rust live on).
|
|
||||||
instance = null
|
|
||||||
disabledRemotely.set(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
|
||||||
fun setMaxLevel(level: LogLevelFilter) {
|
|
||||||
if (isEnabled) {
|
|
||||||
rustCall { e ->
|
|
||||||
LibRustLogAdapter.INSTANCE.rc_log_adapter_set_max_level(
|
|
||||||
level.value,
|
|
||||||
e,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <U> rustCall(callback: (RustError.ByReference) -> U): U {
|
|
||||||
val e = RustError.ByReference()
|
|
||||||
val ret: U = callback(e)
|
|
||||||
if (e.isFailure()) {
|
|
||||||
val msg = e.consumeErrorMessage()
|
|
||||||
throw LogAdapterUnexpectedError(msg)
|
|
||||||
} else {
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All errors emitted by the LogAdapter will subclass this.
|
|
||||||
*/
|
|
||||||
sealed class LogAdapterError(msg: String) : Exception(msg)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error indicating that the log adapter cannot be enabled because it is already enabled.
|
|
||||||
*/
|
|
||||||
class LogAdapterCannotEnable(msg: String) : LogAdapterError("Log adapter may not be enabled: $msg")
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown for unexpected log adapter errors (generally rust panics).
|
|
||||||
*/
|
|
||||||
class LogAdapterUnexpectedError(msg: String) : LogAdapterError("Unexpected log adapter error: $msg")
|
|
||||||
|
|
||||||
// Note: keep values in sync with level_filter_from_i32 in rust.
|
|
||||||
/** Level filters, for use with setMaxLevel. */
|
|
||||||
enum class LogLevelFilter(internal val value: Int) {
|
|
||||||
/** Disable all logging */
|
|
||||||
OFF(0),
|
|
||||||
|
|
||||||
/** Only allow ERROR logs. */
|
|
||||||
ERROR(1),
|
|
||||||
|
|
||||||
/** Allow WARN and ERROR logs. */
|
|
||||||
WARN(2),
|
|
||||||
|
|
||||||
/** Allow WARN, ERROR, and INFO logs. The default. */
|
|
||||||
INFO(3),
|
|
||||||
|
|
||||||
/** Allow WARN, ERROR, INFO, and DEBUG logs. */
|
|
||||||
DEBUG(4),
|
|
||||||
|
|
||||||
/** Allow all logs, including those that may contain PII. */
|
|
||||||
TRACE(5),
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class RawLogCallbackImpl(private val onLog: OnLog) : RawLogCallback {
|
|
||||||
@Suppress("TooGenericExceptionCaught")
|
|
||||||
override fun invoke(level: Int, tag: Pointer?, message: Pointer): Byte {
|
|
||||||
// We can't safely throw here!
|
|
||||||
val result = try {
|
|
||||||
val tagStr = tag?.getString(0, "utf8")
|
|
||||||
val msgStr = message.getString(0, "utf8")
|
|
||||||
onLog(level, tagStr, msgStr)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
try {
|
|
||||||
println("Exception when logging: $e")
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
// :(
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
return if (result) {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
RustLogAdapter.disabledRemotely.set(true)
|
|
||||||
0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Pointer.getAndConsumeRustString(): String {
|
|
||||||
try {
|
|
||||||
return this.getString(0, "utf8")
|
|
||||||
} finally {
|
|
||||||
LibRustLogAdapter.INSTANCE.rc_log_adapter_destroy_string(this)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,178 +0,0 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
package mozilla.appservices.rustlog
|
|
||||||
|
|
||||||
import mozilla.appservices.Megazord
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.robolectric.RobolectricTestRunner
|
|
||||||
import org.robolectric.annotation.Config
|
|
||||||
import java.lang.RuntimeException
|
|
||||||
import java.util.WeakHashMap
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner::class)
|
|
||||||
@Config(manifest = Config.NONE)
|
|
||||||
class LogTest {
|
|
||||||
|
|
||||||
fun writeTestLog(m: String) {
|
|
||||||
LibRustLogAdapter.INSTANCE.rc_log_adapter_test__log_msg(m)
|
|
||||||
Thread.sleep(100) // Wait for it to arrive...
|
|
||||||
}
|
|
||||||
|
|
||||||
// This should be split up now that we can re-enable after disabling
|
|
||||||
// (note that it will still need to run sequentially!)
|
|
||||||
@Test
|
|
||||||
fun testLogging() {
|
|
||||||
Megazord.init()
|
|
||||||
val logs: MutableList<String> = mutableListOf()
|
|
||||||
val threadIds = mutableSetOf<Long>()
|
|
||||||
val threads = WeakHashMap<Thread, Long>()
|
|
||||||
fun handler(level: Int, tag: String?, msg: String) {
|
|
||||||
val threadId = Thread.currentThread().id
|
|
||||||
threads.set(Thread.currentThread(), threadId)
|
|
||||||
threadIds.add(threadId)
|
|
||||||
val info = "Rust log from $threadId | Level: $level | tag: $tag| message: $msg"
|
|
||||||
println(info)
|
|
||||||
logs += info
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(!RustLogAdapter.isEnabled)
|
|
||||||
|
|
||||||
RustLogAdapter.enable { level, tagStr, msgStr ->
|
|
||||||
handler(level, tagStr, msgStr)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
// We log an informational message after initializing (but it's processed asynchronously).
|
|
||||||
Thread.sleep(100)
|
|
||||||
assertEquals(logs.size, 1)
|
|
||||||
writeTestLog("Test1")
|
|
||||||
assertEquals(logs.size, 2)
|
|
||||||
assert(RustLogAdapter.isEnabled)
|
|
||||||
|
|
||||||
// Check that trying to enable again throws
|
|
||||||
@Suppress("EmptyCatchBlock")
|
|
||||||
try {
|
|
||||||
RustLogAdapter.enable { _, _, _ -> true }
|
|
||||||
} catch (e: LogAdapterCannotEnable) {
|
|
||||||
}
|
|
||||||
|
|
||||||
var wasCalled = false
|
|
||||||
|
|
||||||
val didEnable = RustLogAdapter.tryEnable { _, _, _ ->
|
|
||||||
wasCalled = true
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(!didEnable)
|
|
||||||
writeTestLog("Test2")
|
|
||||||
|
|
||||||
assertEquals(logs.size, 3)
|
|
||||||
assert(!wasCalled)
|
|
||||||
|
|
||||||
repeat(15) {
|
|
||||||
Thread.sleep(10)
|
|
||||||
@Suppress("ExplicitGarbageCollectionCall")
|
|
||||||
System.gc()
|
|
||||||
}
|
|
||||||
// Make sure GC can't collect our background thread (we're still using it)
|
|
||||||
assertEquals(threads.size, 1)
|
|
||||||
|
|
||||||
// Adjust the max level so that the test log (which is logged at info level)
|
|
||||||
// will not be present.
|
|
||||||
RustLogAdapter.setMaxLevel(LogLevelFilter.WARN)
|
|
||||||
|
|
||||||
writeTestLog("Test3")
|
|
||||||
|
|
||||||
assertEquals(logs.size, 3)
|
|
||||||
|
|
||||||
// Make sure we can re-enable it
|
|
||||||
RustLogAdapter.setMaxLevel(LogLevelFilter.INFO)
|
|
||||||
writeTestLog("Test4")
|
|
||||||
|
|
||||||
assertEquals(logs.size, 4)
|
|
||||||
// All the previous calls should have been run on the same background thread
|
|
||||||
assertEquals(threadIds.size, 1)
|
|
||||||
|
|
||||||
RustLogAdapter.disable()
|
|
||||||
assert(!RustLogAdapter.isEnabled)
|
|
||||||
|
|
||||||
// Shouldn't do anything, we disabled the log.
|
|
||||||
writeTestLog("Test5")
|
|
||||||
|
|
||||||
assertEquals(logs.size, 4)
|
|
||||||
assert(!wasCalled)
|
|
||||||
|
|
||||||
val didEnable2 = RustLogAdapter.tryEnable { level, tagStr, msgStr ->
|
|
||||||
handler(level, tagStr, msgStr)
|
|
||||||
wasCalled = true
|
|
||||||
true
|
|
||||||
}
|
|
||||||
Thread.sleep(100)
|
|
||||||
assert(didEnable2)
|
|
||||||
assertEquals(logs.size, 5)
|
|
||||||
|
|
||||||
writeTestLog("Test6")
|
|
||||||
assert(wasCalled)
|
|
||||||
assertEquals(logs.size, 6)
|
|
||||||
|
|
||||||
// We called `enable` again, so we expect to have used another thread
|
|
||||||
|
|
||||||
// TODO: changing to indirect binding has chnged how JNA allocates threads
|
|
||||||
// for our callbacks, and has this next line fail. We should change it back
|
|
||||||
// once things are back to normal. Ditto for commented out lines below labeled
|
|
||||||
// assertEquals(threadIds.size, 2) // INDIRECT
|
|
||||||
|
|
||||||
RustLogAdapter.disable()
|
|
||||||
|
|
||||||
// Check behavior of 'disable by returning false'
|
|
||||||
RustLogAdapter.enable { level, tagStr, msgStr ->
|
|
||||||
handler(level, tagStr, msgStr)
|
|
||||||
// Stop after we log twice
|
|
||||||
logs.size < 8
|
|
||||||
}
|
|
||||||
Thread.sleep(100)
|
|
||||||
// Initial log emitted when we set the adapter.
|
|
||||||
assertEquals(logs.size, 7)
|
|
||||||
writeTestLog("Test7")
|
|
||||||
assertEquals(logs.size, 8)
|
|
||||||
assert(!RustLogAdapter.isEnabled)
|
|
||||||
|
|
||||||
// new log callback, new thread.
|
|
||||||
// assertEquals(threadIds.size, 3) // INDIRECT
|
|
||||||
|
|
||||||
// Check behavior of 'disable by throw'
|
|
||||||
RustLogAdapter.enable { level, tagStr, msgStr ->
|
|
||||||
handler(level, tagStr, msgStr)
|
|
||||||
if (logs.size >= 10) {
|
|
||||||
@Suppress("TooGenericExceptionThrown")
|
|
||||||
throw RuntimeException("Throw an exception to stop logging")
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
Thread.sleep(100)
|
|
||||||
// Initial log emitted when we set the adapter.
|
|
||||||
assertEquals(logs.size, 9)
|
|
||||||
|
|
||||||
writeTestLog("Test8")
|
|
||||||
assertEquals(logs.size, 10)
|
|
||||||
assert(!RustLogAdapter.isEnabled)
|
|
||||||
|
|
||||||
// new log callback, new thread.
|
|
||||||
// assertEquals(threadIds.size, 4) // INDIRECT
|
|
||||||
|
|
||||||
// Clean up
|
|
||||||
RustLogAdapter.disable()
|
|
||||||
|
|
||||||
// Make sure the GC can now collect the background threads.
|
|
||||||
repeat(15) {
|
|
||||||
Thread.sleep(10)
|
|
||||||
@Suppress("ExplicitGarbageCollectionCall")
|
|
||||||
System.gc()
|
|
||||||
}
|
|
||||||
// assertEquals(threads.size, 0) // INDIRECT
|
|
||||||
assertEquals(threads.size, 1)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,250 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
#if canImport(MozillaRustComponents)
|
|
||||||
import MozillaRustComponents
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// The level of a log message
|
|
||||||
public enum LogLevel: Int32 {
|
|
||||||
/// The log message in question is verbose information which
|
|
||||||
/// may contain user PII.
|
|
||||||
case trace = 2
|
|
||||||
/// The log message in question is verbose information,
|
|
||||||
/// but should contain no PII.
|
|
||||||
case debug
|
|
||||||
/// The log message is informational
|
|
||||||
case info
|
|
||||||
/// The log message is a warning
|
|
||||||
case warn
|
|
||||||
/// The log message indicates an error.
|
|
||||||
case error
|
|
||||||
|
|
||||||
init(safeRawValue value: Int32) {
|
|
||||||
if let result = LogLevel(rawValue: value) {
|
|
||||||
self = result
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if value < LogLevel.trace.rawValue {
|
|
||||||
self = .trace
|
|
||||||
} else {
|
|
||||||
self = .error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An enum representing a maximum log level. It is used with
|
|
||||||
/// `RustLog.shared.setLevelFilter`.
|
|
||||||
///
|
|
||||||
/// This is roughly equivalent to LogLevel, however contains
|
|
||||||
/// `Off`, for filtering all logging.
|
|
||||||
public enum LogLevelFilter: Int32 {
|
|
||||||
/// Disable all logging
|
|
||||||
case off
|
|
||||||
/// Only allow `error` logs.
|
|
||||||
case error
|
|
||||||
/// Allow `warn` and `error` logs.
|
|
||||||
case warn
|
|
||||||
/// Allow `warn`, `error`, and `info` logs.
|
|
||||||
case info
|
|
||||||
/// Allow `warn`, `error`, `info`, and `debug` logs. The default.
|
|
||||||
case debug
|
|
||||||
/// Allow all logs, including those that may contain PII.
|
|
||||||
case trace
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The type of the log callback. You can provide a value of this type to
|
|
||||||
/// `RustLog.shared.enable` or `RustLog.shared.tryEnable`, and it will be called for
|
|
||||||
/// all log messages emitted by Rust code.
|
|
||||||
///
|
|
||||||
/// The first argument is the level of the log. The maximum value of this can
|
|
||||||
/// be provided using the `RustLog.shared.setLevelFilter` method.
|
|
||||||
///
|
|
||||||
/// The second argument is the tag, which is typically a rust module path
|
|
||||||
/// string. It might be nil in some cases that aren't documented by the
|
|
||||||
/// underlying rust log crate.
|
|
||||||
///
|
|
||||||
/// The last argument is the log message. It will not be nil.
|
|
||||||
///
|
|
||||||
/// This callback should return `true` to indicate everything is fine, and
|
|
||||||
/// false if we should disable the logger. You cannot call `disable()`
|
|
||||||
/// from inside the callback (it's protected by a dispatch queue you're
|
|
||||||
/// already running on).
|
|
||||||
public typealias LogCallback = (_ level: LogLevel, _ tag: String?, _ message: String) -> Bool
|
|
||||||
|
|
||||||
/// The public interface to Rust's logger.
|
|
||||||
///
|
|
||||||
/// This is a singleton, and should be used via the
|
|
||||||
/// `shared` static member.
|
|
||||||
public class RustLog {
|
|
||||||
fileprivate let state = RustLogState()
|
|
||||||
fileprivate let queue = DispatchQueue(label: "com.mozilla.appservices.rust-log")
|
|
||||||
/// The singleton instance of RustLog
|
|
||||||
public static let shared = RustLog()
|
|
||||||
|
|
||||||
private init() {}
|
|
||||||
|
|
||||||
/// True if the logger currently has a bound callback.
|
|
||||||
public var isEnabled: Bool {
|
|
||||||
return queue.sync { state.isEnabled }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the current log callback.
|
|
||||||
///
|
|
||||||
/// Note that by default, after enabling the level filter
|
|
||||||
/// will be at the `debug` level. If you want to increase or decrease it,
|
|
||||||
/// you may use `setLevelFilter`
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// See alse `tryEnable`.
|
|
||||||
///
|
|
||||||
/// Throws:
|
|
||||||
///
|
|
||||||
/// - `RustLogError.alreadyEnabled`: If we're already enabled. Explicitly disable first.
|
|
||||||
///
|
|
||||||
/// - `RustLogError.unexpectedError`: If the rust code panics. This shouldn't happen,
|
|
||||||
/// but if it does, we would appreciate reports from telemetry or similar
|
|
||||||
public func enable(_ callback: @escaping LogCallback) throws {
|
|
||||||
try queue.sync {
|
|
||||||
try state.enable(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the level filter (the maximum log level) of the logger.
|
|
||||||
///
|
|
||||||
/// Throws:
|
|
||||||
/// - `RustLogError.unexpectedError`: If the rust code panics. This shouldn't happen,
|
|
||||||
/// but if it does, we would appreciate reports from telemetry or similar
|
|
||||||
public func setLevelFilter(filter: LogLevelFilter) throws {
|
|
||||||
// Note: Doesn't need to synchronize.
|
|
||||||
try rustCall { error in
|
|
||||||
rc_log_adapter_set_max_level(filter.rawValue, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Disable the previously set logger. This also sets the level filter to `.off`.
|
|
||||||
///
|
|
||||||
/// Does nothing if the logger is disabled
|
|
||||||
public func disable() {
|
|
||||||
queue.sync {
|
|
||||||
state.disable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Enable the logger if possible.
|
|
||||||
///
|
|
||||||
/// Returns false in the cases where `enable` would throw, true otherwise.
|
|
||||||
///
|
|
||||||
/// If it would throw due to a panic, it also writes some information about
|
|
||||||
/// the panic to the provided callback
|
|
||||||
public func tryEnable(_ callback: @escaping LogCallback) -> Bool {
|
|
||||||
return queue.sync {
|
|
||||||
state.tryEnable(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Log a test message at `.info` severity.
|
|
||||||
public func logTestMessage(message: String) {
|
|
||||||
rc_log_adapter_test__log_msg(message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The type of errors reported by RustLog. These either indicate bugs
|
|
||||||
/// in our logging code (as in `UnexpectedError`), or usage errors
|
|
||||||
/// (as in `AlreadyEnabled`)
|
|
||||||
public enum RustLogError: Error {
|
|
||||||
/// This generally means a panic occurred, or something went very wrong.
|
|
||||||
/// We would appreciate bug reports about when these appear in the wild, if they do.
|
|
||||||
case unexpectedError(message: String)
|
|
||||||
|
|
||||||
/// Error indicating that the log adapter cannot be enabled
|
|
||||||
/// because it is already enabled.
|
|
||||||
///
|
|
||||||
/// This is a usage error, either `disable` it first, or
|
|
||||||
/// use `RustLog.shared.tryEnable`
|
|
||||||
case alreadyEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
private func rustCall<T>(_ callback: (UnsafeMutablePointer<RcLogError>) throws -> T) throws -> T {
|
|
||||||
var err = RcLogError(code: 0, message: nil)
|
|
||||||
let result = try callback(&err)
|
|
||||||
if err.code != 0 {
|
|
||||||
let message: String
|
|
||||||
if let messageP = err.message {
|
|
||||||
defer { rc_log_adapter_destroy_string(messageP) }
|
|
||||||
message = String(cString: messageP)
|
|
||||||
} else {
|
|
||||||
message = "Bug: No message provided with code \(err.code)!"
|
|
||||||
}
|
|
||||||
throw RustLogError.unexpectedError(message: message)
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the function actually passed to Rust.
|
|
||||||
private func logCallbackFunc(level: Int32, optTagP: UnsafePointer<CChar>?, msgP: UnsafePointer<CChar>) {
|
|
||||||
guard let callback = RustLog.shared.state.callback else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let msg = String(cString: msgP)
|
|
||||||
// Probably a better way to do this...
|
|
||||||
let tag: String?
|
|
||||||
if let optTagP = optTagP {
|
|
||||||
tag = String(cString: optTagP)
|
|
||||||
} else {
|
|
||||||
tag = nil
|
|
||||||
}
|
|
||||||
RustLog.shared.queue.async {
|
|
||||||
if !callback(LogLevel(safeRawValue: level), tag, msg) {
|
|
||||||
RustLog.shared.state.disable()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This implements everything, but without synchronization. It needs to be
|
|
||||||
// guarded by a queue, which is done by the RustLog class.
|
|
||||||
private class RustLogState {
|
|
||||||
var adapter: OpaquePointer?
|
|
||||||
var callback: LogCallback?
|
|
||||||
|
|
||||||
var isEnabled: Bool { return adapter != nil }
|
|
||||||
|
|
||||||
func enable(_ callback: @escaping LogCallback) throws {
|
|
||||||
if isEnabled {
|
|
||||||
throw RustLogError.alreadyEnabled
|
|
||||||
}
|
|
||||||
assert(self.callback == nil)
|
|
||||||
self.callback = callback
|
|
||||||
adapter = try rustCall { error in
|
|
||||||
rc_log_adapter_create(logCallbackFunc, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func disable() {
|
|
||||||
guard let adapter = adapter else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.adapter = nil
|
|
||||||
callback = nil
|
|
||||||
rc_log_adapter_destroy(adapter)
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryEnable(_ callback: @escaping LogCallback) -> Bool {
|
|
||||||
if isEnabled {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
try enable(callback)
|
|
||||||
return true
|
|
||||||
} catch {
|
|
||||||
_ = callback(.error,
|
|
||||||
"RustLog.swift",
|
|
||||||
"RustLog.enable failed: \(error)")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <Foundation/NSObjCRuntime.h>
|
|
||||||
|
|
||||||
typedef struct RcLogError {
|
|
||||||
int32_t code;
|
|
||||||
char *_Nullable message;
|
|
||||||
} RcLogError;
|
|
||||||
|
|
||||||
typedef void RustLogCallback(int32_t level,
|
|
||||||
char const *_Nullable tag,
|
|
||||||
char const *_Nonnull msg);
|
|
||||||
|
|
||||||
typedef struct RustLogAdapter RustLogAdapter;
|
|
||||||
|
|
||||||
RustLogAdapter *_Nullable rc_log_adapter_create(RustLogCallback *_Nonnull callback,
|
|
||||||
RcLogError *_Nonnull out_err);
|
|
||||||
|
|
||||||
void rc_log_adapter_set_max_level(int32_t max_level,
|
|
||||||
RcLogError *_Nonnull out_err);
|
|
||||||
|
|
||||||
void rc_log_adapter_destroy(RustLogAdapter *_Nonnull to_destroy);
|
|
||||||
|
|
||||||
void rc_log_adapter_test__log_msg(char const *_Nonnull msg);
|
|
||||||
|
|
||||||
// Only use for Error strings!
|
|
||||||
void rc_log_adapter_destroy_string(char *_Nonnull msg);
|
|
|
@ -1,218 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
//! This is the android backend for rc_log. It has a decent amount of
|
|
||||||
//! complexity, as Rust logs can be emitted by any thread, regardless of whether
|
|
||||||
//! or not they have an associated JVM thread. JNA's Callback class helps us
|
|
||||||
//! here, by providing a way for mapping native threads to JVM threads.
|
|
||||||
//! Unfortunately, naive usage of this class in a multithreaded context will be
|
|
||||||
//! very suboptimal in terms of memory and thread usage.
|
|
||||||
//!
|
|
||||||
//! To avoid this, we only call into the JVM from a single thread, which we
|
|
||||||
//! launch when initializing the logger. This thread just polls a channel
|
|
||||||
//! listening for log messages, where a log message is an enum (`LogMessage`)
|
|
||||||
//! that either tells it to log an item, or to stop logging all together.
|
|
||||||
//!
|
|
||||||
//! 1. We cannot guarantee that the callback from android lives past when the
|
|
||||||
//! android code tells us to stop logging, so in order to be memory safe, we
|
|
||||||
//! need to stop logging immediately when this happens. We do this using an
|
|
||||||
//! `Arc<AtomicBool>`, used to indicate that we should stop logging.
|
|
||||||
//!
|
|
||||||
//! 2. There's no safe way to terminate a thread in Rust (for good reason), so
|
|
||||||
//! the background thread must close willingly. To make sure this happens
|
|
||||||
//! promptly (e.g. to avoid a case where we're blocked until some thread
|
|
||||||
//! somewhere else happens to log something), we need to add something onto
|
|
||||||
//! the log channel, hence the existence of `LogMessage::Stop`.
|
|
||||||
//!
|
|
||||||
//! It's important to note that because of point 1, the polling thread may
|
|
||||||
//! have to stop prior to getting `LogMessage::Stop`. We do not want to wait
|
|
||||||
//! for it to process whatever log messages were sent prior to being told to
|
|
||||||
//! stop.
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
ffi::CString,
|
|
||||||
os::raw::c_char,
|
|
||||||
sync::{
|
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
mpsc::{sync_channel, SyncSender},
|
|
||||||
Arc, Mutex,
|
|
||||||
},
|
|
||||||
thread,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::LogLevel;
|
|
||||||
|
|
||||||
/// Type of the log callback provided to us by java/swift. Takes the following
|
|
||||||
/// arguments:
|
|
||||||
///
|
|
||||||
/// - Log level (an i32).
|
|
||||||
///
|
|
||||||
/// - Tag: a (nullable) nul terminated c string. The callback must not free this
|
|
||||||
/// string, which is only valid until the the callback returns. If you need
|
|
||||||
/// it past that, you must copy it into an internal buffer!
|
|
||||||
///
|
|
||||||
/// - Message: a (non-nullable) nul terminated c string. The callback must not free this
|
|
||||||
/// string, which is only valid until the the callback returns. If you need
|
|
||||||
/// it past that, you must copy it into an internal buffer!
|
|
||||||
///
|
|
||||||
/// and returns 0 if we should close the thread, and 1 otherwise. This is done
|
|
||||||
/// because attempting to call `disable` from within the log callback will
|
|
||||||
/// deadlock.
|
|
||||||
pub type LogCallback = unsafe extern "C" fn(i32, *const c_char, *const c_char) -> u8;
|
|
||||||
|
|
||||||
// TODO: use serde to send this to the other thread as bincode or something,
|
|
||||||
// rather than allocating all these strings for every message.
|
|
||||||
struct LogRecord {
|
|
||||||
level: LogLevel,
|
|
||||||
tag: Option<CString>,
|
|
||||||
message: CString,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> From<&'b log::Record<'a>> for LogRecord {
|
|
||||||
// XXX important! Don't log in this function!
|
|
||||||
fn from(r: &'b log::Record<'a>) -> Self {
|
|
||||||
let thread_id = format!("{:?}", std::thread::current().id());
|
|
||||||
let thread_id = if thread_id.starts_with("ThreadId(") && thread_id.ends_with(')') {
|
|
||||||
format!("t{}", &thread_id[9..(thread_id.len() - 1)])
|
|
||||||
} else {
|
|
||||||
thread_id
|
|
||||||
};
|
|
||||||
let message = format!("{} {}", thread_id, r.args());
|
|
||||||
let level = LogLevel::from_level_and_message(r.level(), &message);
|
|
||||||
Self {
|
|
||||||
level,
|
|
||||||
tag: r
|
|
||||||
.module_path()
|
|
||||||
.and_then(|mp| CString::new(mp.to_owned()).ok()),
|
|
||||||
message: crate::string_to_cstring_lossy(message),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum LogMessage {
|
|
||||||
Stop,
|
|
||||||
Record(LogRecord),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LogAdapterState {
|
|
||||||
// Thread handle for the BG thread.
|
|
||||||
handle: Option<std::thread::JoinHandle<()>>,
|
|
||||||
stopped: Arc<Mutex<bool>>,
|
|
||||||
sender: SyncSender<LogMessage>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LogSink {
|
|
||||||
sender: SyncSender<LogMessage>,
|
|
||||||
// Used locally for preventing unnecessary work after the `sender`
|
|
||||||
// is closed. Not shared. Not required for correctness.
|
|
||||||
disabled: AtomicBool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl log::Log for LogSink {
|
|
||||||
fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool {
|
|
||||||
// Really this could just be Acquire but whatever
|
|
||||||
!self.disabled.load(Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&self) {}
|
|
||||||
fn log(&self, record: &log::Record<'_>) {
|
|
||||||
// Important: we check stopped before writing, which means
|
|
||||||
// it must be set before
|
|
||||||
if self.disabled.load(Ordering::SeqCst) {
|
|
||||||
// Note: `enabled` is not automatically called.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Either the queue is full, or the receiver is closed.
|
|
||||||
// In either case, we want to stop all logging immediately.
|
|
||||||
if self
|
|
||||||
.sender
|
|
||||||
.try_send(LogMessage::Record(record.into()))
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
self.disabled.store(true, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LogAdapterState {
|
|
||||||
#[allow(clippy::mutex_atomic)]
|
|
||||||
pub fn init(callback: LogCallback) -> Self {
|
|
||||||
// This uses a mutex (instead of an atomic bool) to avoid a race condition
|
|
||||||
// where `stopped` gets set by another thread between when we read it and
|
|
||||||
// when we call the callback. This way, they'll block.
|
|
||||||
let stopped = Arc::new(Mutex::new(false));
|
|
||||||
let (message_sender, message_recv) = sync_channel(4096);
|
|
||||||
let handle = {
|
|
||||||
let stopped = stopped.clone();
|
|
||||||
thread::spawn(move || {
|
|
||||||
// We stop if we see `Err` (which means the channel got closed,
|
|
||||||
// which probably can't happen since the sender owned by the
|
|
||||||
// logger will never get dropped), or if we get `LogMessage::Stop`,
|
|
||||||
// which means we should stop processing.
|
|
||||||
while let Ok(LogMessage::Record(record)) = message_recv.recv() {
|
|
||||||
let LogRecord {
|
|
||||||
tag,
|
|
||||||
level,
|
|
||||||
message,
|
|
||||||
} = record;
|
|
||||||
let tag_ptr = tag
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| s.as_ptr())
|
|
||||||
.unwrap_or_else(std::ptr::null);
|
|
||||||
let msg_ptr = message.as_ptr();
|
|
||||||
|
|
||||||
let mut stop_guard = stopped.lock().unwrap();
|
|
||||||
if *stop_guard {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let keep_going = unsafe { callback(level as i32, tag_ptr, msg_ptr) };
|
|
||||||
if keep_going == 0 {
|
|
||||||
*stop_guard = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
let sink = LogSink {
|
|
||||||
sender: message_sender.clone(),
|
|
||||||
disabled: AtomicBool::new(false),
|
|
||||||
};
|
|
||||||
|
|
||||||
crate::settable_log::set_logger(Box::new(sink));
|
|
||||||
log::set_max_level(log::LevelFilter::Debug);
|
|
||||||
log::info!("rc_log adapter initialized!");
|
|
||||||
Self {
|
|
||||||
handle: Some(handle),
|
|
||||||
stopped,
|
|
||||||
sender: message_sender,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for LogAdapterState {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
{
|
|
||||||
// It would be nice to write a log that says something like
|
|
||||||
// "if we deadlock here it's because you tried to close the
|
|
||||||
// log adapter from within the log callback", but, well, we
|
|
||||||
// can't exactly log anything from here (and even if we could,
|
|
||||||
// they'd never see it if they hit that situation)
|
|
||||||
let mut stop_guard = self.stopped.lock().unwrap();
|
|
||||||
*stop_guard = true;
|
|
||||||
// We can ignore a failure here because it means either
|
|
||||||
// - The recv is dropped, in which case we don't need to send anything
|
|
||||||
// - The recv is completely full, in which case it will see the flag we
|
|
||||||
// wrote into `stop_guard` soon enough anyway.
|
|
||||||
let _ = self.sender.try_send(LogMessage::Stop);
|
|
||||||
}
|
|
||||||
// Wait for the calling thread to stop. This should be relatively
|
|
||||||
// quickly unless something terrible has happened.
|
|
||||||
if let Some(h) = self.handle.take() {
|
|
||||||
h.join().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi_support::implement_into_ffi_by_pointer!(LogAdapterState);
|
|
|
@ -1,90 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
use crate::LogLevel;
|
|
||||||
use std::ffi::CString;
|
|
||||||
use std::os::raw::c_char;
|
|
||||||
use std::sync::{
|
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Type of the log callback provided to us by swift. Takes the following
|
|
||||||
/// arguments:
|
|
||||||
///
|
|
||||||
/// - Log level (an i32).
|
|
||||||
///
|
|
||||||
/// - Tag: a (nullable) nul terminated c string. The callback must not free this
|
|
||||||
/// string, which is only valid until the the callback returns. If you need
|
|
||||||
/// it past that, you must copy it into an internal buffer!
|
|
||||||
///
|
|
||||||
/// - Message: a (non-nullable) nul terminated c string. The callback must not free this
|
|
||||||
/// string, which is only valid until the the callback returns. If you need
|
|
||||||
/// it past that, you must copy it into an internal buffer!
|
|
||||||
///
|
|
||||||
/// This is equivalent to the callback java uses **except** it cannot return 1/0
|
|
||||||
/// for disabling. Instead, the swift bindings allow calling disable from the
|
|
||||||
/// callback (which is more difficult for the java bindings).
|
|
||||||
pub type LogCallback = unsafe extern "C" fn(i32, *const c_char, *const c_char);
|
|
||||||
|
|
||||||
pub struct LogAdapterState {
|
|
||||||
stop: Arc<AtomicBool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Logger {
|
|
||||||
callback: LogCallback,
|
|
||||||
stop: Arc<AtomicBool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl log::Log for Logger {
|
|
||||||
fn enabled(&self, _metadata: &log::Metadata<'_>) -> bool {
|
|
||||||
!self.stop.load(Ordering::SeqCst)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&self) {}
|
|
||||||
fn log(&self, record: &log::Record<'_>) {
|
|
||||||
if self.stop.load(Ordering::SeqCst) {
|
|
||||||
// Note: `enabled` is not automatically called.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let tag = record
|
|
||||||
.module_path()
|
|
||||||
.and_then(|mp| CString::new(mp.as_bytes()).ok());
|
|
||||||
|
|
||||||
// TODO: use SmallVec<[u8; 4096]> or something?
|
|
||||||
let msg_string = format!("{}", record.args());
|
|
||||||
let level = LogLevel::from_level_and_message(record.level(), &msg_string);
|
|
||||||
let msg_cstring = crate::string_to_cstring_lossy(msg_string);
|
|
||||||
|
|
||||||
let tag_ptr = tag
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| s.as_ptr())
|
|
||||||
.unwrap_or_else(std::ptr::null);
|
|
||||||
let msg_ptr = msg_cstring.as_ptr() as *const c_char;
|
|
||||||
|
|
||||||
unsafe { (self.callback)(level as i32, tag_ptr, msg_ptr) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LogAdapterState {
|
|
||||||
pub fn init(callback: LogCallback) -> Self {
|
|
||||||
let stop = Arc::new(AtomicBool::new(false));
|
|
||||||
let log = Logger {
|
|
||||||
callback,
|
|
||||||
stop: stop.clone(),
|
|
||||||
};
|
|
||||||
crate::settable_log::set_logger(Box::new(log));
|
|
||||||
log::set_max_level(log::LevelFilter::Debug);
|
|
||||||
log::info!("rc_log adapter initialized!");
|
|
||||||
Self { stop }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for LogAdapterState {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.stop.store(true, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi_support::implement_into_ffi_by_pointer!(LogAdapterState);
|
|
|
@ -1,170 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
//! This crate allows users from the other side of the FFI to hook into Rust's
|
|
||||||
//! `log` crate, which is used by us and several of our dependencies. The
|
|
||||||
//! primary use case is providing logs to Android and iOS in a way that is more
|
|
||||||
//! flexible than writing to liblog (which goes to logcat, which cannot be
|
|
||||||
//! accessed by programs on the device, short of rooting it), or stdout/stderr.
|
|
||||||
//!
|
|
||||||
//! See the header comment in android.rs and fallback.rs for details.
|
|
||||||
//!
|
|
||||||
//! It's worth noting that the log crate is rather inflexable, in that
|
|
||||||
//! it does not allow users to change loggers after the first initialization. We
|
|
||||||
//! work around this using our `settable_log` module.
|
|
||||||
|
|
||||||
#![allow(unknown_lints)]
|
|
||||||
#![allow(clippy::upper_case_acronyms)]
|
|
||||||
#![warn(rust_2018_idioms)]
|
|
||||||
// We always include both modules when doing test builds, so for test builds,
|
|
||||||
// allow dead code.
|
|
||||||
#![cfg_attr(test, allow(dead_code))]
|
|
||||||
|
|
||||||
use std::ffi::CString;
|
|
||||||
|
|
||||||
// Import this in tests (even on non-android builds / cases where the
|
|
||||||
// force_android feature is not enabled) so we can check that it compiles
|
|
||||||
// easily.
|
|
||||||
#[cfg(any(test, os = "android", feature = "force_android"))]
|
|
||||||
pub mod android;
|
|
||||||
// Import this in tests (even if we're building for android or force_android is
|
|
||||||
// turned on) so we can check that it compiles easily
|
|
||||||
#[cfg(any(test, not(any(os = "android", feature = "force_android"))))]
|
|
||||||
pub mod ios;
|
|
||||||
|
|
||||||
mod settable_log;
|
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(any(os = "android", feature = "force_android"))] {
|
|
||||||
use crate::android as imp;
|
|
||||||
} else {
|
|
||||||
use crate::ios as imp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn string_to_cstring_lossy(s: String) -> CString {
|
|
||||||
let mut bytes = s.into_bytes();
|
|
||||||
for byte in bytes.iter_mut() {
|
|
||||||
if *byte == 0 {
|
|
||||||
*byte = b'?';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CString::new(bytes).expect("Bug in string_to_cstring_lossy!")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
|
||||||
#[repr(i32)]
|
|
||||||
pub enum LogLevel {
|
|
||||||
// Android logger levels
|
|
||||||
VERBOSE = 2,
|
|
||||||
DEBUG = 3,
|
|
||||||
INFO = 4,
|
|
||||||
WARN = 5,
|
|
||||||
ERROR = 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LogLevel {
|
|
||||||
/// Equivalent to the `into()` conversion but avoids reporting network
|
|
||||||
/// errors as errors, and downgrades them into warnings.
|
|
||||||
pub(crate) fn from_level_and_message(mut level: log::Level, msg: &str) -> Self {
|
|
||||||
if level == log::Level::Error && msg.contains("[no-sentry]") {
|
|
||||||
level = log::Level::Warn;
|
|
||||||
}
|
|
||||||
level.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<log::Level> for LogLevel {
|
|
||||||
fn from(l: log::Level) -> Self {
|
|
||||||
match l {
|
|
||||||
log::Level::Trace => LogLevel::VERBOSE,
|
|
||||||
log::Level::Debug => LogLevel::DEBUG,
|
|
||||||
log::Level::Info => LogLevel::INFO,
|
|
||||||
log::Level::Warn => LogLevel::WARN,
|
|
||||||
log::Level::Error => LogLevel::ERROR,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn rc_log_adapter_create(
|
|
||||||
callback: imp::LogCallback,
|
|
||||||
out_err: &mut ffi_support::ExternError,
|
|
||||||
) -> *mut imp::LogAdapterState {
|
|
||||||
ffi_support::call_with_output(out_err, || imp::LogAdapterState::init(callback))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: keep in sync with LogLevelFilter in kotlin.
|
|
||||||
fn level_filter_from_i32(level_arg: i32) -> log::LevelFilter {
|
|
||||||
match level_arg {
|
|
||||||
4 => log::LevelFilter::Debug,
|
|
||||||
3 => log::LevelFilter::Info,
|
|
||||||
2 => log::LevelFilter::Warn,
|
|
||||||
1 => log::LevelFilter::Error,
|
|
||||||
// We clamp out of bounds level values.
|
|
||||||
n if n <= 0 => log::LevelFilter::Off,
|
|
||||||
n if n >= 5 => log::LevelFilter::Trace,
|
|
||||||
_ => unreachable!("This is actually exhaustive"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn rc_log_adapter_set_max_level(level: i32, out_err: &mut ffi_support::ExternError) {
|
|
||||||
ffi_support::call_with_output(out_err, || log::set_max_level(level_filter_from_i32(level)))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't use define_box_destructor because this can panic. TODO: Maybe we should
|
|
||||||
// keep this around globally (as lazy_static or something) and basically just
|
|
||||||
// turn it on/off in create/destroy... Might be more reliable?
|
|
||||||
/// # Safety
|
|
||||||
/// Unsafe because it frees it's argument.
|
|
||||||
#[no_mangle]
|
|
||||||
pub unsafe extern "C" fn rc_log_adapter_destroy(to_destroy: *mut imp::LogAdapterState) {
|
|
||||||
ffi_support::abort_on_panic::call_with_output(move || {
|
|
||||||
log::set_max_level(log::LevelFilter::Off);
|
|
||||||
drop(Box::from_raw(to_destroy));
|
|
||||||
settable_log::unset_logger();
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used just to allow tests to produce logs.
|
|
||||||
#[no_mangle]
|
|
||||||
pub extern "C" fn rc_log_adapter_test__log_msg(msg: ffi_support::FfiStr<'_>) {
|
|
||||||
ffi_support::abort_on_panic::call_with_output(|| {
|
|
||||||
log::info!("testing: {}", msg.as_str());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi_support::define_string_destructor!(rc_log_adapter_destroy_string);
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
#[test]
|
|
||||||
fn test_level_msg() {
|
|
||||||
assert_eq!(
|
|
||||||
LogLevel::ERROR,
|
|
||||||
LogLevel::from_level_and_message(
|
|
||||||
log::Level::Error,
|
|
||||||
"Rusqlite Error: The database is wrong and bad.",
|
|
||||||
),
|
|
||||||
"Normal errors should come through as errors",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
LogLevel::WARN,
|
|
||||||
LogLevel::from_level_and_message(
|
|
||||||
log::Level::Error,
|
|
||||||
"Network Error: [no-sentry] Network error: the server is furious at you.",
|
|
||||||
),
|
|
||||||
"[no-sentry] errors should come through as warnings",
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
LogLevel::INFO,
|
|
||||||
LogLevel::from_level_and_message(log::Level::Info, "[no-sentry] 🙀"),
|
|
||||||
"Everything else should be unchanged, even if it has a [no-sentry] tag",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use std::sync::{Once, RwLock};
|
|
||||||
|
|
||||||
use log::Log;
|
|
||||||
|
|
||||||
struct SettableLog {
|
|
||||||
inner: RwLock<Option<Box<dyn Log>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref SETTABLE_LOG: SettableLog = SettableLog {
|
|
||||||
inner: RwLock::new(None)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SettableLog {
|
|
||||||
fn set(&self, logger: Box<dyn Log>) {
|
|
||||||
let mut write_lock = self.inner.write().unwrap();
|
|
||||||
*write_lock = Some(logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn unset(&self) {
|
|
||||||
let mut write_lock = self.inner.write().unwrap();
|
|
||||||
drop(write_lock.take());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Log for SettableLog {
|
|
||||||
fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
|
|
||||||
let inner = self.inner.read().unwrap();
|
|
||||||
if let Some(log) = &*inner {
|
|
||||||
log.enabled(metadata)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&self) {
|
|
||||||
let inner = self.inner.read().unwrap();
|
|
||||||
if let Some(log) = &*inner {
|
|
||||||
log.flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn log(&self, record: &log::Record<'_>) {
|
|
||||||
let inner = self.inner.read().unwrap();
|
|
||||||
if let Some(log) = &*inner {
|
|
||||||
log.log(record);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn init_once() {
|
|
||||||
static INITIALIZER: Once = Once::new();
|
|
||||||
INITIALIZER.call_once(|| {
|
|
||||||
log::set_logger(&*SETTABLE_LOG).expect(
|
|
||||||
"Failed to initialize SettableLog, other log implementation already initialized?",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_logger(logger: Box<dyn Log>) {
|
|
||||||
init_once();
|
|
||||||
SETTABLE_LOG.set(logger);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unset_logger() {
|
|
||||||
init_once();
|
|
||||||
SETTABLE_LOG.unset();
|
|
||||||
}
|
|
|
@ -17,7 +17,6 @@ sync_manager = { path = "../../components/sync_manager/" }
|
||||||
# webext-storage = { path = "../../components/webext-storage/" }
|
# webext-storage = { path = "../../components/webext-storage/" }
|
||||||
places = { path = "../../components/places" }
|
places = { path = "../../components/places" }
|
||||||
push = { path = "../../components/push" }
|
push = { path = "../../components/push" }
|
||||||
rc_log_ffi = { path = "../../components/rc_log" }
|
|
||||||
remote_settings = { path = "../../components/remote_settings" }
|
remote_settings = { path = "../../components/remote_settings" }
|
||||||
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
|
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
|
||||||
viaduct = { path = "../../components/viaduct" }
|
viaduct = { path = "../../components/viaduct" }
|
||||||
|
|
|
@ -16,9 +16,6 @@ pub use logins;
|
||||||
pub use nimbus;
|
pub use nimbus;
|
||||||
pub use places;
|
pub use places;
|
||||||
pub use push;
|
pub use push;
|
||||||
// TODO: Drop this dependency once android-components switches to using `rust_log_forwarder` for
|
|
||||||
// log forwarding.
|
|
||||||
pub use rc_log_ffi;
|
|
||||||
pub use remote_settings;
|
pub use remote_settings;
|
||||||
pub use rust_log_forwarder;
|
pub use rust_log_forwarder;
|
||||||
pub use suggest;
|
pub use suggest;
|
||||||
|
|
|
@ -9,7 +9,6 @@ license = "MPL-2.0"
|
||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rc_log_ffi = { path = "../../components/rc_log" }
|
|
||||||
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
|
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
|
||||||
viaduct = { path = "../../components/viaduct" }
|
viaduct = { path = "../../components/viaduct" }
|
||||||
viaduct-reqwest = { path = "../../components/support/viaduct-reqwest" }
|
viaduct-reqwest = { path = "../../components/support/viaduct-reqwest" }
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
// This is the "umbrella header" for our combined Rust code library.
|
// This is the "umbrella header" for our combined Rust code library.
|
||||||
// It needs to import all of the individual headers.
|
// It needs to import all of the individual headers.
|
||||||
|
|
||||||
#import "RustLogFFI.h"
|
|
||||||
#import "RustViaductFFI.h"
|
#import "RustViaductFFI.h"
|
||||||
#import "autofillFFI.h"
|
#import "autofillFFI.h"
|
||||||
#import "crashtestFFI.h"
|
#import "crashtestFFI.h"
|
||||||
|
|
|
@ -14,10 +14,8 @@
|
||||||
1B3BC94327B1D63B00229CF6 /* PlacesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC54027AE065300DAFEF2 /* PlacesTests.swift */; };
|
1B3BC94327B1D63B00229CF6 /* PlacesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC54027AE065300DAFEF2 /* PlacesTests.swift */; };
|
||||||
1B3BC94527B1D6A500229CF6 /* SyncUnlockInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC5B027AE11AA00DAFEF2 /* SyncUnlockInfo.swift */; };
|
1B3BC94527B1D6A500229CF6 /* SyncUnlockInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC5B027AE11AA00DAFEF2 /* SyncUnlockInfo.swift */; };
|
||||||
1B3BC94627B1D6A500229CF6 /* ResultError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC5B127AE11AA00DAFEF2 /* ResultError.swift */; };
|
1B3BC94627B1D6A500229CF6 /* ResultError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC5B127AE11AA00DAFEF2 /* ResultError.swift */; };
|
||||||
1B3BC94727B1D73500229CF6 /* LogTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC53F27AE065300DAFEF2 /* LogTest.swift */; };
|
|
||||||
1B3BC94827B1D73700229CF6 /* LoginsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC53B27AE065300DAFEF2 /* LoginsTests.swift */; };
|
1B3BC94827B1D73700229CF6 /* LoginsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC53B27AE065300DAFEF2 /* LoginsTests.swift */; };
|
||||||
1B3BC94B27B1D79B00229CF6 /* LoginsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC58627AE09C600DAFEF2 /* LoginsStorage.swift */; };
|
1B3BC94B27B1D79B00229CF6 /* LoginsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC58627AE09C600DAFEF2 /* LoginsStorage.swift */; };
|
||||||
1B3BC94D27B1D7B700229CF6 /* RustLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC55327AE076200DAFEF2 /* RustLog.swift */; };
|
|
||||||
1B3BC94F27B1D92800229CF6 /* NimbusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC53E27AE065300DAFEF2 /* NimbusTests.swift */; };
|
1B3BC94F27B1D92800229CF6 /* NimbusTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC53E27AE065300DAFEF2 /* NimbusTests.swift */; };
|
||||||
1B3BC95027B1D92A00229CF6 /* NimbusFeatureVariablesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC54427AE065300DAFEF2 /* NimbusFeatureVariablesTests.swift */; };
|
1B3BC95027B1D92A00229CF6 /* NimbusFeatureVariablesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC54427AE065300DAFEF2 /* NimbusFeatureVariablesTests.swift */; };
|
||||||
1B3BC95127B1D93100229CF6 /* CrashTestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC54327AE065300DAFEF2 /* CrashTestTests.swift */; };
|
1B3BC95127B1D93100229CF6 /* CrashTestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC54327AE065300DAFEF2 /* CrashTestTests.swift */; };
|
||||||
|
@ -152,14 +150,11 @@
|
||||||
1BBAC53B27AE065300DAFEF2 /* LoginsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LoginsTests.swift; path = MozillaTestServicesTests/LoginsTests.swift; sourceTree = SOURCE_ROOT; };
|
1BBAC53B27AE065300DAFEF2 /* LoginsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LoginsTests.swift; path = MozillaTestServicesTests/LoginsTests.swift; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC53C27AE065300DAFEF2 /* FxAccountMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FxAccountMocks.swift; path = MozillaTestServicesTests/FxAccountMocks.swift; sourceTree = SOURCE_ROOT; };
|
1BBAC53C27AE065300DAFEF2 /* FxAccountMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FxAccountMocks.swift; path = MozillaTestServicesTests/FxAccountMocks.swift; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC53E27AE065300DAFEF2 /* NimbusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NimbusTests.swift; path = MozillaTestServicesTests/NimbusTests.swift; sourceTree = SOURCE_ROOT; };
|
1BBAC53E27AE065300DAFEF2 /* NimbusTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NimbusTests.swift; path = MozillaTestServicesTests/NimbusTests.swift; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC53F27AE065300DAFEF2 /* LogTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = LogTest.swift; path = MozillaTestServicesTests/LogTest.swift; sourceTree = SOURCE_ROOT; };
|
|
||||||
1BBAC54027AE065300DAFEF2 /* PlacesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlacesTests.swift; path = MozillaTestServicesTests/PlacesTests.swift; sourceTree = SOURCE_ROOT; };
|
1BBAC54027AE065300DAFEF2 /* PlacesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = PlacesTests.swift; path = MozillaTestServicesTests/PlacesTests.swift; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC54127AE065300DAFEF2 /* FxAccountManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FxAccountManagerTests.swift; path = MozillaTestServicesTests/FxAccountManagerTests.swift; sourceTree = SOURCE_ROOT; };
|
1BBAC54127AE065300DAFEF2 /* FxAccountManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = FxAccountManagerTests.swift; path = MozillaTestServicesTests/FxAccountManagerTests.swift; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC54227AE065300DAFEF2 /* NimbusMessagingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NimbusMessagingTests.swift; path = MozillaTestServicesTests/NimbusMessagingTests.swift; sourceTree = SOURCE_ROOT; };
|
1BBAC54227AE065300DAFEF2 /* NimbusMessagingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NimbusMessagingTests.swift; path = MozillaTestServicesTests/NimbusMessagingTests.swift; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC54327AE065300DAFEF2 /* CrashTestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CrashTestTests.swift; path = MozillaTestServicesTests/CrashTestTests.swift; sourceTree = SOURCE_ROOT; };
|
1BBAC54327AE065300DAFEF2 /* CrashTestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = CrashTestTests.swift; path = MozillaTestServicesTests/CrashTestTests.swift; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC54427AE065300DAFEF2 /* NimbusFeatureVariablesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NimbusFeatureVariablesTests.swift; path = MozillaTestServicesTests/NimbusFeatureVariablesTests.swift; sourceTree = SOURCE_ROOT; };
|
1BBAC54427AE065300DAFEF2 /* NimbusFeatureVariablesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NimbusFeatureVariablesTests.swift; path = MozillaTestServicesTests/NimbusFeatureVariablesTests.swift; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC55327AE076200DAFEF2 /* RustLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RustLog.swift; path = ../../../components/rc_log/ios/RustLog.swift; sourceTree = SOURCE_ROOT; };
|
|
||||||
1BBAC55427AE076200DAFEF2 /* RustLogFFI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RustLogFFI.h; path = ../../../components/rc_log/ios/RustLogFFI.h; sourceTree = SOURCE_ROOT; };
|
|
||||||
1BBAC56127AE085F00DAFEF2 /* PersistedFirefoxAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PersistedFirefoxAccount.swift; path = "../../../components/fxa-client/ios/FxAClient/PersistedFirefoxAccount.swift"; sourceTree = SOURCE_ROOT; };
|
1BBAC56127AE085F00DAFEF2 /* PersistedFirefoxAccount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PersistedFirefoxAccount.swift; path = "../../../components/fxa-client/ios/FxAClient/PersistedFirefoxAccount.swift"; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC56227AE085F00DAFEF2 /* FxAccountDeviceConstellation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FxAccountDeviceConstellation.swift; path = "../../../components/fxa-client/ios/FxAClient/FxAccountDeviceConstellation.swift"; sourceTree = SOURCE_ROOT; };
|
1BBAC56227AE085F00DAFEF2 /* FxAccountDeviceConstellation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FxAccountDeviceConstellation.swift; path = "../../../components/fxa-client/ios/FxAClient/FxAccountDeviceConstellation.swift"; sourceTree = SOURCE_ROOT; };
|
||||||
1BBAC56427AE085F00DAFEF2 /* KeychainWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = KeychainWrapper.swift; path = "../../../components/fxa-client/ios/FxAClient/MZKeychain/KeychainWrapper.swift"; sourceTree = SOURCE_ROOT; };
|
1BBAC56427AE085F00DAFEF2 /* KeychainWrapper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = KeychainWrapper.swift; path = "../../../components/fxa-client/ios/FxAClient/MZKeychain/KeychainWrapper.swift"; sourceTree = SOURCE_ROOT; };
|
||||||
|
@ -369,7 +364,6 @@
|
||||||
1BBAC5AF27AE112D00DAFEF2 /* Sync15 */,
|
1BBAC5AF27AE112D00DAFEF2 /* Sync15 */,
|
||||||
1BBAC58027AE099B00DAFEF2 /* Logins */,
|
1BBAC58027AE099B00DAFEF2 /* Logins */,
|
||||||
1BBAC55B27AE082400DAFEF2 /* FxAClient */,
|
1BBAC55B27AE082400DAFEF2 /* FxAClient */,
|
||||||
1BBAC55227AE073A00DAFEF2 /* RustLog */,
|
|
||||||
);
|
);
|
||||||
path = MozillaTestServices;
|
path = MozillaTestServices;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -383,7 +377,6 @@
|
||||||
1BBAC53C27AE065300DAFEF2 /* FxAccountMocks.swift */,
|
1BBAC53C27AE065300DAFEF2 /* FxAccountMocks.swift */,
|
||||||
1BBAC54227AE065300DAFEF2 /* NimbusMessagingTests.swift */,
|
1BBAC54227AE065300DAFEF2 /* NimbusMessagingTests.swift */,
|
||||||
1BBAC53B27AE065300DAFEF2 /* LoginsTests.swift */,
|
1BBAC53B27AE065300DAFEF2 /* LoginsTests.swift */,
|
||||||
1BBAC53F27AE065300DAFEF2 /* LogTest.swift */,
|
|
||||||
1BBAC54427AE065300DAFEF2 /* NimbusFeatureVariablesTests.swift */,
|
1BBAC54427AE065300DAFEF2 /* NimbusFeatureVariablesTests.swift */,
|
||||||
1BBAC53E27AE065300DAFEF2 /* NimbusTests.swift */,
|
1BBAC53E27AE065300DAFEF2 /* NimbusTests.swift */,
|
||||||
39083AAA29561E2400FDD302 /* OperationTests.swift */,
|
39083AAA29561E2400FDD302 /* OperationTests.swift */,
|
||||||
|
@ -396,16 +389,6 @@
|
||||||
path = MozillaTestServicesTests;
|
path = MozillaTestServicesTests;
|
||||||
sourceTree = SOURCE_ROOT;
|
sourceTree = SOURCE_ROOT;
|
||||||
};
|
};
|
||||||
1BBAC55227AE073A00DAFEF2 /* RustLog */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
1BBAC55327AE076200DAFEF2 /* RustLog.swift */,
|
|
||||||
1BBAC55427AE076200DAFEF2 /* RustLogFFI.h */,
|
|
||||||
);
|
|
||||||
name = RustLog;
|
|
||||||
path = ../../../../components/rc_log/ios;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
1BBAC55B27AE082400DAFEF2 /* FxAClient */ = {
|
1BBAC55B27AE082400DAFEF2 /* FxAClient */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -865,7 +848,6 @@
|
||||||
1BF50F1027B1E17B00A9C8A5 /* FxAccountStorage.swift in Sources */,
|
1BF50F1027B1E17B00A9C8A5 /* FxAccountStorage.swift in Sources */,
|
||||||
39EE00FD29F6DB2A001E7758 /* ArgumentProcessor.swift in Sources */,
|
39EE00FD29F6DB2A001E7758 /* ArgumentProcessor.swift in Sources */,
|
||||||
1BF50F0E27B1E17B00A9C8A5 /* FxAccountLogging.swift in Sources */,
|
1BF50F0E27B1E17B00A9C8A5 /* FxAccountLogging.swift in Sources */,
|
||||||
1B3BC94D27B1D7B700229CF6 /* RustLog.swift in Sources */,
|
|
||||||
1BF50F1627B1E18000A9C8A5 /* KeychainWrapper.swift in Sources */,
|
1BF50F1627B1E18000A9C8A5 /* KeychainWrapper.swift in Sources */,
|
||||||
1BF50F1227B1E17B00A9C8A5 /* FxAccountManager.swift in Sources */,
|
1BF50F1227B1E17B00A9C8A5 /* FxAccountManager.swift in Sources */,
|
||||||
39F5D7642956161E004E2384 /* Operation+.swift in Sources */,
|
39F5D7642956161E004E2384 /* Operation+.swift in Sources */,
|
||||||
|
@ -898,7 +880,6 @@
|
||||||
39EE00FF29F6DBBA001E7758 /* NimbusArgumentProcessorTests.swift in Sources */,
|
39EE00FF29F6DBBA001E7758 /* NimbusArgumentProcessorTests.swift in Sources */,
|
||||||
1BF50F1A27B1E19800A9C8A5 /* FxAccountMocks.swift in Sources */,
|
1BF50F1A27B1E19800A9C8A5 /* FxAccountMocks.swift in Sources */,
|
||||||
1B3BC95127B1D93100229CF6 /* CrashTestTests.swift in Sources */,
|
1B3BC95127B1D93100229CF6 /* CrashTestTests.swift in Sources */,
|
||||||
1B3BC94727B1D73500229CF6 /* LogTest.swift in Sources */,
|
|
||||||
F85ED649299C1F49005EEF36 /* RustSyncTelemetryPingTests.swift in Sources */,
|
F85ED649299C1F49005EEF36 /* RustSyncTelemetryPingTests.swift in Sources */,
|
||||||
3937440A29E43B8800726A72 /* EventStoreTests.swift in Sources */,
|
3937440A29E43B8800726A72 /* EventStoreTests.swift in Sources */,
|
||||||
1B3BC94327B1D63B00229CF6 /* PlacesTests.swift in Sources */,
|
1B3BC94327B1D63B00229CF6 /* PlacesTests.swift in Sources */,
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
import Foundation
|
|
||||||
import XCTest
|
|
||||||
|
|
||||||
@testable import MozillaTestServices
|
|
||||||
|
|
||||||
class LogTests: XCTestCase {
|
|
||||||
func writeTestLog(_ message: String) {
|
|
||||||
RustLog.shared.logTestMessage(message: message)
|
|
||||||
// Wait for the log to come in. Cludgey but good enough.
|
|
||||||
Thread.sleep(forTimeInterval: 0.1)
|
|
||||||
// Force us to synchronize on the queue.
|
|
||||||
_ = RustLog.shared.isEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLogging() {
|
|
||||||
var logs: [(LogLevel, String?, String)] = []
|
|
||||||
|
|
||||||
assert(!RustLog.shared.isEnabled)
|
|
||||||
|
|
||||||
try! RustLog.shared.enable { level, tag, msg in
|
|
||||||
let info = "Rust | Level: \(level) | tag: \(String(describing: tag)) | message: \(msg)"
|
|
||||||
print(info)
|
|
||||||
logs.append((level, tag, msg))
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// We log an informational message after initializing (but it's processed asynchronously).
|
|
||||||
Thread.sleep(forTimeInterval: 0.1)
|
|
||||||
XCTAssert(RustLog.shared.isEnabled)
|
|
||||||
XCTAssertEqual(logs.count, 1)
|
|
||||||
writeTestLog("Test1")
|
|
||||||
XCTAssertEqual(logs.count, 2)
|
|
||||||
do {
|
|
||||||
try RustLog.shared.enable { _, _, _ in true }
|
|
||||||
XCTFail("Enable should fail")
|
|
||||||
} catch let error as RustLogError {
|
|
||||||
switch error {
|
|
||||||
case .alreadyEnabled:
|
|
||||||
print("enable failed as it should")
|
|
||||||
default:
|
|
||||||
XCTFail("Wrong RustLogError: \(error)")
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
XCTFail("Wrong error: \(error)")
|
|
||||||
}
|
|
||||||
// tryEnable should return false
|
|
||||||
XCTAssert(!RustLog.shared.tryEnable { _, _, _ in true })
|
|
||||||
|
|
||||||
// Adjust the max level so that the test log (which is logged at info level)
|
|
||||||
// will not be present.
|
|
||||||
try! RustLog.shared.setLevelFilter(filter: .warn)
|
|
||||||
writeTestLog("Test2")
|
|
||||||
// Should not increase
|
|
||||||
XCTAssertEqual(logs.count, 2)
|
|
||||||
try! RustLog.shared.setLevelFilter(filter: .info)
|
|
||||||
writeTestLog("Test3")
|
|
||||||
XCTAssertEqual(logs.count, 3)
|
|
||||||
|
|
||||||
RustLog.shared.disable()
|
|
||||||
XCTAssert(!RustLog.shared.isEnabled)
|
|
||||||
|
|
||||||
// Shouldn't do anything, we disabled the log.
|
|
||||||
writeTestLog("Test4")
|
|
||||||
|
|
||||||
XCTAssertEqual(logs.count, 3)
|
|
||||||
var counter = 0
|
|
||||||
let didEnable = RustLog.shared.tryEnable { level, tag, msg in
|
|
||||||
let info = "Rust | Level: \(level) | tag: \(String(describing: tag)) | message: \(msg)"
|
|
||||||
print(info)
|
|
||||||
logs.append((level, tag, msg))
|
|
||||||
counter += 1
|
|
||||||
if counter == 3 {
|
|
||||||
// Test disabling from inside log callback
|
|
||||||
print("Disabling log from inside callback")
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XCTAssert(didEnable)
|
|
||||||
// Wait for the 'we enabled logging' log to come in asynchronously
|
|
||||||
Thread.sleep(forTimeInterval: 0.1)
|
|
||||||
XCTAssert(RustLog.shared.isEnabled)
|
|
||||||
XCTAssertEqual(logs.count, 4)
|
|
||||||
|
|
||||||
writeTestLog("Test5")
|
|
||||||
XCTAssertEqual(logs.count, 5)
|
|
||||||
|
|
||||||
writeTestLog("Test6")
|
|
||||||
XCTAssertEqual(logs.count, 6)
|
|
||||||
// Should be disabled now,
|
|
||||||
XCTAssert(!RustLog.shared.isEnabled)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -142,7 +142,6 @@ mkdir -p "$COMMON/Headers"
|
||||||
# First we move the files that are common between both
|
# First we move the files that are common between both
|
||||||
# firefox-ios and Focus
|
# firefox-ios and Focus
|
||||||
cp "$WORKING_DIR/$FRAMEWORK_NAME.h" "$COMMON/Headers"
|
cp "$WORKING_DIR/$FRAMEWORK_NAME.h" "$COMMON/Headers"
|
||||||
cp "$REPO_ROOT/components/rc_log/ios/RustLogFFI.h" "$COMMON/Headers"
|
|
||||||
cp "$REPO_ROOT/components/viaduct/ios/RustViaductFFI.h" "$COMMON/Headers"
|
cp "$REPO_ROOT/components/viaduct/ios/RustViaductFFI.h" "$COMMON/Headers"
|
||||||
$CARGO uniffi-bindgen generate "$REPO_ROOT/components/remote_settings/src/remote_settings.udl" -l swift -o "$COMMON/Headers"
|
$CARGO uniffi-bindgen generate "$REPO_ROOT/components/remote_settings/src/remote_settings.udl" -l swift -o "$COMMON/Headers"
|
||||||
$CARGO uniffi-bindgen generate "$REPO_ROOT/components/nimbus/src/nimbus.udl" -l swift -o "$COMMON/Headers"
|
$CARGO uniffi-bindgen generate "$REPO_ROOT/components/nimbus/src/nimbus.udl" -l swift -o "$COMMON/Headers"
|
||||||
|
|
|
@ -9,7 +9,6 @@ license = "MPL-2.0"
|
||||||
crate-type = ["staticlib"]
|
crate-type = ["staticlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rc_log_ffi = { path = "../../../components/rc_log" }
|
|
||||||
rust-log-forwarder = { path = "../../../components/support/rust-log-forwarder" }
|
rust-log-forwarder = { path = "../../../components/support/rust-log-forwarder" }
|
||||||
viaduct = { path = "../../../components/viaduct" }
|
viaduct = { path = "../../../components/viaduct" }
|
||||||
viaduct-reqwest = { path = "../../../components/support/viaduct-reqwest" }
|
viaduct-reqwest = { path = "../../../components/support/viaduct-reqwest" }
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
// This is the "umbrella header" for our combined Rust code library.
|
// This is the "umbrella header" for our combined Rust code library.
|
||||||
// It needs to import all of the individual headers.
|
// It needs to import all of the individual headers.
|
||||||
|
|
||||||
#import "RustLogFFI.h"
|
|
||||||
#import "RustViaductFFI.h"
|
#import "RustViaductFFI.h"
|
||||||
#import "nimbusFFI.h"
|
#import "nimbusFFI.h"
|
||||||
#import "errorFFI.h"
|
#import "errorFFI.h"
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
pub use error_support;
|
pub use error_support;
|
||||||
pub use nimbus;
|
pub use nimbus;
|
||||||
pub use rc_log_ffi;
|
|
||||||
pub use remote_settings;
|
pub use remote_settings;
|
||||||
pub use rust_log_forwarder;
|
pub use rust_log_forwarder;
|
||||||
pub use viaduct_reqwest;
|
pub use viaduct_reqwest;
|
||||||
|
|
|
@ -14,9 +14,6 @@ pub use logins;
|
||||||
pub use nimbus;
|
pub use nimbus;
|
||||||
pub use places;
|
pub use places;
|
||||||
pub use push;
|
pub use push;
|
||||||
// TODO: Drop this dependency once firefox-ios switches to using `rust_log_forwarder` for log
|
|
||||||
// forwarding.
|
|
||||||
pub use rc_log_ffi;
|
|
||||||
pub use remote_settings;
|
pub use remote_settings;
|
||||||
pub use rust_log_forwarder;
|
pub use rust_log_forwarder;
|
||||||
pub use suggest;
|
pub use suggest;
|
||||||
|
|
|
@ -44,14 +44,12 @@ SOURCE_TO_COPY = [
|
||||||
"components/places/ios/Places",
|
"components/places/ios/Places",
|
||||||
"components/sync15/ios/Sync15",
|
"components/sync15/ios/Sync15",
|
||||||
"components/sync_manager/ios/SyncManager",
|
"components/sync_manager/ios/SyncManager",
|
||||||
"components/rc_log/ios/*",
|
|
||||||
"components/viaduct/ios/*",
|
"components/viaduct/ios/*",
|
||||||
]
|
]
|
||||||
|
|
||||||
# List of udl_paths to generate bindings for
|
# List of udl_paths to generate bindings for
|
||||||
FOCUS_SOURCE_TO_COPY = [
|
FOCUS_SOURCE_TO_COPY = [
|
||||||
"components/nimbus/ios/Nimbus",
|
"components/nimbus/ios/Nimbus",
|
||||||
"components/rc_log/ios/*",
|
|
||||||
"components/viaduct/ios/*",
|
"components/viaduct/ios/*",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ existing_tasks:
|
||||||
module-build-places: CjxF6i5XTUuue3N6hur76A
|
module-build-places: CjxF6i5XTUuue3N6hur76A
|
||||||
module-build-push: Eb1ryYa0TQas2d6L8shrKg
|
module-build-push: Eb1ryYa0TQas2d6L8shrKg
|
||||||
module-build-rust-log-forwarder: PavZP7KCQUa6eIiH7rcguw
|
module-build-rust-log-forwarder: PavZP7KCQUa6eIiH7rcguw
|
||||||
module-build-rustlog: DzsCxDB2RfmjXjSb9sXUcg
|
|
||||||
module-build-sync15: c-mAP_Z5Tt-jep2NqUVEPQ
|
module-build-sync15: c-mAP_Z5Tt-jep2NqUVEPQ
|
||||||
module-build-syncmanager: aO_QpDq_RoOcNf56oxDtug
|
module-build-syncmanager: aO_QpDq_RoOcNf56oxDtug
|
||||||
module-build-tabs: d_M2TbtARdeqZdNuLDGMxA
|
module-build-tabs: d_M2TbtARdeqZdNuLDGMxA
|
||||||
|
|
|
@ -26,7 +26,6 @@ existing_tasks:
|
||||||
module-build-places: CjxF6i5XTUuue3N6hur76A
|
module-build-places: CjxF6i5XTUuue3N6hur76A
|
||||||
module-build-push: Eb1ryYa0TQas2d6L8shrKg
|
module-build-push: Eb1ryYa0TQas2d6L8shrKg
|
||||||
module-build-rust-log-forwarder: PavZP7KCQUa6eIiH7rcguw
|
module-build-rust-log-forwarder: PavZP7KCQUa6eIiH7rcguw
|
||||||
module-build-rustlog: DzsCxDB2RfmjXjSb9sXUcg
|
|
||||||
module-build-sync15: c-mAP_Z5Tt-jep2NqUVEPQ
|
module-build-sync15: c-mAP_Z5Tt-jep2NqUVEPQ
|
||||||
module-build-syncmanager: aO_QpDq_RoOcNf56oxDtug
|
module-build-syncmanager: aO_QpDq_RoOcNf56oxDtug
|
||||||
module-build-tabs: d_M2TbtARdeqZdNuLDGMxA
|
module-build-tabs: d_M2TbtARdeqZdNuLDGMxA
|
||||||
|
|
Загрузка…
Ссылка в новой задаче