-
Notifications
You must be signed in to change notification settings - Fork 0
[Feature] LogKit 생성 및 로그 출력 방식 일괄 통일 #114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
NeoSelf1
wants to merge
25
commits into
develop
Choose a base branch
from
feature/logKit
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
742aeb8
[Feature] DiskCache 클래스 구현 (#109)
NeoSelf1 3cf7d37
[Delete] 불필요 라이브러리 import문 제거 (#109)
NeoSelf1 cab7e9d
[Refactor] 동시성 제어 관련 컴파일 에러 해결 (#109)
NeoSelf1 2d105ab
[Refactor] Actor 어노테이션 불필요한 곳 수정 (#109)
NeoSelf1 722a706
[Feat] ImageProcessor 추가 (#109)
NeoSelf1 5e82583
[Feat] ImageWrapper 추가 (#109)
NeoSelf1 daddb31
[Feat] ImageDownloader 구현 (#109)
NeoSelf1 852c52c
[Fix] Task-isolated options 관련 에러 수정 위해 임시 커밋 (#109)
NeoSelf1 152b503
[Fix] Concurrency 문제 해결 (#109)
NeoSelf1 c1006f6
[Fix] NeoImageWrapper 자체를 Sendable하게 변경하여 해결 (#109)
NeoSelf1 ee1273c
[Refactor] ImageCache 패키지 구현 (#109)
NeoSelf1 f636f62
[Refactor] 디렉토리 구조 변경 (#109)
NeoSelf1 0942e0b
[Feat] 메모리 및 디스크 저장소 조회하여 기존 이미지 가져오는 로직 추가 (#109)
NeoSelf1 7e72a5c
[Refactor] BookMatchKit loggers 초기과 구문 임시 저장
NeoSelf1 b2922a3
[Feat] LogCategory, LogSubsystem, LogLevel 열거형 정의
NeoSelf1 f56862b
[Feat] LogKitActor 및 LogKit 클래스 구현체 구현
NeoSelf1 a50887a
[Feat] LogKit Facade 계층 구현
NeoSelf1 72831f4
[Refactor] CSV 파일 형식에 맞춰 다중라인 출력 String문 단일라인으로 수정
NeoSelf1 422e571
[Delete] 각종 모듈에서 사용되고 있던 Logger 일괄 제거 및 LogKit로 통합
NeoSelf1 f345304
[Refactor] 각 패키지에 대해 LogKit 패키지 의존성 추가
NeoSelf1 6d43051
[Refactor] Log 관련 메서드 모두 LogKit 패키지 메서드로 일괄 변경
NeoSelf1 610e367
[Refactor] DesignSystem 라이브러리 내부 Kingfisher 라이브러리 의존성 제거 및 LogKit 내부 …
NeoSelf1 4a9ef1f
[Feat] Swift 6 점진적 대응 위한 컴파일 버전 낮추기
ericKwon95 7d1c4bb
[Feat] FIFO 순서의 로깅 위한 시리얼큐 적용
ericKwon95 67f3355
[Refactor] 파일 쓰기, 로그 엔진, 로그 엔진 래퍼로 역할 및 파일 분리
ericKwon95 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
196 changes: 196 additions & 0 deletions
196
BookKitty/BookKitty/LogKit/Sources/LogKit/LogKitActor.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,196 @@ | ||
| import Foundation | ||
| import OSLog | ||
|
|
||
| struct LoggerKey: Hashable { | ||
| let subSystem: LogSubSystem | ||
| let category: LogCategory | ||
| } | ||
|
|
||
| /// 단순히 전역 액터 속성만 정의하는 것이 아니라, 로깅 인터페이스도 제공 | ||
| @globalActor | ||
| public actor LogKitActor { | ||
| // MARK: - Static Properties | ||
|
|
||
| public static let shared = LogKitActor() | ||
|
|
||
| // MARK: - Properties | ||
|
|
||
| /// _LogKit의 인스턴스를 직접 관리 | ||
| private let logKit = _LogKit() | ||
|
|
||
| // MARK: - Functions | ||
|
|
||
| /// _LogKit의 메서드를 액터 내부에서 호출 | ||
| public func log( | ||
| _ level: LogLevel, | ||
| message: String, | ||
| subSystem: LogSubSystem = .app, | ||
| category: LogCategory = .general, | ||
| file: String = #file, | ||
| function: String = #function, | ||
| line: Int = #line | ||
| ) { | ||
| // 액터 내부에서는 await 없이 동기적으로 호출 가능 | ||
| logKit.log( | ||
| level, | ||
| message: message, | ||
| subSystem: subSystem, | ||
| category: category, | ||
| file: file, | ||
| function: function, | ||
| line: line | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| final class _LogKit { | ||
| // MARK: - Properties | ||
|
|
||
| /// @globalActor를 사용해 shared 인스턴스에만 격리 도메인을 지정하는 것은 동시성 격리를 부분적으로 선택적으로 적용 | ||
| /// 어떤 부분이 격리되어야 하고 어떤 부분이 일반 동기 코드로 실행되어도 되는지 더 세밀하게 제어 | ||
| /// 실제로 모든 코드가 항상 격리될 필요는 없기 때문에 성능상 이점도 존재 | ||
| /// 공유 자원에 대한 접근은 조정해야 하지만, 모든 기능이 액터 내부에 있을 필요는 없는 경우 적합 | ||
| private let dateFormatter: DateFormatter | ||
| private let fileManager: FileManager | ||
| private var loggers: [LoggerKey: Logger] = [:] | ||
|
|
||
| private let appStartTime: String | ||
| private var currentCSVFileID = 1 | ||
| private var currentCSVFileURL: URL? | ||
| private var currentCSVFileSize: UInt64 = 0 | ||
| private let maxCSVFileSize: UInt64 = 60 * 1024 // 60KB | ||
|
|
||
| // MARK: - Lifecycle | ||
|
|
||
| init() { | ||
| dateFormatter = DateFormatter() | ||
| dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" | ||
|
|
||
| fileManager = FileManager.default | ||
|
|
||
| let startTimeFormatter = DateFormatter() | ||
| startTimeFormatter.dateFormat = "yyyyMMdd_HHmm" | ||
| appStartTime = startTimeFormatter.string(from: Date()) | ||
|
|
||
| initializeLoggers() | ||
| createNewCSVFile() | ||
| } | ||
|
|
||
| // MARK: - Functions | ||
|
|
||
| // MARK: - Public Methods | ||
|
|
||
| func log( | ||
|
||
| _ level: LogLevel, | ||
| message: String, | ||
| subSystem: LogSubSystem = .app, | ||
| category: LogCategory = .general, | ||
| file: String = #file, | ||
| function: String = #function, | ||
| line: Int = #line | ||
| ) { | ||
| let fileName = (file as NSString).lastPathComponent | ||
|
|
||
| let logger = getLogger(subSystem: subSystem, category: category) | ||
| logger.log(level: level.osLogType, "[\(fileName):\(line)] \(function) - \(message)") | ||
|
|
||
| let timestamp = dateFormatter.string(from: Date()) | ||
|
|
||
| // CSV 로그 추가 | ||
| writeToCSVFile( | ||
| timestamp: timestamp, | ||
| level: level.rawValue, | ||
| fileName: fileName, | ||
| line: String(line), | ||
| function: function, | ||
| message: message, | ||
| subSystem: subSystem.rawValue, | ||
| category: category.rawValue | ||
| ) | ||
| } | ||
|
|
||
| // MARK: - Private Methods | ||
|
|
||
| private func initializeLoggers() { | ||
| var map: [LoggerKey: Logger] = [:] | ||
|
|
||
| for subSystem in LogSubSystem.allCases { | ||
| for category in LogCategory.allCases { | ||
| let logger = Logger(subsystem: subSystem.rawValue, category: category.rawValue) | ||
| map[LoggerKey(subSystem: subSystem, category: category)] = logger | ||
| } | ||
| } | ||
|
|
||
| loggers = map | ||
| } | ||
|
|
||
| private func getLogger(subSystem: LogSubSystem, category: LogCategory) -> Logger { | ||
| let key = LoggerKey(subSystem: subSystem, category: category) | ||
|
|
||
| if let logger = loggers[key] { | ||
| return logger | ||
| } else { | ||
| return Logger(subsystem: subSystem.rawValue, category: category.rawValue) | ||
| } | ||
| } | ||
|
|
||
| private func createNewCSVFile() { | ||
| let documentsPath = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0] | ||
| let fileName = "\(appStartTime)-\(currentCSVFileID).csv" | ||
| let fileURL = documentsPath.appendingPathComponent(fileName) | ||
|
|
||
| // CSV 헤더 생성 | ||
| let headerRow = "Timestamp,Level,FileName,Line,Function,Message,SubSystem,Category\n" | ||
|
|
||
| do { | ||
| try headerRow.write(to: fileURL, atomically: true, encoding: .utf8) | ||
| currentCSVFileURL = fileURL | ||
| currentCSVFileSize = UInt64(headerRow.utf8.count) | ||
| print("새 CSV 로그 파일이 생성되었습니다: \(fileName)") | ||
| } catch { | ||
| print("CSV 로그 파일 생성 실패: \(error)") | ||
| } | ||
| } | ||
|
|
||
| private func writeToCSVFile( | ||
| timestamp: String, | ||
| level: String, | ||
| fileName: String, | ||
| line: String, | ||
| function: String, | ||
| message: String, | ||
| subSystem: String, | ||
| category: String | ||
| ) { | ||
| let escapedMessage = message.replacingOccurrences(of: "\"", with: "\"\"") | ||
| let escapedFunction = function.replacingOccurrences(of: "\"", with: "\"\"") | ||
|
|
||
| // CSV 행 생성 | ||
| let csvRow = | ||
| "\"\(timestamp)\",\"\(level)\",\"\(fileName)\",\"\(line)\",\"\(escapedFunction)\",\"\(escapedMessage)\",\"\(subSystem)\",\"\(category)\"\n" | ||
|
|
||
| guard let csvData = csvRow.data(using: .utf8) else { | ||
| return | ||
| } | ||
| let dataSize = UInt64(csvData.count) | ||
|
|
||
| // 현재 파일이 최대 크기를 초과하는지 확인 | ||
| if currentCSVFileSize + dataSize > maxCSVFileSize { | ||
| currentCSVFileID += 1 | ||
| createNewCSVFile() | ||
| } | ||
|
|
||
| // CSV 파일에 로그 추가 | ||
| guard let fileURL = currentCSVFileURL else { | ||
| return | ||
| } | ||
|
|
||
| if let fileHandle = try? FileHandle(forWritingTo: fileURL) { | ||
| fileHandle.seekToEndOfFile() | ||
| fileHandle.write(csvData) | ||
| try? fileHandle.close() | ||
|
|
||
| currentCSVFileSize += dataSize | ||
| } | ||
| } | ||
| } | ||
34 changes: 34 additions & 0 deletions
34
BookKitty/BookKitty/LogKit/Sources/LogKit/LogKitType.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| import OSLog | ||
|
|
||
| public enum LogSubSystem: String, CaseIterable, Sendable { | ||
| case bookOCR = "bookOCR" | ||
| case bookRecommendation = "bookRecommendation" | ||
| case designSystem = "designSystem" | ||
| case database = "database" | ||
| case app = "app" | ||
| } | ||
|
|
||
| public enum LogCategory: String, CaseIterable, Sendable { | ||
| case general = "general" | ||
| case network = "network" | ||
| case userAction = "userAction" | ||
| case lifecycle = "lifecycle" | ||
| } | ||
|
|
||
| public enum LogLevel: String { | ||
| case debug = "DEBUG" | ||
| case info = "INFO" | ||
| case log = "LOG" | ||
| case error = "ERROR" | ||
|
|
||
| // MARK: - Computed Properties | ||
|
|
||
| var osLogType: OSLogType { | ||
| switch self { | ||
| case .debug: return .debug | ||
| case .info: return .info | ||
| case .log: return .default | ||
| case .error: return .error | ||
| } | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FileManager 자체가 thread-safe한 것으로 알고 있는데, actor로 한 번 더 감싸주신 이유가 궁금합니다~!