我正在try 更新我的应用程序以使用Oslog(Logger). 我目前使用的系统允许我使用简单的字符串插值,我也期望Oslog也会如此,但我在一个简单的测试中看到了所有类型的错误:

import SwiftUI
import OSLog

extension Logger {
    static let statistics = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "NS")
}

struct MyCustom: CustomStringConvertible {
    let description = "My Custom description"
}

struct MyDebug: CustomDebugStringConvertible {
    let debugDescription = "My Debug description"
}

struct NoneOfTheAbove {
    var defaultValue = false
}

struct Person: Identifiable {
    let id = UUID()
    
    let index: Int
    let name: String
    let age: Int
    
    static let maxNameLength = 15
}

@main
struct OggTstApp: App {
    let myCustom = MyCustom()
    let myDebug = MyDebug()
    let noneOfTheAbove = NoneOfTheAbove()
    var optionalCustom: MyCustom?
    var optionalDebug: MyDebug? = MyDebug()
    
    init() {
        print("init")
        Logger.statistics.debug("debug init")
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear {
                   testLogs()
                }
        }
    }
    
    func testLogs() {
        print("structs")
        Logger.statistics.error("\(myCustom)")
        
//        Logger.statistics.error("This is a test: \(myDebug)") // Type of expression is ambiguous without a type annotation
        let string = "\(myDebug)"
        Logger.statistics.error("\(string)")
        
//        Logger.statistics.error(noneOfTheAbove) // Cannot convert value of type 'NoneOfTheAbove' to expected argument type 'OSLogMessage'
//        Logger.statistics.error("\(noneOfTheAbove)") // Type of expression is ambiguous without a type annotation
        let noneOTA = "\(noneOfTheAbove)"
//        Logger.statistics.error(noneOTA) // Cannot convert value of type 'String' to expected argument type 'OSLogMessage'
        Logger.statistics.error("\(noneOTA)")
        
//        Logger.statistics.warning(optionalCustom) // Cannot convert value of type 'MyCustom?' to expected argument type 'OSLogMessage'
        let optCust = "\(optionalCustom)" // Warning
        Logger.statistics.warning("\(optCust)")
        
//        Logger.statistics.log("Optional not nil: \(optionalDebug)") // No exact matches in call to instance method 'appendInterpolation'
        let optNotNil = "\(optionalDebug)" // Warning
        Logger.statistics.log("\(optNotNil)")
        
        let aPerson = Person(index: 2, name: "George", age: 21)
        let people = [aPerson]
        
        people.forEach {
            testLog($0)
        }
    }
    
    func testLog(_ person: Person) {
        Logger.statistics.debug("\(person.index) \(person.name) \(person.id) \(person.age)")
//        Logger.statistics.debug("\(person.index) \(person.name, align: .left(columns: Person.maxNameLength)) \(person.id) \(person.age, format: .fixed(precision: 2))") // No exact matches in call to instance method 'appendInterpolation'
    }
}

董说双弦插值才能发挥作用,感觉真的很痛苦. 这些警告是意料之中的,尽管我希望我能写一些扩展来让它们消失,但目前我的重点是错误.

我做错了什么吗?这有什么技巧吗? 顺便说一句,我只在控制台中使用这些日志(log),我不太关心能否检索它们(我可以将内插字符串值保持为私有,以防万一).

推荐答案

两点观察:

  1. LoggerOSLogMessage中的字符串插值需要CustomStringConvertible符合性.(见下文.)因此,我们通常只需扩展我们想要记录的任何类型,使其符合CustomStringConvertible并完成.这消除了您为记录目的创建临时字符串的需要.

  2. Person示例的问题略有不同:您正在使用具有非浮动类型的OSLogFloatFormatting选项(precision参数).鉴于您正在处理的是一个integer类型,因此指定小数位数的 idea 没有意义.


关于CustomStringConvertible一致性要求,请参阅OSLogInterpolation内插的定义:

extension OSLogInterpolation {

