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

feat: Add support for Sentry Spotlight #3642

Merged
merged 14 commits into from
Feb 19, 2024
Merged
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Features

- Add support for Sentry [Spotlight](https://spotlightjs.com/) (#3642), which is basically Sentry
for development. Read our [blog post](https://blog.sentry.io/sentry-for-development/) to find out more.
- Add field `SentrySDK.detectedStartUpCrash` (#3644)
- Automatically profile app launches (#3529)

Expand Down
1 change: 1 addition & 0 deletions Samples/iOS-Swift/iOS-Swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1219,6 +1219,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "RELEASE=1";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
Expand Down
2 changes: 2 additions & 0 deletions Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
options.sessionTrackingIntervalMillis = 5_000
options.attachScreenshot = true
options.attachViewHierarchy = true

#if targetEnvironment(simulator)
options.enableSpotlight = true
philipphofmann marked this conversation as resolved.
Show resolved Hide resolved
options.environment = "test-app"
#else
options.environment = "device-tests"
Expand Down
16 changes: 15 additions & 1 deletion Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,12 @@
62950F1029E7FE0100A42624 /* SentryTransactionContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62950F0F29E7FE0100A42624 /* SentryTransactionContextTests.swift */; };
629690532AD3E060000185FA /* SentryReachabilitySwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 629690522AD3E060000185FA /* SentryReachabilitySwiftTests.swift */; };
62986F032B03D250008E2D62 /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = 62986F022B03D250008E2D62 /* Nimble */; };
62A3C7BE2B7E2A6A00C75227 /* SentrySpotlightTransportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62A3C7BD2B7E2A6A00C75227 /* SentrySpotlightTransportTests.swift */; };
62A456E12B03704A003F19A1 /* SentryUIEventTrackerMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 62A456E02B03704A003F19A1 /* SentryUIEventTrackerMode.h */; };
62A456E32B0370AA003F19A1 /* SentryUIEventTrackerTransactionMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 62A456E22B0370AA003F19A1 /* SentryUIEventTrackerTransactionMode.h */; };
62A456E52B0370E0003F19A1 /* SentryUIEventTrackerTransactionMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 62A456E42B0370E0003F19A1 /* SentryUIEventTrackerTransactionMode.m */; };
62B86CFC29F052BB008F3947 /* SentryTestLogConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 62B86CFB29F052BB008F3947 /* SentryTestLogConfig.m */; };
62C1AFAB2B7E10EA0038C5F7 /* SentrySpotlightTransport.m in Sources */ = {isa = PBXBuildFile; fileRef = 62C1AFAA2B7E10EA0038C5F7 /* SentrySpotlightTransport.m */; };
62C25C862B075F4900C68CBD /* TestOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62C25C852B075F4900C68CBD /* TestOptions.swift */; };
62C316812B1F2E93000D7031 /* SentryDelayedFramesTracker.h in Headers */ = {isa = PBXBuildFile; fileRef = 62C316802B1F2E93000D7031 /* SentryDelayedFramesTracker.h */; };
62C316832B1F2EA1000D7031 /* SentryDelayedFramesTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 62C316822B1F2EA1000D7031 /* SentryDelayedFramesTracker.m */; };
Expand Down Expand Up @@ -1006,10 +1008,13 @@
62885DA629E946B100554F38 /* TestConncurrentModifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestConncurrentModifications.swift; sourceTree = "<group>"; };
62950F0F29E7FE0100A42624 /* SentryTransactionContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTransactionContextTests.swift; sourceTree = "<group>"; };
629690522AD3E060000185FA /* SentryReachabilitySwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryReachabilitySwiftTests.swift; sourceTree = "<group>"; };
62A3C7BD2B7E2A6A00C75227 /* SentrySpotlightTransportTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySpotlightTransportTests.swift; sourceTree = "<group>"; };
62A456E02B03704A003F19A1 /* SentryUIEventTrackerMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryUIEventTrackerMode.h; path = include/SentryUIEventTrackerMode.h; sourceTree = "<group>"; };
62A456E22B0370AA003F19A1 /* SentryUIEventTrackerTransactionMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryUIEventTrackerTransactionMode.h; path = include/SentryUIEventTrackerTransactionMode.h; sourceTree = "<group>"; };
62A456E42B0370E0003F19A1 /* SentryUIEventTrackerTransactionMode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryUIEventTrackerTransactionMode.m; sourceTree = "<group>"; };
62B86CFB29F052BB008F3947 /* SentryTestLogConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryTestLogConfig.m; sourceTree = "<group>"; };
62C1AFA92B7E10D30038C5F7 /* SentrySpotlightTransport.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentrySpotlightTransport.h; path = include/SentrySpotlightTransport.h; sourceTree = "<group>"; };
62C1AFAA2B7E10EA0038C5F7 /* SentrySpotlightTransport.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentrySpotlightTransport.m; sourceTree = "<group>"; };
62C25C852B075F4900C68CBD /* TestOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestOptions.swift; sourceTree = "<group>"; };
62C316802B1F2E93000D7031 /* SentryDelayedFramesTracker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryDelayedFramesTracker.h; path = include/SentryDelayedFramesTracker.h; sourceTree = "<group>"; };
62C316822B1F2EA1000D7031 /* SentryDelayedFramesTracker.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryDelayedFramesTracker.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1929,6 +1934,8 @@
7BAF3DB8243C9777008A5414 /* SentryTransport.h */,
6383953523ABA42C000C1594 /* SentryHttpTransport.h */,
7DB3A684238EA75E00A2D442 /* SentryHttpTransport.m */,
62C1AFA92B7E10D30038C5F7 /* SentrySpotlightTransport.h */,
62C1AFAA2B7E10EA0038C5F7 /* SentrySpotlightTransport.m */,
7BA0C04528055F8E003E0326 /* SentryTransportAdapter.h */,
7BA0C0472805600A003E0326 /* SentryTransportAdapter.m */,
7BC8523624588115005A70F0 /* SentryDataCategory.h */,
Expand Down Expand Up @@ -2817,6 +2824,7 @@
8ED2D27F26A6581C00CA8329 /* NSURLProtocolSwizzle.m */,
8E0551DF26A7A63C00400526 /* TestProtocolClient.swift */,
7B4D308926FC616B00C94DE9 /* SentryHttpTransportTests.swift */,
62A3C7BD2B7E2A6A00C75227 /* SentrySpotlightTransportTests.swift */,
7BA0C04B28056556003E0326 /* SentryTransportAdapterTests.swift */,
7B01CE3C271993AB00B5AF31 /* SentryTransportFactoryTests.swift */,
7B5CAF7C27F5AD0600ED0DB6 /* TestNSURLRequestBuilder.h */,
Expand Down Expand Up @@ -4195,6 +4203,7 @@
63FE711D20DA4C1000CDBAE8 /* SentryCrashCPU_arm64.c in Sources */,
844EDC77294144DB00C86F34 /* SentrySystemWrapper.mm in Sources */,
630435FF1EBCA9D900C4D3FA /* SentryNSURLRequest.m in Sources */,
62C1AFAB2B7E10EA0038C5F7 /* SentrySpotlightTransport.m in Sources */,
7B5CAF7727F5A68C00ED0DB6 /* SentryNSURLRequestBuilder.m in Sources */,
639FCFA11EBC804600778193 /* SentryException.m in Sources */,
33042A0D29DAF79A00C60085 /* SentryExtraContextProvider.m in Sources */,
Expand Down Expand Up @@ -4403,6 +4412,7 @@
A8AFFCD42907E0CA00967CD7 /* SentryRequestTests.swift in Sources */,
7BD4BD4D27EB31820071F4FF /* SentryClientReportTests.swift in Sources */,
7B98D7EC25FB7C4900C5A389 /* SentryAppStateTests.swift in Sources */,
62A3C7BE2B7E2A6A00C75227 /* SentrySpotlightTransportTests.swift in Sources */,
8EE017A126704CD500470616 /* SentryUIViewControllerPerformanceTrackerTests.swift in Sources */,
7B18DE4428D9F8F6004845C6 /* TestNSNotificationCenterWrapper.swift in Sources */,
7B5B94352657AD21002E474B /* SentryFramesTrackingIntegrationTests.swift in Sources */,
Expand Down Expand Up @@ -4824,6 +4834,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "RELEASE=1";
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
Expand Down Expand Up @@ -5688,7 +5699,10 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "SENTRY_NO_UIKIT=1";
GCC_PREPROCESSOR_DEFINITIONS = (
"SENTRY_NO_UIKIT=1",
"RELEASE=1",
);
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
Expand Down
18 changes: 17 additions & 1 deletion Sources/Sentry/Public/SentryOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ NS_SWIFT_NAME(Options)
* The maximum size for each attachment in bytes.
* @note Default is 20 MiB (20 ✕ 1024 ✕ 1024 bytes).
* @note Please also check the maximum attachment size of relay to make sure your attachments don't
* get discarded there: https://docs.sentry.io/product/relay/options/
* get discarded there:
* https://docs.sentry.io/product/relay/options/
*/
@property (nonatomic, assign) NSUInteger maxAttachmentSize;

