1/* 2 * libjingle 3 * Copyright 2015 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28#import "RTCFileLogger.h" 29 30#include "webrtc/base/checks.h" 31#include "webrtc/base/filerotatingstream.h" 32#include "webrtc/base/logging.h" 33#include "webrtc/base/logsinks.h" 34#include "webrtc/base/scoped_ptr.h" 35 36NSString *const kDefaultLogDirName = @"webrtc_logs"; 37NSUInteger const kDefaultMaxFileSize = 10 * 1024 * 1024; // 10MB. 38const char *kRTCFileLoggerRotatingLogPrefix = "rotating_log"; 39 40@implementation RTCFileLogger { 41 BOOL _hasStarted; 42 NSString *_dirPath; 43 NSUInteger _maxFileSize; 44 rtc::scoped_ptr<rtc::FileRotatingLogSink> _logSink; 45} 46 47@synthesize severity = _severity; 48@synthesize rotationType = _rotationType; 49 50- (instancetype)init { 51 NSArray *paths = NSSearchPathForDirectoriesInDomains( 52 NSDocumentDirectory, NSUserDomainMask, YES); 53 NSString *documentsDirPath = [paths firstObject]; 54 NSString *defaultDirPath = 55 [documentsDirPath stringByAppendingPathComponent:kDefaultLogDirName]; 56 return [self initWithDirPath:defaultDirPath 57 maxFileSize:kDefaultMaxFileSize]; 58} 59 60- (instancetype)initWithDirPath:(NSString *)dirPath 61 maxFileSize:(NSUInteger)maxFileSize { 62 return [self initWithDirPath:dirPath 63 maxFileSize:maxFileSize 64 rotationType:kRTCFileLoggerTypeCall]; 65} 66 67- (instancetype)initWithDirPath:(NSString *)dirPath 68 maxFileSize:(NSUInteger)maxFileSize 69 rotationType:(RTCFileLoggerRotationType)rotationType { 70 NSParameterAssert(dirPath.length); 71 NSParameterAssert(maxFileSize); 72 if (self = [super init]) { 73 BOOL isDir = NO; 74 NSFileManager *fileManager = [NSFileManager defaultManager]; 75 if ([fileManager fileExistsAtPath:dirPath isDirectory:&isDir]) { 76 if (!isDir) { 77 // Bail if something already exists there. 78 return nil; 79 } 80 } else { 81 if (![fileManager createDirectoryAtPath:dirPath 82 withIntermediateDirectories:NO 83 attributes:nil 84 error:nil]) { 85 // Bail if we failed to create a directory. 86 return nil; 87 } 88 } 89 _dirPath = dirPath; 90 _maxFileSize = maxFileSize; 91 _severity = kRTCFileLoggerSeverityInfo; 92 } 93 return self; 94} 95 96- (void)dealloc { 97 [self stop]; 98} 99 100- (void)start { 101 if (_hasStarted) { 102 return; 103 } 104 switch (_rotationType) { 105 case kRTCFileLoggerTypeApp: 106 _logSink.reset( 107 new rtc::FileRotatingLogSink(_dirPath.UTF8String, 108 kRTCFileLoggerRotatingLogPrefix, 109 _maxFileSize, 110 _maxFileSize / 10)); 111 break; 112 case kRTCFileLoggerTypeCall: 113 _logSink.reset( 114 new rtc::CallSessionFileRotatingLogSink(_dirPath.UTF8String, 115 _maxFileSize)); 116 break; 117 } 118 if (!_logSink->Init()) { 119 LOG(LS_ERROR) << "Failed to open log files at path: " 120 << _dirPath.UTF8String; 121 _logSink.reset(); 122 return; 123 } 124 rtc::LogMessage::LogThreads(true); 125 rtc::LogMessage::LogTimestamps(true); 126 rtc::LogMessage::AddLogToStream(_logSink.get(), [self rtcSeverity]); 127 _hasStarted = YES; 128} 129 130- (void)stop { 131 if (!_hasStarted) { 132 return; 133 } 134 RTC_DCHECK(_logSink); 135 rtc::LogMessage::RemoveLogToStream(_logSink.get()); 136 _hasStarted = NO; 137 _logSink.reset(); 138} 139 140- (NSData *)logData { 141 if (_hasStarted) { 142 return nil; 143 } 144 NSMutableData* logData = [NSMutableData data]; 145 rtc::scoped_ptr<rtc::FileRotatingStream> stream; 146 switch(_rotationType) { 147 case kRTCFileLoggerTypeApp: 148 stream.reset( 149 new rtc::FileRotatingStream(_dirPath.UTF8String, 150 kRTCFileLoggerRotatingLogPrefix)); 151 break; 152 case kRTCFileLoggerTypeCall: 153 stream.reset(new rtc::CallSessionFileRotatingStream(_dirPath.UTF8String)); 154 break; 155 } 156 if (!stream->Open()) { 157 return logData; 158 } 159 size_t bufferSize = 0; 160 if (!stream->GetSize(&bufferSize) || bufferSize == 0) { 161 return logData; 162 } 163 size_t read = 0; 164 // Allocate memory using malloc so we can pass it direcly to NSData without 165 // copying. 166 rtc::scoped_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(bufferSize))); 167 stream->ReadAll(buffer.get(), bufferSize, &read, nullptr); 168 logData = [[NSMutableData alloc] initWithBytesNoCopy:buffer.release() 169 length:read]; 170 return logData; 171} 172 173#pragma mark - Private 174 175- (rtc::LoggingSeverity)rtcSeverity { 176 switch (_severity) { 177 case kRTCFileLoggerSeverityVerbose: 178 return rtc::LS_VERBOSE; 179 case kRTCFileLoggerSeverityInfo: 180 return rtc::LS_INFO; 181 case kRTCFileLoggerSeverityWarning: 182 return rtc::LS_WARNING; 183 case kRTCFileLoggerSeverityError: 184 return rtc::LS_ERROR; 185 } 186} 187 188@end 189