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

Java Source Context #633

Closed
Tracked by #42 ...
fprochazka opened this issue Nov 6, 2018 · 16 comments
Closed
Tracked by #42 ...

Java Source Context #633

fprochazka opened this issue Nov 6, 2018 · 16 comments
Assignees
Labels
enhancement New feature or request Platform: Java
Milestone

Comments

@fprochazka
Copy link

fprochazka commented Nov 6, 2018

Edit by maintainers: We've put Java Source Context on our roadmap. More details and progress can be found in a comment further down (#633 (comment))

Summary

I would like to be able to see sources in stacktraces of java exceptions, the same way they're visible for javascript.

Motivation

This would improve my ability to see the problem in the context of my application.

Additional Context

Sentry java client, should be able to access classpath of the running application and build process should be able to provide versions of installed libraries and built artefacts. Combining these two informations, Sentry should be able to render source code of Java. This would allow you to render on at least some frames of the stacktrace (there will always be generated/proxy frames, that will have no "visible" source code available).

By uploading correct artifact (with sources) to a release, I should be able fill in the main part - source code of my own application.

And it would be ideal, if fetching "public" maven jars with sources could be a native part of Sentry, since these libraries take up a massive amount of disk storage, and if they would duplicate on each release, this would be unfeasible. Having a shared cache between releases would be ideal.

Refs

@yurik94
Copy link

yurik94 commented Sep 3, 2019

Still not possible?

@bruno-garcia bruno-garcia changed the title Java Source Maps Java Source Context Sep 22, 2019
@bruno-garcia bruno-garcia added the enhancement New feature or request label Sep 22, 2019
@maciejwalkowiak maciejwalkowiak added this to the Backlog milestone Oct 1, 2020
@arvkonstantin
Copy link

Still not possible?

@marandaneto marandaneto added this to Backlog in Mobile Platform Team Archived via automation Jun 30, 2021
@marandaneto marandaneto moved this from Backlog to Needs Discussion in Mobile Platform Team Archived Jun 30, 2021
@bruno-garcia bruno-garcia moved this from Needs Discussion to Backlog in Mobile Platform Team Archived Jun 30, 2021
@romtsn
Copy link
Member

romtsn commented Feb 16, 2022

Still not possible?

@mitsuhiko
Copy link
Member

So I tried doing this a few years ago and mainly ran into complications with the tooling and introspection. For source context to work we would like to ideally piggyback on the source bundle system we use for a few other platforms. For this to work we need to do the following things:

  • send up the ID of the source bundle with the event is sent
  • get the file name of the file that declares a java class
  • look up the source by file name in the source bundle

There are a handful of restrictions unfortunately. The biggest one is that the file name does not actually exist in the stack trace. This is not an unsolvable problem as we could derive made up file names based on the class path (replace . with / and add .java to the end). However I was unable to actually create the source bundle in a good way as there (at the time) did not appear to be a standardized way to hook into the .jar creation process to extract the source files.

There were also too many different build systems (gradle, maven etc.) to make this work and a lot of code uses published maven archives and it was unclear how we get access to those sources.

So maybe a new approach could be found where we leverage the gradle plugin for Android as a first step and start creating source bundles there. For the build ID to give the bundle we could use a similar system as we already use for the proguard files. Then we have a starting point and can see if there is a way to expand it from there.

@fprochazka
Copy link
Author

fprochazka commented May 31, 2022

The build system is aware of the full dependency tree for a given JAR artifact and a custom maven/gradle plugin should be able to dump the info into some resource. If the format is simple enough, I believe the community would have no problem providing implementations for less common build systems.

Once you have that info, you can attach it to the event, or publish it as a release artifact (better).

Creating a hack that would derive filenames from class paths is IMHO a bad idea, as JVM doesn't really care much about filenames, it cares about classpath/modulepath. The final classpath/modulepath can be also derived from build system and included in the artefact describing the dependencies. Once you know the installed dependencies and the final classpath, you can then fetch the dependencies and lookup the correct file using the classpath.

Most artifacts can be found on public maven registry, but some will be privately published on company's internal package registry, which would make fetching the sourcecode a bit harder.

@maciejwalkowiak
Copy link
Contributor

