Swift에서 기본적으로 제공하는 print()
문은 간단하게 디버깅할 수 있지만, 로그 레벨이나 파일 위치 등 세부 정보를 제공하지 않아 큰 프로젝트에서는 효과적으로 오류를 추적하기 어려운 경우가 많습니다. 특히 여러 개발자들이 협업할 때는 코드 흐름과 오류 발생 위치를 빠르게 파악할 수 있는 체계적인 로그 시스템이 필요합니다.
이 문제를 해결하기 위해, 과거 사이드 프로젝트에서 사용했던 Logger 형식을 수정해 더욱 효율적이고 직관적인 로그 출력을 구현하게 되었습니다. 이번 글에서는 이 Logger 클래스를 단계별로 만들어가며, 로그 레벨, 시간 포맷팅, 파일 위치 등 다양한 정보가 포함된 커스텀 로그 기능을 Swift에서 구현하는 방법을 공유합니다.
목표
- 로그 레벨 지원: 로그의 중요도에 따라
debug
,info
,warning
,error
,severe
등의 레벨을 구분하여 관리할 수 있도록 합니다. - 위치 정보 제공: 로그를 남긴 파일 이름, 함수 이름, 라인 및 컬럼 번호를 표시해 로그가 출력된 위치를 쉽게 추적할 수 있게 합니다.
- 타임스탬프 지원: 로그가 출력된 시점을 기록하여 시간 정보를 확인할 수 있도록 합니다.
- 스레드 안전성: 멀티 스레드 환경에서도 안정적으로 동작하도록 구현합니다.
Logger 클래스 전체 코드 및 구조
Logger 클래스를 extension
으로 구분하여, 기능별로 쉽게 이해할 수 있도록 구조화하였습니다.
import Foundation
final class Logger {
static let shared = Logger()
}
MARK: - 로그 레벨 정의
extension Logger {
fileprivate enum LogEvent: String {
case d = "[💬]" // 디버그
case e = "[‼️]" // 에러
case i = "[ℹ️]" // 정보
case v = "[🔬]" // 상세
case w = "[⚠️]" // 경고
case s = "[🔥]" // 심각
}
}
설명:
- LogEvent 열거형: 로그 레벨을 정의하는 열거형으로, 각 레벨은 한눈에 구분할 수 있도록 이모지와 함께 설정했습니다. 이 레벨을 사용해 로그의 중요도를 시각적으로 쉽게 구분할 수 있습니다.
MARK: - 로그 레벨별 함수
extension Logger {
static func d(_ object: Any, filename: String = #file, line: Int = #line, column: Int = #column, funcName: String = #function) {
log(.d, object, filename: filename, line: line, column: column, funcName: funcName)
}
static func e(_ object: Any, filename: String = #file, line: Int = #line, column: Int = #column, funcName: String = #function) {
log(.e, object, filename: filename, line: line, column: column, funcName: funcName)
}
static func i(_ object: Any, filename: String = #file, line: Int = #line, column: Int = #column, funcName: String = #function) {
log(.i, object, filename: filename, line: line, column: column, funcName: funcName)
}
static func v(_ object: Any, filename: String = #file, line: Int = #line, column: Int = #column, funcName: String = #function) {
log(.v, object, filename: filename, line: line, column: column, funcName: funcName)
}
static func w(_ object: Any, filename: String = #file, line: Int = #line, column: Int = #column, funcName: String = #function) {
log(.w, object, filename: filename, line: line, column: column, funcName: funcName)
}
static func s(_ object: Any, filename: String = #file, line: Int = #line, column: Int = #column, funcName: String = #function) {
log(.s, object, filename: filename, line: line, column: column, funcName: funcName)
}
}
설명:
- 로그 레벨별 함수: 각 로그 레벨별로 호출할 수 있는 함수를 정의했습니다. 예를 들어,
d()
는 디버그 레벨의 로그를 출력하고,e()
는 에러 레벨의 로그를 출력합니다. - 각 함수에서
#file
,#line
,#column
,#function
을 기본 매개변수로 사용하여 호출 위치의 정보가 자동으로 전달됩니다.
MARK: - 메인 로깅 함수
extension Logger {
private static func log(_ level: LogEvent,
_ object: Any,
filename: String,
line: Int,
column: Int,
funcName: String) {
let timestamp = dateFormatter.string(from: Date())
let fileName = sourceFileName(filePath: filename)
let logMessage = """
-------------------- LOG START --------------------
Timestamp : \(timestamp)
Level : \(level.rawValue)
File : \(fileName)
Line : \(line), Column: \(column)
Function : \(funcName)
Message : \(object)
-------------------- LOG END ----------------------
"""
DispatchQueue.global(qos: .utility).sync {
print(logMessage, level)
}
}
}
설명:
- 메인 로깅 함수:
log()
함수는 로그 메시지를 출력하는 메인 함수로, 타임스탬프, 로그 레벨, 파일 이름, 함수 이름, 줄 번호, 컬럼 번호 등 여러 정보를 포함하여 로그를 출력합니다. - 동시성 제어:
DispatchQueue.global(qos: .utility).sync
를 사용해 멀티 스레드 환경에서도 안전하게 로그를 출력할 수 있습니다.
MARK: - 유틸리티 함수
extension Logger {
// 시간 포맷팅을 위한 DateFormatter
private static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
return formatter
}()
// DEBUG 또는 VERBOSE 모드에서만 메시지를 출력하는 함수
private static func print(_ message: String, _ level: LogEvent) {
#if DEBUG || VERBOSE
Swift.print(message)
#endif
}
// 파일 경로에서 파일 이름만 추출하는 함수
private static func sourceFileName(filePath: String) -> String {
return (filePath as NSString).lastPathComponent
}
}
설명:
- 시간 포맷팅 최적화:
DateFormatter
는 정적으로 선언해 성능을 최적화하고, 타임스탬프를 일관되게 유지합니다. - 환경별 출력 제어:
print()
함수는 DEBUG 또는 VERBOSE 모드에서만 메시지를 출력하도록 설정해, 릴리스 빌드에서는 로그가 출력되지 않도록 제어합니다. - 파일 이름 추출: 전체 파일 경로에서 파일 이름만을 추출하여 로그에 표시하는 함수입니다.
사용 예시
구현한 Logger 클래스는 다음과 같이 사용할 수 있습니다:
Logger.d("디버그 메시지")
Logger.e("에러 메시지")
Logger.i("정보 메시지")
Logger.w("경고 메시지")
Logger.s("심각한 에러 발생!")
마무리
이 글에서는 Swift에서 커스텀 Logger를 만들면서 프로젝트의 유지보수성과 디버깅 효율을 높이는 방법을 소개했습니다. 이번에 구현한 Logger 클래스는 가독성 좋은 로그 출력을 제공하며, 타임스탬프, 파일 및 함수 위치 등 다양한 정보를 포함하고 있어 디버깅 시 유용하게 활용할 수 있습니다. Swift 프로젝트에서 체계적인 로그 시스템이 필요할 때, 이 Logger 클래스를 적용해 보세요.
'iOS' 카테고리의 다른 글
Fastlane에서 Unauthorized Access 에러 해결: 세션 갱신 방법 (0) | 2024.11.25 |
---|---|
Swift에서 Data를 객체로 유연하게 변환하기 (0) | 2024.11.08 |