Expand Down Expand Up @@ -541,6 +542,21 @@ NS_SWIFT_NAME(Options)
YES)`.
*/
@property (nonatomic, copy) NSString *cacheDirectoryPath;

/**
* Whether to enable Spotlight for local development. For more information see
* https://spotlightjs.com/.
*
* @note Only set this option to @c YES while developing, not in production!
*/
@property (nonatomic, assign) BOOL enableSpotlight;
philipphofmann marked this conversation as resolved.
Show resolved Hide resolved

/**
* The Spotlight URL. Defaults to http://localhost:8969/stream. For more information see
* https://spotlightjs.com/
*/
@property (nonatomic, copy) NSString *spotlightUrl;

@end

NS_ASSUME_NONNULL_END
22 changes: 18 additions & 4 deletions Sources/Sentry/SentryNSURLRequest.m
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,28 @@ - (_Nullable instancetype)initEnvelopeRequestWithDsn:(SentryDsn *)dsn
didFailWithError:(NSError *_Nullable *_Nullable)error
{
NSURL *apiURL = [dsn getEnvelopeEndpoint];
self = [super initWithURL:apiURL
NSString *authHeader = newAuthHeader(dsn.url);

return [self initEnvelopeRequestWithURL:apiURL
andData:data
authHeader:authHeader
didFailWithError:error];
}

- (instancetype)initEnvelopeRequestWithURL:(NSURL *)url
andData:(NSData *)data
authHeader:(nullable NSString *)authHeader
didFailWithError:(NSError *_Nullable *_Nullable)error
{
self = [super initWithURL:url
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:SentryRequestTimeout];
if (self) {
NSString *authHeader = newAuthHeader(dsn.url);

self.HTTPMethod = @"POST";
[self setValue:authHeader forHTTPHeaderField:@"X-Sentry-Auth"];

if (authHeader != nil) {
[self setValue:authHeader forHTTPHeaderField:@"X-Sentry-Auth"];
}
[self setValue:@"application/x-sentry-envelope" forHTTPHeaderField:@"Content-Type"];
[self setValue:SentryMeta.sdkName forHTTPHeaderField:@"User-Agent"];
[self setValue:@"gzip" forHTTPHeaderField:@"Content-Encoding"];
Expand Down
11 changes: 11 additions & 0 deletions Sources/Sentry/SentryNSURLRequestBuilder.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ - (NSURLRequest *)createEnvelopeRequest:(SentryEnvelope *)envelope
didFailWithError:error];
}

- (NSURLRequest *)createEnvelopeRequest:(SentryEnvelope *)envelope
url:(NSURL *)url
didFailWithError:(NSError *_Nullable *_Nullable)error
{
return [[SentryNSURLRequest alloc]
initEnvelopeRequestWithURL:url
andData:[SentrySerialization dataWithEnvelope:envelope error:error]
authHeader:nil
didFailWithError:error];
}

@end

NS_ASSUME_NONNULL_END
20 changes: 20 additions & 0 deletions Sources/Sentry/SentryOptions.m
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ - (instancetype)init
_enableSwizzling = YES;
self.sendClientReports = YES;
self.swiftAsyncStacktraces = NO;
self.enableSpotlight = NO;
self.spotlightUrl = @"http://localhost:8969/stream";

#if TARGET_OS_OSX
NSString *dsn = [[[NSProcessInfo processInfo] environment] objectForKey:@"SENTRY_DSN"];
Expand Down Expand Up @@ -488,6 +490,13 @@ - (BOOL)validateOptions:(NSDictionary<NSString *, id> *)options
}
#endif // SENTRY_HAS_METRIC_KIT

[self setBool:options[@"enableSpotlight"]
block:^(BOOL value) { self->_enableSpotlight = value; }];

if ([options[@"spotlightUrl"] isKindOfClass:[NSString class]]) {
self.spotlightUrl = options[@"spotlightUrl"];
}
philipphofmann marked this conversation as resolved.
Show resolved Hide resolved

return YES;
}

Expand Down Expand Up @@ -693,6 +702,17 @@ - (void)setEnablePreWarmedAppStartTracing:(BOOL)enablePreWarmedAppStartTracing

#endif // SENTRY_UIKIT_AVAILABLE

- (void)setEnableSpotlight:(BOOL)value
{
_enableSpotlight = value;
#if defined(RELEASE)
if (value) {
SENTRY_LOG_WARN(@"Enabling Spotlight for a release build. We recommend running Spotlight "
@"only for local development.");
}
#endif // defined(RELEASE)
}

#if defined(DEBUG) || defined(TEST) || defined(TESTCI)
- (NSString *)debugDescription
{
Expand Down
108 changes: 108 additions & 0 deletions Sources/Sentry/SentrySpotlightTransport.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#import "SentrySpotlightTransport.h"
#import "SentryDispatchQueueWrapper.h"
#import "SentryEnvelope.h"
#import "SentryEnvelopeItemHeader.h"
#import "SentryEnvelopeItemType.h"
#import "SentryLog.h"
#import "SentryNSURLRequest.h"
#import "SentryNSURLRequestBuilder.h"
#import "SentryOptions.h"
#import "SentrySerialization.h"
#import "SentryTransport.h"

NS_ASSUME_NONNULL_BEGIN

@interface
SentrySpotlightTransport ()

@property (nonatomic, strong) id<SentryRequestManager> requestManager;
@property (nonatomic, strong) SentryNSURLRequestBuilder *requestBuilder;
@property (nonatomic, strong) SentryOptions *options;
@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueue;
@property (nonatomic, strong, nullable) NSURL *apiURL;

@end

@implementation SentrySpotlightTransport

- (id)initWithOptions:(SentryOptions *)options
requestManager:(id<SentryRequestManager>)requestManager
requestBuilder:(SentryNSURLRequestBuilder *)requestBuilder
dispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper
{

if (self = [super init]) {
self.options = options;
self.requestManager = requestManager;
self.requestBuilder = requestBuilder;
self.dispatchQueue = dispatchQueueWrapper;
self.apiURL = [[NSURL alloc] initWithString:options.spotlightUrl];
}

return self;
}

- (void)sendEnvelope:(SentryEnvelope *)envelope
{
if (self.apiURL == nil) {
SENTRY_LOG_WARN(@"Malformed Spotlight URL passed from the options. Not sending envelope to "
@"Spotlight with URL:%@",
self.options.spotlightUrl);
return;
}

// Spotlight can only handle the following envelope items.
// Not removing them leads to an error and events won't get displayed.
NSMutableArray<SentryEnvelopeItem *> *allowedEnvelopeItems = [NSMutableArray new];
for (SentryEnvelopeItem *item in envelope.items) {
if ([item.header.type isEqualToString:SentryEnvelopeItemTypeEvent]) {
[allowedEnvelopeItems addObject:item];
}
if ([item.header.type isEqualToString:SentryEnvelopeItemTypeTransaction]) {
[allowedEnvelopeItems addObject:item];
}
}

SentryEnvelope *envelopeToSend = [[SentryEnvelope alloc] initWithHeader:envelope.header
items:allowedEnvelopeItems];

NSError *requestError = nil;
NSURLRequest *request = [self.requestBuilder createEnvelopeRequest:envelopeToSend
url:self.apiURL
didFailWithError:&requestError];

if (requestError) {
SENTRY_LOG_ERROR(@"Unable to build envelope request with error %@", requestError);
return;
}

[self.requestManager
addRequest:request
completionHandler:^(NSHTTPURLResponse *_Nullable response, NSError *_Nullable error) {
if (error) {
SENTRY_LOG_ERROR(@"Error while performing request %@", requestError);
}
}];
}

- (SentryFlushResult)flush:(NSTimeInterval)timeout
{
// Empty on purpose
return kSentryFlushResultSuccess;
}

- (void)recordLostEvent:(SentryDataCategory)category reason:(SentryDiscardReason)reason
{
// Empty on purpose
}

#if TEST || TESTCI
- (void)setStartFlushCallback:(nonnull void (^)(void))callback
{
// Empty on purpose
}
#endif

@end

NS_ASSUME_NONNULL_END
16 changes: 14 additions & 2 deletions Sources/Sentry/SentryTransportFactory.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#import "SentryRateLimits.h"

#import "SentryRetryAfterHeaderParser.h"
#import "SentrySpotlightTransport.h"
#import "SentryTransport.h"
#import <Foundation/Foundation.h>

Expand Down Expand Up @@ -51,16 +52,27 @@ @implementation SentryTransportFactory
[[SentryDispatchQueueWrapper alloc] initWithName:"sentry-http-transport"
attributes:attributes];

SentryNSURLRequestBuilder *requestBuilder = [[SentryNSURLRequestBuilder alloc] init];

SentryHttpTransport *httpTransport =
[[SentryHttpTransport alloc] initWithOptions:options
fileManager:sentryFileManager
requestManager:requestManager
requestBuilder:[[SentryNSURLRequestBuilder alloc] init]
requestBuilder:requestBuilder
rateLimits:rateLimits
envelopeRateLimit:envelopeRateLimit
dispatchQueueWrapper:dispatchQueueWrapper];

return @[ httpTransport ];
if (options.enableSpotlight) {
SentrySpotlightTransport *spotlightTransport =
[[SentrySpotlightTransport alloc] initWithOptions:options
requestManager:requestManager
requestBuilder:requestBuilder
dispatchQueueWrapper:dispatchQueueWrapper];
return @[ httpTransport, spotlightTransport ];
} else {
return @[ httpTransport ];
}
}

@end
Expand Down
5 changes: 5 additions & 0 deletions Sources/Sentry/include/SentryNSURLRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ NS_ASSUME_NONNULL_BEGIN
andData:(NSData *)data
didFailWithError:(NSError *_Nullable *_Nullable)error;

- (instancetype)initEnvelopeRequestWithURL:(NSURL *)url
andData:(NSData *)data
authHeader:(nullable NSString *)authHeader
didFailWithError:(NSError *_Nullable *_Nullable)error;

@end

NS_ASSUME_NONNULL_END
Loading
Loading