Most artifacts can be found on public maven registry, but some will be privately published on company's internal package registry, which would make fetching the sourcecode a bit harder.

I think it's fair to assume that if a company uses internal package registry, to get source context support they must publish there sources jars as well.

@adinauer
Copy link
Member

adinauer commented Mar 13, 2023

We have released Java Source context for Android, Backends, Desktop, ... now. Below you can find some more details on how this works under the hood, limitations, etc.

Docs

Known limitations

  • We only support Gradle and Maven at the moment. You may invoke sentry-cli manually to enjoy Source Context with other build tools. If you're interested, please take a look at the "Implementation Details" in this comment
  • Multiple files with same name but different extension will lead to undefined behaviour
    • e.g. MainActivity.java and MainActivity.kt will both be renamed to MainActivity.jvm
  • Package declaration and file tree must match for source lookup to work
    • e.g. a class io.sentry.sample.MainActivity.java has to be stored in io/sentry/sample
  • Kotlin files may contain multiple classes but this feature may be broken by code obfuscation tools like ProGuard or R8
  • For AGP < 7.4 we don't add generated sources yet
  • For the Maven plugin you have to manually download sentry-cli and point the plugin to it

Minimum required versions

Follow Up Tasks

  • Release as experimental
  • Docs
  • Maven Plugin
  • Gradle Plugin for Backend builds
  • Speed up plugin e.g. by symlinking files instead of copying; needs test on how slow this actually is for larger projects - if you have feedback, please leave a comment 🙏

What it looks like in Sentry UI

