iOS アプリエラー監視の設計とその効果

151a0b14f5914e786e2e104cfb3a9b2f?s=47 Kuniwak
November 20, 2017

iOS アプリエラー監視の設計とその効果

151a0b14f5914e786e2e104cfb3a9b2f?s=128

Kuniwak

November 20, 2017
Tweet

Transcript

  1. J04ΞϓϦΤϥʔ؂ࢹͷ
 ઃܭͱͦͷޮՌ

  2. "CPVUNF

  3. ,VOJXBL גࣜձࣾ%JWFSTFͷJ04ςοΫϦʔυɻ
 ͱ͋ΔΞϓϦͷϦχϡʔΞϧϓϩδΣΫτʹ
 ్த͔ΒՃΘͬͨ΋ͷͷɺςετࠔ೉ͳઃܭΛ
 ໨ͷ౰ͨΓʹͨͨ͠ΊɺཪϦχϡʔΞϧΛ։࢝ɻ
 ࠷ऴతʹςετΛಋೖ͠ɺ։ൃ଎౓Λഒʹͨ͠ɻ झຯ͸5%%ɻ

  4. Τϥʔ؂ࢹͱ͸

  5. w ΞϓϦͰൃੜͨ͠ΤϥʔΛαʔόʹૹΔͳͲͯ͠
 Τϥʔͷൃੜ਺ͳͲΛ؂ࢹ͢Δٕज़ w ྫ͑͹ɺΞϓϦ͕Ϋϥογϡͨ͠ΒϨϙʔτΛ
 ඈ͹͢ΫϥογϡϨϙʔτ͸͜ͷҰछ w ΫϥογϡϨϙʔτղੳαʔϏεͰ͸
 $SBTIMZUJDT͕༗໊

  6. Τϥʔͷൃੜ਺΍66 Τϥʔͷ*% ΤϥʔͷΧςΰϦ Τϥʔൃੜ਺ͷ࣌ؒతਪҠ Τϥʔൃੜ࣌ͷσόΠε Τϥʔൃੜ࣌ͷ04

  7. w ࣮͸ɺଟ͘ͷΫϥογϡϨϙʔταʔϏε͸
 ΫϥογϡҎ֎ͷΤϥʔ΋؂ࢹͰ͖Δ w ྫ͑͹ɺ8FC"1*ݺͼग़࣌͠ͷΤϥʔ΍ɺ
 ಺෦ঢ়ଶͷෆ੔߹ͳͲͷΤϥʔ΋؂ࢹͰ͖Δ ૹ৴ͷ࢓ํ͸ޙͰ঺հ͠·͢

  8. Τϥʔ؂ࢹͷಛੑ

  9. w Τϥʔ؂ࢹͷಛੑ͸ҎԼͷͭɿ w ඇৗʹ޿ൣͳόάΛݕ஌Ͱ͖Δ w Τϯόά͔Βൃݟ·Ͱͷ͕࣌ؒ௕͍

  10. ਤղ

  11. όάΛݟ͚ͭΔͨΊͷखஈ ߏจϋΠϥΠτ Ϧϯτ ੩తܕݕࠪ ୯ମɾ݁߹ςετ 6*ςετ ςελʔʹΑΔಈ࡞֬ೝ Τϥʔ؂ࢹ Ϣʔβʔ͔Βͷ͓໰͍߹Θͤ

  12. όά9Λݟ͚ͭΒΕΔ৔ॴ όά9 ߏจϋΠϥΠτ Ϧϯτ ੩తܕݕࠪ ୯ମɾ݁߹ςετ 6*ςετ ςελʔʹΑΔಈ࡞֬ೝ Τϥʔ؂ࢹ Ϣʔβʔ͔Βͷ͓໰͍߹Θͤ

  13. όά9 ߏจϋΠϥΠτ Ϧϯτ ੩తܕݕࠪ ୯ମɾ݁߹ςετ 6*ςετ ςελʔʹΑΔಈ࡞֬ೝ Τϥʔ؂ࢹ Ϣʔβʔ͔Βͷ͓໰͍߹Θͤ ্ͷํͰࢭ·Δͱ

    ݪҼڀ໌ͷ͕࣌ؒ୹͍ όά: ಛघͳΤϥʔ͸ԼͷํͰ
 ͔͠ݟ͚ͭΒΕͳ͍
  14. ߏจϋΠϥΠτ Ϧϯτ ੩తܕݕࠪ ୯ମɾ݁߹ςετ 6*ςετ ςελʔʹΑΔಈ࡞֬ೝ Τϥʔ؂ࢹ Ϣʔβʔ͔Βͷ͓໰͍߹Θͤ Τϥʔ؂ࢹ͸ಛघͳ
 ΤϥʔΛݟ͚ͭΒΕΔ


    ΄΅࠷ޙͷࡆ όά: ͔͠͠ɺ্ͷ૚ΑΓ΋
 Τϯόά͔Βൃݟ·Ͱͷ
 ͕࣌ؒͱͯ΋௕͍
  15. Τϥʔ؂ࢹͰݟ͚ͭΔ΂͖
 Τϥʔͱ͸

  16. ߏจϋΠϥΠτ Ϧϯτ ੩తܕݕࠪ ୯ମɾ݁߹ςετ 6*ςετ ςελʔʹΑΔಈ࡞֬ೝ Τϥʔ؂ࢹ Ϣʔβʔ͔Βͷ͓໰͍߹Θͤ ༧໿ޠλΠϙ ม਺໊λΠϙ

    ϓϩύςΟ໊
 λΠϙ ෼ذߟྀ࿙Ε ίϯϙʔωϯτ
 ࢓༷ͷෆ੔߹ ࢓༷΁ͷޡղ ಛघͳ؀ڥͰ͔͠
 ࠶ݱ͠ͳ͍Τϥʔ ҋ ཧ૝ͷόάݕ஌ͷ࢟
  17. ߏจϋΠϥΠτ Ϧϯτ ੩తܕݕࠪ ୯ମɾ݁߹ςετ 6*ςετ ςελʔʹΑΔಈ࡞֬ೝ Τϥʔ؂ࢹ Ϣʔβʔ͔Βͷ͓໰͍߹Θͤ ༧໿ޠλΠϙ ม਺໊λΠϙ

    ϓϩύςΟ໊
 λΠϙ ෼ذߟྀ࿙Ε ίϯϙʔωϯτ
 ࢓༷ͷෆ੔߹ ࢓༷΁ͷޡղ ಛघͳ؀ڥͰ͔͠
 ࠶ݱ͠ͳ͍Τϥʔ ҋ όά͕Լͷ૚·Ͱ
 ಥ͖ൈ͚ͯ͠·͏ͷͰ
 ݪҼڀ໌ʹ͕͔͔࣌ؒΔ μϝͳόάݕ஌ͷ࢟
  18. ߏจϋΠϥΠτ Ϧϯτ ੩తܕݕࠪ ୯ମɾ݁߹ςετ 6*ςετ ςελʔʹΑΔಈ࡞֬ೝ Τϥʔ؂ࢹ Ϣʔβʔ͔Βͷ͓໰͍߹Θͤ ༧໿ޠλΠϙ ม਺໊λΠϙ

    ϓϩύςΟ໊
 λΠϙ ෼ذߟྀ࿙Ε ίϯϙʔωϯτ
 ࢓༷ͷෆ੔߹ ࢓༷΁ͷޡղ ಛघͳ؀ڥͰ͔͠
 ࠶ݱ͠ͳ͍Τϥʔ ҋ ཧ૝ͷόάݕ஌ͷ࢟
  19. ߏจϋΠϥΠτ Ϧϯτ ੩తܕݕࠪ ୯ମɾ݁߹ςετ 6*ςετ ςελʔʹΑΔಈ࡞֬ೝ Τϥʔ؂ࢹ Ϣʔβʔ͔Βͷ͓໰͍߹Θͤ ༧໿ޠλΠϙ ม਺໊λΠϙ

    ϓϩύςΟ໊
 λΠϙ ෼ذߟྀ࿙Ε ίϯϙʔωϯτ
 ࢓༷ͷෆ੔߹ ࢓༷΁ͷޡղ ಛघͳ؀ڥͰ͔͠
 ࠶ݱ͠ͳ͍Τϥʔ ҋ Τϥʔ؂ࢹͰݕ஌͢΂͖
 Τϥʔ͸͜ͷ͋ͨΓ
  20. ͜͜·Ͱͷ·ͱΊ w Τϥʔ؂ࢹ͸ಛघͳόάΛ
 ݕ஌͢ΔͨΊͷखஈ w ͳ͓ɺͦͷલͷखஈͰόάΛ
 ݟ͚ͭΒΕΔͳΒͦͷํ͕
 ݪҼڀ໌Λ଎͘Ͱ͖Δ

  21. Τϥʔ؂ࢹͷ࣮૷ํ๏

  22.  Τϥʔ؂ࢹαʔϏεͷ"1*Λ೺Ѳ͢Δ  ΤϥʔϨϙʔλʔΛΧελϚΠζ͢Δ  ΤϥʔϨϙʔλʔΛ૊ΈࠐΉ

  23. Τϥʔ؂ࢹαʔϏεͷ
 "1*Λ೺Ѳ͢Δ 

  24. Crashlytics.sharedInstance().recordError(error) $SBTIMZUJDTʹΤϥʔΛૹ৴͢Δίʔυ &SSPSͰ͸ͳ͘/4&SSPSͳͷͰ஫ҙ

  25. ΤϥʔϨϙʔλʔΛ
 ΧελϚΠζ͢Δ 

  26. ౎౓/4&SSPSʹม׵͢Δͷ͸
 ໘౗ͳͷͰɺ4XJGUͷ&SSPSΛ
 /4&SSPS΁͍͍ײ͡ʹ
 ม׵ͯ͘͠ΕΔϥούʔΛॻ͘ $SBTIMZUJDTͷ৔߹

  27. class CrashlyticsErrorReporter { private let crashlytics = Crashlytics.sharedInstance() func report(error:

    Error, from reporter: Any) { #if DEBUG let prefix = "DEBUG " #else let prefix = "" #endif let detailedError = NSError( domain: "\(prefix)\(type(of: reporter))", code: CrashlyticsErrorTracker.getNSErrorCode(bySwiftError: error), userInfo: [ NSLocalizedDescriptionKey: "\(error)", ] ) self.crashlytics.recordError(detailedError) } private static func getNSErrorCode(bySwiftError error: Error) -> Int { let bridgedError = error as NSError return bridgedError.code } } %&#6(ϏϧυΛ۠ผͰ͖ΔΑ͏ʹ͓ͯ͘͠ͱ
 ຊ൪؀ڥͱ։ൃ؀ڥΛ۠ผ͠΍͘͢ͳΔ ΤϥʔϨϙʔλʔʹͲͷΑ͏ʹ
 ूܭ͞ΕΔ͔Λҙࣝ͢Δ ྫ͑͹ɺ$SBTIMZUJDT͸/4&SSPSͷ
 EPNBJOͱDPEFͷ૊ͰΤϥʔΛ
 άϧʔϐϯά͢ΔͷͰɺͦΕͧΕ
 Τϥʔ*%ͱൃੜݩΛࢦఆ͢Δͱ
 ៉ྷʹΤϥʔ͕·ͱ·Δ Τϥʔͷશจ͸άϧʔϐϯάʹ࢖Θͳ͍Α͏ʹ͢Δ
 ʢϢʔβʔ*%ͱ͔͕ೖΔͱݸผͷΤϥʔʹͳͬͯ
 ૯਺Λ೺ѲͰ͖ͳ͘ͳΔͷͰʣ $SBTIMZUJDT༻ͷΤϥʔϨϙʔλʔΛ࡞੒ SFQPSUؔ਺ΛݺͿͱΤϥʔϨϙʔτ͕
 ૹ৴͞ΕΔΑ͏ʹ࣮૷͍ͯ͠Δ ΤϥʔͱҰॹʹൃੜݩ͕
 Θ͔ΔΑ͏ʹ͓ͯ͘͠ͱ
 ݪҼͷݟ౰͕͖ͭ΍͘͢ͳΔ
  28. ΤϥʔϨϙʔλʔΛ
 ૊ΈࠐΉ 

  29. Α͘ΈΔ૊ΈࠐΈํ๏

  30. class UserApiRepository: UserRepositoryProtocol { private let api: GitHubApiClientProtocol func get(by

    id: GitHubUser.Id) -> Promise<GitHubUser> { return self.api .fetch( endpoint: GitHubApiEndpoint(path: "/user/" + id.text), headers: [:], parameters: [] ) .then { data -> GitHubUser in let response: GitHubUserResponse = try unbox(data: data) return response.user } .catch { error in ErrorReporter.shared.report( error: error, reporter: self ) } } } Τϥʔ͕ฦ͖ͬͯͨΒ
 ΤϥʔϨϙʔτΛૹ৴
 ʢͭ·Γϋʔυίʔυʣ "1*ݺͼग़͠ͷίʔυ
  31. w ͔͠͠ϋʔυίʔυʹ͸໰୊͕ଟ͍ɿ w "1*ͷݺͼग़͠ʹؔ܎ͷͳ͍ίʔυ͕ೖΔͨΊ
 ୯Ұ੹຿ݪଇʹҧ൓͢Δ w ୯ମςετͰΤϥʔϨϙʔτ͕ૹΒΕͯ͠·͏ w ςετ͚࣌ͩ༗ޮͳϑϥάΛ௥Ճͯ͠
 ΤϥʔϨϙʔτΛૹ৴͠ͳ͍Α͏ʹ


    Ͱ͖ͳ͘͸ͳ͍͕΍Γͨ͘ͳ͍
  32. ͦ͜Ͱ0CTFSWFSύλʔϯ

  33. w 0CTFSWFSύλʔϯͰ͸ɺ؂ࢹऀ͕؂ࢹର৅ͷ
 ঢ়ଶมԽΛ࡯஌ͯ͠ಈ࡞͢Δ w ΤϥʔϨϙʔτʹԠ༻͢Δͱɿ w ؂ࢹऀ͸ΤϥʔϨϙʔλʔ w ؂ࢹର৅͸Τϥʔ͕ൃੜ͠͏ΔΦϒδΣΫτ w

    0CTFSWFSύλʔϯΛ࢖͑͹ϋʔυίʔυͷ
 ໰୊఺ΛղফͰ͖Δ
  34. Τϥʔͷൃੜݯ Τϥʔ͕ൃੜ͠·ͨ͠ ΤϥʔϨϙʔλʔ ؂ࢹ

  35. Τϥʔͷൃੜݯ ΤϥʔϨϙʔλʔ ؂ࢹ Τϥʔ͕ى͖ͨͷͰ
 ΤϥʔϨϙʔτΛ
 ૹ৴͠·͢

  36. Τϥʔͷൃੜݯ ΤϥʔϨϙʔλʔ ΤϥʔϨϙʔτૹ৴
 ͷ੹຿ Τϥʔ͕ى͜Γ͏Δ
 ॲཧͷ੹຿

  37. Τϥʔͷൃੜݯ ΤϥʔϨϙʔλʔ ςετͷͱ͖͸
 ΤϥʔϨϙʔλʔ
 ΛऔΓ෇͚ͳ͍

  38. w ͨͩ͠ɺ0CTFSWFSύλʔϯΛ࢖͑Δ৔ॴ͸
 ݶΒΕ͍ͯΔ w ؂ࢹର৅͕؂ࢹՄೳͳΠϯλʔϑΣʔεΛ
 උ͍͑ͯͳ͍ͱ͍͚ͳ͍ͨΊ w .7 ΞʔΩςΫνϟͳΒɺ.PEFM͸؂ࢹՄೳͳ
 ΠϯλʔϑΣʔεΛ͍࣋ͬͯΔ͸ͣͳͷͰɺ.PEFMʹ


    ΤϥʔϨϙʔλʔΛ૊ΈࠐΈ΍͍͢͸ͣ w 'MVYΞʔΩςΫνϟͷ৔߹͸4UPSF͕؂ࢹՄೳͳ
 ΠϯλʔϑΣʔεΛ͍࣋ͬͯΔ͸ͣ
  39. .PEFM 7JFX &SSPS3FQPSUFS ؂ࢹ ؂ࢹ Τϥʔ͕ൃੜ͠·ͨ͠

  40. .PEFM 7JFX &SSPS3FQPSUFS ؂ࢹ ؂ࢹ ΤϥʔΛදࣔ͠·͢ ΤϥʔϨϙʔτΛ
 ૹ৴͠·͢

  41. func fetch() { switch self.currentState { case .fetching: return case

    .fetched: self.stateMachine.transit(to: .fetching) self.repository.get(by: self.id) .then { user in self.stateMachine.transit(to: .fetched( result: .success(user) )) } .catch { error in self.stateMachine.transit(to: .fetched( result: .failure(.unspecified(debugInfo: "\(error) )) } } } ͜ͷ.PEFM͸"1*ݺͼग़͕͠Τϥʔʹ
 ͳͬͨΒΤϥʔঢ়ଶ΁มԽ͢Δ ͦͷͨΊɺΤϥʔϨϙʔλʔ͔Β؂ࢹ͠΍͍͢ .PEFM૚ͷ"1*ݺͼग़͠ͷίʔυ ͜ͷΑ͏ͳ.PEFM૚ͷઃܭͷৄࡉ͸IUUQTHPPHM1G'+-Λࢀর͍ͯͩ͘͠͞
  42. class UserModelErrorReporter { private let model: UserModelProtocol private let errorReporter:

    ErrorReporterProtocol private let disposeBag = RxSwift.DisposeBag() init( observing model: UserModelProtocol, reportingBy errorReporter: ErrorReporterProtocol ) { self.model = model self.errorReporter = errorReporter self.model.didChange .subscribe(onNext: { [weak self] (state: UserModelState) in guard let `self` = self else { return } switch state { case let .fetched(result: .failure(error)): self.errorReporter.report(error: error, reporter: self) default: return } }) .disposed(by: disposeBag) } } .PEFM͕Τϥʔঢ়ଶʹͳͬͨΒ
 ΤϥʔϨϙʔτΛૹ৴ .PEFMͷঢ়ଶભҠΛ؂ࢹ .PEFMΛ؂ࢹ͢ΔΤϥʔϨϙʔλʔΛ࡞੒
  43. ͜͜·Ͱͷ·ͱΊ w Τϥʔ؂ࢹͷ࣮૷ํ๏ʹ͸
 0CTFSWFSύλʔϯ͕ద͍ͯ͠Δ w .PEFM΍4UPSFΛ؂ࢹ͢ΔͱΑ͍

  44. Τϥʔ؂ࢹͷίπ

  45. Τϥʔ؂ࢹΛ։࢝͢Δͱ
 ࠔౕ͕ͬͨͪΐͪ͘ΐ͘ग़ͯ͘Δ

  46. Fatal Exception: SomethingError *** nil ৘ใྔ͕΄ͱΜͲͳ͍ʂ

  47. Τϥʔʹ͸ͳΔ΂͘
 ΫϦςΟΧϧͳ৘ใΛ٧ΊΑ͏

  48. func example(input: String) -> String? { guard validate1(input) else {

    return nil } guard validate2(input) else { return nil } guard validate3(input) else { return nil } return "OK! Hello \(input)" } ݪҼڀ໌Λ஗͘͢Δѱ͍ྫ ࣦഊͨ͠ݪҼ͕۠ผͰ͖ͳ͍
  49. enum ExampleError: Error { case validate1(debugInfo: String) case validate2(debugInfo: String)

    case validate3(debugInfo: String) } ͦ͏͍͏࣌͸FOVNͰΤϥʔΛ۠ผͰ͖ΔΑ͏ʹ͢Δ
  50. func example(input: String) -> Result<String> { guard validate1(input) else {

    return .failure(.validate1(debugInfo: input)) } guard validate2(input) else { return .failure(.validate2(debugInfo: input)) } guard validate3(input) else { return .failure(.validate3(debugInfo: input)) } return .success("OK! Hello \(input)") } ΤϥʔΛ۠ผ͠΍͍͢Α͍ྫ ࣦഊͨ͠ݪҼ͕۠ผͰ͖Δ ࣦഊͨ͠ࡍͷೖྗ΋ೖखͰ͖Δ
  51. Τϥʔ؂ࢹͷ݁Ռ

  52. w ϢʔβʔͷखݩͰൃੜ͍ͯ͠Δόάͷछྨ΍
 ن໛Λ೺ѲͰ͖ΔΑ͏ʹͳͬͨ w छྨ΍ن໛͕೺ѲͰ͖ΔΑ͏ʹͳΔͱɺόάमਖ਼ͷ
 τϦΞʔδΛ΍Γ΍͘͢ͳΔ w ՝ۚܥ͸࠷༏ઌͱ͔ɺΤϥʔ਺ͷଟ͍΋ͷ
 ͔ΒରॲͳͲ

  53. w ϢʔβʔͷखݩͰ͸༧૝֎ͷΤϥʔ͕ى͍ͬͯͨ͜ w ςελʔʹΑΔಈ࡞֬ೝͱ͸؀ڥ͕
 ҟͳ͍ͬͯͨͨΊɺൃݟͰ͖ͳ͔ͬͨΑ͏ͩ w ໘ന͍͜ͱʹɺ"QQMFͷϨϏϡʔΞ͕ૺ۰ͨ͠
 όάͷݪҼڀ໌ʹ΋໾ཱͬͨ w 4BOECPYͷϨγʔτ͡Όͳ͍ͱ࠶ݱ͠ͳ͍


    όάͩͬͨ
  54. w خ͍͠෭࡞༻ͱͯ͠ɺςελʔͷૺ۰ͨ͠όάͷ
 ৄࡉ͕ೖखͰ͖ΔΑ͏ʹͳͬͨ w ςελʔͷಈ࡞֬ೝதʹΤϥʔϨϙʔτ͕
 ඈΜͰ͘ΔͨΊʢ%&#6(ϏϧυͳͷͰ͙͢Θ͔Δʣ w σόοά͕͍͢͝ḿΓ·͢

  55. ·ͱΊ w Τϥʔ؂ࢹʹΑΓɺςελʔʹΑΔಈ࡞֬ೝͰ΋
 ݟ͚ͭΒΕͳ͔ͬͨΤϥʔΛൃݟͰ͖ΔΑ͏ʹ
 ͳͬͨ w ઃܭͷίπ͸ҎԼͷͭɿ w 0CTFSWFSύλʔϯΛ࢖͏ w

    ͳΔ΂͘Τϥʔ৘ใΛΘ͔Γ΍͘͢͢Δ
  56. એ఻ w %JWFSTFͰ͸৽نࣄۀͷJ04։ൃνʔϜͷ
 νʔϜϦʔμʔΛืू͍ͯ͠·͢ʂ w ੒௕தͷαʔϏεΛࣗ෼ͷྗͰಋ͍ͯΈ͍ͨͱ
 ࢥ͏ํʹɺͥͻ͖ͯ΄͍͠ͱࢥ͍ͬͯ·͢ʂ w ͝ڵຯ͕͋Γ·ͨ͠ΒɺҰॹʹϥϯνʹ
 ߦ͖·ͤΜ͔ʁ