iOS

Logger, OSLogPrivacy

Phililip
728x90

안녕하세요.

 

이번에는 iOS 14부터 추가된 Logger와 OSLogPrivacy에 대해 간단하게 알아볼게요.

 


 

만약 버그 제보를 받게 된다면, 무엇을 가장 먼저 해볼까요?

 

저라면 제보받은 상황을 똑같이 따라 해서 이슈가 재현되는지 제 눈으로 직접 확인해볼 것 같아요.

 

 

 

재현이 된다면 땡큐고....

 

만약 재현이 안된다면??

 

이런 경우는 상당히 난감합니다... 로그를 남기지 않았다면 말이죠.

 

 

 

로그를 통해서 앱이 정확하게 어떤 상황이고 어떤 특정 조건에 의해 이슈가 발생했는지 트랙킹 할 수 있기 때문에,

 

로그를 남기는 것은 서비스를 제공할 때 꼭 필요한 요소 중 하나예요.

 

애플은 로그를 쉽게 남길 수 있도록 Logger라는 구조체를 iOS 14부터 제공하고 있습니다. (물론 iOS 14 이전에는 os_log라는 매크로를 제공하고 있었으니 참고해주세요.)

 

 

 

# 1. Logger

역시 첫 시작은 애플 공식 문서죠..

 

Overview를 한번 볼까요??

 

 

 

앱 동작에 대해 로그를 남기기 위해 Logger 구조체를 생성해줘야 합니다.

 

로그 레벨도 정할 수 있어요. (debug, info, error, warning 등)

 

시스템은 로그 레벨에 따라서 메모리에 로그를 남길지, 디스크에 로그를 남길지 결정합니다.

 

 

 

Logger 구조체를 생성할 때, 선택적으로 subsystemcategory를 설정할 수 있어요. (이걸로 필터링이 가능해집니다!!)

 

일반적으로 subsystem에는 앱의 큰 영역을 의미(보통 bundleID를 사용하는 듯 합니다...)를 뜻하고, category는 subsystem 내의 특정 영역을 말합니다.

 

로그 메시지에는 대부분의 값을 넣을 수 있어요. (String, Int, Float 등)

 

이때 주의할 점은 커스텀 객체(구조체, 클래스)를 로그로 남기고 싶을 때에는 CustomStringConvertible 프로토콜을 준수하고 있어야 합니다.

 

 

 

어느 정도 개념은 잡힌 것 같으니 직접 Logger를 사용해봅시다.

(버튼을 누르면 count가 증가하고 그때마다 로그가 찍히도록 구현해볼게요!!)

 

 

우선 상단에 os 프레임워크를 import 해주세요.

import os

 

그다음에 뷰를 구성하고, Logger 인스턴스도 생성해줄게요.

struct ContentView: View {
    private let logger = Logger()
    @State private var count: Int = 0
    
    var body: some View {
        VStack {
            Text("\(count)")
            Button("Increase Count") {
                count += 1
                logger.info("Count: \(count)")
            }
        }
    }
}

 

 

 

로그 레벨은 다양하게 선택할 수 있으니 적절하게 사용해주세요.

 

 

그다음에 콘솔 앱을 켜주세요.

(동작 메뉴에서 '정보 메시지 포함', '디버그 메시지 포함' 설정이 켜져 있는지 확인해주세요!)

 

 

 

그리고 앱을 실행시켜서 버튼을 누르면 로그가 잘 찍히는 것을 볼 수 있어요ㅎㅎ

 

 

그런데 제가 출력시킨 로그 뿐만 아니라 기타 시스템 로그도 많이 찍혀서 한눈에 보기가 어렵습니다.

 

이럴 때, Logger에 subsystem과 category를 설정해주면 로그를 쉽게 필터링 가능합니다.

struct ContentView: View {
    private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ContentView")    ✅
    @State private var count: Int = 0
    
    var body: some View {
        VStack {
            Text("\(count)")
            Button("Increase Count") {
                count += 1
                logger.trace("Count: \(count)")
            }
        } 
    }
}

 

 

👍 👍 👍

 

 

 

이번에는 여러 가지 타입에 대해서 로그를 찍어볼까요??

 

