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

[#117] Send Sentry report if file path given for logs is not valid #122

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

ssawchenko
Copy link
Collaborator

@ssawchenko ssawchenko commented Aug 15, 2023

Related Issue: #117

Summary of Problem:

We are seeing issues where some of the standard file paths are not actually valid on some devices - and as such writes to these file locations are failing. We would like Steamclog to report when this is occurring for it's log files.

Proposed Solution:

  • If the file path given is null or does not exist, fire an error report to Sentry
  • To avoid possible noisy reports for this issue, I have tried to use a DataStore to store a value that lets us know if we've already logged this issue.
    • Since this is my first time using DataStores, I added a quick test in the sample app to make sure that app's can have their own DataStores independent from the SClog one (they can).
  • Sample app allows us to toggle between valid and invalid file paths

Note, #121 was created so that we can investigate further error handling around User Reports not being sent.

Testing Steps:

  1. Delete all sample Sentry logs by going to https://steamclock-software.sentry.io/issues/?project=5399932, selecting all issues, pressing the triple dot button and selecting "Delete"
  2. Uninstall sample app if its on your device and reinstall
  3. When app opens, change the log level to "Release" (to enable sending Sentry reports)
  4. "Show file dump" and make sure that a log file is shown and there is "Full Content"
  5. Check the "Force invalid file write path" checkbox
  6. Press "Show file dump" again and make sure that no files are listed
  7. Go back to the Sentry dashboard, wait a few seconds (reloading page helps sometimes)
  8. Make sure a report now exists indicating that the file could not be created
    image
  9. Toggle the checkbox multiple times
  10. Go back to the Sentry dashboard, wait a few seconds (reloading page helps sometimes)
  11. Make sure no new reports were created; there should only be the one report of the failure

logInternal(LogLevel.Warn, sentryErrorTitle)
}
} catch (e: Exception) {
logInternal(LogLevel.Info, "Could not read local DataStore")
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Safeguarding against any error that might occur with DataStore; since this is the first I've worked with it I want to make sure that failures with it do cause the app to crash.

// call the datastore methods; we need to use this with caution, but
// https://developer.android.com/topic/libraries/architecture/datastore#synchronous
val dataStore = SClogDataStore(context)
val alreadyReportedFailure = runBlocking { dataStore.getHasReportedFilepathError.first() }
Copy link

Choose a reason for hiding this comment

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

This will cause whoever calls initWith() to be blocked.
Can we instead create a new coroutine scope (eg. something like SupervisorJob() + Dispatchers.IO) and wrap the whole try-catch inside a suspend function that can be called inside a coroutine launched by the newly created coroutine scope?
eg.

newCoroutineScope.launch { doInitWith() }
....
private suspend fun doInitWith() { try-catch etc.... }

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good call. I didn't make the entire method suspend (Timber documentation for planting/updating trees has historically been called from the main thread I believe, at least it's been that way since first implementation)... but I did move the code that checked for file access errors and called runBlocking on the dataStore to be in a new coroutine scope.

launch {
// Doesn't need to be lifecycle aware for this test
val timestamp =
AppDataStore(baseContext).apply {
Copy link

Choose a reason for hiding this comment

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

curious to know why baseContext is passed in here here, but applicationContext is passed into SClogDataStore

Copy link
Collaborator Author

@ssawchenko ssawchenko Aug 22, 2023

Choose a reason for hiding this comment

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

Hrm. Honestly I think that was a discrepancy I missed. Because we're using the context to lookup the datastore instance I don't think it matters which we use (although I could be wrong), but you're right, to be safe we should probably ensure we are using the same context. The examples I have found all use Context, but seem to be passing in the application context. To make this completely explicit I have changed my code to request the Application instead of the Context (Application is a ContextWrapper); I don't know if there's ramifications to this, but it seems to behave the same.

Copy link

Choose a reason for hiding this comment

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

passing in Application is the right call. Activity Context is tied to the Activity lifecycle, so we don't want any potential issues from carrying out operations tied to an Activity lifecycle.

@phileo phileo removed their assignment Aug 17, 2023
@ssawchenko
Copy link
Collaborator Author

@phileo Hopefully I've addressed your comments. I've been playing around with DataStore a little more this afternoon and there's something I still don't like about having to pass in the Application/Context into the containing class, but I all of the examples I have found that create a DataStoreRepository type equivalent are doing it, so I guess that's the way forward for now.

Copy link

@phileo phileo left a comment

Choose a reason for hiding this comment

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

re-tested, everything looks good! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants