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