Screenshot ![Screenshot 2023-03-29 at 10 39 36](https://user-images.githubusercontent.com/2542832/228498054-247a67cc-8a02-4888-9603-aa9084e66e6d.png)

Implementation Details

Implementation Details ### During build time

In Build System

  • PR: Bundle Java Sources and upload to Sentry sentry-android-gradle-plugin#472
  • In build system (gradle, SAGP for starters) we have to hook into the build and gain access to the sources
  • Copy sources into a build/reports/sentry-source-bundle/<VARIANT>/intermediates/sentry/source-to-bundle dir
  • Call sentry-cli difutil bundle-jvm <PATH> to bundle sources
  • Call sentry-cli upload-dif --type=jvm <SOURCE_BUNDLE.ZIP> to upload source bundle
  • Embed bundle ID into AndroidManifest so it can be read at runtime

sentry-cli changes

Relay changes

Changes in Sentry

  • New processor for adding context_line, pre_context and post_context to stack frames (Add Java Source context to stack frames sentry#46497)
    • This delegates to the existing JavaStacktraceProcessor which uses ProGuard / R8 mapping files to deobfuscate code before looking up source context

During runtime

For matching running code to source

  • While bundling sources we replace the real file extension with a fake one (.jvm)
  • We look at module of the stack frame
  • We look at abs_path (sent as filename by Java SDK)
    • If it looks valid we extract the package from module and convert it to a path (. -> /) and append abs_path to obtain the source path to look up in the bundle, replacing the file extension with .jvm. So module=io.sentry.samples.spring.boot.AnotherService$InnerClassOfAnotherService$InnerClassLvl2OfAnotherService and abs_path=PersonController.kt becomes io/sentry/samples/spring/boot/PersonController.jvm
    • If it looks obfuscated (contains $) we ignore abs_path and use only module for obtaining the path of the source file by stripping possible inner classes ($...) from the class name and appending .jvm. So io.sentry.samples.spring.boot.AnotherService$InnerClassOfAnotherService$InnerClassLvl2OfAnotherService becomes io/sentry/samples/spring/boot/AnotherService.jvm

Other TODOs

  • Might need to update manifest structure of source bundles to support dotted notation instead of paths
  • Probably won't need symbolicator but there's no way to just look up source code yet, may need to change that Changes are only done in python part of Sentry, see above
  • We may need a new debug file type: jvm

What do jvm source bundles look like?

Entries are source files in a tree structure with file extension replaced by .jvm. In combination with code obfuscation tools like ProGuard or R8 we sometimes encounter filenames for stack frames that are obfuscated / made up. This means we can't know the file extension of the source file. In order not to have to guess the file extension we decided to use a fake extension (.jvm) instead. This means MainActivity.java is renamed to MainActivity.jvm and SampleActivity.kt is renamed to SampleActicity.jvm.

Example of contents:

  • io/sentry/sample/MainActivity.java
    • stored as io/sentry/sample/MainActivity.jvm
    • shows up in stack frame as io.sentry.sample.MainActivity
  • io/sentry/sample/SampleActivity.kt
    • stored as io/sentry/sample/SampleActivity.jvm
    • shows up in stack frame as io.sentry.sample.SampleActivity

Things to test

  • Android
  • Android with ProGuard turned on
  • Bytecode manipulation on Android
  • Java Agent manipulation on backend
  • Generated code
  • Kotlin code

@adinauer
Copy link
Member

We've just released Source Context support for Android. Please give it a try: https://github.com/getsentry/sentry-android-gradle-plugin/releases/tag/3.7.0 (which uses https://github.com/getsentry/sentry-java/releases/tag/6.19.0)

Any feedback is welcome 🙏

We're still working on adding Source Context for Backend etc. - stay tuned.

@beheh
Copy link

beheh commented May 26, 2023

I'm not quite sure where to ask this, but I thought here was more appropriate than the sentry-android-gradle-plugin PR and because you specifically asked for feedback:

Is there a path to source code uploading for Android with isMinifyEnabled set to false? For example, in a small app with have disabled minification for better stack traces and reproducibility. However it seems right now minification is a requirement for uploading (as per getsentry/sentry-android-gradle-plugin#86).

Or maybe this is more of a hosted Sentry question, where we're used to have source code available for non-processed files in e.g. Python and/or direct links to GitHub. It would be nice if we were able to have a similar experience in the issue interface to see source code directly in Sentry for context.

@romtsn
Copy link
Member

romtsn commented May 26, 2023

I'm not quite sure where to ask this, but I thought here was more appropriate than the sentry-android-gradle-plugin PR and because you specifically asked for feedback:

Is there a path to source code uploading for Android with isMinifyEnabled set to false? For example, in a small app with have disabled minification for better stack traces and reproducibility. However it seems right now minification is a requirement for uploading (as per getsentry/sentry-android-gradle-plugin#86).

Or maybe this is more of a hosted Sentry question, where we're used to have source code available for non-processed files in e.g. Python and/or direct links to GitHub. It would be nice if we were able to have a similar experience in the issue interface to see source code directly in Sentry for context.

The PR linked is rather about uploading proguard mappings, but not the source context. For source context we just hook into the release build type. So if you're running assembleRelease and have the includeSourceContext flag enabled, you should be good.

As for self-hosted - yes you'd have to update to the latest version, as there were some necessary changes on the stacktrace processing.

@beheh
Copy link

beheh commented May 26, 2023

I see now, I spoke too soon. With includeSourceContext explicitly set it all works! The line above ("If you aren't using ProGuard (yet), you'll have to supply some properties.") confused me because it made it sound like ProGuard is a requirement. Thanks!

@adinauer
Copy link
Member

I've added another sentence to clarify ProGuard isn't a requirement for using Source Context.

@adinauer
Copy link
Member

adinauer commented Jun 1, 2023

We have now released Java Source Context support for Backend, Desktop, etc. via Gradle and Maven as well. Please take a look at #633 (comment) for more details.

Docs are coming soon.

@adinauer
Copy link
Member

adinauer commented Jun 9, 2023

Docs shave now been merged and can be found here:

Closing this now. We can do improvements in separate issues.

@androidacy-user
Copy link

androidacy-user commented Jul 20, 2023

Docs shave now been merged and can be found here:

Closing this now. We can do improvements in separate issues.

Um is there a reason we can't specify the URL for self hosted in Gradle?

EDIT: okay so docs don't bother to mention that the usual sentry.properties is still respected for this, instead suggesting you have to specify the org/project/auth token in build.gradle.kts

@adinauer
Copy link
Member

@androidacy-user thanks for bringing this up, we'll improve docs. I've created #2854 to track.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request Platform: Java
Projects
Archived in project
Development

No branches or pull requests