    /// Defines interpolation for values conforming to CustomStringConvertible. The values
    /// are displayed using the description methods on them.
    ///
    /// Do not call this function directly. It will be called automatically when interpolating
    /// a value conforming to CustomStringConvertible in the string interpolations passed
    /// to the log APIs.
    ///
    /// - Parameters:
    ///   - value: The interpolated expression conforming to CustomStringConvertible.
    ///   - align: Left or right alignment with the minimum number of columns as
    ///     defined by the type `OSLogStringAlignment`.
    ///   - privacy: A privacy qualifier which is either private or public.
    ///     It is auto-inferred by default.

    public mutating func appendInterpolation<T>(
        _ value: @autoclosure @escaping () -> T, 
        align: OSLogStringAlignment = .none, 
        privacy: OSLogPrivacy = .auto
    ) where T : CustomStringConvertible

     …
}

您的MyCustom示例(这是唯一符合CustomStringConvertible的示例)的成功说明了这一点.此外,2020年WWDC视频Exploring logging in Swift中讨论了这一CustomStringConvertible一致性要求.但它不支持CustomDebugStringConvertible.

现在,看来优雅的解决方案是扩展OSLogInterpolation以支持其他类型的插值(例如CustomDebugStringConvertible).但是,try 过之后,结果是一个编译器错误,这表明他们已经 Select 明确禁止:

/…/MyApp/ContentView.swift:70:53: error: invalid log message; extending types defined in the os module is not supported
        Logger.statistics.error("MyDebug: \(myDebug)") // Type of expression is ambiguous without a type annotation
                                                    ^

话虽如此,您可以编写一个Logger扩展来接受其他值/字符串,显式地将privacy设置为.private:

import os.log

extension Logger {
    public func error<T: CustomStringConvertible>(value: T) {
        error("\(value, privacy: .private)")
    }

    public func error<T: CustomDebugStringConvertible>(value: T) {
        error("\(value.debugDescription, privacy: .private)")
    }

    public func error(string: String) {
        error("\(string, privacy: .private)")
    }

    …
}

您可以对warninglog等重复此模式.

无论如何,假设您有一个Logger实例,即logger,您可以执行以下操作:

logger.error(value: myCustom)
logger.error(value: myDebug)
logger.error(string: "My debug: \(myDebug)")
logger.error(string: "\(noneOfTheAbove)")
logger.error(value: optionalCustom)
logger.error(value: optionalDebug)

话虽如此,我承认这不是我倾向于采用的模式.有两个问题:

  1. OSLogMessage的整个激励 idea (而不是让这些方法采用String参数)是能够在更广泛的Logger消息中指定个人值的隐私设置.您建议您对此没有意见,但不必要地失go 记录消息的这方面是一种耻辱.

  2. Xcode 15中我最喜欢的功能之一是能够控制-单击(或右键单击)Xcode日志(log)消息并 Select "跳转到源".一旦您开始使用此功能,它就会成为调试过程中无价的一部分.(例如,"嘿,我在Xcode控制台中看到一个错误;让我跳到有问题的代码.")

    如果您调用Logger的内置方法,它将带您到达代码中的适当点.但是,如果您调用上述扩展方法之一,Xcode将带您到Logger扩展,而不是实际报告错误的地方.

Ios相关问答推荐

通过在SWIFT中不起作用的泛型传递协议一致性类型

如何使用超薄material ,但没有浅色/填充?

ios PWA从外部链接返回后降低了innerheight

GraphQL iOS Apollo客户端自动持久化查询导致崩溃

如何防止UITest套件与Fastlane一起执行?

objc[25442]:Swift类的扩展和类别不允许有+load方法

使用 AVCaptureDeviceTypeBuiltInTripleCamera 时 Select 合适的相机进行条码扫描

SwiftUI:同时拖动父视图和子视图,但手指分开

TextField 重复输入 SwiftUI

Apple Push Service 证书不受信任

将flutter android项目导入iOS

如何在 Swift 中旋转 UIButton 和 UILabel 的文本?

在 Swift 中执行 while 循环

Xcode 故事板:内部错误.请提交错误

通过 segue 传递数据

在滚动视图中使用动态大小的控制器调整容器视图的大小

如何像在 Facebook 应用程序中一样以编程方式打开设置?

iOS UITextView 或 UILabel 带有可点击的动作链接

UITableView:从空白部分隐藏标题

我可以在 UIScrollView 中使用 UIRefreshControl 吗?