Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
833ee24
Add automatic stdout log integration when `enableLogs` is set to true
denrase Oct 16, 2025
60de7e4
Merge branch 'main' into denrase/structured-logs-stdout
denrase Oct 16, 2025
e5f4284
update changelog
denrase Oct 16, 2025
3b1fac8
fix weak ref handling
denrase Oct 16, 2025
17e96a7
remove SentrySDKLog from logger
denrase Oct 16, 2025
da2e360
Merge branch 'main' into denrase/structured-logs-stdout
denrase Nov 10, 2025
d591b0e
remove dep container import (ported to swift)
denrase Nov 10, 2025
6dfb50f
add SentryStdOutLogIntegration as default
denrase Nov 10, 2025
d75f26c
filter out strings containing [Sentry]
denrase Nov 10, 2025
73a3bf3
Merge branch 'main' into denrase/structured-logs-stdout
denrase Nov 10, 2025
1b8ada0
update changelog
denrase Nov 10, 2025
99c3dfe
Merge branch 'main' into denrase/structured-logs-stdout
denrase Nov 12, 2025
4d10782
Merge branch 'main' into denrase/structured-logs-stdout
denrase Nov 13, 2025
e317e53
Merge branch 'main' into denrase/structured-logs-stdout
denrase Nov 17, 2025
0b164a9
don’t install directly. prepare to move part of code outside (not use…
denrase Nov 17, 2025
4d79ab7
Merge branch 'main' into denrase/structured-logs-stdout
denrase Nov 17, 2025
5ea5d93
move main implementation to swift (driver pattern)
denrase Nov 17, 2025
44062ff
Merge branch 'main' into denrase/structured-logs-stdout
denrase Nov 17, 2025
441a879
re-direct SentrySDKLog to default stdout
denrase Nov 17, 2025
629ee22
Merge branch 'main' into denrase/structured-logs-stdout
denrase Nov 17, 2025
02ffb6c
revert vendedUtilityQueueHandler
denrase Nov 17, 2025
87239f4
update changelog
denrase Nov 17, 2025
78af4be
fix cl merge
denrase Nov 17, 2025
ff3a6ca
make driver nullable
denrase Nov 17, 2025
0169ba4
Add experimental log capture
denrase Nov 18, 2025
005efa3
Merge branch 'main' into denrase/structured-logs-stdout
denrase Nov 18, 2025
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased

- Structured Logs: Collect `stdout/stderr` (#6441)

## 9.0.0-rc.0

### Breaking Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ public struct SentrySDKWrapper {
#endif // !os(macOS) && !os(tvOS) && !os(watchOS) && !os(visionOS)

options.enableLogs = true
options.experimental.enableStdOutCapture = true
options.beforeSendLog = { log in
print("foo")
return log
}

// Experimental features
options.enableFileManagerSwizzling = !SentrySDKOverrides.Other.disableFileManagerSwizzling.boolValue
Expand Down
1 change: 1 addition & 0 deletions Samples/iOS-Swift/iOS-Swift/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

SentrySDKWrapper.shared.startSentry()

SampleAppDebugMenu.shared.display()

metricKit.receiveReports()
Expand Down
40 changes: 40 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -711,10 +711,14 @@
92235CAC2E15369900865983 /* SentryLogBatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92235CAB2E15369900865983 /* SentryLogBatcher.swift */; };
92235CAE2E15549C00865983 /* SentryLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92235CAD2E15549C00865983 /* SentryLogger.swift */; };
92235CB02E155B2600865983 /* SentryLoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92235CAF2E155B2600865983 /* SentryLoggerTests.swift */; };
9229D1462E9FF09E00FD09ED /* SentryStdOutLogIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9229D1452E9FF09E00FD09ED /* SentryStdOutLogIntegrationTests.swift */; };
925824C22CB5897700C9B20B /* SentrySessionReplayIntegration-Hybrid.h in Headers */ = {isa = PBXBuildFile; fileRef = D80382BE2C09C6FD0090E048 /* SentrySessionReplayIntegration-Hybrid.h */; settings = {ATTRIBUTES = (Private, ); }; };
925B67CC2EA11970005B2D3B /* SentryStdOutLogIntegration.h in Headers */ = {isa = PBXBuildFile; fileRef = 925B67CB2EA11970005B2D3B /* SentryStdOutLogIntegration.h */; };
925B67D02EA11B0E005B2D3B /* SentryStdoutLogIntegration.m in Sources */ = {isa = PBXBuildFile; fileRef = 925B67CF2EA11B0E005B2D3B /* SentryStdoutLogIntegration.m */; };
9264E1EB2E2E385E00B077CF /* SentryLogMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9264E1EA2E2E385B00B077CF /* SentryLogMessage.swift */; };
9264E1ED2E2E397C00B077CF /* SentryLogMessageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9264E1EC2E2E397400B077CF /* SentryLogMessageTests.swift */; };
92672BB629C9A2A9006B021C /* SentryBreadcrumb+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 92672BB529C9A2A9006B021C /* SentryBreadcrumb+Private.h */; settings = {ATTRIBUTES = (Private, ); }; };
92793D542ECB7509007EA926 /* SentryStdOutLogIntegrationDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92793D522ECB7509007EA926 /* SentryStdOutLogIntegrationDriver.swift */; };
927A5CC42DD7626B00B82404 /* SentryEnvelopeItemHeaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 927A5CC32DD7626400B82404 /* SentryEnvelopeItemHeaderTests.swift */; };
928207C42E251B8F009285A4 /* SentryScope+PrivateSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = 928207C32E251B8F009285A4 /* SentryScope+PrivateSwift.h */; };
9286059529A5096600F96038 /* SentryGeo.h in Headers */ = {isa = PBXBuildFile; fileRef = 9286059429A5096600F96038 /* SentryGeo.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -2074,9 +2078,13 @@
92235CAB2E15369900865983 /* SentryLogBatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogBatcher.swift; sourceTree = "<group>"; };
92235CAD2E15549C00865983 /* SentryLogger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogger.swift; sourceTree = "<group>"; };
92235CAF2E155B2600865983 /* SentryLoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLoggerTests.swift; sourceTree = "<group>"; };
9229D1452E9FF09E00FD09ED /* SentryStdOutLogIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryStdOutLogIntegrationTests.swift; sourceTree = "<group>"; };
925B67CB2EA11970005B2D3B /* SentryStdOutLogIntegration.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryStdOutLogIntegration.h; path = include/SentryStdOutLogIntegration.h; sourceTree = "<group>"; };
925B67CF2EA11B0E005B2D3B /* SentryStdoutLogIntegration.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryStdoutLogIntegration.m; sourceTree = "<group>"; };
9264E1EA2E2E385B00B077CF /* SentryLogMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogMessage.swift; sourceTree = "<group>"; };
9264E1EC2E2E397400B077CF /* SentryLogMessageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryLogMessageTests.swift; sourceTree = "<group>"; };
92672BB529C9A2A9006B021C /* SentryBreadcrumb+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SentryBreadcrumb+Private.h"; path = "include/HybridPublic/SentryBreadcrumb+Private.h"; sourceTree = "<group>"; };
92793D522ECB7509007EA926 /* SentryStdOutLogIntegrationDriver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryStdOutLogIntegrationDriver.swift; sourceTree = "<group>"; };
927A5CC32DD7626400B82404 /* SentryEnvelopeItemHeaderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryEnvelopeItemHeaderTests.swift; sourceTree = "<group>"; };
928207C32E251B8F009285A4 /* SentryScope+PrivateSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "SentryScope+PrivateSwift.h"; path = "include/SentryScope+PrivateSwift.h"; sourceTree = "<group>"; };
9286059429A5096600F96038 /* SentryGeo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SentryGeo.h; path = Public/SentryGeo.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2939,6 +2947,7 @@
D85596EF280580BE0041FF8B /* Screenshot */,
0A9BF4E028A114690068D266 /* ViewHierarchy */,
D80CD8D52B752FD9002F710B /* SessionReplay */,
925B67892EA118EA005B2D3B /* StdOutLog */,
FA034AC72DD3DB4900FE3107 /* SentryIntegrationProtocol.h */,
7BA235622600B61200E12865 /* SentryInternalNotificationNames.h */,
0A2D8D5C289815EB008720F6 /* SentryBaseIntegration.h */,
Expand Down Expand Up @@ -3529,6 +3538,7 @@
7BE0DC3F272AE9F0004FA8B7 /* Session */,
7BE0DC3E272AE9DC004FA8B7 /* SentryCrash */,
D80694C12B7CC85800B820E6 /* SessionReplay */,
9292AA712EA1110E005DF5E2 /* StdOutLog */,
7B59398324AB481B0003AAD2 /* NotificationCenterTestCase.swift */,
0A2D8D8628992260008720F6 /* SentryBaseIntegrationTests.swift */,
);
Expand Down Expand Up @@ -4196,6 +4206,31 @@
name = Transaction;
sourceTree = "<group>";
};
925B67892EA118EA005B2D3B /* StdOutLog */ = {
isa = PBXGroup;
children = (
925B67CB2EA11970005B2D3B /* SentryStdOutLogIntegration.h */,
925B67CF2EA11B0E005B2D3B /* SentryStdoutLogIntegration.m */,
);
name = StdOutLog;
sourceTree = "<group>";
};
92793D532ECB7509007EA926 /* StdOutLog */ = {
isa = PBXGroup;
children = (
92793D522ECB7509007EA926 /* SentryStdOutLogIntegrationDriver.swift */,
);
path = StdOutLog;
sourceTree = "<group>";
};
9292AA712EA1110E005DF5E2 /* StdOutLog */ = {
isa = PBXGroup;
children = (
9229D1452E9FF09E00FD09ED /* SentryStdOutLogIntegrationTests.swift */,
);
path = StdOutLog;
sourceTree = "<group>";
};
D4009EA02D77196F0007AF30 /* ViewCapture */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4839,6 +4874,7 @@
D8CAC02D2BA0663E00E38F34 /* Integrations */ = {
isa = PBXGroup;
children = (
92793D532ECB7509007EA926 /* StdOutLog */,
FAB0073C2E9F47DE001C806A /* Session */,
FAE579B42E7DBE9400B710F9 /* SentryGlobalEventProcessor.swift */,
D49064862DFAE1B700555785 /* Screenshot */,
Expand Down Expand Up @@ -5305,6 +5341,7 @@
7BA61CC8247D125400C130A8 /* SentryDefaultThreadInspector.h in Headers */,
63FE713320DA4C1100CDBAE8 /* SentryCrashCPU.h in Headers */,
6271ADF32BA06D9B0098D2E9 /* SentryInternalSerializable.h in Headers */,
925B67CC2EA11970005B2D3B /* SentryStdOutLogIntegration.h in Headers */,
D8853C842833EABC00700D64 /* SentryANRTrackerV1.h in Headers */,
63FE715B20DA4C1100CDBAE8 /* SentryCrashSignalInfo.h in Headers */,
63FE70E520DA4C1000CDBAE8 /* SentryCrashMonitor_CPPException.h in Headers */,
Expand Down Expand Up @@ -5774,6 +5811,7 @@
84DBC62C2CE82F12000C4904 /* SentryFeedback.swift in Sources */,
F41362132E1C566100B84443 /* SentryScopePersistentStore+User.swift in Sources */,
63B818FA1EC34639002FDF4C /* SentryDebugMeta.m in Sources */,
925B67D02EA11B0E005B2D3B /* SentryStdoutLogIntegration.m in Sources */,
7B98D7D325FB65AE00C5A389 /* SentryWatchdogTerminationTracker.m in Sources */,
8E564AE8267AF22600FE117D /* SentryNetworkTrackingIntegration.m in Sources */,
63AA75EF1EB8B3C400D153DE /* SentryClient.m in Sources */,
Expand Down Expand Up @@ -6158,6 +6196,7 @@
620379DD2AFE1432005AC0C1 /* SentryBuildAppStartSpans.m in Sources */,
6292585F2DAFA8290049388F /* SentryCrashMach-O.c in Sources */,
7B77BE3727EC8460003C9020 /* SentryDiscardReasonMapper.m in Sources */,
92793D542ECB7509007EA926 /* SentryStdOutLogIntegrationDriver.swift in Sources */,
63FE712520DA4C1000CDBAE8 /* SentryCrashSignalInfo.c in Sources */,
63FE70F320DA4C1000CDBAE8 /* SentryCrashMonitor_Signal.c in Sources */,
D859696F27BECDA20036A46E /* SentryCoreDataTracker.m in Sources */,
Expand Down Expand Up @@ -6229,6 +6268,7 @@
FA27EBF52EB82FAD00F2ECF7 /* FileIOTrackerTestHelpers.swift in Sources */,
7B0A54562523178700A71716 /* SentryScopeSwiftTests.swift in Sources */,
7B5B94332657A816002E474B /* SentryAppStartTrackingIntegrationTests.swift in Sources */,
9229D1462E9FF09E00FD09ED /* SentryStdOutLogIntegrationTests.swift in Sources */,
62278CA82E30B21A0022ABC6 /* SentryHttpTransportFlushIntegrationTests.swift in Sources */,
0A5370A128A3EC2400B2DCDE /* SentryViewHierarchyProviderTests.swift in Sources */,
D8FFE50C2703DBB400607131 /* SwizzlingCallTests.swift in Sources */,
Expand Down
3 changes: 2 additions & 1 deletion Sources/Sentry/SentrySDKInternal.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#import "SentryScope.h"
#import "SentrySerialization.h"
#import "SentrySessionReplayIntegration.h"
#import "SentryStdOutLogIntegration.h"
#import "SentrySwift.h"
#import "SentrySwiftAsyncIntegration.h"
#import "SentryTransactionContext.h"
Expand Down Expand Up @@ -539,7 +540,7 @@ + (void)endSession
[SentryANRTrackingIntegration class], [SentryAutoBreadcrumbTrackingIntegration class],
[SentryAutoSessionTrackingIntegration class], [SentryCoreDataTrackingIntegration class],
[SentryFileIOTrackingIntegration class], [SentryNetworkTrackingIntegration class],
[SentrySwiftAsyncIntegration class], nil];
[SentrySwiftAsyncIntegration class], [SentryStdOutLogIntegration class], nil];

