Slide 1

Slide 1 text

2022/10/20 【Da Vinci Studio/ロコガイド/Chatwork/STORES】iOS Meetup STORES 決済で使っているLoggerの実装を刷新した話 k-kohey (@k_koheyi)

Slide 2

Slide 2 text

概要 - STORES 決済(iOS)で利用しているロガー実装を刷新した - ユーザ行動やシステムの不具合などのログを収集する実装を ロガー実装とする - どのような背景で既存の実装に問題があったか およびそれをどう対応したかについて発表します🙌 2

Slide 3

Slide 3 text

背景 | STORES 決済が提供するサービス 3 - STORES 決済では決済に関するサービスを 下記の2つの形で提供している - STORES 決済を介して クレジットカードや電子マネー決済ができる - 決済アプリ(以降、アプリと呼ぶ) - App Storeにて 公開しているアプリ - 決済SDK(以降、SDKと呼ぶ) - 決済機能を自社アプリに組み込みたい 企業向けに公開しているSDK

Slide 4

Slide 4 text

背景 | ログの活用 アプリではサービス改善に役立てるため ユーザ行動やシステムの不具合をログとしてバックエンドに送信している 4 バックエン ドA バックエン ドB バックエン ドC ユーザ行動や不具合を蓄積するバックエンド 決済が 失敗しました 決済が 失敗しました 開発者

Slide 5

Slide 5 text

背景 | ログの活用 バックエンドに送信されたログから下記のような事がわかる - システムに不具合が生じていること - および不具合が発生している箇所 - ユーザがアプリに対してどのような操作を行なっているか これらの情報は下記のような改善に役立ちます - システムに問題が発生した際に早期に発見し修正すること - ユーザの操作からボトルネックを発見し使いやすいアプリにすること 5 ログは継続的なサービス開発において重要

Slide 6

Slide 6 text

背景 | SDKにおけるロギングの問題 一方でSDKは後述する理由からログを送信できておらず(※) ログの恩恵を十分に得られていない状態だった 6 バックエンド A バックエンド B バックエンド C ユーザ行動や不具合を蓄積するバックエンド アプリが起動 されたわよ (※)クライアントのログ情報に限った話です。バックエンドでは収集できています。

Slide 7

Slide 7 text

背景 | SDKにおけるロギングの問題 - アプリとSDKでは次のような外部のライブラリの利用に対して 異なる考え方で開発を進めている - SDKは外部のライブラリを利用しない方針で開発している - SDKは外部(SDKクライアント)から利用されることを前提にしている - SDKクライアントとSDKが同じライブラリを利用する場合に、 そのライブラリのバージョンを解決できない可能性がある - アプリは外部のライブラリを利用している - アプリにはクライアントが存在せず、SDKと同じ考えは必要ないため - アプリはFirebaseやMixpanel等の一般公開されている ライブラリを利用してログを収集している 7 SDKはアプリと同様の方法でロギングできない

Slide 8

Slide 8 text

背景 | SDKにおけるロギングの問題 - アプリとSDKでは次のような外部依存に対して異なる考え方で 開発を進めている - SDKは外部のライブラリを利用しない方針で開発している - SDKは外部(SDKクライアント)から利用されることを前提にしている - SDKクライアントとSDKが同じライブラリを利用する場合に、 そのライブラリのバージョンを解決できない可能性があるため - アプリは外部のライブラリを利用している - アプリにはクライアントが存在せず、SDKと同じ考えは必要ないため - アプリはFirebaseやMixpanel等の一般公開されている ライブラリを利用してログを収集している 8 SDKはアプリと同様の方法でロギングできない SDKでもロギングできるように ロガーの実装を刷新することに

Slide 9

Slide 9 text

実装 | 方針 SDKがロギングできるようになるだけではなく、アプリとロギングの処理を共通化 する上で次の事を実現したいと考えた - アプリとSDKで同じインタフェースを介してロギングするが どういう実装でバックエンドとコミュニケーションするかは アプリとSDKで柔軟に切り替えられるようにしたい - アプリでは外部のライブラリを用いたい。SDKでは用いない。 - アプリとSDKでログのパラメータを変更したい - 例えばアプリではアプリのバージョン、SDKではSDKのバージョンを付与する 9

Slide 10

Slide 10 text

実装 | 方針 10 ロガーの実装をする上で次の事は気をつけたいと考えた - ログの欠損を防ぐため、送信に失敗したログは端末に保存したい - 簡単に新しいイベントログを追加したい

Slide 11

Slide 11 text

実装 | ロガー実装の共通化 11 ログ収集SDK Firebaseや MixPanel(※)等の SDK ログ収集SDK Firebaseや MixPanel(※)の SDK ロガー実装 アプリから DIして参照 MixPanelの HTTPクライアント 必要に応じてSDKから DIして参照 SDKを介して参照 追加 追加 (※)弊社にて使っている分析ツール

