blob: cc3a39d0beefb7c5c330134ad8c2e3e44a716aaf [file] [log] [blame]
Zeke Chin2d3b7e22015-07-14 12:55:44 -07001/*
Jon Hjelle6140fcc2016-02-24 16:33:12 -08002 * Copyright 2015 The WebRTC project authors. All Rights Reserved.
Zeke Chin2d3b7e22015-07-14 12:55:44 -07003 *
Jon Hjelle6140fcc2016-02-24 16:33:12 -08004 * 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.
Zeke Chin2d3b7e22015-07-14 12:55:44 -07009 */
10
11#import "RTCFileLogger.h"
12
13#include "webrtc/base/checks.h"
tkchin28bae022015-07-23 12:27:02 -070014#include "webrtc/base/filerotatingstream.h"
Zeke Chin2d3b7e22015-07-14 12:55:44 -070015#include "webrtc/base/logging.h"
tkchin28bae022015-07-23 12:27:02 -070016#include "webrtc/base/logsinks.h"
Zeke Chin2d3b7e22015-07-14 12:55:44 -070017#include "webrtc/base/scoped_ptr.h"
Zeke Chin2d3b7e22015-07-14 12:55:44 -070018
tkchin28bae022015-07-23 12:27:02 -070019NSString *const kDefaultLogDirName = @"webrtc_logs";
Zeke Chin2d3b7e22015-07-14 12:55:44 -070020NSUInteger const kDefaultMaxFileSize = 10 * 1024 * 1024; // 10MB.
hayscd02b0fa2015-12-08 13:59:05 -080021const char *kRTCFileLoggerRotatingLogPrefix = "rotating_log";
Zeke Chin2d3b7e22015-07-14 12:55:44 -070022
Zeke Chin2d3b7e22015-07-14 12:55:44 -070023@implementation RTCFileLogger {
24 BOOL _hasStarted;
tkchin28bae022015-07-23 12:27:02 -070025 NSString *_dirPath;
Zeke Chin2d3b7e22015-07-14 12:55:44 -070026 NSUInteger _maxFileSize;
hayscd02b0fa2015-12-08 13:59:05 -080027 rtc::scoped_ptr<rtc::FileRotatingLogSink> _logSink;
Zeke Chin2d3b7e22015-07-14 12:55:44 -070028}
29
30@synthesize severity = _severity;
hayscd02b0fa2015-12-08 13:59:05 -080031@synthesize rotationType = _rotationType;
tkchind162a5e2016-01-27 15:11:47 -080032@synthesize shouldDisableBuffering = _shouldDisableBuffering;
Zeke Chin2d3b7e22015-07-14 12:55:44 -070033
34- (instancetype)init {
35 NSArray *paths = NSSearchPathForDirectoriesInDomains(
36 NSDocumentDirectory, NSUserDomainMask, YES);
37 NSString *documentsDirPath = [paths firstObject];
tkchin28bae022015-07-23 12:27:02 -070038 NSString *defaultDirPath =
39 [documentsDirPath stringByAppendingPathComponent:kDefaultLogDirName];
40 return [self initWithDirPath:defaultDirPath
41 maxFileSize:kDefaultMaxFileSize];
Zeke Chin2d3b7e22015-07-14 12:55:44 -070042}
43
tkchin28bae022015-07-23 12:27:02 -070044- (instancetype)initWithDirPath:(NSString *)dirPath
45 maxFileSize:(NSUInteger)maxFileSize {
hayscd02b0fa2015-12-08 13:59:05 -080046 return [self initWithDirPath:dirPath
47 maxFileSize:maxFileSize
48 rotationType:kRTCFileLoggerTypeCall];
49}
50
51- (instancetype)initWithDirPath:(NSString *)dirPath
52 maxFileSize:(NSUInteger)maxFileSize
53 rotationType:(RTCFileLoggerRotationType)rotationType {
tkchin28bae022015-07-23 12:27:02 -070054 NSParameterAssert(dirPath.length);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070055 NSParameterAssert(maxFileSize);
56 if (self = [super init]) {
tkchin28bae022015-07-23 12:27:02 -070057 BOOL isDir = NO;
58 NSFileManager *fileManager = [NSFileManager defaultManager];
59 if ([fileManager fileExistsAtPath:dirPath isDirectory:&isDir]) {
60 if (!isDir) {
61 // Bail if something already exists there.
62 return nil;
63 }
64 } else {
65 if (![fileManager createDirectoryAtPath:dirPath
66 withIntermediateDirectories:NO
67 attributes:nil
68 error:nil]) {
69 // Bail if we failed to create a directory.
70 return nil;
71 }
72 }
73 _dirPath = dirPath;
Zeke Chin2d3b7e22015-07-14 12:55:44 -070074 _maxFileSize = maxFileSize;
75 _severity = kRTCFileLoggerSeverityInfo;
76 }
77 return self;
78}
79
80- (void)dealloc {
81 [self stop];
82}
83
84- (void)start {
85 if (_hasStarted) {
86 return;
87 }
hayscd02b0fa2015-12-08 13:59:05 -080088 switch (_rotationType) {
89 case kRTCFileLoggerTypeApp:
90 _logSink.reset(
91 new rtc::FileRotatingLogSink(_dirPath.UTF8String,
92 kRTCFileLoggerRotatingLogPrefix,
93 _maxFileSize,
94 _maxFileSize / 10));
95 break;
96 case kRTCFileLoggerTypeCall:
97 _logSink.reset(
98 new rtc::CallSessionFileRotatingLogSink(_dirPath.UTF8String,
99 _maxFileSize));
100 break;
101 }
tkchin28bae022015-07-23 12:27:02 -0700102 if (!_logSink->Init()) {
103 LOG(LS_ERROR) << "Failed to open log files at path: "
104 << _dirPath.UTF8String;
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700105 _logSink.reset();
106 return;
107 }
tkchind162a5e2016-01-27 15:11:47 -0800108 if (_shouldDisableBuffering) {
109 _logSink->DisableBuffering();
110 }
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700111 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 }
henrikg91d6ede2015-09-17 00:24:34 -0700121 RTC_DCHECK(_logSink);
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700122 rtc::LogMessage::RemoveLogToStream(_logSink.get());
123 _hasStarted = NO;
tkchin28bae022015-07-23 12:27:02 -0700124 _logSink.reset();
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700125}
126
127- (NSData *)logData {
128 if (_hasStarted) {
129 return nil;
130 }
tkchin28bae022015-07-23 12:27:02 -0700131 NSMutableData* logData = [NSMutableData data];
hayscd02b0fa2015-12-08 13:59:05 -0800132 rtc::scoped_ptr<rtc::FileRotatingStream> stream;
133 switch(_rotationType) {
134 case kRTCFileLoggerTypeApp:
135 stream.reset(
136 new rtc::FileRotatingStream(_dirPath.UTF8String,
137 kRTCFileLoggerRotatingLogPrefix));
138 break;
139 case kRTCFileLoggerTypeCall:
140 stream.reset(new rtc::CallSessionFileRotatingStream(_dirPath.UTF8String));
141 break;
142 }
tkchin28bae022015-07-23 12:27:02 -0700143 if (!stream->Open()) {
144 return logData;
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700145 }
tkchin28bae022015-07-23 12:27:02 -0700146 size_t bufferSize = 0;
147 if (!stream->GetSize(&bufferSize) || bufferSize == 0) {
148 return logData;
149 }
150 size_t read = 0;
151 // Allocate memory using malloc so we can pass it direcly to NSData without
152 // copying.
153 rtc::scoped_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(bufferSize)));
154 stream->ReadAll(buffer.get(), bufferSize, &read, nullptr);
155 logData = [[NSMutableData alloc] initWithBytesNoCopy:buffer.release()
156 length:read];
157 return logData;
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700158}
159
160#pragma mark - Private
161
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700162- (rtc::LoggingSeverity)rtcSeverity {
163 switch (_severity) {
164 case kRTCFileLoggerSeverityVerbose:
165 return rtc::LS_VERBOSE;
166 case kRTCFileLoggerSeverityInfo:
167 return rtc::LS_INFO;
168 case kRTCFileLoggerSeverityWarning:
169 return rtc::LS_WARNING;
170 case kRTCFileLoggerSeverityError:
171 return rtc::LS_ERROR;
172 }
173}
174
175@end