Person 이란 커스텀 구조체를 만들어서 로그로 찍어볼게요.

struct Person {
    var name: String
    var age: Int
    var height: Float
}

struct ContentView: View {
    private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ContentView")
    
    var body: some View {
        Text("Hello World")
            .onAppear {
                let philip = Person(name: "philip", age: 19, height: 215.2)
                logger.debug("Person Info: \(philip)")    ❎
                logger.trace("Person name: \(philip.name)")
                logger.error("Person age: \(philip.age)")
                logger.warning("Person height: \(philip.height)")
            }
    }
}

 

그럼 위의 ❎  부분에서 에러가 날 것입니다.

 

Instance method 'appendInterpolation(_:align:privacy:)' requires that 'Person' conform to 'CustomStringConvertible'

 

 

아하!

 

커스텀 객체를 로그로 찍으려면 CustomStringConvertible 프로토콜을 준수해야 한다고 했죠??

 

 

Person 구조체를 아래처럼 바꿔줍시다.

struct Person: CustomStringConvertible {
    var name: String
    var age: Int
    var height: Float
    
    var description: String {
        "Person(name: \(name), age: \(age), height: \(height))"
    }
}

 

 

 

잘 나오죠??ㅎㅎ

 

진짜 잘 나오고 있는 걸까요....??  😈

 

 

 

지금 제가 보여드린 화면은 Xcode로 앱을 빌드함과 동시에 콘솔 앱에서 로그를 확인한 경우예요.

 

Xcode에서 실행 중인 앱을 다 끄고, 디바이스에 설치된 앱을 직접 실행시켜보면 결과가 다르게 나옵니다.

 

 

 

이렇게요....

 

갑자기 "<private>"으로 출력되는 것 보이시나요??

 

요걸 "Redacted 되었다"라고 합니다.

 

 

로그에 interpolated string 또는 커스텀 객체가 포함되었을 경우에 시스템이 자동으로 "private"으로 바꿔버립니다.

(민감한 정보일 수도 있기 때문이죠.)

 

 

자동으로 민감한 정보를 차단시켜줘서 너무너무 감사하지만... 민감한 정보가 아니라서 로그에 출력시키고 싶다면??

 

이럴 때 OSLogPrivacy 옵션을 사용합니다.

 

 

 

 

# 2. OSLogPrivacy

The privacy options that determine when to redact or display values in log messages.

 

로그 메시지에 있는 value를 redact 할지 display 할지 결정할 privacy options

 

즉, OSLogPrivacy를 사용하면 interpolated string 또는 커스텀 객체에 대해서 자동으로 redacted("private"으로 출력되는 거) 되던 것을 display(출력)시킬 수 있다는 것이에요.

 

물론 redacted 대상이 아니었던 Int, Float 등에 대해서도 강제로 redacted 시켜줄 수도 있구요.

 

 

직접 해볼까요??

struct ContentView: View {
    private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "ContentView")
    
    var body: some View {
        Text("Hello World")
            .onAppear {
                let philip = Person(name: "philip", age: 19, height: 215.2)
                logger.debug("Person Info: \(philip, privacy: .public)")
                logger.trace("Person name: \(philip.name, privacy: .public)")
                logger.error("Person age: \(philip.age, privacy: .private)")
                logger.warning("Person height: \(philip.height, privacy: .private(mask: .hash))")
            }
    }
}

 

 

redacted 시킬지 display 시킬지는 private, public 설정으로 바꿔줄 수 있고 hash 값을 출력하게끔도 할 수 있습니다.

 

👍 👍 👍

 

 

 

 

## 참고

- https://developer.apple.com/documentation/os/logger

- https://swiftwithmajid.com/2022/04/06/logging-in-swift/

 

 


이번 글은 여기서 마무리.

 

 

 

 

 

반응형

'iOS' 카테고리의 다른 글

Private Pod 배포 방법  (0) 2022.06.01
SwiftUI <-> UIKit 이벤트 전달 방법  (0) 2022.05.21
[오픈소스] Inject  (0) 2022.04.12
Notification에 Action 버튼 추가하기  (0) 2022.04.05
[오픈소스] Bagbutik  (0) 2022.03.28