Allow setting custom log levels (#1592)

* Allow using custom tracing configuration from the developer options screen. Clean up RustTracing
* Move log level configurations to a separate view
* Disable autocorrection
* Fix unit tests
* Use TracingConfiguration.info as the default text value, switch to a TextEditor
This commit is contained in:
Stefan Ceriu
2023-08-30 15:35:49 +03:00
committed by GitHub
parent 3c3e31f9e4
commit 3258d3a85d
4 changed files with 109 additions and 44 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ enum MXLog {
return return
} }
setupTracing(configuration: .custom(logLevel: logLevel), otlpConfiguration: otlpConfiguration) setupTracing(configuration: .init(logLevel: logLevel), otlpConfiguration: otlpConfiguration)
if let target { if let target {
self.target = target self.target = target
@@ -27,31 +27,45 @@ struct OTLPConfiguration {
// We can filter by level, crate and even file. See more details here: // We can filter by level, crate and even file. See more details here:
// https://docs.rs/tracing-subscriber/0.2.7/tracing_subscriber/filter/struct.EnvFilter.html#examples // https://docs.rs/tracing-subscriber/0.2.7/tracing_subscriber/filter/struct.EnvFilter.html#examples
struct TracingConfiguration { struct TracingConfiguration {
/// Configure tracing with certain overrides in place enum LogLevel: Codable, Hashable {
/// - Parameter overrides: the desired overrides case error, warn, info, debug, trace
/// - Returns: a custom tracing configuration case custom(String)
static func custom(overrides: [Target: LogLevel]) -> TracingConfiguration {
TracingConfiguration(overrides: overrides) var title: String {
} switch self {
case .error:
/// Sets the same log level for all Targets return "Error"
/// - Parameter logLevel: the desired log level case .warn:
/// - Returns: a custom tracing configuration return "Warning"
static func custom(logLevel: LogLevel) -> TracingConfiguration { case .info:
let overrides = targets.keys.reduce(into: [Target: LogLevel]()) { partialResult, target in return "Info"
// Keep the defaults here case .debug:
if target == .common || target == .hyper { return "Debug"
return case .trace:
return "Trace"
case .custom:
return "Custom"
}
}
fileprivate var rawValue: String {
switch self {
case .error:
return "error"
case .warn:
return "warn"
case .info:
return "info"
case .debug:
return "debug"
case .trace:
return "trace"
case .custom(let filter):
return filter
} }
partialResult[target] = logLevel
} }
return TracingConfiguration(overrides: overrides)
} }
enum LogLevel: String, Codable, CaseIterable { case error, warn, info, debug, trace }
enum Target: String { enum Target: String {
case common = "" case common = ""
@@ -77,9 +91,26 @@ struct TracingConfiguration {
.matrix_sdk_ui_timeline: .info .matrix_sdk_ui_timeline: .info
] ]
var overrides = [Target: LogLevel]() let filter: String
var filter: String { /// Sets the same log level for all Targets
/// - Parameter logLevel: the desired log level
/// - Returns: a custom tracing configuration
init(logLevel: LogLevel) {
if case let .custom(filter) = logLevel {
self.filter = filter
return
}
let overrides = Self.targets.keys.reduce(into: [Target: LogLevel]()) { partialResult, target in
// Keep the defaults here
if target == .common || target == .hyper {
return
}
partialResult[target] = logLevel
}
var newTargets = Self.targets var newTargets = Self.targets
for (target, logLevel) in overrides { for (target, logLevel) in overrides {
newTargets.updateValue(logLevel, forKey: target) newTargets.updateValue(logLevel, forKey: target)
@@ -93,7 +124,7 @@ struct TracingConfiguration {
return "\(target.rawValue)=\(logLevel.rawValue)" return "\(target.rawValue)=\(logLevel.rawValue)"
} }
return components.joined(separator: ",") filter = components.joined(separator: ",")
} }
} }
@@ -19,18 +19,11 @@ import SwiftUI
struct DeveloperOptionsScreen: View { struct DeveloperOptionsScreen: View {
@ObservedObject var context: DeveloperOptionsScreenViewModel.Context @ObservedObject var context: DeveloperOptionsScreenViewModel.Context
@State private var showConfetti = false @State private var showConfetti = false
var body: some View { var body: some View {
Form { Form {
Section("Logging") { Section("Logging") {
Picker(selection: $context.logLevel) { LogLevelConfigurationView(logLevel: $context.logLevel)
ForEach(TracingConfiguration.LogLevel.allCases, id: \.self) { logLevel in
Text(logLevel.rawValue.capitalized)
}
} label: {
Text("Log level")
Text("Requires app reboot")
}
Toggle(isOn: $context.otlpTracingEnabled) { Toggle(isOn: $context.otlpTracingEnabled) {
Text("OTLP tracing") Text("OTLP tracing")
@@ -130,6 +123,51 @@ struct DeveloperOptionsScreen: View {
} }
} }
private struct LogLevelConfigurationView: View {
@Binding var logLevel: TracingConfiguration.LogLevel
@State private var customTracingConfiguration: String
init(logLevel: Binding<TracingConfiguration.LogLevel>) {
_logLevel = logLevel
if case .custom(let configuration) = logLevel.wrappedValue {
customTracingConfiguration = configuration
} else {
customTracingConfiguration = TracingConfiguration(logLevel: .info).filter
}
}
var body: some View {
Picker(selection: $logLevel) {
ForEach(logLevels, id: \.self) { logLevel in
Text(logLevel.title)
}
} label: {
Text("Log level")
Text("Requires app reboot")
}
if case .custom = logLevel {
TextEditor(text: $customTracingConfiguration)
.textInputAutocapitalization(.never)
.autocorrectionDisabled()
.onChange(of: customTracingConfiguration) { newValue in
logLevel = .custom(newValue)
}
}
}
/// Allows the picker to work with associated values
private var logLevels: [TracingConfiguration.LogLevel] {
if case let .custom(filter) = logLevel {
return [.error, .warn, .info, .debug, .trace, .custom(filter)]
} else {
return [.error, .warn, .info, .debug, .trace, .custom("")]
}
}
}
// MARK: - Previews // MARK: - Previews
struct DeveloperOptionsScreen_Previews: PreviewProvider { struct DeveloperOptionsScreen_Previews: PreviewProvider {
@@ -20,17 +20,13 @@ import XCTest
class TracingConfigurationTests: XCTestCase { class TracingConfigurationTests: XCTestCase {
func testConfiguration() { func testConfiguration() {
let configuration = TracingConfiguration(overrides: [.common: .trace, let configuration = TracingConfiguration(logLevel: .trace)
.matrix_sdk_base_sliding_sync: .error,
.matrix_sdk_http_client: .warn,
.matrix_sdk_crypto: .info,
.hyper: .debug])
let filterComponents = configuration.filter.components(separatedBy: ",") let filterComponents = configuration.filter.components(separatedBy: ",")
XCTAssertEqual(filterComponents.first, "trace") XCTAssertEqual(filterComponents.first, "info")
XCTAssertTrue(filterComponents.contains("matrix_sdk_base::sliding_sync=error")) XCTAssertTrue(filterComponents.contains("matrix_sdk_base::sliding_sync=trace"))
XCTAssertTrue(filterComponents.contains("matrix_sdk::http_client=warn")) XCTAssertTrue(filterComponents.contains("matrix_sdk::http_client=trace"))
XCTAssertTrue(filterComponents.contains("matrix_sdk_crypto=info")) XCTAssertTrue(filterComponents.contains("matrix_sdk_crypto=trace"))
XCTAssertTrue(filterComponents.contains("hyper=debug")) XCTAssertTrue(filterComponents.contains("hyper=warn"))
} }
} }