DatadogをiOSアプリに入れてみた

Datadogは色々なデータを可視化するプラットフォーム。そして、さまざまなアプリや環境に対応している。　次お客様に提案する機会があれば提案したいぐらいいい感じ。

Trial試し期間以降はきっと使えなくなるので　色々とメモ。

## Datadogの公式情報

[Datadog - iOS Log Collection](https://docs.datadoghq.com/logs/log_collection/ios/?tab=swiftpackagemanagerspm)

## iOSアプリに導入

- SPMで導入 `https://github.com/Datadog/dd-sdk-ios.git` 執筆時点では1.9.0が最新。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83390/cd8cbf6a-a7b0-5d14-bf7c-08e315490772.png" width="500">
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83390/02339993-19be-ecf9-81b8-bfbeb0becb79.png" width="500">

- 自分のクライアントトークンでDatadog SDKを初期化＋ログするクラスを作成

```swift

import Foundation
import Datadog

class AnalyticsLogger {

    /// どもからでもログを出すのでSingletton
    static let instance = AnalyticsLogger()

    private let ddLogger: DDLogger

    /// 初期化
    init() {
        Datadog.initialize(
            appContext: .init(),
            trackingConsent: TrackingConsent.granted,
            configuration: Datadog.Configuration
                .builderUsing(clientToken: SecretsVault.current.datadogClientToken, environment: "dev1")
                .build()
        )

        ddLogger = DDLogger.builder
            .sendNetworkInfo(true)
            .sendLogsToDatadog(true)
            .set(loggerName: "Analytics")
            .set(serviceName: "MyExampleApp")
            .printLogsToConsole(true, usingFormat: .shortWith(prefix: "[Datadog] "))
            .build()
    }
}

extension AnalyticsLogger {

    /// INFO ログを送信
    /// - Parameters:
    ///   - message: ログに出力するメッセージ
    ///   - error: 出力したい `Error` インスタンスがあれば、指定
    ///   - attributes: 追加属性。辞書型で指定。EncodableなオブジェクトをValueとして指定可能
    public func info(_ message: StaticString, error: Error? = nil, attributes: [AttributeKey: AttributeValue]? = nil) {
        ddLogger.info(message.description, error: error, attributes: attributes)
    }

    /// WARN 警告ログを送信
    /// - Parameters:
    ///   - message: ログに出力するメッセージ
    ///   - error: 出力したい `Error` インスタンスがあれば、指定
    ///   - attributes: 追加属性。辞書型で指定。EncodableなオブジェクトをValueとして指定可能
    public func warn(_ message: StaticString, error: Error? = nil, attributes: [AttributeKey: AttributeValue]? = nil) {
        ddLogger.warn(message.description, error: error, attributes: attributes)
    }

    /// ERROR エラーログを送信
    /// - Parameters:
    ///   - message: ログに出力するメッセージ
    ///   - error: 出力したい `Error` インスタンスがあれば、指定
    ///   - attributes: 追加属性。辞書型で指定。EncodableなオブジェクトをValueとして指定可能
    public func error(_ message: StaticString, error: Error? = nil, attributes: [AttributeKey: AttributeValue]? = nil) {
        ddLogger.error(message.description, error: error, attributes: attributes)
    }
}
```

あとは上記のコードを適切なタイミングで呼ぶ

### 初期化

以下はSwiftUIだが UIKitの人はAppDelegateで書くことが一般的

```swift
import SwiftUI

@main
struct MyExampleApp: App {

    init() {
        // クライアントトークンなどの秘密情報の準備
        SecretsVault.prepareVault(environmentName: "Dev")
        // ログ出力のための初期化
        AnalyticsLogger.instance.info("App initialized")
    }

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
```

### ログ出力

以下はボタンアクションに対するログを出力する。SwiftUIの例だが UIKitの人はUIViewControllerなどで書くことが一般的


```swift
import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack {
            Button("Log it!") {
                AnalyticsLogger.instance.warn(
                    "Hello world button pressed",
                    attributes: ["user_id" : "12345",
                                 "view": "ContentView",
                                 "event_id": "BTN01"])
            }
        }
        .padding()
    }
}
```

実装完了！


## Datadogでの見方

動作確認してDatadogでどう見えるか

Logs → Live Tailで ほぼリアルタイムで見える。時々数分遅延があるが　せっかちな人じはアプリをbackground/foreground切り替えるとログ送信トリガーになっている様。Searchなど可能。

Dashboardに入れることもできる。Monitor機能も色々と便利！

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83390/59004d86-b9ae-eedb-b485-77ea92aa1572.png" width="250">

デフォルトでDate, Host, Service, Contentという列のみ表示される。

![default_columns.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83390/d9330b4b-a7a6-43eb-6671-f5534e73f640.png)

右側のOptionsボタンで必要な列を追加することができる。ログを出力する際にattributesも設定しているならば　とても便利。に1列対1attribute。

また、属性名がDatadogの内部の属性名と重複された場合でも大丈夫。`"event_id"`という名前の属性をlog送信した場合は Datadog上で列を追加する際に @を先頭に入れる見つかる `event_id` → `@event_id`

```swift
            logger.warn(
                "Hello world button pressed",
                attributes: ["user_id" : "12345",
                             "view": "ContentView",
                             "event_id": "BTN01"])
```

![more_columns.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83390/43b778ec-c074-595b-eb94-668606b84410.png)

## ちょっとハマったポイント

- ClientTokenではなく API Keyを指定してしまった。この状態でもちろんログが出ないです。経験上DatadogのClient Tokenは”pub"で始まるみたい。![client_token.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83390/32ab33a2-df31-6fae-85e3-422fd9f3b8cc.png)

- SPMで導入の際に DatadogStaticを選びたかったが、SwiftUIプロジェクトでは Datadogを選択する必要があった。もう少し古めなプロジェクト（Obj-C + UIKit Swift)で DatadogStaticでもOKでした。きっとStaticの方がProjectのコンパイラーフラグ追加が必要だと思われる。

![library.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/83390/0eef704c-894d-dc60-de39-e000b74d7a1c.png)