OSLog(记录器)通过简单的字符串插值给出错误

问题描述 投票:0回答:1

我正在尝试更新我的应用程序以使用 OSLog(记录器)。 我当前使用的系统允许我使用简单的字符串插值,我期望 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'
    }
}

Dong 那种双字符串插值使其工作的感觉真的很痛苦。 这些警告是预料之中的,尽管我希望我可以编写一些扩展来使它们消失,但现在我的重点是错误。

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

ios swift logging oslog
1个回答
0
投票

两个观察:

  1. Logger
    中的
    OSLogMessage
    中的字符串插值需要
    CustomStringConvertible
    一致性。 (见下文。)因此,我们通常只会扩展我们想要记录的任何类型以符合
    CustomStringConvertible
    并完成它。这样您就无需为日志记录目的创建临时字符串。

  2. Person
    示例的问题有点不同:您使用的是非浮点类型的
    OSLogFloatFormatting
    选项(
    precision
    参数)。


关于

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
的示例)说明了这一点。此外,这个
CustomStringConvertible
一致性要求在 2020 年 WWDC 视频探索 Swift 中的日志记录中进行了讨论。但不支持
CustomDebugStringConvertible

现在看来,优雅的解决方案是扩展

OSLogInterpolation
以支持其他类型的插值(例如
CustomDebugStringConvertible
)。但是,尝试过之后,出现了一条编译器错误消息,表明他们已选择明确禁止这样做:

无效的日志消息;不支持扩展 os 模块中定义的类型

话虽如此,您可以编写一个

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)")
    }

    …
}

您可以对

warning
log
等重复此模式。

无论如何,你可以做这样的事情:

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

话虽如此,我承认这不是我愿意采用的模式。有两个问题:

  1. OSLogMessage
    (而不是让这些方法采用
    String
    参数)的整个激励思想是能够在更广泛的
    Logger
    消息中为各个值指定隐私设置。您建议您对此表示同意,但不必要地丢失日志消息的这方面是一种耻辱。

  2. Xcode 15 中我最喜欢的功能之一是能够control单击(或右键单击)Xcode 日志消息并选择“跳转到源代码”。一旦开始使用此功能,它就成为调试过程中非常宝贵的部分。 (例如,“嘿,Xcode 向我显示了一些我没有预料到的随机错误;让我跳到该代码并找出问题所在。”)

    如果您调用

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

© www.soinside.com 2019 - 2024. All rights reserved.