| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- // Software License Agreement (BSD License)
- //
- // Copyright (c) 2010-2024, Deusty, LLC
- // All rights reserved.
- //
- // Redistribution and use of this software in source and binary forms,
- // with or without modification, are permitted provided that the following conditions are met:
- //
- // * Redistributions of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- //
- // * Neither the name of Deusty nor the names of its contributors may be used
- // to endorse or promote products derived from this software without specific
- // prior written permission of Deusty, LLC.
- #import <sys/mount.h>
- #import <CocoaLumberjack/DDFileLogger+Buffering.h>
- #import "../DDFileLogger+Internal.h"
- static const NSUInteger kDDDefaultBufferSize = 4096; // 4 kB, block f_bsize on iphone7
- static const NSUInteger kDDMaxBufferSize = 1048576; // ~1 mB, f_iosize on iphone7
- // Reads attributes from base file system to determine buffer size.
- // see statfs in sys/mount.h for descriptions of f_iosize and f_bsize.
- // f_bsize == "default", and f_iosize == "max"
- static inline NSUInteger p_DDGetDefaultBufferSizeBytesMax(const BOOL max) {
- struct statfs *mountedFileSystems = NULL;
- __auto_type count = getmntinfo(&mountedFileSystems, 0);
- for (int i = 0; i < count; i++) {
- __auto_type mounted = mountedFileSystems[i];
- __auto_type name = mounted.f_mntonname;
- // We can use 2 as max here, since any length > 1 will fail the if-statement.
- if (strnlen(name, 2) == 1 && *name == '/') {
- return max ? (NSUInteger)mounted.f_iosize : (NSUInteger)mounted.f_bsize;
- }
- }
- return max ? kDDMaxBufferSize : kDDDefaultBufferSize;
- }
- static NSUInteger DDGetMaxBufferSizeBytes(void) {
- static NSUInteger maxBufferSize = 0;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- maxBufferSize = p_DDGetDefaultBufferSizeBytesMax(YES);
- });
- return maxBufferSize;
- }
- static NSUInteger DDGetDefaultBufferSizeBytes(void) {
- static NSUInteger defaultBufferSize = 0;
- static dispatch_once_t onceToken;
- dispatch_once(&onceToken, ^{
- defaultBufferSize = p_DDGetDefaultBufferSizeBytesMax(NO);
- });
- return defaultBufferSize;
- }
- @interface DDBufferedProxy : NSProxy
- @property (nonatomic) DDFileLogger *fileLogger;
- @property (nonatomic) NSOutputStream *buffer;
- @property (nonatomic) NSUInteger maxBufferSizeBytes;
- @property (nonatomic) NSUInteger currentBufferSizeBytes;
- @end
- @implementation DDBufferedProxy
- - (instancetype)initWithFileLogger:(DDFileLogger *)fileLogger {
- _fileLogger = fileLogger;
- _maxBufferSizeBytes = DDGetDefaultBufferSizeBytes();
- [self flushBuffer];
- return self;
- }
- - (void)dealloc {
- __auto_type block = ^{
- [self lt_sendBufferedDataToFileLogger];
- self.fileLogger = nil;
- };
- if ([self->_fileLogger isOnInternalLoggerQueue]) {
- block();
- } else {
- dispatch_sync(self->_fileLogger.loggerQueue, block);
- }
- }
- #pragma mark - Buffering
- - (void)flushBuffer {
- [_buffer close];
- _buffer = [NSOutputStream outputStreamToMemory];
- [_buffer open];
- _currentBufferSizeBytes = 0;
- }
- - (void)lt_sendBufferedDataToFileLogger {
- NSData *data = [_buffer propertyForKey:NSStreamDataWrittenToMemoryStreamKey];
- [_fileLogger lt_logData:data];
- [self flushBuffer];
- }
- #pragma mark - Logging
- - (void)logMessage:(DDLogMessage *)logMessage {
- // Don't need to check for isOnInternalLoggerQueue, -lt_dataForMessage: will do it for us.
- __auto_type data = [_fileLogger lt_dataForMessage:logMessage];
- if (data.length == 0) {
- return;
- }
- [data enumerateByteRangesUsingBlock:^(const void * __nonnull bytes, NSRange byteRange, BOOL * __nonnull __unused stop) {
- __auto_type bytesLength = byteRange.length;
- #ifdef NS_BLOCK_ASSERTIONS
- __unused
- #endif
- __auto_type written = [_buffer write:bytes maxLength:bytesLength];
- NSAssert(written > 0 && (NSUInteger)written == bytesLength, @"Failed to write to memory buffer.");
- _currentBufferSizeBytes += bytesLength;
- if (_currentBufferSizeBytes >= _maxBufferSizeBytes) {
- [self lt_sendBufferedDataToFileLogger];
- }
- }];
- }
- - (void)flush {
- // This method is public.
- // We need to execute the rolling on our logging thread/queue.
- __auto_type block = ^{
- @autoreleasepool {
- [self lt_sendBufferedDataToFileLogger];
- [self.fileLogger flush];
- }
- };
- // The design of this method is taken from the DDAbstractLogger implementation.
- // For extensive documentation please refer to the DDAbstractLogger implementation.
- if ([self.fileLogger isOnInternalLoggerQueue]) {
- block();
- } else {
- NSAssert(![self.fileLogger isOnGlobalLoggingQueue], @"Core architecture requirement failure");
- dispatch_sync(DDLog.loggingQueue, ^{
- dispatch_sync(self.fileLogger.loggerQueue, block);
- });
- }
- }
- #pragma mark - Properties
- - (void)setMaxBufferSizeBytes:(NSUInteger)newBufferSizeBytes {
- _maxBufferSizeBytes = MIN(newBufferSizeBytes, DDGetMaxBufferSizeBytes());
- }
- #pragma mark - Wrapping
- - (DDFileLogger *)wrapWithBuffer {
- return (DDFileLogger *)self;
- }
- - (DDFileLogger *)unwrapFromBuffer {
- return (DDFileLogger *)self.fileLogger;
- }
- #pragma mark - NSProxy
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
- return [self.fileLogger methodSignatureForSelector:sel];
- }
- - (BOOL)respondsToSelector:(SEL)aSelector {
- return [self.fileLogger respondsToSelector:aSelector];
- }
- - (void)forwardInvocation:(NSInvocation *)invocation {
- [invocation invokeWithTarget:self.fileLogger];
- }
- @end
- @implementation DDFileLogger (Buffering)
- - (instancetype)wrapWithBuffer {
- return (DDFileLogger *)[[DDBufferedProxy alloc] initWithFileLogger:self];
- }
- - (instancetype)unwrapFromBuffer {
- return self;
- }
- @end
|