#if TARGET_OS_IOS && SENTRY_HAS_UIKIT
[defaultIntegrations addObject:[SentryUserFeedbackIntegration class]];
Expand Down
59 changes: 59 additions & 0 deletions Sources/Sentry/SentryStdoutLogIntegration.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#import "SentryStdOutLogIntegration.h"
#import "SentrySDK+Private.h"
#import "SentrySwift.h"

@interface SentryStdOutLogIntegration ()

@property (strong, nonatomic, nullable) SentryStdOutLogIntegrationDriver *driver;

@end

@implementation SentryStdOutLogIntegration

// Only for testing
- (instancetype)initWithDispatchQueue:(SentryDispatchQueueWrapper *)dispatchQueue
logger:(SentryLogger *)logger
{
if (self = [super init]) {
_driver = [[SentryStdOutLogIntegrationDriver alloc] initWithDispatchQueue:dispatchQueue
logger:logger];
}
return self;
}

- (BOOL)installWithOptions:(SentryOptions *)options
{
if (![super installWithOptions:options]) {
return NO;
}

// Only install if logs are enabled
if (!options.enableLogs) {
return NO;
}

// Only install if experimental flag is enabled
if (!options.experimental.enableStdOutCapture) {
return NO;
}

// Use default instances if driver wasn't initialized (for production use)
if (!_driver) {
SentryLogger *logger = SentrySDK.logger;
SentryDispatchQueueWrapper *dispatchQueue
= SentryDependencyContainer.sharedInstance.dispatchQueueWrapper;
_driver = [[SentryStdOutLogIntegrationDriver alloc] initWithDispatchQueue:dispatchQueue
logger:logger];
}

[_driver start];

return YES;
}

