Skip to content
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

Track non-fatal issues via analytics / Sentry if consent provided #6308

Merged
merged 2 commits into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ abstract_target 'RiotPods' do

# PostHog for analytics
pod 'PostHog', '~> 1.4.4'
pod 'Sentry', '~> 7.15.0'
pod 'AnalyticsEvents', :git => 'https://github.com/matrix-org/matrix-analytics-events.git', :branch => 'release/swift', :inhibit_warnings => false
# pod 'AnalyticsEvents', :path => '../matrix-analytics-events/AnalyticsEvents.podspec'

Expand Down
8 changes: 7 additions & 1 deletion Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ PODS:
- Reusable/View (= 4.1.2)
- Reusable/Storyboard (4.1.2)
- Reusable/View (4.1.2)
- Sentry (7.15.0):
- Sentry/Core (= 7.15.0)
- Sentry/Core (7.15.0)
- SideMenu (6.5.0)
- SwiftBase32 (0.9.0)
- SwiftGen (6.5.1)
Expand Down Expand Up @@ -126,6 +129,7 @@ DEPENDENCIES:
- PostHog (~> 1.4.4)
- ReadMoreTextView (~> 3.0.1)
- Reusable (~> 4.1)
- Sentry (~> 7.15.0)
- SideMenu (~> 6.5)
- SwiftBase32 (~> 0.9.0)
- SwiftGen (~> 6.3)
Expand Down Expand Up @@ -169,6 +173,7 @@ SPEC REPOS:
- ReadMoreTextView
- Realm
- Reusable
- Sentry
- SideMenu
- SwiftBase32
- SwiftGen
Expand Down Expand Up @@ -223,6 +228,7 @@ SPEC CHECKSUMS:
ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d
Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2
Reusable: 6bae6a5e8aa793c9c441db0213c863a64bce9136
Sentry: 63ca44f5e0c8cea0ee5a07686b02e56104f41ef7
SideMenu: f583187d21c5b1dd04c72002be544b555a2627a2
SwiftBase32: 9399c25a80666dc66b51e10076bf591e3bbb8f17
SwiftGen: a6d22010845f08fe18fbdf3a07a8e380fd22e0ea
Expand All @@ -235,6 +241,6 @@ SPEC CHECKSUMS:
zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c
ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb

PODFILE CHECKSUM: fdddeaf3f403004b1cc6200add1b6b9e00d40906
PODFILE CHECKSUM: b3c7c064fc2b74dc937762364faab403fc3fd041

COCOAPODS: 1.11.2
17 changes: 15 additions & 2 deletions Riot/Modules/Analytics/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
import PostHog
import AnalyticsEvents

/// A class responsible for managing an analytics client
/// and sending events through this client.
/// A class responsible for managing a variety of analytics clients
/// and sending events through these clients.
///
/// Events may include user activity, or app health data such as crashes,
/// non-fatal issues and performance. `Analytics` class serves as a façade
/// to all these use cases.
///
/// ## Creating Analytics Events
///
Expand All @@ -42,6 +46,9 @@ import AnalyticsEvents
/// The analytics client to send events with.
private var client: AnalyticsClientProtocol = PostHogAnalyticsClient()

/// The monitoring client to track crashes, issues and performance
private var monitoringClient = SentryMonitoringClient()

/// The service used to interact with account data settings.
private var service: AnalyticsService?

Expand Down Expand Up @@ -106,6 +113,7 @@ import AnalyticsEvents
// The order is important here. PostHog ignores the reset if stopped.
reset()
client.stop()
monitoringClient.stop()

MXLog.debug("[Analytics] Stopped.")
}
Expand All @@ -115,6 +123,7 @@ import AnalyticsEvents
guard RiotSettings.shared.enableAnalytics, !isRunning else { return }

client.start()
monitoringClient.start()

// Sanity check in case something went wrong.
guard client.isRunning else { return }
Expand Down Expand Up @@ -163,6 +172,7 @@ import AnalyticsEvents
/// Note: **MUST** be called before stopping PostHog or the reset is ignored.
func reset() {
client.reset()
monitoringClient.reset()
MXLog.debug("[Analytics] Reset.")
RiotSettings.shared.isIdentifiedForAnalytics = false

Expand Down Expand Up @@ -374,4 +384,7 @@ extension Analytics: MXAnalyticsDelegate {
capture(event: event)
}

func trackNonFatalIssue(_ issue: String, details: [String : Any]?) {
monitoringClient.trackNonFatalIssue(issue, details: details)
}
}
67 changes: 67 additions & 0 deletions Riot/Modules/Analytics/SentryMonitoringClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import Sentry
import MatrixSDK

/// Sentry client used as part of the Analytics set of tools to track health metrics
/// of the application, such as crashes, non-fatal issues and performance.
///
/// All analytics tracking, incl. health metrics, is subject to user consent,
/// configurable in user settings.
struct SentryMonitoringClient {
private static let sentryDSN = "https://a5e37731f9b94642a1b93093cacbee4c@sentry.tools.element.io/47"

func start() {
guard !SentrySDK.isEnabled else { return }

MXLog.debug("[SentryMonitoringClient] Started")
SentrySDK.start { options in
options.dsn = Self.sentryDSN
options.tracesSampleRate = 1.0

options.beforeSend = { event in
MXLog.error("[SentryMonitoringClient] Issue detected: \(event)")
return event
}

options.onCrashedLastRun = { event in
MXLog.debug("[SentryMonitoringClient] Last run crashed: \(event)")
}
}
}

func stop() {
MXLog.debug("[SentryMonitoringClient] Stopped")
SentrySDK.close()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if SentrySDK.endSession makes more sense here. Not sure but feels like close is an unrecoverable action.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

endSession only really rotates session IDs etc, but keeps the Sentry service running. Having tested this out close is not in fact unrecoverable and SentrySDK can be properly restarted again.

}

func reset() {
MXLog.debug("[SentryMonitoringClient] Reset")
SentrySDK.startSession()
}

func trackNonFatalIssue(_ issue: String, details: [String: Any]?) {
guard SentrySDK.isEnabled else { return }

let event = Event()
event.level = .error
event.message = .init(formatted: issue)
event.extra = details
SentrySDK.capture(event: event)
}
}
1 change: 1 addition & 0 deletions changelog.d/pr-6308.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Analytics: Track non-fatal issues if consent provided