This commit is contained in:
Mark Hammond 2024-03-05 10:44:24 -05:00
Родитель 47ef7c8c98
Коммит a5e715b286
33 изменённых файлов: 6 добавлений и 1510 удалений

Просмотреть файл

@ -49,13 +49,6 @@ projects:
- name: remotesettings
type: aar
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:
path: components/support/rust-log-forwarder/android
artifactId: rust-log-forwarder

Просмотреть файл

@ -2,6 +2,8 @@
## 🦊 What's Changed 🦊
- The long-deprecated `rc_log` crate has been removed.
### Android
- Upgraded NDK from r25c to r26c. ([#6134](https://github.com/mozilla/application-services/pull/6134))

13
Cargo.lock сгенерированный
Просмотреть файл

@ -2352,7 +2352,6 @@ dependencies = [
"nimbus-sdk",
"places",
"push",
"rc_log_ffi",
"remote_settings",
"rust-log-forwarder",
"suggest",
@ -2367,7 +2366,6 @@ version = "0.1.0"
dependencies = [
"error-support",
"nimbus-sdk",
"rc_log_ffi",
"remote_settings",
"rust-log-forwarder",
"viaduct",
@ -2387,7 +2385,6 @@ dependencies = [
"nimbus-sdk",
"places",
"push",
"rc_log_ffi",
"remote_settings",
"rust-log-forwarder",
"suggest",
@ -3484,16 +3481,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "rc_log_ffi"
version = "0.1.0"
dependencies = [
"cfg-if 0.1.10",
"ffi-support",
"lazy_static",
"log",
]
[[package]]
name = "redox_syscall"
version = "0.2.13"

Просмотреть файл

@ -12,7 +12,6 @@ members = [
"components/places",
"components/push",
"components/remote_settings",
"components/rc_log",
"components/suggest",
"components/support/error",
"components/support/error/tests",
@ -91,7 +90,6 @@ default-members = [
"components/places",
"components/push",
"components/remote_settings",
"components/rc_log",
"components/support/error",
"components/support/error/macros",
"components/support/error/tests",

Просмотреть файл

@ -57,14 +57,11 @@ The application-services library primary consumers are Fenix (Firefox on Android
# Rust Components
[./components/](components) contains the source for each component, and its
FFI bindings.
> 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.
[./components/](components) contains the source for each component. Note that most components have their FFI generated
by the [uniffi](https://github.com/mozilla/uniffi-rs/) library.
* See [./components/places/](components/places) for an example, where you can
find:
* 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
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
* [push](components/push) - for applications to receive real-time updates via
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/rc_crypto](components/rc_crypto) - handles cryptographic needs backed by Mozilla's
[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
* [webext-storage](components/webext-storage) - powers an implementation of the
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()

21
components/rc_log/android/proguard-rules.pro поставляемый
Просмотреть файл

@ -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/" }
places = { path = "../../components/places" }
push = { path = "../../components/push" }
rc_log_ffi = { path = "../../components/rc_log" }
remote_settings = { path = "../../components/remote_settings" }
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
viaduct = { path = "../../components/viaduct" }

Просмотреть файл

@ -16,9 +16,6 @@ pub use logins;
pub use nimbus;
pub use places;
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 rust_log_forwarder;
pub use suggest;

Просмотреть файл

@ -9,7 +9,6 @@ license = "MPL-2.0"
crate-type = ["staticlib"]
[dependencies]
rc_log_ffi = { path = "../../components/rc_log" }
rust-log-forwarder = { path = "../../components/support/rust-log-forwarder" }
viaduct = { path = "../../components/viaduct" }
viaduct-reqwest = { path = "../../components/support/viaduct-reqwest" }

Просмотреть файл

@ -5,7 +5,6 @@
// This is the "umbrella header" for our combined Rust code library.
// It needs to import all of the individual headers.
#import "RustLogFFI.h"
#import "RustViaductFFI.h"
#import "autofillFFI.h"
#import "crashtestFFI.h"

Просмотреть файл

@ -14,10 +14,8 @@
1B3BC94327B1D63B00229CF6 /* PlacesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC54027AE065300DAFEF2 /* PlacesTests.swift */; };
1B3BC94527B1D6A500229CF6 /* SyncUnlockInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC5B027AE11AA00DAFEF2 /* SyncUnlockInfo.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 */; };
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 */; };
1B3BC95027B1D92A00229CF6 /* NimbusFeatureVariablesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BBAC54427AE065300DAFEF2 /* NimbusFeatureVariablesTests.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; };
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; };
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; };
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; };
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; };
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; };
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; };
@ -369,7 +364,6 @@
1BBAC5AF27AE112D00DAFEF2 /* Sync15 */,
1BBAC58027AE099B00DAFEF2 /* Logins */,
1BBAC55B27AE082400DAFEF2 /* FxAClient */,
1BBAC55227AE073A00DAFEF2 /* RustLog */,
);
path = MozillaTestServices;
sourceTree = "<group>";
@ -383,7 +377,6 @@
1BBAC53C27AE065300DAFEF2 /* FxAccountMocks.swift */,
1BBAC54227AE065300DAFEF2 /* NimbusMessagingTests.swift */,
1BBAC53B27AE065300DAFEF2 /* LoginsTests.swift */,
1BBAC53F27AE065300DAFEF2 /* LogTest.swift */,
1BBAC54427AE065300DAFEF2 /* NimbusFeatureVariablesTests.swift */,
1BBAC53E27AE065300DAFEF2 /* NimbusTests.swift */,
39083AAA29561E2400FDD302 /* OperationTests.swift */,
@ -396,16 +389,6 @@
path = MozillaTestServicesTests;
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 */ = {
isa = PBXGroup;
children = (
@ -865,7 +848,6 @@
1BF50F1027B1E17B00A9C8A5 /* FxAccountStorage.swift in Sources */,
39EE00FD29F6DB2A001E7758 /* ArgumentProcessor.swift in Sources */,
1BF50F0E27B1E17B00A9C8A5 /* FxAccountLogging.swift in Sources */,
1B3BC94D27B1D7B700229CF6 /* RustLog.swift in Sources */,
1BF50F1627B1E18000A9C8A5 /* KeychainWrapper.swift in Sources */,
1BF50F1227B1E17B00A9C8A5 /* FxAccountManager.swift in Sources */,
39F5D7642956161E004E2384 /* Operation+.swift in Sources */,
@ -898,7 +880,6 @@
39EE00FF29F6DBBA001E7758 /* NimbusArgumentProcessorTests.swift in Sources */,
1BF50F1A27B1E19800A9C8A5 /* FxAccountMocks.swift in Sources */,
1B3BC95127B1D93100229CF6 /* CrashTestTests.swift in Sources */,
1B3BC94727B1D73500229CF6 /* LogTest.swift in Sources */,
F85ED649299C1F49005EEF36 /* RustSyncTelemetryPingTests.swift in Sources */,
3937440A29E43B8800726A72 /* EventStoreTests.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
# firefox-ios and Focus
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"
$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"

Просмотреть файл

@ -9,7 +9,6 @@ license = "MPL-2.0"
crate-type = ["staticlib"]
[dependencies]
rc_log_ffi = { path = "../../../components/rc_log" }
rust-log-forwarder = { path = "../../../components/support/rust-log-forwarder" }
viaduct = { path = "../../../components/viaduct" }
viaduct-reqwest = { path = "../../../components/support/viaduct-reqwest" }

Просмотреть файл

@ -5,7 +5,6 @@
// This is the "umbrella header" for our combined Rust code library.
// It needs to import all of the individual headers.
#import "RustLogFFI.h"
#import "RustViaductFFI.h"
#import "nimbusFFI.h"
#import "errorFFI.h"

Просмотреть файл

@ -7,7 +7,6 @@
pub use error_support;
pub use nimbus;
pub use rc_log_ffi;
pub use remote_settings;
pub use rust_log_forwarder;
pub use viaduct_reqwest;

Просмотреть файл

@ -14,9 +14,6 @@ pub use logins;
pub use nimbus;
pub use places;
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 rust_log_forwarder;
pub use suggest;

Просмотреть файл

@ -44,14 +44,12 @@ SOURCE_TO_COPY = [
"components/places/ios/Places",
"components/sync15/ios/Sync15",
"components/sync_manager/ios/SyncManager",
"components/rc_log/ios/*",
"components/viaduct/ios/*",
]
# List of udl_paths to generate bindings for
FOCUS_SOURCE_TO_COPY = [
"components/nimbus/ios/Nimbus",
"components/rc_log/ios/*",
"components/viaduct/ios/*",
]

Просмотреть файл

@ -25,7 +25,6 @@ existing_tasks:
module-build-places: CjxF6i5XTUuue3N6hur76A
module-build-push: Eb1ryYa0TQas2d6L8shrKg
module-build-rust-log-forwarder: PavZP7KCQUa6eIiH7rcguw
module-build-rustlog: DzsCxDB2RfmjXjSb9sXUcg
module-build-sync15: c-mAP_Z5Tt-jep2NqUVEPQ
module-build-syncmanager: aO_QpDq_RoOcNf56oxDtug
module-build-tabs: d_M2TbtARdeqZdNuLDGMxA

Просмотреть файл

@ -26,7 +26,6 @@ existing_tasks:
module-build-places: CjxF6i5XTUuue3N6hur76A
module-build-push: Eb1ryYa0TQas2d6L8shrKg
module-build-rust-log-forwarder: PavZP7KCQUa6eIiH7rcguw
module-build-rustlog: DzsCxDB2RfmjXjSb9sXUcg
module-build-sync15: c-mAP_Z5Tt-jep2NqUVEPQ
module-build-syncmanager: aO_QpDq_RoOcNf56oxDtug
module-build-tabs: d_M2TbtARdeqZdNuLDGMxA