Skip to content
This repository was archived by the owner on Aug 30, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f107786
Initial Sentry Spring Boot Starter.
maciejwalkowiak Aug 14, 2020
58aab5a
Pass request and user data to Sentry event.
maciejwalkowiak Aug 18, 2020
b4ef72e
Set cookies on SentryEvent.
maciejwalkowiak Aug 18, 2020
b15ae23
Merge branch 'feat/sentry-java' into sentry-java-spring-boot
maciejwalkowiak Aug 20, 2020
f2b950e
Generate properties files from Spring Boot auto-configuration and con…
maciejwalkowiak Aug 20, 2020
fada413
Raise required test coverage to 60%
maciejwalkowiak Aug 21, 2020
3588a51
Refactor & add more auto-configuration tests.
maciejwalkowiak Aug 21, 2020
7714510
Polish.
maciejwalkowiak Aug 24, 2020
73a4d56
Add more auto-configuration tests.
maciejwalkowiak Aug 24, 2020
b9adb07
Set Git commit id as release version.
maciejwalkowiak Aug 24, 2020
fa510f3
Improve printing failed tests reason.
maciejwalkowiak Aug 24, 2020
fdb3b7e
Await assertions as events are sent asynchronously.
maciejwalkowiak Aug 24, 2020
1acf623
Polish.
maciejwalkowiak Aug 24, 2020
1219ad1
Catch exceptions thrown by event processors.
maciejwalkowiak Aug 24, 2020
fd609ce
Polish.
maciejwalkowiak Aug 24, 2020
f5490cc
Fix attaching request and user information for uncaught exceptions.
maciejwalkowiak Aug 25, 2020
21bc5be
Add Sentry Spring Boot Sample.
maciejwalkowiak Aug 25, 2020
c2f7f57
Add Sentry Spring Boot Sample.
maciejwalkowiak Aug 25, 2020
f8c5352
Import classes.
maciejwalkowiak Aug 25, 2020
75f8d55
Polish.
maciejwalkowiak Aug 25, 2020
5a7f15e
Polish.
maciejwalkowiak Aug 25, 2020
85ad96d
Polish.
maciejwalkowiak Aug 26, 2020
6cba5b5
Polish.
maciejwalkowiak Aug 26, 2020
8e6ad89
Change group id in Spring Boot Sample.
maciejwalkowiak Aug 26, 2020
bb863f2
Handle multiple ip addresses in X-FORWARDED-FOR header.
maciejwalkowiak Aug 26, 2020
41b845e
Polish Gradle configuration.
maciejwalkowiak Aug 26, 2020
cb0a1a9
Polish Gradle configuration.
maciejwalkowiak Aug 26, 2020
c311428
Polish Gradle configuration.
maciejwalkowiak Aug 26, 2020
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
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ allprojects {
dependsOn("cleanTest")
}
withType<JavaCompile> {
options.compilerArgs.addAll(arrayOf("-Xlint:all", "-Werror", "-Xlint:-classfile"))
options.compilerArgs.addAll(arrayOf("-Xlint:all", "-Werror", "-Xlint:-classfile", "-Xlint:-processing"))
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@ object Config {
val kotlinVersion = "1.3.72"
val kotlinStdLib = "stdlib-jdk8"

val springBootVersion = "2.3.3.RELEASE"

object BuildPlugins {
val androidGradle = "com.android.tools.build:gradle:4.0.1"
val kotlinGradlePlugin = "gradle-plugin"
val buildConfig = "com.github.gmazzo.buildconfig"
val buildConfigVersion = "2.0.2"
val springBoot = "org.springframework.boot"
val springDependencyManagement = "io.spring.dependency-management"
val springDependencyManagementVersion = "1.0.10.RELEASE"
}

object Android {
Expand Down Expand Up @@ -35,6 +40,16 @@ object Config {

val logbackVersion = "1.2.3"
val logbackClassic = "ch.qos.logback:logback-classic:$logbackVersion"

val springBootStarter = "org.springframework.boot:spring-boot-starter:$springBootVersion"

val springWeb = "org.springframework:spring-webmvc"
val servletApi = "javax.servlet:javax.servlet-api"
}

object AnnotationProcessors {
val springBootAutoConfigure = "org.springframework.boot:spring-boot-autoconfigure-processor"
val springBootConfiguration = "org.springframework.boot:spring-boot-configuration-processor"
}

object TestLibs {
Expand All @@ -47,6 +62,9 @@ object Config {
val robolectric = "org.robolectric:robolectric:4.3.1"
val mockitoKotlin = "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
val awaitility = "org.awaitility:awaitility-kotlin:4.0.3"
val springBootStarterTest = "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
val springBootStarterWeb = "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
val springBootStarterSecurity = "org.springframework.boot:spring-boot-starter-security:$springBootVersion"
}

object QualityPlugins {
Expand All @@ -66,6 +84,7 @@ object Config {
val SENTRY_JAVA_SDK_NAME = "sentry.java"
val SENTRY_ANDROID_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.android"
val SENTRY_LOGBACK_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.logback"
val SENTRY_SPRING_BOOT_SDK_NAME = "$SENTRY_JAVA_SDK_NAME.spring-boot"
val group = "io.sentry"
val description = "SDK for sentry.io"
val website = "https://sentry.io"
Expand Down
11 changes: 11 additions & 0 deletions sentry-core/src/main/java/io/sentry/core/Sentry.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -123,6 +124,16 @@ public static void init(
init(options, globalHubMode);
}

/**
* Initializes the SDK with a SentryOptions.
*
* @param options options the SentryOptions
*/
@ApiStatus.Internal
public static void init(final @NotNull SentryOptions options) {
init(options, GLOBAL_HUB_DEFAULT_MODE);
}

/**
* Initializes the SDK with a SentryOptions and globalHubMode
*
Expand Down
60 changes: 34 additions & 26 deletions sentry-core/src/main/java/io/sentry/core/SentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import org.jetbrains.annotations.ApiStatus;
Expand Down Expand Up @@ -78,19 +79,7 @@ public SentryClient(final @NotNull SentryOptions options, @Nullable Connection c
.log(SentryLevel.DEBUG, "Event was cached so not applying scope: %s", event.getEventId());
}

for (EventProcessor processor : options.getEventProcessors()) {
event = processor.process(event, hint);

if (event == null) {
options
.getLogger()
.log(
SentryLevel.DEBUG,
"Event was dropped by processor: %s",
processor.getClass().getName());
break;
}
}
event = processEvent(event, hint, options.getEventProcessors());

if (event == null) {
return SentryId.EMPTY_ID;
Expand Down Expand Up @@ -127,6 +116,37 @@ public SentryClient(final @NotNull SentryOptions options, @Nullable Connection c
return event.getEventId();
}

@Nullable
private SentryEvent processEvent(
@NotNull SentryEvent event,
final @Nullable Object hint,
final @NotNull List<EventProcessor> eventProcessors) {
for (EventProcessor processor : eventProcessors) {
try {
event = processor.process(event, hint);
} catch (Exception e) {
options
.getLogger()
.log(
SentryLevel.ERROR,
e,
"An exception occurred while processing event by processor: %s",
processor.getClass().getName());
}

if (event == null) {
options
.getLogger()
.log(
SentryLevel.DEBUG,
"Event was dropped by a processor: %s",
processor.getClass().getName());
break;
}
}
return event;
}

/**
* Updates the session data based on the event, hint and scope data
*
Expand Down Expand Up @@ -274,19 +294,7 @@ public void captureSession(final @NotNull Session session, final @Nullable Objec
event.setLevel(scope.getLevel());
}

for (EventProcessor processor : scope.getEventProcessors()) {
event = processor.process(event, hint);

if (event == null) {
options
.getLogger()
.log(
SentryLevel.DEBUG,
"Event was dropped by scope processor: %s",
processor.getClass().getName());
break;
}
}
event = processEvent(event, hint, scope.getEventProcessors());
}
return event;
}
Expand Down
8 changes: 8 additions & 0 deletions sentry-core/src/test/java/io/sentry/core/SentryClientTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import io.sentry.core.transport.AsyncConnection
import io.sentry.core.transport.HttpTransport
import io.sentry.core.transport.ITransportGate
import java.io.IOException
import java.lang.RuntimeException
import java.net.URL
import java.util.UUID
import kotlin.test.Ignore
Expand Down Expand Up @@ -655,6 +656,13 @@ class SentryClientTest {
}, anyOrNull())
}

@Test
fun `exception thrown by an event processor is handled gracefully`() {
fixture.sentryOptions.addEventProcessor { _, _ -> throw RuntimeException() }
val sut = fixture.getSut()
sut.captureEvent(SentryEvent())
}

private fun createScope(): Scope {
return Scope(SentryOptions()).apply {
addBreadcrumb(Breadcrumb().apply {
Expand Down
40 changes: 40 additions & 0 deletions sentry-samples/sentry-samples-spring-boot/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
id(Config.BuildPlugins.springBoot) version Config.springBootVersion
id(Config.BuildPlugins.springDependencyManagement) version Config.BuildPlugins.springDependencyManagementVersion
kotlin("jvm")
kotlin("plugin.spring") version Config.kotlinVersion
}

group = "io.sentry.sample.spring-boot"
version = "0.0.1-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8

repositories {
mavenCentral()
}

dependencies {
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation(project(":sentry-spring-boot-starter"))
implementation(project(":sentry-logback"))
testImplementation("org.springframework.boot:spring-boot-starter-test") {
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
}
}

tasks.withType<Test> {
useJUnitPlatform()
}

tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we need this? just curiosity

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's also added by start.spring.io - i think it's meant to tell Kotlin to respect Spring Framework nullability related annotations.

jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.sentry.samples.spring;

public class Person {
private final String firstName;
private final String lastName;

public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

public String getFirstName() {
return firstName;
}

public String getLastName() {
return lastName;
}

@Override
public String toString() {
return "Person{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + '}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package io.sentry.samples.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/person/")
public class PersonController {
private static final Logger LOGGER = LoggerFactory.getLogger(PersonController.class);

@GetMapping("{id}")
Person person(@PathVariable Long id) {
throw new IllegalArgumentException("Something went wrong [id=" + id + "]");
}

@PostMapping
Person create(@RequestBody Person person) {
LOGGER.warn("Creating person: {}", person);
return person;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package io.sentry.samples.spring;

import io.sentry.core.IHub;
import io.sentry.spring.boot.SentrySecurityFilter;
import org.jetbrains.annotations.NotNull;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

private final @NotNull IHub hub;

public SecurityConfiguration(final @NotNull IHub hub) {
this.hub = hub;
}

@Override
protected void configure(final @NotNull HttpSecurity http) throws Exception {
// register SentrySecurityFilter to attach user information to SentryEvents
http.addFilterAfter(new SentrySecurityFilter(hub), AnonymousAuthenticationFilter.class)
.csrf()
.disable()
.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}

@Bean
@Override
public @NotNull UserDetailsService userDetailsService() {
final PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

final UserDetails user =
User.builder()
.passwordEncoder(encoder::encode)
.username("user")
.password("password")
.roles("USER")
.build();

return new InMemoryUserDetailsManager(user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.sentry.samples.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SentryDemoApplication {

public static void main(String[] args) {
SpringApplication.run(SentryDemoApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# NOTE: Replace the test DSN below with YOUR OWN DSN to see the events from this app in your Sentry project/dashboard
sentry.dsn=https://f7f320d5c3a54709be7b28e0f2ca7081@sentry.io/1808954
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml" />

<appender name="SENTRY" class="io.sentry.logback.SentryAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>WARN</level>
</filter>
</appender>

<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="SENTRY" />
</root>
</configuration>
Loading