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:
@@ -59,7 +59,7 @@ enum MXLog {
|
||||
return
|
||||
}
|
||||
|
||||
setupTracing(configuration: .custom(logLevel: logLevel), otlpConfiguration: otlpConfiguration)
|
||||
setupTracing(configuration: .init(logLevel: logLevel), otlpConfiguration: otlpConfiguration)
|
||||
|
||||
if let target {
|
||||
self.target = target
|
||||
|
||||
@@ -27,31 +27,45 @@ struct OTLPConfiguration {
|
||||
// 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
|
||||
struct TracingConfiguration {
|
||||
/// Configure tracing with certain overrides in place
|
||||
/// - Parameter overrides: the desired overrides
|
||||
/// - Returns: a custom tracing configuration
|
||||
static func custom(overrides: [Target: LogLevel]) -> TracingConfiguration {
|
||||
TracingConfiguration(overrides: overrides)
|
||||
}
|
||||
|
||||
/// Sets the same log level for all Targets
|
||||
/// - Parameter logLevel: the desired log level
|
||||
/// - Returns: a custom tracing configuration
|
||||
static func custom(logLevel: LogLevel) -> TracingConfiguration {
|
||||
let overrides = targets.keys.reduce(into: [Target: LogLevel]()) { partialResult, target in
|
||||
// Keep the defaults here
|
||||
if target == .common || target == .hyper {
|
||||
return
|
||||
enum LogLevel: Codable, Hashable {
|
||||
case error, warn, info, debug, trace
|
||||
case custom(String)
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .error:
|
||||
return "Error"
|
||||
case .warn:
|
||||
return "Warning"
|
||||
case .info:
|
||||
return "Info"
|
||||
case .debug:
|
||||
return "Debug"
|
||||
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 {
|
||||
case common = ""
|
||||
|
||||
@@ -77,9 +91,26 @@ struct TracingConfiguration {
|
||||
.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
|
||||
for (target, logLevel) in overrides {
|
||||
newTargets.updateValue(logLevel, forKey: target)
|
||||
@@ -93,7 +124,7 @@ struct TracingConfiguration {
|
||||
return "\(target.rawValue)=\(logLevel.rawValue)"
|
||||
}
|
||||
|
||||
return components.joined(separator: ",")
|
||||
filter = components.joined(separator: ",")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,18 +19,11 @@ import SwiftUI
|
||||
struct DeveloperOptionsScreen: View {
|
||||
@ObservedObject var context: DeveloperOptionsScreenViewModel.Context
|
||||
@State private var showConfetti = false
|
||||
|
||||
|
||||
var body: some View {
|
||||
Form {
|
||||
Section("Logging") {
|
||||
Picker(selection: $context.logLevel) {
|
||||
ForEach(TracingConfiguration.LogLevel.allCases, id: \.self) { logLevel in
|
||||
Text(logLevel.rawValue.capitalized)
|
||||
}
|
||||
} label: {
|
||||
Text("Log level")
|
||||
Text("Requires app reboot")
|
||||
}
|
||||
LogLevelConfigurationView(logLevel: $context.logLevel)
|
||||
|
||||
Toggle(isOn: $context.otlpTracingEnabled) {
|
||||
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
|
||||
|
||||
struct DeveloperOptionsScreen_Previews: PreviewProvider {
|
||||
|
||||
@@ -20,17 +20,13 @@ import XCTest
|
||||
|
||||
class TracingConfigurationTests: XCTestCase {
|
||||
func testConfiguration() {
|
||||
let configuration = TracingConfiguration(overrides: [.common: .trace,
|
||||
.matrix_sdk_base_sliding_sync: .error,
|
||||
.matrix_sdk_http_client: .warn,
|
||||
.matrix_sdk_crypto: .info,
|
||||
.hyper: .debug])
|
||||
let configuration = TracingConfiguration(logLevel: .trace)
|
||||
|
||||
let filterComponents = configuration.filter.components(separatedBy: ",")
|
||||
XCTAssertEqual(filterComponents.first, "trace")
|
||||
XCTAssertTrue(filterComponents.contains("matrix_sdk_base::sliding_sync=error"))
|
||||
XCTAssertTrue(filterComponents.contains("matrix_sdk::http_client=warn"))
|
||||
XCTAssertTrue(filterComponents.contains("matrix_sdk_crypto=info"))
|
||||
XCTAssertTrue(filterComponents.contains("hyper=debug"))
|
||||
XCTAssertEqual(filterComponents.first, "info")
|
||||
XCTAssertTrue(filterComponents.contains("matrix_sdk_base::sliding_sync=trace"))
|
||||
XCTAssertTrue(filterComponents.contains("matrix_sdk::http_client=trace"))
|
||||
XCTAssertTrue(filterComponents.contains("matrix_sdk_crypto=trace"))
|
||||
XCTAssertTrue(filterComponents.contains("hyper=warn"))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user