대부분 프로젝트에서 코드를 작성하고 난 뒤 결과물을 확인하기 위해 print문을 적거나 debugging
을 이용하여 값을 확인하곤 합니다. 특히 print문을 남발하게 되면, debug screen
을 깔끔하게 유지하고 싶은 사람에게는 고역이나 다름이 없습니다. 값을 때때로 확인하고 싶고, debug screen
도 깔끔하게 유지되면서 로깅 처리를 할 수 있는 방법은 무엇이 있을까요?
OSLog는 iOS와 macOS에서 로그를 기록하고 관리하는 데 사용되는 강력한 프레임워크입니다.
OSLog를 사용하면 다음과 같은 이점이 있습니다:
Level | Disk | Notes |
---|---|---|
Debug | X | 개발 중에만 유용한 상세 정보를 캡처하여 보여줍니다. 코드 디버깅에서 사용됩니다. |
Info | O | 문제 해결에 도움이 되는 정보를 캡처하지만 필수적이지는 않습니다. |
Notice (Default) | O | 문제 해결의 필수적인 정보를 캡처합니다.ex) 실패 원인이 될 수 있는 정보 |
Error | O | 코드 실행 중 발생한 오류를 캡처합니다. 관련 프로세스 체인에 대한 정보가 있는 경우 해당 정보도 함께 캡처됩니다. |
Fault | O | 코드의 결함 및 버그 정보를 캡처합니다. 관련 프로세스 체인에 대한 정보가 있는 경우 해당 정보도 함께 캡처됩니다. |
예시를 통해 OSLog의 사용법을 살펴보겠습니다. 기존에 다음과 같이 print문을 사용했다고 가정해봅시다.
print("User's email: \(user.email)")
이제 OSLog를 사용하여 위의 역할을 하는 로그를 작성합니다.
import OSLog
let log = OSLog(subsystem: "com.example.yourapp", category: "user")
// Level: Default
// Subsystem: com.example.yourapp
// Category: user
os_log("User's email: %{public}@", log: log, type: .default, user.email)
그리고 Console
애플리케이션을 열어 실행한 기기의 로그를 확인해보면 아래와 같이 복잡하게 나오게 됩니다.
이는 해당하는 기기의 모든 로그를 보여주는 것이기 때문에, 앞서 작성했던 문자열을 특정하여 필터링하면 원하는 결과값이 나오게 됩니다.
위에서 언급한 Level을 이용하여 로깅의 중요도를 나타낼 수 있습니다.
os_log("User's email: %{public}@", log: log, type: .error, user.email)
os_log("User's email: %{public}@", log: log, type: .fault, user.email)
여러 로그가 즐비한 가운데, 자기가 로깅하고자 하는 앱만 보고 싶은 경우 subsystem
값을 설정하여 필터링할 수 있습니다.
let log = OSLog(subsystem: "com.example.yourapp", category: "user")
os_log("User's email: %{public}@", log: log, type: .default, "test@test.com")
os_log("User's name: %{public}@", log: log, type: .default, "홍길동")
더 나아가서, 특정 기능에 대해서만 로그를 보고 싶다면, category를 설정하여 필터링하면 손쉽게 필터링이 가능합니다.
let log = OSLog(subsystem: "com.example.yourapp", category: "user")
os_log("User's email: %{public}@", log: log, type: .default, "test@test.com")
os_log("User's name: %{public}@", log: log, type: .default, "홍길동")
let uiLog = OSLog(subsystem: "com.example.yourapp", category: "UI")
let viewFrame = yourView.frame
os_log(
"UIView frame: x: %.2f, y: %.2f, width: %.2f, height: %.2f",
log: uiLog,
type: .info,
viewFrame.origin.x, viewFrame.origin.y, viewFrame.size.width, viewFrame.size.height
)
카테고리별 인스턴스를 생성해서 앱의 다양한 구성 요소에 맞게 로그 처리를 할 수 있습니다.
extension OSLog {
// 앱의 서브시스템 정의
private static var subsystem = Bundle.main.bundleIdentifier!
// 카테고리별 로그 객체 생성
static let networking = OSLog(subsystem: subsystem, category: "Networking")
static let database = OSLog(subsystem: subsystem, category: "Database")
static let userInterface = OSLog(subsystem: subsystem, category: "UserInterface")
// 추가로 필요한 카테고리를 여기에 정의할 수 있습니다.
// 로그 레벨에 따른 메시지 출력 함수 생성
static func logError(_ message: StaticString, category: OSLog, _ args: CVarArg...) {
log(message, type: .fault, category: category, args: args)
}
static func logWarning(_ message: StaticString, category: OSLog, _ args: CVarArg...) {
log(message, type: .error, category: category, args: args)
}
static func logDefault(_ message: StaticString, category: OSLog, _ args: CVarArg...) {
log(message, type: .default, category: category, args: args)
}
static func logInfo(_ message: StaticString, category: OSLog, _ args: CVarArg...) {
log(message, type: .info, category: category, args: args)
}
static func logDebug(_ message: StaticString, category: OSLog, _ args: CVarArg...) {
log(message, type: .debug, category: category, args: args)
}
// 로그 출력을 위한 공통 함수
private static func log(_ message: StaticString, type: OSLogType, category: OSLog, args: [CVarArg]) {
os_log(message, log: category, type: type, args)
}
}
위 처럼 확장했을 경우 다음과 같이 코드 작성이 가능해집니다.
OSLog.logError("Network error: %@", category: .networking, error.localizedDescription)
OSLog.logInfo("User successfully logged in.", category: .userInterface)
이처럼 OSLog의 Extension을 사용하게 되면, 협업할 때 로그 레벨과 카테고리를 일관되게 사용할 수 있고, 로그를 작성하기가 더 편리해지는 장점이 있습니다.