DDFileLogger+Buffering.m 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. // Software License Agreement (BSD License)
  2. //
  3. // Copyright (c) 2010-2024, Deusty, LLC
  4. // All rights reserved.
  5. //
  6. // Redistribution and use of this software in source and binary forms,
  7. // with or without modification, are permitted provided that the following conditions are met:
  8. //
  9. // * Redistributions of source code must retain the above copyright notice,
  10. // this list of conditions and the following disclaimer.
  11. //
  12. // * Neither the name of Deusty nor the names of its contributors may be used
  13. // to endorse or promote products derived from this software without specific
  14. // prior written permission of Deusty, LLC.
  15. #import <sys/mount.h>
  16. #import <CocoaLumberjack/DDFileLogger+Buffering.h>
  17. #import "../DDFileLogger+Internal.h"
  18. static const NSUInteger kDDDefaultBufferSize = 4096; // 4 kB, block f_bsize on iphone7
  19. static const NSUInteger kDDMaxBufferSize = 1048576; // ~1 mB, f_iosize on iphone7
  20. // Reads attributes from base file system to determine buffer size.
  21. // see statfs in sys/mount.h for descriptions of f_iosize and f_bsize.
  22. // f_bsize == "default", and f_iosize == "max"
  23. static inline NSUInteger p_DDGetDefaultBufferSizeBytesMax(const BOOL max) {
  24. struct statfs *mountedFileSystems = NULL;
  25. __auto_type count = getmntinfo(&mountedFileSystems, 0);
  26. for (int i = 0; i < count; i++) {
  27. __auto_type mounted = mountedFileSystems[i];
  28. __auto_type name = mounted.f_mntonname;
  29. // We can use 2 as max here, since any length > 1 will fail the if-statement.
  30. if (strnlen(name, 2) == 1 && *name == '/') {
  31. return max ? (NSUInteger)mounted.f_iosize : (NSUInteger)mounted.f_bsize;
  32. }
  33. }
  34. return max ? kDDMaxBufferSize : kDDDefaultBufferSize;
  35. }
  36. static NSUInteger DDGetMaxBufferSizeBytes(void) {
  37. static NSUInteger maxBufferSize = 0;
  38. static dispatch_once_t onceToken;
  39. dispatch_once(&onceToken, ^{
  40. maxBufferSize = p_DDGetDefaultBufferSizeBytesMax(YES);
  41. });
  42. return maxBufferSize;
  43. }
  44. static NSUInteger DDGetDefaultBufferSizeBytes(void) {
  45. static NSUInteger defaultBufferSize = 0;
  46. static dispatch_once_t onceToken;
  47. dispatch_once(&onceToken, ^{
  48. defaultBufferSize = p_DDGetDefaultBufferSizeBytesMax(NO);
  49. });
  50. return defaultBufferSize;
  51. }
  52. @interface DDBufferedProxy : NSProxy
  53. @property (nonatomic) DDFileLogger *fileLogger;
  54. @property (nonatomic) NSOutputStream *buffer;
  55. @property (nonatomic) NSUInteger maxBufferSizeBytes;
  56. @property (nonatomic) NSUInteger currentBufferSizeBytes;
  57. @end
  58. @implementation DDBufferedProxy
  59. - (instancetype)initWithFileLogger:(DDFileLogger *)fileLogger {
  60. _fileLogger = fileLogger;
  61. _maxBufferSizeBytes = DDGetDefaultBufferSizeBytes();
  62. [self flushBuffer];
  63. return self;
  64. }
  65. - (void)dealloc {
  66. __auto_type block = ^{
  67. [self lt_sendBufferedDataToFileLogger];
  68. self.fileLogger = nil;
  69. };
  70. if ([self->_fileLogger isOnInternalLoggerQueue]) {
  71. block();
  72. } else {
  73. dispatch_sync(self->_fileLogger.loggerQueue, block);
  74. }
  75. }
  76. #pragma mark - Buffering
  77. - (void)flushBuffer {
  78. [_buffer close];
  79. _buffer = [NSOutputStream outputStreamToMemory];
  80. [_buffer open];
  81. _currentBufferSizeBytes = 0;
  82. }
  83. - (void)lt_sendBufferedDataToFileLogger {
  84. NSData *data = [_buffer propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
  85. [_fileLogger lt_logData:data];
  86. [self flushBuffer];
  87. }
  88. #pragma mark - Logging
  89. - (void)logMessage:(DDLogMessage *)logMessage {
  90. // Don't need to check for isOnInternalLoggerQueue, -lt_dataForMessage: will do it for us.
  91. __auto_type data = [_fileLogger lt_dataForMessage:logMessage];
  92. if (data.length == 0) {
  93. return;
  94. }
  95. [data enumerateByteRangesUsingBlock:^(const void * __nonnull bytes, NSRange byteRange, BOOL * __nonnull __unused stop) {
  96. __auto_type bytesLength = byteRange.length;
  97. #ifdef NS_BLOCK_ASSERTIONS
  98. __unused
  99. #endif
  100. __auto_type written = [_buffer write:bytes maxLength:bytesLength];
  101. NSAssert(written > 0 && (NSUInteger)written == bytesLength, @"Failed to write to memory buffer.");
  102. _currentBufferSizeBytes += bytesLength;
  103. if (_currentBufferSizeBytes >= _maxBufferSizeBytes) {
  104. [self lt_sendBufferedDataToFileLogger];
  105. }
  106. }];
  107. }
  108. - (void)flush {
  109. // This method is public.
  110. // We need to execute the rolling on our logging thread/queue.
  111. __auto_type block = ^{
  112. @autoreleasepool {
  113. [self lt_sendBufferedDataToFileLogger];
  114. [self.fileLogger flush];
  115. }
  116. };
  117. // The design of this method is taken from the DDAbstractLogger implementation.
  118. // For extensive documentation please refer to the DDAbstractLogger implementation.
  119. if ([self.fileLogger isOnInternalLoggerQueue]) {
  120. block();
  121. } else {
  122. NSAssert(![self.fileLogger isOnGlobalLoggingQueue], @"Core architecture requirement failure");
  123. dispatch_sync(DDLog.loggingQueue, ^{
  124. dispatch_sync(self.fileLogger.loggerQueue, block);
  125. });
  126. }
  127. }
  128. #pragma mark - Properties
  129. - (void)setMaxBufferSizeBytes:(NSUInteger)newBufferSizeBytes {
  130. _maxBufferSizeBytes = MIN(newBufferSizeBytes, DDGetMaxBufferSizeBytes());
  131. }
  132. #pragma mark - Wrapping
  133. - (DDFileLogger *)wrapWithBuffer {
  134. return (DDFileLogger *)self;
  135. }
  136. - (DDFileLogger *)unwrapFromBuffer {
  137. return (DDFileLogger *)self.fileLogger;
  138. }
  139. #pragma mark - NSProxy
  140. - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
  141. return [self.fileLogger methodSignatureForSelector:sel];
  142. }
  143. - (BOOL)respondsToSelector:(SEL)aSelector {
  144. return [self.fileLogger respondsToSelector:aSelector];
  145. }
  146. - (void)forwardInvocation:(NSInvocation *)invocation {
  147. [invocation invokeWithTarget:self.fileLogger];
  148. }
  149. @end
  150. @implementation DDFileLogger (Buffering)
  151. - (instancetype)wrapWithBuffer {
  152. return (DDFileLogger *)[[DDBufferedProxy alloc] initWithFileLogger:self];
  153. }
  154. - (instancetype)unwrapFromBuffer {
  155. return self;
  156. }
  157. @end