Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

- Add SentryDistribution as Swift Package Manager target (#6149)
- Add option `enablePropagateTraceparent` to support OTel/W3C trace propagation (#6356)
- Structured Logs: Collect `stdout/stderr` per default (#6441)

### Fixes

Expand Down
28 changes: 28 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,10 @@
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, ); }; };
Expand Down Expand Up @@ -2077,6 +2080,9 @@
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>"; };
Expand Down Expand Up @@ -2918,6 +2924,7 @@
D85596EF280580BE0041FF8B /* Screenshot */,
0A9BF4E028A114690068D266 /* ViewHierarchy */,
D80CD8D52B752FD9002F710B /* SessionReplay */,
925B67892EA118EA005B2D3B /* StdOutLog */,
FA034AC72DD3DB4900FE3107 /* SentryIntegrationProtocol.h */,
7BA235622600B61200E12865 /* SentryInternalNotificationNames.h */,
0A2D8D5C289815EB008720F6 /* SentryBaseIntegration.h */,
Expand Down Expand Up @@ -3515,6 +3522,7 @@
7BE0DC3F272AE9F0004FA8B7 /* Session */,
7BE0DC3E272AE9DC004FA8B7 /* SentryCrash */,
D80694C12B7CC85800B820E6 /* SessionReplay */,
9292AA712EA1110E005DF5E2 /* StdOutLog */,
7B59398324AB481B0003AAD2 /* NotificationCenterTestCase.swift */,
0A2D8D8628992260008720F6 /* SentryBaseIntegrationTests.swift */,
);
Expand Down Expand Up @@ -4222,6 +4230,23 @@
name = Transaction;
sourceTree = "<group>";
};
925B67892EA118EA005B2D3B /* StdOutLog */ = {
isa = PBXGroup;
children = (
925B67CB2EA11970005B2D3B /* SentryStdOutLogIntegration.h */,
925B67CF2EA11B0E005B2D3B /* SentryStdoutLogIntegration.m */,
);
name = StdOutLog;
sourceTree = "<group>";
};
9292AA712EA1110E005DF5E2 /* StdOutLog */ = {
isa = PBXGroup;
children = (
9229D1452E9FF09E00FD09ED /* SentryStdOutLogIntegrationTests.swift */,
);
path = StdOutLog;
sourceTree = "<group>";
};
D4009EA02D77196F0007AF30 /* ViewCapture */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -5205,6 +5230,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 @@ -5676,6 +5702,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 @@ -6122,6 +6149,7 @@
8431EE5B29ADB8EA00D8DC56 /* SentryTimeTests.m 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
5 changes: 4 additions & 1 deletion SentryTestUtils/TestDispatchFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Foundation
@_spi(Private) public class TestDispatchFactory: SentryDispatchFactory {
public var vendedSourceHandler: ((TestDispatchSourceWrapper) -> Void)?
public var vendedQueueHandler: ((TestSentryDispatchQueueWrapper) -> Void)?
public var vendedUtilityQueueHandler: ((TestSentryDispatchQueueWrapper) -> Void)?

public var createUtilityQueueInvocations = Invocations<(name: String, relativePriority: Int32)>()

Expand All @@ -18,7 +19,9 @@ import Foundation
createUtilityQueueInvocations.record((String(cString: name), relativePriority))
// Due to the absense of `dispatch_queue_attr_make_with_qos_class` in Swift, we do not pass any attributes.
// This will not affect the tests as they do not need an actual low priority queue.
return TestSentryDispatchQueueWrapper(name: name, attributes: nil)
let queue = TestSentryDispatchQueueWrapper(name: name, attributes: nil)
vendedUtilityQueueHandler?(queue)
return queue
}

public override func source(withInterval interval: Int, leeway: Int, queueName: UnsafePointer<CChar>, attributes: __OS_dispatch_queue_attr, eventHandler: @escaping () -> Void) -> SentryDispatchSourceWrapper {
Expand Down
168 changes: 168 additions & 0 deletions Sources/Sentry/SentryStdoutLogIntegration.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#import "SentryStdOutLogIntegration.h"
#import "SentryDependencyContainer.h"

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Lint

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit macOS 26 Sentry

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Sample watchOS-Swift WatchKit App Debug

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit tvOS 26 Sentry

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Sample tvOS-Swift DebugV9

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Check no UIKit linkage (DebugWithoutUIKit)

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Sample iOS-ObjectiveC DebugV9

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Build SDK v9

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Check API Stability ()

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit iOS 16 Sentry

'SentryDependencyContainer.h' file not found

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Build with SPM

'SentryDependencyContainer.h' file not found (in target 'SentryObjc' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit macOS 15 Sentry

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit tvOS 18 Sentry

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Build XCFramework Slices (SentrySwiftUI, mh_dylib, sentry-swiftui) / appletvos

'SentryDependencyContainer.h' file not found

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit iOS 18 Sentry

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Build XCFramework Slices (Sentry, staticlib, sentry-static) / appletvos

'SentryDependencyContainer.h' file not found

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit tvOS 17 Sentry

'SentryDependencyContainer.h' file not found

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit Catalyst 14 Sentry

'SentryDependencyContainer.h' file not found

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit Catalyst 15 Sentry

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Check UIKit linkage (Debug)

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Check no UIKit linkage (ReleaseWithoutUIKit)

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Sample iOS-Swift DebugV9

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit iOS 17 Sentry

'SentryDependencyContainer.h' file not found

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit iOS 17 SentrySwiftUI

'SentryDependencyContainer.h' file not found

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Check compiling Async Safe Logs

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Sample iOS-SwiftUI DebugV9

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit macOS 14 Sentry

'SentryDependencyContainer.h' file not found

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Sample SessionReplay-CameraTest Debug

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Check UIKit linkage (Release)

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Sample visionOS-Swift DebugV9

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Sample macOS-Swift DebugV9

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Sample macOS-SwiftUI Debug

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit with Test Server macOS 15

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Analyze (cpp)

'SentryDependencyContainer.h' file not found

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Run SwiftUI Crash Test

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry' at path '/Users/runner/work/sentry-cocoa/sentry-cocoa/Sentry.xcodeproj')

Check failure on line 2 in Sources/Sentry/SentryStdoutLogIntegration.m

View workflow job for this annotation

GitHub Actions / Unit iOS 26 Sentry

'SentryDependencyContainer.h' file not found (in target 'Sentry' from project 'Sentry')
Copy link
Contributor

Choose a reason for hiding this comment

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

SentryDependencyContainer is written in Swift now

#import "SentryLogC.h"
#import "SentryOptions.h"
#import "SentrySwift.h"
#import <Foundation/Foundation.h>
#import <stdatomic.h>

@interface SentryStdOutLogIntegration ()

@property (strong, nonatomic) NSPipe *stdErrPipe;
@property (strong, nonatomic) NSPipe *stdOutPipe;
@property (nonatomic, copy) void (^logHandler)(NSData *, BOOL isStderr);
@property (nonatomic, assign) int originalStdOut;
@property (nonatomic, assign) int originalStdErr;
@property (strong, nonatomic, nullable) SentryLogger *injectedLogger;
@property (strong, nonatomic, nullable) SentryDispatchFactory *injectedDispatchFactory;
@property (strong, nonatomic, nullable) SentryDispatchQueueWrapper *dispatchQueueWrapper;

@end

// Global atomic flag for infinite loop protection
static _Atomic bool _isForwardingLogs = false;

@implementation SentryStdOutLogIntegration

- (instancetype)init:(SentryDispatchFactory *)dispatchFactory
{
return [self initWithDispatchFactory:dispatchFactory logger:nil];
}

// Only for testing
- (instancetype)initWithDispatchFactory:(SentryDispatchFactory *)dispatchFactory
logger:(nullable SentryLogger *)logger
{
if (self = [super init]) {
self.injectedLogger = logger;
self.injectedDispatchFactory = dispatchFactory;
}
return self;
}

- (SentryLogger *)logger
{
return self.injectedLogger ?: SentrySDK.logger;
}

- (SentryDispatchFactory *)dispatchFactory
{
return self.injectedDispatchFactory ?: SentryDependencyContainer.sharedInstance.dispatchFactory;
}

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

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

self.dispatchQueueWrapper =
[self.dispatchFactory createUtilityQueue:"com.sentry.stdout_log_writing_queue"
relativePriority:-3];

__weak typeof(self) weakSelf = self;
self.logHandler = ^(NSData *data, BOOL isStderr) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf)
return;

if (data && data.length > 0) {
NSString *logString = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
if (logString) {
// Check global atomic flag to avoid infinite loops
if (atomic_exchange(&_isForwardingLogs, true)) {
return; // Already forwarding, break the loop.
}
NSDictionary *attributes =
@{ @"sentry.log.source" : isStderr ? @"stderr" : @"stdout" };
if (isStderr) {
[strongSelf.logger warn:logString attributes:attributes];
} else {
[strongSelf.logger info:logString attributes:attributes];
}

// Clear global atomic flag
atomic_store(&_isForwardingLogs, false);
}
}
};

[self start];

return YES;
}

- (void)start
{
self.originalStdOut = dup(STDOUT_FILENO);
self.originalStdErr = dup(STDERR_FILENO);

self.stdOutPipe = [self duplicateFileDescriptor:STDOUT_FILENO isStderr:NO];
self.stdErrPipe = [self duplicateFileDescriptor:STDERR_FILENO isStderr:YES];
}

- (void)stop
{
if (self.stdOutPipe || self.stdErrPipe) {
// Restore original file descriptors
if (self.originalStdOut >= 0) {
dup2(self.originalStdOut, STDOUT_FILENO);
close(self.originalStdOut);
self.originalStdOut = -1;
}

if (self.originalStdErr >= 0) {
dup2(self.originalStdErr, STDERR_FILENO);
close(self.originalStdErr);
self.originalStdErr = -1;
}

// Clean up pipes
self.stdOutPipe.fileHandleForReading.readabilityHandler = nil;
self.stdErrPipe.fileHandleForReading.readabilityHandler = nil;

self.stdOutPipe = nil;
self.stdErrPipe = nil;
self.logHandler = nil;
}
}

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

// Write the input file descriptor to the input file handle, preserving the original output as well.
// This can be used to save stdout/stderr to a file while also keeping it on the console.
- (NSPipe *)duplicateFileDescriptor:(int)fileDescriptor isStderr:(BOOL)isStderr
{
NSPipe *pipe = [[NSPipe alloc] init];
int newDescriptor = dup(fileDescriptor);
NSFileHandle *newFileHandle = [[NSFileHandle alloc] initWithFileDescriptor:newDescriptor
closeOnDealloc:YES];

if (dup2(pipe.fileHandleForWriting.fileDescriptor, fileDescriptor) < 0) {
SENTRY_LOG_ERROR(@"Unable to duplicate file descriptor %d", fileDescriptor);
close(newDescriptor);
return nil;
}

__weak typeof(self) weakSelf = self;
pipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle *handle) {
NSData *data = handle.availableData;
if (weakSelf.logHandler) {
weakSelf.logHandler(data, isStderr);
}
[weakSelf.dispatchQueueWrapper dispatchAsyncWithBlock:^{ [newFileHandle writeData:data]; }];
};

return pipe;
}

@end
3 changes: 2 additions & 1 deletion Sources/Sentry/SentyOptionsInternal.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import "SentryOptions.h"
#import "SentryOptionsInternal.h"
#import "SentrySessionReplayIntegration.h"
#import "SentryStdoutLogIntegration.h"
#import "SentrySwift.h"
#import "SentrySwiftAsyncIntegration.h"

Expand Down Expand Up @@ -54,7 +55,7 @@ @implementation SentryOptionsInternal
[SentryANRTrackingIntegration class], [SentryAutoBreadcrumbTrackingIntegration class],
[SentryAutoSessionTrackingIntegration class], [SentryCoreDataTrackingIntegration class],
[SentryFileIOTrackingIntegration class], [SentryNetworkTrackingIntegration class],
[SentrySwiftAsyncIntegration class], nil];
[SentryStdOutLogIntegration class], [SentrySwiftAsyncIntegration class], nil];

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

NS_ASSUME_NONNULL_BEGIN

@class SentryLogger;
@class SentryDispatchFactory;
@class SentryDispatchQueueWrapper;

/**
* Integration that captures stdout and stderr output and forwards it to Sentry logs.
* This integration is automatically enabled when enableLogs is set to true.
*/
@interface SentryStdOutLogIntegration : SentryBaseIntegration

// Only for testing
- (instancetype)initWithDispatchFactory:(SentryDispatchFactory *)dispatchFactory
logger:(nullable SentryLogger *)logger;

@end

NS_ASSUME_NONNULL_END
4 changes: 0 additions & 4 deletions Sources/Swift/Tools/SentryLogger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,6 @@ public final class SentryLogger: NSObject {
}

if let processedLog {
SentrySDKLog.log(
message: "[SentryLogger] \(processedLog.body)",
andLevel: processedLog.level.toSentryLevel()
)
batcher.add(processedLog)
}
}
Expand Down
Loading
Loading