|
11 | 11 |
|
12 | 12 | int saved_stdout;
|
13 | 13 | int saved_stderr;
|
14 |
| -FILE* fileDescriptor; |
| 14 | +int stdoutPipe[2]; |
| 15 | +int stderrPipe[2]; |
15 | 16 |
|
16 | 17 | @interface NRAutoLogCollector()
|
17 | 18 |
|
18 | 19 | @end
|
19 | 20 |
|
20 | 21 | @implementation NRAutoLogCollector
|
21 | 22 |
|
22 |
| -+ (NSURL *) logFileURL { |
23 |
| - NSFileManager *fileManager = [NSFileManager defaultManager]; |
24 |
| - NSArray<NSURL *> *urls = [fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; |
25 |
| - NSURL *logsDirectory = [urls firstObject]; |
26 |
| - return [logsDirectory URLByAppendingPathComponent:@"agent.log"]; |
27 |
| -} |
28 |
| - |
29 |
| -+ (void) clearLogFile { |
30 |
| - [[NSFileManager defaultManager] removeItemAtURL:[NRAutoLogCollector logFileURL] error:nil]; |
31 |
| - |
32 |
| - [NRAutoLogCollector redirectStandardOutputAndError]; |
33 |
| -} |
34 |
| - |
35 | 23 | + (void) redirectStandardOutputAndError {
|
36 |
| - // Save the original stdout file descriptor |
| 24 | + // Create pipes for stdout and stderr |
| 25 | + if (pipe(stdoutPipe) == -1 || pipe(stderrPipe) == -1) { |
| 26 | + return; |
| 27 | + } |
| 28 | + |
| 29 | + // Save the original stdout and stderr file descriptors |
37 | 30 | saved_stdout = dup(fileno(stdout));
|
38 | 31 | saved_stderr = dup(fileno(stderr));
|
| 32 | + if (saved_stdout == -1 || saved_stderr == -1) { |
| 33 | + close(stdoutPipe[0]); |
| 34 | + close(stdoutPipe[1]); |
| 35 | + close(stderrPipe[0]); |
| 36 | + close(stderrPipe[1]); |
| 37 | + return; |
| 38 | + } |
39 | 39 |
|
40 |
| - // Redirect stdout to the file |
41 |
| - freopen([[NRAutoLogCollector logFileURL].path cStringUsingEncoding:NSUTF8StringEncoding], "a+", stdout); |
42 |
| - fileDescriptor = freopen([[NRAutoLogCollector logFileURL].path cStringUsingEncoding:NSUTF8StringEncoding], "a+", stderr); |
43 |
| - |
44 |
| - [NRAutoLogCollector monitorFile:[NRAutoLogCollector logFileURL].path]; |
| 40 | + // Redirect stdout and stderr to the write ends of the pipes |
| 41 | + if (dup2(stdoutPipe[1], fileno(stdout)) == -1 || dup2(stderrPipe[1], fileno(stderr)) == -1) { |
| 42 | + close(stdoutPipe[0]); |
| 43 | + close(stdoutPipe[1]); |
| 44 | + close(stderrPipe[0]); |
| 45 | + close(stderrPipe[1]); |
| 46 | + close(saved_stdout); |
| 47 | + close(saved_stderr); |
| 48 | + return; |
| 49 | + } |
| 50 | + close(stdoutPipe[1]); // Close the original write end of the stdout pipe |
| 51 | + close(stderrPipe[1]); // Close the original write end of the stderr pipe |
| 52 | + |
| 53 | + // Read from the pipes in background threads |
| 54 | + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ |
| 55 | + [NRAutoLogCollector readAndLog:stdoutPipe[0]]; |
| 56 | + }); |
| 57 | + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ |
| 58 | + [NRAutoLogCollector readAndLog:stderrPipe[0]]; |
| 59 | + }); |
| 60 | + |
| 61 | + // Restore the original stdout and stderr when done |
| 62 | + atexit_b(^{ |
| 63 | + [NRAutoLogCollector restoreStandardOutputAndError]; |
| 64 | + }); |
| 65 | +} |
| 66 | + |
| 67 | ++ (void) readAndLog:(int) fd { |
| 68 | + char buffer[2048]; |
| 69 | + ssize_t count; |
| 70 | + while ((count = read(fd, buffer, sizeof(buffer) - 1)) > 0) { |
| 71 | + buffer[count] = '\0'; // Null-terminate the string |
| 72 | + NSString *output = [NSString stringWithUTF8String:buffer]; |
| 73 | + NSArray<NSString *> *newLogEntries = [output componentsSeparatedByString:@"\n\n"]; |
| 74 | + |
| 75 | + // Process each log entry |
| 76 | + for (NSString *logEntry in newLogEntries) { |
| 77 | + if ([logEntry length] > 0) { |
| 78 | + [NRLogger log:[NRAutoLogCollector extractType:logEntry] withMessage:logEntry withTimestamp:[NRAutoLogCollector extractTimestamp:logEntry]]; |
| 79 | + } |
| 80 | + } |
| 81 | + } |
| 82 | + close(fd); |
45 | 83 | }
|
46 | 84 |
|
47 | 85 | + (void) restoreStandardOutputAndError {
|
48 |
| - [NRAutoLogCollector readAndParseLogFile]; |
49 |
| - |
50 |
| - // Restore the original stdout |
51 | 86 | dup2(saved_stdout, fileno(stdout));
|
52 | 87 | dup2(saved_stderr, fileno(stderr));
|
53 | 88 | close(saved_stdout);
|
54 | 89 | close(saved_stderr);
|
55 | 90 | }
|
56 | 91 |
|
57 |
| -+ (void) readAndParseLogFile { |
58 |
| - fflush(stdout); |
59 |
| - fflush(stderr); |
60 |
| - // Check if the file exists |
61 |
| - if (![[NSFileManager defaultManager] fileExistsAtPath:[NRAutoLogCollector logFileURL].path]) { |
62 |
| - return; |
63 |
| - } |
64 |
| - |
65 |
| - // Read the file content into an NSString |
66 |
| - NSError *error = nil; |
67 |
| - NSString *fileContents = [NSString stringWithContentsOfFile:[NRAutoLogCollector logFileURL].path |
68 |
| - encoding:NSUTF8StringEncoding |
69 |
| - error:&error]; |
70 |
| - [NRAutoLogCollector clearLogFile]; |
71 |
| - |
72 |
| - if (error) { |
73 |
| - return; |
74 |
| - } else if (fileContents.length == 0){ |
75 |
| - return; |
76 |
| - } |
77 |
| - |
78 |
| - // Split the file contents into individual log entries |
79 |
| - NSArray<NSString *> *newLogEntries = [fileContents componentsSeparatedByString:@"\n\n"]; |
80 |
| - |
81 |
| - // Process each log entry |
82 |
| - for (NSString *logEntry in newLogEntries) { |
83 |
| - if ([logEntry length] > 0) { |
84 |
| - [NRLogger log:[NRAutoLogCollector extractType:logEntry] withMessage:logEntry withTimestamp:[NRAutoLogCollector extractTimestamp:logEntry]]; |
85 |
| - } |
86 |
| - } |
87 |
| -} |
88 |
| - |
89 | 92 | + (BOOL) isValidTimestamp:(NSString *) timestampString {
|
90 | 93 | // Check if the timestamp string can be converted to a double
|
91 | 94 | double timestamp = [timestampString doubleValue];
|
@@ -148,29 +151,5 @@ + (unsigned int) extractType:(NSString *) inputString {
|
148 | 151 |
|
149 | 152 | return NRLogLevelNone;
|
150 | 153 | }
|
151 |
| - |
152 |
| -+ (void) monitorFile:(NSString *) filePath { |
153 |
| - // Create a dispatch queue for handling log file events |
154 |
| - dispatch_queue_t queue = dispatch_queue_create("newrelic.log.monitor.queue", NULL); |
155 |
| - |
156 |
| - // Create a dispatch source to monitor the file descriptor for writes |
157 |
| - dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fileno(fileDescriptor), DISPATCH_VNODE_WRITE, queue); |
158 |
| - |
159 |
| - // Set the event handler block |
160 |
| - dispatch_source_set_event_handler(source, ^{ |
161 |
| - unsigned long flags = dispatch_source_get_data(source); |
162 |
| - if (flags & DISPATCH_VNODE_WRITE) { |
163 |
| - [NRAutoLogCollector readAndParseLogFile]; |
164 |
| - } |
165 |
| - }); |
166 |
| - |
167 |
| - // Set the cancel handler block |
168 |
| - dispatch_source_set_cancel_handler(source, ^{ |
169 |
| - close(fileno(fileDescriptor)); |
170 |
| - }); |
171 |
| - |
172 |
| - // Start monitoring |
173 |
| - dispatch_resume(source); |
174 |
| -} |
175 | 154 |
|
176 | 155 | @end
|
0 commit comments