如何使用 Crashlytics 登录 Swift?
How to use Crashlytics logging in Swift?
This 文章描述了如何使用 Crashlytics 登录 objective-c。但是,在完成将 Crashlytics 和 Fabric 正确引用到我的项目中的安装步骤后,我似乎无法访问该方法。
查看 Crashlytics.h 文件,我可以看到它是使用编译器标志定义的:
#ifdef DEBUG
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
根据编译器标志,此块似乎包装了 CLSNLog
和 CLSLog
函数。
所以,我想直接找到源代码,我尝试直接从 swift 文件中引用 CLSLog。仍然没有运气:
我的桥接-Header.h:
#import <Crashlytics/Crashlytics.h>
Log.swift:
import Foundation
import Fabric
import Crashlytics
func Log(message: String) {
NSLog("%@", message)
CLS_LOG("%@", message)
CLSLog("%@", message)
}
Log 函数中的最后两行抛出错误,Use of unresolved identifier
。 Crashlytics 崩溃报告工作得很好,除了日志记录功能。根据 this 文章,已实现对 Swift 的日志记录支持。
就版本而言,我 运行 是 Fabric/Crashlytics 的最新版本(12 月发布,post)。
(有趣的是,我可以see/use CLSLogv()
...)
有人知道将 CLS_LOG
合并到 Swift 项目中的正确方法吗?
来自 Crashlytics 的 Mike。
要在 Swift 中使用自定义日志记录,只需使用 CLSLogv 或 CLSNSLogv。您需要创建一个数组,然后对该数组调用 getVaList 函数。
这是一个片段:
CLSLogv("Log something %d %d %@", getVaList([1, 2, "three"]))
对于 CLSNSLogv:
CLSNSLogv("hello %@", getVaList(["goodbye"]))
你必须像这样创建一个中间桥:
CrashlyticsBridge.h:
#import <Foundation/Foundation.h>
@interface CrashlyticsBridge : NSObject
+ (void)log:(NSString *)message;
@end
CrashlyticsBridge.m
#import "CrashlyticsBridge.h"
#import <Crashlytics/Crashlytics.h>
@implementation CrashlyticsBridge
+ (void)log:(NSString *)message {
CLS_LOG(@"%@", message);
}
@end
我的桥接-Header.h:
#import "CrashlyticsBridge.h"
然后,您只需将其添加到您的 Log
函数中即可:
func Log(message: String) {
CrashlyticsBridge.log(message)
}
这将在您调试时为您提供 Crashlytics 日志记录和 NSLogging。
我需要类似于 Swift 中的 CLS_LOG()
的东西,它打印出关于呼叫位置的上下文信息。通常,如果没有预处理器指令,这是不可能的,但我在 Swift 中找到了如何非常接近地复制此行为:
https://developer.apple.com/swift/blog/?id=15
如果您将标识符设置为参数列表中的默认值,我们需要的标识符 (#file, #function, #line
) 会显示有关调用方的信息。
注意: 如果您记录的错误中可能包含 %
符号,例如网络查询字符串,这可能会崩溃。您需要先加入字符串(例如 let string = "\(filename).\(function) line \(line) $ \(message)"
)
Swift 3 版本(注意:这是一个全局函数,所以它应该放在任何结构或 class 定义之外):
/// Usage:
///
/// CLS.log("message!")
/// CLS.log("message with parameter 1: %@ and 2: %@", ["First", "Second"])
///
func CLS_LOG_SWIFT(format: String = "", _ args: [CVarArg] = [], file: String = #file, function: String = #function, line: Int = #line)
{
let filename = URL(string: file)?.lastPathComponent.components(separatedBy: ".").first
#if DEBUG
CLSNSLogv("\(filename).\(function) line \(line) $ \(format)", getVaList(args))
#else
CLSLogv("\(filename).\(function) line \(line) $ \(format)", getVaList(args))
#endif
}
Swift 2 版本:
// CLS_LOG_SWIFT()
// CLS_LOG_SWIFT("message!")
// CLS_LOG_SWIFT("message with parameter 1: %@ and 2: %@", ["First", "Second"])
func CLS_LOG_SWIFT(format: String = "",
_ args:[CVarArgType] = [],
file: String = __FILE__,
function: String = __FUNCTION__,
line: Int = __LINE__)
{
let filename = NSURL(string:file)?.lastPathComponent?.componentsSeparatedByString(".").first
#if DEBUG
CLSNSLogv("\(filename).\(function) line \(line) $ \(format)", getVaList(args))
#else
CLSLogv("\(filename).\(function) line \(line) $ \(format)", getVaList(args))
#endif
}
// CLS_LOG() output: -[ClassName methodName:] line 10 $
// CLS_LOG_SWIFT() output: ClassName.methodName line 10 $
这是一个包含更多信息的要点以及我将此代码放入的实际文件:https://gist.github.com/DimaVartanian/a8aa73ba814a61f749c0
如您所见,它与原始宏非常接近,唯一的区别在于您看不到您是在调用 class 方法还是实例方法,并且您需要包含您的包含在数组中的格式参数列表。这两个都是限制,我认为现在没有办法解决,但非常小。您还需要确保在 Swift 编译器标志中定义了 DEBUG。它不会自动继承您的常规标志。
这是我根据 Dima 的回答改编的版本。我不需要参数,因为您可以在传递的 Swift 字符串中进行所有格式设置。
func DebugLog(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
let output: String
if let filename = URL(fileURLWithPath: file.description).lastPathComponent.components(separatedBy: ".").first {
output = "\(filename).\(function) line \(line) $ \(message)"
} else {
output = "\(file).\(function) line \(line) $ \(message)"
}
#if targetEnvironment(simulator)
NSLogv("%@", getVaList([output]))
#elseif DEBUG
CLSNSLogv("%@", getVaList([output]))
#else
CLSLogv("%@", getVaList([output]))
#endif
}
你会像这样使用它:
DebugLog("this is a log message")
DebugLog("this is a log message \(param1) \(param2)")
编辑:更新为Swift 3.1
Swift 3兼容
您需要设置编译器标志才能使用 Swift 预处理器 - 转到 Build Settings
的 Swift Compiler - Custom Flags
部分设置 -D DEBUG
标志
func dLog(message: Any, filename: String = #file, function: String = #function, line: Int = #line) {
#if DEBUG
print("[\(filename.lastPathComponent):\(line)] \(function) - \(message)")
#else
CLSLogv("[\(filename.lastPathComponent):\(line)] \(function) - \(message)", getVaList([""]))
#endif
}
dLog(object)
任何想使用 Crashlytics 记录错误的人都可以使用下面的代码,它对我来说工作正常:)
Crashlytics.sharedInstance().recordError(error)
error 是 NSERROR 对象,它保存在某些操作中产生的错误
Swift 3 Crashlytics 中日志消息的兼容版本
func CLS_LOG_SWIFT(_ format: String = "", _ args: [CVarArg] = [], file: String = #file, function: String = #function, line: Int = #line) {
let formatString: String!
if let filename = file.components(separatedBy: "/").last?.components(separatedBy: ".").first {
formatString = "\(filename).\(function) line \(line) $ \(format)"
}else{
formatString = "\(file).\(function) line \(line) $ \(format)"
}
#if DEBUG
CLSNSLogv(formatString, getVaList(args))
#else
CLSLogv(formatString, getVaList(args))
#endif
}
这样怎么样?
import Foundation
import Crashlytics
func CLSLog(_ format: String = "", _ args: CVarArg..., file: String = #file, function: String = #function, line: Int = #line) {
let formatString: String!
if let filename = file.components(separatedBy: "/").last?.components(separatedBy: ".").first {
formatString = "\(filename).\(function) line \(line) $ \(format)"
} else {
formatString = "\(file).\(function) line \(line) $ \(format)"
}
#if DEBUG
CLSNSLogv(formatString, getVaList(args))
#else
CLSLogv(formatString, getVaList(args))
#endif
}
那么不需要数组,只需列出可变参数
CLSLog("message")
CLSLog("message %@ %@", "one", "two")
This 文章描述了如何使用 Crashlytics 登录 objective-c。但是,在完成将 Crashlytics 和 Fabric 正确引用到我的项目中的安装步骤后,我似乎无法访问该方法。
查看 Crashlytics.h 文件,我可以看到它是使用编译器标志定义的:
#ifdef DEBUG
#define CLS_LOG(__FORMAT__, ...) CLSNSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define CLS_LOG(__FORMAT__, ...) CLSLog((@"%s line %d $ " __FORMAT__), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#endif
根据编译器标志,此块似乎包装了 CLSNLog
和 CLSLog
函数。
所以,我想直接找到源代码,我尝试直接从 swift 文件中引用 CLSLog。仍然没有运气:
我的桥接-Header.h:
#import <Crashlytics/Crashlytics.h>
Log.swift:
import Foundation
import Fabric
import Crashlytics
func Log(message: String) {
NSLog("%@", message)
CLS_LOG("%@", message)
CLSLog("%@", message)
}
Log 函数中的最后两行抛出错误,Use of unresolved identifier
。 Crashlytics 崩溃报告工作得很好,除了日志记录功能。根据 this 文章,已实现对 Swift 的日志记录支持。
就版本而言,我 运行 是 Fabric/Crashlytics 的最新版本(12 月发布,post)。
(有趣的是,我可以see/use CLSLogv()
...)
有人知道将 CLS_LOG
合并到 Swift 项目中的正确方法吗?
来自 Crashlytics 的 Mike。
要在 Swift 中使用自定义日志记录,只需使用 CLSLogv 或 CLSNSLogv。您需要创建一个数组,然后对该数组调用 getVaList 函数。
这是一个片段:
CLSLogv("Log something %d %d %@", getVaList([1, 2, "three"]))
对于 CLSNSLogv:
CLSNSLogv("hello %@", getVaList(["goodbye"]))
你必须像这样创建一个中间桥:
CrashlyticsBridge.h:
#import <Foundation/Foundation.h>
@interface CrashlyticsBridge : NSObject
+ (void)log:(NSString *)message;
@end
CrashlyticsBridge.m
#import "CrashlyticsBridge.h"
#import <Crashlytics/Crashlytics.h>
@implementation CrashlyticsBridge
+ (void)log:(NSString *)message {
CLS_LOG(@"%@", message);
}
@end
我的桥接-Header.h:
#import "CrashlyticsBridge.h"
然后,您只需将其添加到您的 Log
函数中即可:
func Log(message: String) {
CrashlyticsBridge.log(message)
}
这将在您调试时为您提供 Crashlytics 日志记录和 NSLogging。
我需要类似于 Swift 中的 CLS_LOG()
的东西,它打印出关于呼叫位置的上下文信息。通常,如果没有预处理器指令,这是不可能的,但我在 Swift 中找到了如何非常接近地复制此行为:
https://developer.apple.com/swift/blog/?id=15
如果您将标识符设置为参数列表中的默认值,我们需要的标识符 (#file, #function, #line
) 会显示有关调用方的信息。
注意: 如果您记录的错误中可能包含 %
符号,例如网络查询字符串,这可能会崩溃。您需要先加入字符串(例如 let string = "\(filename).\(function) line \(line) $ \(message)"
)
Swift 3 版本(注意:这是一个全局函数,所以它应该放在任何结构或 class 定义之外):
/// Usage:
///
/// CLS.log("message!")
/// CLS.log("message with parameter 1: %@ and 2: %@", ["First", "Second"])
///
func CLS_LOG_SWIFT(format: String = "", _ args: [CVarArg] = [], file: String = #file, function: String = #function, line: Int = #line)
{
let filename = URL(string: file)?.lastPathComponent.components(separatedBy: ".").first
#if DEBUG
CLSNSLogv("\(filename).\(function) line \(line) $ \(format)", getVaList(args))
#else
CLSLogv("\(filename).\(function) line \(line) $ \(format)", getVaList(args))
#endif
}
Swift 2 版本:
// CLS_LOG_SWIFT()
// CLS_LOG_SWIFT("message!")
// CLS_LOG_SWIFT("message with parameter 1: %@ and 2: %@", ["First", "Second"])
func CLS_LOG_SWIFT(format: String = "",
_ args:[CVarArgType] = [],
file: String = __FILE__,
function: String = __FUNCTION__,
line: Int = __LINE__)
{
let filename = NSURL(string:file)?.lastPathComponent?.componentsSeparatedByString(".").first
#if DEBUG
CLSNSLogv("\(filename).\(function) line \(line) $ \(format)", getVaList(args))
#else
CLSLogv("\(filename).\(function) line \(line) $ \(format)", getVaList(args))
#endif
}
// CLS_LOG() output: -[ClassName methodName:] line 10 $
// CLS_LOG_SWIFT() output: ClassName.methodName line 10 $
这是一个包含更多信息的要点以及我将此代码放入的实际文件:https://gist.github.com/DimaVartanian/a8aa73ba814a61f749c0
如您所见,它与原始宏非常接近,唯一的区别在于您看不到您是在调用 class 方法还是实例方法,并且您需要包含您的包含在数组中的格式参数列表。这两个都是限制,我认为现在没有办法解决,但非常小。您还需要确保在 Swift 编译器标志中定义了 DEBUG。它不会自动继承您的常规标志。
这是我根据 Dima 的回答改编的版本。我不需要参数,因为您可以在传递的 Swift 字符串中进行所有格式设置。
func DebugLog(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
let output: String
if let filename = URL(fileURLWithPath: file.description).lastPathComponent.components(separatedBy: ".").first {
output = "\(filename).\(function) line \(line) $ \(message)"
} else {
output = "\(file).\(function) line \(line) $ \(message)"
}
#if targetEnvironment(simulator)
NSLogv("%@", getVaList([output]))
#elseif DEBUG
CLSNSLogv("%@", getVaList([output]))
#else
CLSLogv("%@", getVaList([output]))
#endif
}
你会像这样使用它:
DebugLog("this is a log message")
DebugLog("this is a log message \(param1) \(param2)")
编辑:更新为Swift 3.1
Swift 3兼容
您需要设置编译器标志才能使用 Swift 预处理器 - 转到 Build Settings
的 Swift Compiler - Custom Flags
部分设置 -D DEBUG
标志
func dLog(message: Any, filename: String = #file, function: String = #function, line: Int = #line) {
#if DEBUG
print("[\(filename.lastPathComponent):\(line)] \(function) - \(message)")
#else
CLSLogv("[\(filename.lastPathComponent):\(line)] \(function) - \(message)", getVaList([""]))
#endif
}
dLog(object)
任何想使用 Crashlytics 记录错误的人都可以使用下面的代码,它对我来说工作正常:)
Crashlytics.sharedInstance().recordError(error)
error 是 NSERROR 对象,它保存在某些操作中产生的错误
Swift 3 Crashlytics 中日志消息的兼容版本
func CLS_LOG_SWIFT(_ format: String = "", _ args: [CVarArg] = [], file: String = #file, function: String = #function, line: Int = #line) {
let formatString: String!
if let filename = file.components(separatedBy: "/").last?.components(separatedBy: ".").first {
formatString = "\(filename).\(function) line \(line) $ \(format)"
}else{
formatString = "\(file).\(function) line \(line) $ \(format)"
}
#if DEBUG
CLSNSLogv(formatString, getVaList(args))
#else
CLSLogv(formatString, getVaList(args))
#endif
}
这样怎么样?
import Foundation
import Crashlytics
func CLSLog(_ format: String = "", _ args: CVarArg..., file: String = #file, function: String = #function, line: Int = #line) {
let formatString: String!
if let filename = file.components(separatedBy: "/").last?.components(separatedBy: ".").first {
formatString = "\(filename).\(function) line \(line) $ \(format)"
} else {
formatString = "\(file).\(function) line \(line) $ \(format)"
}
#if DEBUG
CLSNSLogv(formatString, getVaList(args))
#else
CLSLogv(formatString, getVaList(args))
#endif
}
那么不需要数组,只需列出可变参数
CLSLog("message")
CLSLog("message %@ %@", "one", "two")