FLEXASLLogController.m 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. //
  2. // FLEXASLLogController.m
  3. // FLEX
  4. //
  5. // Created by Tanner on 3/14/19.
  6. // Copyright © 2020 FLEX Team. All rights reserved.
  7. //
  8. #import "FLEXASLLogController.h"
  9. #import <asl.h>
  10. // Querying the ASL is much slower in the simulator. We need a longer polling interval to keep things responsive.
  11. #if TARGET_IPHONE_SIMULATOR
  12. #define updateInterval 5.0
  13. #else
  14. #define updateInterval 0.5
  15. #endif
  16. @interface FLEXASLLogController ()
  17. @property (nonatomic, readonly) void (^updateHandler)(NSArray<FLEXSystemLogMessage *> *);
  18. @property (nonatomic) NSTimer *logUpdateTimer;
  19. @property (nonatomic, readonly) NSMutableIndexSet *logMessageIdentifiers;
  20. // ASL stuff
  21. @property (nonatomic) NSUInteger heapSize;
  22. @property (nonatomic) dispatch_queue_t logQueue;
  23. @property (nonatomic) dispatch_io_t io;
  24. @property (nonatomic) NSString *remaining;
  25. @property (nonatomic) int stderror;
  26. @property (nonatomic) NSString *lastTimestamp;
  27. @end
  28. @implementation FLEXASLLogController
  29. + (instancetype)withUpdateHandler:(void(^)(NSArray<FLEXSystemLogMessage *> *newMessages))newMessagesHandler {
  30. return [[self alloc] initWithUpdateHandler:newMessagesHandler];
  31. }
  32. - (id)initWithUpdateHandler:(void(^)(NSArray<FLEXSystemLogMessage *> *newMessages))newMessagesHandler {
  33. NSParameterAssert(newMessagesHandler);
  34. self = [super init];
  35. if (self) {
  36. _updateHandler = newMessagesHandler;
  37. _logMessageIdentifiers = [NSMutableIndexSet new];
  38. self.logUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:updateInterval
  39. target:self
  40. selector:@selector(updateLogMessages)
  41. userInfo:nil
  42. repeats:YES];
  43. }
  44. return self;
  45. }
  46. - (void)dealloc {
  47. [self.logUpdateTimer invalidate];
  48. }
  49. - (BOOL)startMonitoring {
  50. [self.logUpdateTimer fire];
  51. return YES;
  52. }
  53. - (void)updateLogMessages {
  54. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  55. NSArray<FLEXSystemLogMessage *> *newMessages;
  56. @synchronized (self) {
  57. newMessages = [self newLogMessagesForCurrentProcess];
  58. if (!newMessages.count) {
  59. return;
  60. }
  61. for (FLEXSystemLogMessage *message in newMessages) {
  62. [self.logMessageIdentifiers addIndex:(NSUInteger)message.messageID];
  63. }
  64. self.lastTimestamp = @(asl_get(newMessages.lastObject.aslMessage, ASL_KEY_TIME) ?: "null");
  65. }
  66. dispatch_async(dispatch_get_main_queue(), ^{
  67. self.updateHandler(newMessages);
  68. });
  69. });
  70. }
  71. #pragma mark - Log Message Fetching
  72. - (NSArray<FLEXSystemLogMessage *> *)newLogMessagesForCurrentProcess {
  73. if (!self.logMessageIdentifiers.count) {
  74. return [self allLogMessagesForCurrentProcess];
  75. }
  76. aslresponse response = [self ASLMessageListForCurrentProcess];
  77. aslmsg aslMessage = NULL;
  78. NSMutableArray<FLEXSystemLogMessage *> *newMessages = [NSMutableArray new];
  79. while ((aslMessage = asl_next(response))) {
  80. NSUInteger messageID = (NSUInteger)atoll(asl_get(aslMessage, ASL_KEY_MSG_ID));
  81. if (![self.logMessageIdentifiers containsIndex:messageID]) {
  82. [newMessages addObject:[FLEXSystemLogMessage logMessageFromASLMessage:aslMessage]];
  83. }
  84. }
  85. asl_release(response);
  86. return newMessages;
  87. }
  88. - (aslresponse)ASLMessageListForCurrentProcess {
  89. static NSString *pidString = nil;
  90. if (!pidString) {
  91. pidString = @([NSProcessInfo.processInfo processIdentifier]).stringValue;
  92. }
  93. // Create system log query object.
  94. asl_object_t query = asl_new(ASL_TYPE_QUERY);
  95. // Filter for messages from the current process.
  96. // Note that this appears to happen by default on device, but is required in the simulator.
  97. asl_set_query(query, ASL_KEY_PID, pidString.UTF8String, ASL_QUERY_OP_EQUAL);
  98. // Filter for messages after the last retrieved message.
  99. if (self.lastTimestamp) {
  100. asl_set_query(query, ASL_KEY_TIME, self.lastTimestamp.UTF8String, ASL_QUERY_OP_GREATER);
  101. }
  102. return asl_search(NULL, query);
  103. }
  104. - (NSArray<FLEXSystemLogMessage *> *)allLogMessagesForCurrentProcess {
  105. aslresponse response = [self ASLMessageListForCurrentProcess];
  106. aslmsg aslMessage = NULL;
  107. NSMutableArray<FLEXSystemLogMessage *> *logMessages = [NSMutableArray new];
  108. while ((aslMessage = asl_next(response))) {
  109. [logMessages addObject:[FLEXSystemLogMessage logMessageFromASLMessage:aslMessage]];
  110. }
  111. asl_release(response);
  112. return logMessages;
  113. }
  114. @end