Slide 12

Slide 12 text

実装 | ロガー実装の共通化 12 ロガー実装 MixPanelの HTTPクライアント SDKから DIして参照 他社 アプリ (SDKクライアント) アプリからFirebaseや Mixpanl等のSDKの実装 をDIされない場合でも SDKのロガー実装から ログが可能 ログ収集SDK Firebaseや MixPanel等の SDK ロガー実装 アプリから DIして参照 SDKを介して参照 SDKからアプリの ロガー実装を参照 し利用できる 💡アプリとSDKのどちらの利用においても 同じインタフェース(ロガー実装)で バックエンドを意識せずログが可能に

Slide 13

Slide 13 text

実装 | ロガー実装の共通化 13 左のようなインタフェースで次の ように条件に応じてロガーの実装 を切り替えられるようにした - アプリからの利用時は backendBLogger - それ以外の時は backendALogger 注) 決済アプリは外部に公開している決済SDKとほぼ同等の機能を持つ異なるSDKを利用し ています。そのためloggerインスタンスは外部に公開されません

Slide 14

Slide 14 text

実装 | ログのMutation 14 { "eventName": "signin", "parameters": {} } { "eventName": "signin", "parameters": { "sdk_version": "1.0.0" } } { "eventName": "signin", "parameters": { "app_version": "5.5.2" } } - SDKとアプリでログのパラメータ を柔軟に変更したい - 例 - SDKの場合は全てのログ のパラメータにsdk_versionを 付与 - アプリの場合は全てのログ のパラメータにapp_versionを 付与 - ただしSDKとアプリでログ定義は 同じものを使い回したい - 同じログを2重管理はしたくない ログを変換する層が必要

Slide 15

Slide 15 text

実装 | ログのMutation 15 { "eventName": "signin", "parameters": {} } { "eventName": "signin", "parameters": { "sdk_version": "1.0.0" } } { "eventName": "signin", "parameters": { "app_version": "5.5.2" } } Mutation - ログを変化する層 Mutation を用意 - ログは必ずMutationを介して任意の形に変形されて送信される

Slide 16

Slide 16 text

実装 | ログのMutation 16 { "eventName": "signin", "parameters": {} } { "eventName": "signin", "parameters": { "os": "16.0" } } 他にも全てのログのパラメータに入るような汎用的な情報の挿入や パラメータの変換に使えます💡 { "eventName": "signin", "parameters": {} } { "eventName": "signin", "parameters": { "network": "wifi" } } { "eventName": "failed", "parameters": { "error": MyError.invalidSyntax } } { "eventName": "signin", "parameters": { "error description": "invalid syntax" } }

Slide 17

Slide 17 text

実装 | ログの定義を容易にする工夫 - 従来は左図のようにenumを使って ログ定義を管理しており 新規にイベントを追加する際は caseを追加して対応していた - ログを増やすとコンパイルエラーが 発生しenumのプロパティに対して修正が 必要だった 17 enumのパターンマッチングが安全装置 として働いている一方で 拡張に対して開いていないと感じる

Slide 18

Slide 18 text

実装 | ログの定義を容易にする工夫 - ログ定義はenumではなく structを採用 - ログのカテゴリーごとにstructを作り そのstaticなプロパティやメソッドにて 各ログを定義 - structの場合は単に定義を 増やしていくだけで ログを追加していくことが可能 18

Slide 19

Slide 19 text

実装 | ログの欠損を防ぐ工夫 ログを送信するコードを記述しても ネットワークが不安定なときなどにログの送信が失敗する懸念がある 19 失敗時はログをBufferに貯めてリトライする Bufferの実装がDatabaseやファイルシステムに依存していると 複数のスレッドからBufferに書き込みを行うとクラッシュする恐れがある Swift ConcurrencyのActorを利用

Slide 20

Slide 20 text

結果 - 以上までの事を組み合わせた実装を行い下記のようなインタフェースで アプリとSDK両方からログを送信できるようになった 20 - ほぼ同等の実装をGitHubに公開中(SPMで導入可) - https://github.com/k-kohey/Parchment-swift

Slide 21

Slide 21 text

おわりに ● SDKとアプリと共有して使うロガーの実装をした ● 具体的なログの実装はアプリからDIすることによって SDKが直接依存を持たないFirebaseやMixpanel にロギングを命令できるようにした ● Mutationと呼ばれる仕組みを導入しパラメータの変更も容易にした ● 実装はGitHubにOSSとして公開している ○ Starください 21 k-kohey STORES 決済で使っているLoggerの実装を刷新した話