以下是我想要做的一个例子:
func application(application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError)
{
let nm = NetworkModel()
nm.sendlog("file name :AppDelegate , line number : 288", info: " Failed to register: \(error)")
}
当前情况我完成了硬编码值line number
和file name
。但是可以以编程方式选择line number
和file name
。
答案 0 :(得分:87)
Literal Type Value
#file String The name of the file in which it appears.
#line Int The line number on which it appears.
#column Int The column number in which it begins.
#function String The name of the declaration in which it appears.
#dsohandle String The dso handle.
示例强>
print("Function: \(#function), line: \(#line)")
使用参数中的默认值,您还可以创建一个函数
public func track(_ message: String, file: String = #file, function: String = #function, line: Int = #line ) {
print("\(message) called from \(function) \(file):\(line)")
}
可以像这样使用
track("enters app")
在Swift 2.1中
Literal Type Value
__FILE__ String The name of the file in which it appears.
__LINE__ Int The line number on which it appears.
__COLUMN__ Int The column number in which it begins.
__FUNCTION__ String The name of the declaration in which it appears.
有关详细信息,请参阅the documentation
答案 1 :(得分:8)
您可以使用#function
,#file
,#line
以下是swift中的log方法的实现:https://github.com/InderKumarRathore/SwiftLog
以下是摘录
public func debugLog(object: Any, functionName: String = #function, fileName: String = #file, lineNumber: Int = #line) {
#if DEBUG
let className = (fileName as NSString).lastPathComponent
print("<\(className)> \(functionName) [#\(lineNumber)]| \(object)\n")
#endif
}
答案 2 :(得分:2)
static func DLog(message: String, file: String = #file, function: String = #function, line: Int = #line, column: Int = #column) {
print("\(file) : \(function) : \(line) : \(column) - \(message)")
}
答案 3 :(得分:1)
import os.log
@available(OSX 11.0, iOS 14.0, *)
extension Logger {
private static var subsystem = Bundle.main.bundleIdentifier!
/// Logs the payment flows like Apple Pay.
@available(OSX 11.0, iOS 14.0, *)
static let payments = Logger(subsystem: subsystem, category: "payments")
}
static func DLog(message: StaticString, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, column: UInt = #column, category: String, type: OSLogType = .info, bundle: Bundle = .main) {
// This method is only for iOS 14+
if #available(OSX 11.0, iOS 14.0, *) {
Logger.payments.debug("\(file) : \(function) : \(line) : \(column) - \(message, privacy: .private)")
// This makes the message unreadable without a debugger attached.
} else {
// Fallback on earlier versions
let customLog = OSLog(subsystem: bundle.bundleIdentifier!,
category: category)
// IMPORTANT: I have assumed here that you only print out non-sensitive data! Using %{private}@ or %{public}@ in an interpolated string is not portable to `Logger`!
os_log(message, log: customLog, type: type)
// Unfortunately this legacy API doesn't support non-StaticString logs. :(
}
}
请注意,您可能需要重新编写此代码,以提高私有/公共访问级别的灵活性 - os_log
和 Logger
处理隐私级别的方式不同。这只是为了说明如果您不在 os_log
中添加 message
隐私级别,如何使用 API。
Apple 推荐使用 OS Logging 这就是我使用这种方法而不是 print
语句的原因。我添加此答案是因为 iOS 14 中的新 Logger
API 也支持字符串插值。
我认为这里的答案解决了 Logger
API)。 Apple 推荐使用 OS Logging 这就是为什么我使用这种方法而不是 print
语句(尽管这些仍然有效)。
这些是次要改进,但我认为它们可以提供帮助,因为即使是微小的改进也会加起来。事实上,Swift 标准库会在任何地方使用这些优化)!这些优化是 Swift 令人难以置信的内存效率的促成因素尽管是一种高级语言(并且是伟大的 API 设计的一部分!)。 (-:
由于此功能可能非常适合作为通用 (OS) 日志记录服务的一部分,因此我还包括了在应用程序中进行日志记录时的注意事项。我认为这些可以在出于调试目的进行日志记录时为您提供帮助(我回答了这个问题,因为它可能用于调试日志记录!)。
UInt
,如果我知道 Int
会积极,并且任何边缘情况崩溃都不太可能发生。请注意,出于这个原因,我不在与使用 UInt
类型的 Foundation
类交互时使用 Int
。这在运行时分配更少的内存,我发现更具体也有助于我更好地理解代码。StaticString
。从 docs 开始,StaticString
仅提供对 String
内容的低级别访问,并且不可变。因此,仅在适当的地方使用它。即使是串联的字符串文字也不能用于初始化 StaticString
。因为它的功能比 String
更受限制,这意味着它轻量级 - 只需要一个地址指针和长度。使用 StaticString
还为操作系统级内存管理提供了小幅性能提升,因为它既不分配也不释放(不需要引用计数强>).Logger
API 进行日志记录比打印语句更好:性能、隐私和统一管理。它可以更好地调试已发布产品中的问题(操作系统日志提供更多上下文)。我建议在可以使用的地方使用 StaticString
,例如函数名和文件名,以及 UInt
用于行号和列号。现在只能通过 Logger
iOS 14+ API 实现。
我认为日志记录是一种平衡行为。如果您有权访问这些日志,则在查看崩溃报告时,大量日志记录对于调试问题非常有帮助。但是,您需要平衡隐私、编译二进制文件的大小和压倒日志系统。
1.隐私: 使用 "\(message, privacy: .private)"
和 "\(message, privacy: .public)"
和 Logger
遮盖敏感信息,并且在使用 NSLog
时不要打印敏感信息或print
语句。当用于调试目的时,我建议用操作系统日志替换 print
语句(用于生产)。或者在日志服务中使用预处理器指令 (#if DEBUG
) 来 print
仅在 Debug
方案上..
2.编译后的二进制文件的大小: 日志语句本质上是更多的代码行。添加的代码越多,二进制文件就越大。这通常不是问题,只要您不在阳光下记录所有内容,因为该指标只有大幅增加会影响二进制大小。
3.压倒日志系统:如果您记录过多,设备可能不得不丢弃一些日志以继续运行(有限的 RAM 或由于操作系统多任务而在运行时交换空间 - 对于旧的更糟设备)。 Apple 在处理大量用户日志记录方面通常是可靠的,但在实践中很难做到这一点。不过,如果您确实遇到了这种情况,一些更有用的日志事件可能会被推出窗口。
正确处理最重要的事情是日志级别。默认情况下,.info
及以下的 iOS 日志条目在日志生成时将被抑制,因此唯一真正的负面影响是二进制大小。如果您在 .log
(.default
for os_log) 或更高位置登录,您需要注意不要记录太多。 Apple 的一般建议是,您查看这些更高级别的日志条目中的每一个,以确保它们包含在调试时有用的信息。
最后,确保设置子系统和类别。统一日志系统处理大量日志条目 - 带有类别的子系统使您可以更轻松地关注特定问题。
这是一个非常固执己见的话题,所以我将专注于我在决定时会考虑的问题。 YMMV:可能有更好的选择,如果有,请告诉我。
1.按功能(特定于产品):例如,如果您是购物应用,则可以按 subsystem
、payment
或其他应用流程组织 login
。如果您还没有使用模块化代码库来组织功能(或共享代码的框架),那么我可以推荐 this tutorial series。
2.技术方面:我的意思是代码域,是否用于推送通知、深层链接、用户默认值(或持久性)逻辑。这可能对 category
参数很有帮助。
3.按目标:如果可以,我还喜欢使用当前 bundleIdentifier
的 Bundle
来表示 subsystem
。如果您有扩展目标(推送通知)、多个目标或不同的平台(如 WatchOS、SiriKit 扩展等),这更有意义。您可以使用以下代码获取当前包:
let myBundle = Bundle(for: MyClass.self)
有关详细演练 (Access Device Console Logs
),请参阅 here。主要想法是将设备连接到您的 Mac(或将 Mac 本身用于 Mac 应用程序;)),打开 Console.app
,运行应用程序并尝试重现崩溃。如果您一开始就做到了这一点,那么您可以将崩溃时间与日志相关联以获取更多上下文。
如果您是 Apple 的早期采用者,那么您就会知道某些 iOS Beta 版本有多么糟糕;)。向 Apple 发布错误报告时,操作系统日志会包含在 sysdiagnose
文件中,因此您可以获得更快的周转时间,因为您的错误报告中的噪音较少。
取决于您是否负担得起以及您是否真的需要它。我更喜欢最小化我在代码中导入的第三方依赖项的数量(如果我可以控制的话),并且本机 (Apple) Logging API 对大多数情况都运行良好我的用例。这些服务按月收取额外费用,但优点是用户无法查看日志(即使他想查看),只需将他的设备连接到他的 Mac 上的 Console.app
。请注意,如果您应用.private
日志级别,这不是问题,因此我通常不使用第三方服务 (YMMV)。使用网络日志记录的一个潜在原因是更防止逆向工程,只要您信任第三方供应商防止数据泄露(但它也不那么环保 ;)).
答案 4 :(得分:0)
swift 3 和 swift 4 :
func printLog(_ message: String, file: String = #file, function: String = #function, line: Int = #line) {
#if DEVELOPMENT
let className = file.components(separatedBy: "/").last
print(" ❌ Error ----> File: \(className ?? ""), Function: \(function), Line: \(line), Message: \(message)")
#endif
}
// "❌ Error ----> File: classNameViewController.swift, function: functionName(), Line: 123, Message: messageError"