1/* 2 * Copyright 2015 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#import "RTCFileLogger.h" 12 13#include <memory> 14 15#include "rtc_base/checks.h" 16#include "rtc_base/file_rotating_stream.h" 17#include "rtc_base/log_sinks.h" 18#include "rtc_base/logging.h" 19 20NSString *const kDefaultLogDirName = @"webrtc_logs"; 21NSUInteger const kDefaultMaxFileSize = 10 * 1024 * 1024; // 10MB. 22const char *kRTCFileLoggerRotatingLogPrefix = "rotating_log"; 23 24@implementation RTC_OBJC_TYPE (RTCFileLogger) { 25 BOOL _hasStarted; 26 NSString *_dirPath; 27 NSUInteger _maxFileSize; 28 std::unique_ptr<rtc::FileRotatingLogSink> _logSink; 29} 30 31@synthesize severity = _severity; 32@synthesize rotationType = _rotationType; 33@synthesize shouldDisableBuffering = _shouldDisableBuffering; 34 35- (instancetype)init { 36 NSArray *paths = NSSearchPathForDirectoriesInDomains( 37 NSDocumentDirectory, NSUserDomainMask, YES); 38 NSString *documentsDirPath = [paths firstObject]; 39 NSString *defaultDirPath = 40 [documentsDirPath stringByAppendingPathComponent:kDefaultLogDirName]; 41 return [self initWithDirPath:defaultDirPath 42 maxFileSize:kDefaultMaxFileSize]; 43} 44 45- (instancetype)initWithDirPath:(NSString *)dirPath 46 maxFileSize:(NSUInteger)maxFileSize { 47 return [self initWithDirPath:dirPath 48 maxFileSize:maxFileSize 49 rotationType:RTCFileLoggerTypeCall]; 50} 51 52- (instancetype)initWithDirPath:(NSString *)dirPath 53 maxFileSize:(NSUInteger)maxFileSize 54 rotationType:(RTCFileLoggerRotationType)rotationType { 55 NSParameterAssert(dirPath.length); 56 NSParameterAssert(maxFileSize); 57 if (self = [super init]) { 58 BOOL isDir = NO; 59 NSFileManager *fileManager = [NSFileManager defaultManager]; 60 if ([fileManager fileExistsAtPath:dirPath isDirectory:&isDir]) { 61 if (!isDir) { 62 // Bail if something already exists there. 63 return nil; 64 } 65 } else { 66 if (![fileManager createDirectoryAtPath:dirPath 67 withIntermediateDirectories:NO 68 attributes:nil 69 error:nil]) { 70 // Bail if we failed to create a directory. 71 return nil; 72 } 73 } 74 _dirPath = dirPath; 75 _maxFileSize = maxFileSize; 76 _severity = RTCFileLoggerSeverityInfo; 77 } 78 return self; 79} 80 81- (void)dealloc { 82 [self stop]; 83} 84 85- (void)start { 86 if (_hasStarted) { 87 return; 88 } 89 switch (_rotationType) { 90 case RTCFileLoggerTypeApp: 91 _logSink.reset( 92 new rtc::FileRotatingLogSink(_dirPath.UTF8String, 93 kRTCFileLoggerRotatingLogPrefix, 94 _maxFileSize, 95 _maxFileSize / 10)); 96 break; 97 case RTCFileLoggerTypeCall: 98 _logSink.reset( 99 new rtc::CallSessionFileRotatingLogSink(_dirPath.UTF8String, 100 _maxFileSize)); 101 break; 102 } 103 if (!_logSink->Init()) { 104 RTC_LOG(LS_ERROR) << "Failed to open log files at path: " << _dirPath.UTF8String; 105 _logSink.reset(); 106 return; 107 } 108 if (_shouldDisableBuffering) { 109 _logSink->DisableBuffering(); 110 } 111 rtc::LogMessage::LogThreads(true); 112 rtc::LogMessage::LogTimestamps(true); 113 rtc::LogMessage::AddLogToStream(_logSink.get(), [self rtcSeverity]); 114 _hasStarted = YES; 115} 116 117- (void)stop { 118 if (!_hasStarted) { 119 return; 120 } 121 RTC_DCHECK(_logSink); 122 rtc::LogMessage::RemoveLogToStream(_logSink.get()); 123 _hasStarted = NO; 124 _logSink.reset(); 125} 126 127- (nullable NSData *)logData { 128 if (_hasStarted) { 129 return nil; 130 } 131 NSMutableData* logData = [NSMutableData data]; 132 std::unique_ptr<rtc::FileRotatingStreamReader> stream; 133 switch(_rotationType) { 134 case RTCFileLoggerTypeApp: 135 stream = std::make_unique<rtc::FileRotatingStreamReader>(_dirPath.UTF8String, 136 kRTCFileLoggerRotatingLogPrefix); 137 break; 138 case RTCFileLoggerTypeCall: 139 stream = std::make_unique<rtc::CallSessionFileRotatingStreamReader>(_dirPath.UTF8String); 140 break; 141 } 142 size_t bufferSize = stream->GetSize(); 143 if (bufferSize == 0) { 144 return logData; 145 } 146 // Allocate memory using malloc so we can pass it direcly to NSData without 147 // copying. 148 std::unique_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(bufferSize))); 149 size_t read = stream->ReadAll(buffer.get(), bufferSize); 150 logData = [[NSMutableData alloc] initWithBytesNoCopy:buffer.release() 151 length:read]; 152 return logData; 153} 154 155#pragma mark - Private 156 157- (rtc::LoggingSeverity)rtcSeverity { 158 switch (_severity) { 159 case RTCFileLoggerSeverityVerbose: 160 return rtc::LS_VERBOSE; 161 case RTCFileLoggerSeverityInfo: 162 return rtc::LS_INFO; 163 case RTCFileLoggerSeverityWarning: 164 return rtc::LS_WARNING; 165 case RTCFileLoggerSeverityError: 166 return rtc::LS_ERROR; 167 } 168} 169 170@end 171