You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

154 lines
4.6 KiB

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