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

Performance Feature #971

Merged
merged 74 commits into from
Nov 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
e418a43
Make serialization and envelope item creation more generic to handle …
maciejwalkowiak Oct 8, 2020
fb2dd7e
Polish.
maciejwalkowiak Oct 9, 2020
d90e6f5
Add ability to send transaction.
maciejwalkowiak Oct 9, 2020
a8f183c
Remove ConvertibleToEnvelopeItem.java.
maciejwalkowiak Oct 9, 2020
f60144e
Polish.
maciejwalkowiak Oct 12, 2020
1da297b
Add more options to TransactionContexts.
maciejwalkowiak Oct 12, 2020
725eca0
WIP
maciejwalkowiak Oct 14, 2020
0ca29c1
Merge remote-tracking branch 'origin/main' into performance
maciejwalkowiak Oct 22, 2020
2dacfb7
Polish domain classes.
maciejwalkowiak Oct 23, 2020
2fe4df3
Test SentryClient captureTransaction.
maciejwalkowiak Oct 23, 2020
41d493f
Make TransactionContexts inherit from Contexts
maciejwalkowiak Oct 23, 2020
7260ae0
Attach Transaction to the scope.
maciejwalkowiak Oct 23, 2020
6f4855b
Polish.
maciejwalkowiak Oct 25, 2020
afd5c04
Return sentry-trace header in Transaction.
maciejwalkowiak Oct 25, 2020
557ac3e
Add span.
maciejwalkowiak Oct 25, 2020
23cd265
Setting transaction on Scope changes transaction name.
maciejwalkowiak Oct 25, 2020
e79a0b0
Add tests.
maciejwalkowiak Oct 26, 2020
a9f2da5
Add changelog.
maciejwalkowiak Oct 26, 2020
d973b8e
Create contexts from the traceparent header.
maciejwalkowiak Oct 26, 2020
0e18588
Rename tx to transaction on Scope.
maciejwalkowiak Oct 26, 2020
86eeb87
Add test.
maciejwalkowiak Oct 26, 2020
9e5180d
Add test.
maciejwalkowiak Oct 26, 2020
3cbb1da
Add missing SpanStatuses.
maciejwalkowiak Oct 26, 2020
d1c3d57
Remove cloning from Transaction and related classes.
maciejwalkowiak Oct 26, 2020
e2e6763
Polish Transaction API.
maciejwalkowiak Oct 26, 2020
e978227
Polish Transaction API.
maciejwalkowiak Oct 26, 2020
f6a6e16
Get current span from the scope.
maciejwalkowiak Oct 27, 2020
bad07df
Rename traceparent to sentryHeader.
maciejwalkowiak Oct 28, 2020
f50f042
Remove platform field from Transaction.
maciejwalkowiak Oct 28, 2020
e0a8f0a
Polish.
maciejwalkowiak Oct 28, 2020
8d62fb1
Throw checked exception when creating contexts from trace header.
maciejwalkowiak Oct 28, 2020
3e2c740
Raname sentryHeader to sentryTrace
maciejwalkowiak Oct 28, 2020
f14de05
Optimise getting span from transaction.
maciejwalkowiak Oct 28, 2020
048a980
Rename Trace to TraceContext.
maciejwalkowiak Oct 28, 2020
e197af5
Add missing methods, move request to SentryBaseEvent, clear transaction.
maciejwalkowiak Oct 29, 2020
4f39a9a
Polish.
maciejwalkowiak Oct 29, 2020
87c070b
Merge remote-tracking branch 'origin/main' into performance
maciejwalkowiak Oct 29, 2020
7dd242a
Rename Transaction to SentryTransaction and reformat code.
maciejwalkowiak Oct 29, 2020
fa0d04c
Merge remote-tracking branch 'origin/main' into performance
maciejwalkowiak Oct 31, 2020
ae84550
Merge remote-tracking branch 'origin/main' into performance
maciejwalkowiak Nov 3, 2020
0d59ee2
Add performance sampling. (#1025)
maciejwalkowiak Nov 4, 2020
0a67f0c
Support deferred sample decision.
maciejwalkowiak Nov 6, 2020
25e221b
Simplify domain model for Performance.
maciejwalkowiak Nov 9, 2020
a8ba82d
Attach events to active spans.
maciejwalkowiak Nov 9, 2020
ec7aa9e
Do not use events sampling strategy for transactions.
maciejwalkowiak Nov 9, 2020
032f6cb
Add beforeTransaction callback.
maciejwalkowiak Nov 9, 2020
5dd3cce
Merge remote-tracking branch 'origin/main' into performance
maciejwalkowiak Nov 9, 2020
12bc6bb
Polish.
maciejwalkowiak Nov 9, 2020
ffa560c
Polish.
maciejwalkowiak Nov 9, 2020
19fc07e
Merge remote-tracking branch 'origin/main' into performance
maciejwalkowiak Nov 10, 2020
1e60688
Drop beforeTransaction
maciejwalkowiak Nov 11, 2020
f1b050a
Add TransactionContext.
maciejwalkowiak Nov 11, 2020
4d1af25
Pass customSamplingContext to startTransaction.
maciejwalkowiak Nov 11, 2020
cd26031
Remove parentSampled from SamplingContext.
maciejwalkowiak Nov 11, 2020
c222864
Fix formatting.
maciejwalkowiak Nov 11, 2020
3c90862
Add ability to attach exception to a span.
maciejwalkowiak Nov 11, 2020
4b7d88f
Polish.
maciejwalkowiak Nov 11, 2020
75ff4bf
Cleanup.
maciejwalkowiak Nov 12, 2020
3b03e3a
Add performance sample.
maciejwalkowiak Nov 13, 2020
52ba943
Fix formatting.
maciejwalkowiak Nov 13, 2020
8a6fe85
Polish.
maciejwalkowiak Nov 16, 2020
ee2bbe4
Reformat.
maciejwalkowiak Nov 16, 2020
b593dc5
Fix thread-safety.
maciejwalkowiak Nov 16, 2020
91ff82b
Move exceptions to `exceptions` package.
maciejwalkowiak Nov 16, 2020
6eba18d
Polish.
maciejwalkowiak Nov 16, 2020
3fd9c6f
Polish.
maciejwalkowiak Nov 16, 2020
aef2463
Code review fixes.
maciejwalkowiak Nov 17, 2020
503c6a4
Fix concurrent usage of getting span from the scope.
maciejwalkowiak Nov 17, 2020
ef16d98
Merge remote-tracking branch 'origin/main' into performance
maciejwalkowiak Nov 17, 2020
4e7e0fb
Merge remote-tracking branch 'origin/main' into performance
maciejwalkowiak Nov 19, 2020
d46dc3c
Add setTag to ISpan.
maciejwalkowiak Nov 20, 2020
b9d65b3
Drop the idea of WeakReference on Span#throwable.
maciejwalkowiak Nov 20, 2020
a568d39
Bring back an old API for SentryEnvelopeItem.
maciejwalkowiak Nov 20, 2020
1bfb3ad
Performance Feature for Spring & Spring Boot (#1024)
maciejwalkowiak Nov 20, 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: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
* Fix broken NDK integration on 3.1.2 (release failed on packaging a .so file)
* Increase max cached events to 30 (#1029)
* Normalize DSN URI (#1030)
* feat: Performance monitoring (#971)
* feat: Performance monitoring for Spring Boot applications

# 3.1.2

Expand Down
3 changes: 3 additions & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,12 @@ object Config {
val springBootStarter = "org.springframework.boot:spring-boot-starter:$springBootVersion"
val springBootStarterTest = "org.springframework.boot:spring-boot-starter-test:$springBootVersion"
val springBootStarterWeb = "org.springframework.boot:spring-boot-starter-web:$springBootVersion"
val springBootStarterAop = "org.springframework.boot:spring-boot-starter-aop:$springBootVersion"
val springBootStarterSecurity = "org.springframework.boot:spring-boot-starter-security:$springBootVersion"

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.util.Log;
import io.sentry.ILogger;
import io.sentry.SentryLevel;
import org.jetbrains.annotations.Nullable;

final class AndroidLogger implements ILogger {

Expand Down Expand Up @@ -43,6 +44,11 @@ public void log(SentryLevel level, String message, Throwable throwable) {
}
}

@Override
public boolean isEnabled(@Nullable SentryLevel level) {
return true;
}

private int toLogcatLevel(SentryLevel sentryLevel) {
switch (sentryLevel) {
case INFO:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import android.os.Bundle
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.nhaarman.mockitokotlin2.mock
import io.sentry.ILogger
import io.sentry.InvalidDsnException
import io.sentry.Sentry
import io.sentry.exception.InvalidDsnException
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertFailsWith
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
import io.sentry.Sentry;
import io.sentry.SentryEvent;
import io.sentry.SentryLevel;
import io.sentry.SentryTransaction;
import io.sentry.Span;
import io.sentry.SpanStatus;
import io.sentry.protocol.Message;
import io.sentry.protocol.User;
import java.util.Collections;

public class Main {

public static void main(String[] args) {
public static void main(String[] args) throws InterruptedException {
Sentry.init(
options -> {
// NOTE: Replace the test DSN below with YOUR OWN DSN to see the events from this app in
Expand Down Expand Up @@ -60,6 +63,21 @@ public static void main(String[] args) {
// Exclude frames from some packages from being "inApp" so are hidden by default in Sentry
// UI:
options.addInAppExclude("org.jboss");

// Performance configuration options
// Set what percentage of traces should be collected
options.setTracesSampleRate(1.0); // set 0.5 to send 50% of traces

// Determine traces sample rate based on the sampling context
options.setTracesSampler(
context -> {
// only 10% of transactions with "/product" prefix will be collected
if (!context.getTransactionContexts().getName().startsWith("/products")) {
return 0.1;
} else {
return 0.5;
}
});
});

Sentry.addBreadcrumb(
Expand Down Expand Up @@ -116,6 +134,30 @@ public static void main(String[] args) {
Sentry.captureEvent(event, SentryLevel.DEBUG);
}

// Performance feature
//
// Transactions collect execution time of the piece of code that's executed between the start
// and finish of transaction.
SentryTransaction transaction = Sentry.startTransaction("transaction name");
// Transactions can contain one or more Spans
Span outerSpan = transaction.startChild();
Thread.sleep(100);
// Spans create a tree structure. Each span can have one ore more spans inside.
Span innerSpan = outerSpan.startChild();
innerSpan.setOperation("jdbc");
innerSpan.setDescription("select * from product where id = :id");
innerSpan.setStatus(SpanStatus.OK);
Thread.sleep(300);
// Finish the span and mark the end time of the span execution.
// Note: finishing spans does not sent them to Sentry
innerSpan.finish();
// Every SentryEvent reported during the execution of the transaction or a span, will have trace
// context attached
Sentry.captureMessage("this message is connected to the outerSpan");
outerSpan.finish();
// marks transaction as finished and sends it together with all child spans to Sentry
transaction.finish();

// All events that have not been sent yet are being flushed on JVM exit. Events can be also
// flushed manually:
// Sentry.close();
Expand Down
2 changes: 2 additions & 0 deletions sentry-samples/sentry-samples-spring-boot/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ repositories {
dependencies {
implementation(Config.Libs.springBootStarterSecurity)
implementation(Config.Libs.springBootStarterWeb)
implementation(Config.Libs.springBootStarterAop)
implementation(Config.Libs.aspectj)
implementation(Config.Libs.springBootStarter)
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation(kotlin(Config.kotlinStdLib, KotlinCompilerVersion.VERSION))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,13 @@
@RestController
@RequestMapping("/person/")
public class PersonController {
private final PersonService personService;
private static final Logger LOGGER = LoggerFactory.getLogger(PersonController.class);

public PersonController(PersonService personService) {
this.personService = personService;
}

@GetMapping("{id}")
Person person(@PathVariable Long id) {
LOGGER.info("Loading person with id={}", id);
Expand All @@ -22,7 +27,6 @@ Person person(@PathVariable Long id) {

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

import io.sentry.spring.tracing.SentrySpan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class PersonService {
private static final Logger LOGGER = LoggerFactory.getLogger(PersonService.class);

@SentrySpan
Person create(Person person) {
LOGGER.warn("Creating person: {}", person);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
return person;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class SentryDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SentryDemoApplication.class, args);
}

@Bean
RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.sentry.samples.spring.boot;

public class Todo {
private final Long id;
private final String title;
private final boolean completed;

public Todo(Long id, String title, boolean completed) {
this.id = id;
this.title = title;
this.completed = completed;
}

public Long getId() {
return id;
}

public String getTitle() {
return title;
}

public boolean isCompleted() {
return completed;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.sentry.samples.spring.boot;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class TodoController {
private final RestTemplate restTemplate;

public TodoController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}

@GetMapping("/todo/{id}")
Todo todo(@PathVariable Long id) {
return restTemplate.getForObject(
"https://jsonplaceholder.typicode.com/todos/{id}", Todo.class, id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ sentry.max-breadcrumbs=150
# Logback integration configuration options
sentry.logging.minimum-event-level=info
sentry.logging.minimum-breadcrumb-level=debug
# Performance configuration
sentry.enable-tracing=true
sentry.traces-sample-rate=1.0
2 changes: 2 additions & 0 deletions sentry-spring-boot-starter/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies {
implementation(Config.Libs.springBootStarter)
compileOnly(Config.Libs.springWeb)
compileOnly(Config.Libs.servletApi)
compileOnly(Config.Libs.springBootStarterAop)

annotationProcessor(Config.AnnotationProcessors.springBootAutoConfigure)
annotationProcessor(Config.AnnotationProcessors.springBootConfiguration)
Expand All @@ -56,6 +57,7 @@ dependencies {
testImplementation(Config.Libs.springBootStarterTest)
testImplementation(Config.Libs.springBootStarterWeb)
testImplementation(Config.Libs.springBootStarterSecurity)
testImplementation(Config.Libs.springBootStarterAop)
testImplementation(Config.TestLibs.awaitility)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,41 @@
import io.sentry.SentryOptions;
import io.sentry.protocol.SdkVersion;
import io.sentry.spring.SentryExceptionResolver;
import io.sentry.spring.SentryRequestResolver;
import io.sentry.spring.SentryUserProvider;
import io.sentry.spring.SentryUserProviderEventProcessor;
import io.sentry.spring.SentryWebConfiguration;
import io.sentry.spring.tracing.SentrySpan;
import io.sentry.spring.tracing.SentrySpanAdvice;
import io.sentry.spring.tracing.SentryTracingFilter;
import io.sentry.spring.tracing.SentryTransaction;
import io.sentry.spring.tracing.SentryTransactionAdvice;
import io.sentry.transport.ITransport;
import io.sentry.transport.ITransportGate;
import java.util.List;
import org.aopalliance.aop.Advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.jetbrains.annotations.NotNull;
import org.springframework.aop.Advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.info.GitProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.web.client.RestTemplate;

@Configuration
@ConditionalOnProperty(name = "sentry.dsn")
Expand Down Expand Up @@ -100,6 +119,90 @@ static class SentryWebMvcConfiguration {
final @NotNull IHub sentryHub, final @NotNull SentryProperties options) {
return new SentryExceptionResolver(sentryHub, options.getExceptionResolverOrder());
}

@Bean
@ConditionalOnProperty(name = "sentry.enable-tracing", havingValue = "true")
@ConditionalOnMissingBean(name = "sentryTracingFilter")
public FilterRegistrationBean<SentryTracingFilter> sentryTracingFilter(
final @NotNull IHub hub,
final @NotNull SentryOptions options,
final @NotNull SentryRequestResolver sentryRequestResolver) {
FilterRegistrationBean<SentryTracingFilter> filter =
new FilterRegistrationBean<>(
new SentryTracingFilter(hub, options, sentryRequestResolver));
filter.setOrder(Ordered.HIGHEST_PRECEDENCE);
return filter;
}
}

@Configuration
@ConditionalOnProperty(name = "sentry.enable-tracing", havingValue = "true")
@ConditionalOnClass(ProceedingJoinPoint.class)
@Open
static class SentryPerformanceAspectsConfiguration {

/**
* Pointcut around which transactions are created.
*
* <p>This bean is can be replaced with user defined pointcut by specifying a {@link Pointcut}
* bean with name "sentryTransactionPointcut".
*
* @return pointcut used by {@link SentryTransactionAdvice}.
*/
@Bean
@ConditionalOnMissingBean(name = "sentryTransactionPointcut")
public @NotNull Pointcut sentryTransactionPointcut() {
return new AnnotationMatchingPointcut(null, SentryTransaction.class);
}

@Bean
public @NotNull Advice sentryTransactionAdvice(final @NotNull IHub hub) {
return new SentryTransactionAdvice(hub);
}

@Bean
public @NotNull Advisor sentryTransactionAdvisor(
final @NotNull IHub hub,
final @NotNull @Qualifier("sentryTransactionPointcut") Pointcut
sentryTransactionPointcut) {
return new DefaultPointcutAdvisor(sentryTransactionPointcut, sentryTransactionAdvice(hub));
}

/**
* Pointcut around which spans are created.
*
* <p>This bean is can be replaced with user defined pointcut by specifying a {@link Pointcut}
* bean with name "sentrySpanPointcut".
*
* @return pointcut used by {@link SentrySpanAdvice}.
*/
@Bean
@ConditionalOnMissingBean(name = "sentrySpanPointcut")
public @NotNull Pointcut sentrySpanPointcut() {
return new AnnotationMatchingPointcut(null, SentrySpan.class);
}

@Bean
public @NotNull Advice sentrySpanAdvice(final @NotNull IHub hub) {
return new SentrySpanAdvice(hub);
}

@Bean
public @NotNull Advisor sentrySpanAdvisor(
IHub hub, final @NotNull @Qualifier("sentrySpanPointcut") Pointcut sentrySpanPointcut) {
return new DefaultPointcutAdvisor(sentrySpanPointcut, sentrySpanAdvice(hub));
}
}

@Configuration
@AutoConfigureBefore(RestTemplateAutoConfiguration.class)
@ConditionalOnClass(RestTemplate.class)
@Open
static class SentryPerformanceRestTemplateConfiguration {
@Bean
public SentrySpanRestTemplateCustomizer sentrySpanRestTemplateCustomizer(IHub hub) {
return new SentrySpanRestTemplateCustomizer(hub);
}
}

private static @NotNull SdkVersion createSdkVersion(
Expand Down
Loading