- (void)uninstall
{
[_driver stop];
}

@end
19 changes: 19 additions & 0 deletions Sources/Sentry/include/SentryStdOutLogIntegration.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#import "SentryBaseIntegration.h"

NS_ASSUME_NONNULL_BEGIN

@class SentryLogger;
@class SentryDispatchQueueWrapper;

/**
* Integration that captures stdout and stderr output and forwards it to Sentry logs.
*/
@interface SentryStdOutLogIntegration : SentryBaseIntegration

// Only for testing
- (instancetype)initWithDispatchQueue:(SentryDispatchQueueWrapper *)dispatchQueue
logger:(SentryLogger *)logger;

@end

NS_ASSUME_NONNULL_END
9 changes: 5 additions & 4 deletions Sources/Swift/Core/Tools/SentrySDKLog.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

typealias SentryLogOutput = ((String) -> Void)
@_spi(Private) public typealias SentryLogOutput = ((String) -> Void)

/// A note on the thread safety:
/// The methods configure and log don't use synchronization mechanisms, meaning they aren't strictly speaking thread-safe.
Expand Down Expand Up @@ -57,12 +57,13 @@ typealias SentryLogOutput = ((String) -> Void)
return isDebug && level.rawValue >= diagnosticLevel.rawValue
}

#if SENTRY_TEST || SENTRY_TEST_CI

static func setOutput(_ output: @escaping SentryLogOutput) {
/// Set a custom output function for logs. Used by integrations to redirect output.
@objc @_spi(Private) public static func setOutput(_ output: @escaping SentryLogOutput) {
logOutput = output
}

#if SENTRY_TEST || SENTRY_TEST_CI

static func getOutput() -> SentryLogOutput {
return logOutput
}
Expand Down
Loading
Loading