blob: ef130235825ff513707ffbefe4adfbcb02eb4b46 [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
Anders Carlsson7bca8ca2018-08-30 09:30:29 +020011#import "RTCFileLogger.h"
Zeke Chin2d3b7e22015-07-14 12:55:44 -070012
kwibergbfefb032016-05-01 14:53:46 -070013#include <memory>
14
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "rtc_base/checks.h"
Steve Anton10542f22019-01-11 09:11:00 -080016#include "rtc_base/file_rotating_stream.h"
17#include "rtc_base/log_sinks.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020018#include "rtc_base/logging.h"
Zeke Chin2d3b7e22015-07-14 12:55:44 -070019
tkchin28bae022015-07-23 12:27:02 -070020NSString *const kDefaultLogDirName = @"webrtc_logs";
Zeke Chin2d3b7e22015-07-14 12:55:44 -070021NSUInteger const kDefaultMaxFileSize = 10 * 1024 * 1024; // 10MB.
hayscd02b0fa2015-12-08 13:59:05 -080022const char *kRTCFileLoggerRotatingLogPrefix = "rotating_log";
Zeke Chin2d3b7e22015-07-14 12:55:44 -070023
Zeke Chin2d3b7e22015-07-14 12:55:44 -070024@implementation RTCFileLogger {
25 BOOL _hasStarted;
tkchin28bae022015-07-23 12:27:02 -070026 NSString *_dirPath;
Zeke Chin2d3b7e22015-07-14 12:55:44 -070027 NSUInteger _maxFileSize;
kwibergbfefb032016-05-01 14:53:46 -070028 std::unique_ptr<rtc::FileRotatingLogSink> _logSink;
Zeke Chin2d3b7e22015-07-14 12:55:44 -070029}
30
31@synthesize severity = _severity;
hayscd02b0fa2015-12-08 13:59:05 -080032@synthesize rotationType = _rotationType;
tkchind162a5e2016-01-27 15:11:47 -080033@synthesize shouldDisableBuffering = _shouldDisableBuffering;
Zeke Chin2d3b7e22015-07-14 12:55:44 -070034
35- (instancetype)init {
36 NSArray *paths = NSSearchPathForDirectoriesInDomains(
37 NSDocumentDirectory, NSUserDomainMask, YES);
38 NSString *documentsDirPath = [paths firstObject];
tkchin28bae022015-07-23 12:27:02 -070039 NSString *defaultDirPath =
40 [documentsDirPath stringByAppendingPathComponent:kDefaultLogDirName];
41 return [self initWithDirPath:defaultDirPath
42 maxFileSize:kDefaultMaxFileSize];
Zeke Chin2d3b7e22015-07-14 12:55:44 -070043}
44
tkchin28bae022015-07-23 12:27:02 -070045- (instancetype)initWithDirPath:(NSString *)dirPath
46 maxFileSize:(NSUInteger)maxFileSize {
hayscd02b0fa2015-12-08 13:59:05 -080047 return [self initWithDirPath:dirPath
48 maxFileSize:maxFileSize
tkchin8b9ca952016-03-31 12:08:03 -070049 rotationType:RTCFileLoggerTypeCall];
hayscd02b0fa2015-12-08 13:59:05 -080050}
51
52- (instancetype)initWithDirPath:(NSString *)dirPath
53 maxFileSize:(NSUInteger)maxFileSize
54 rotationType:(RTCFileLoggerRotationType)rotationType {
tkchin28bae022015-07-23 12:27:02 -070055 NSParameterAssert(dirPath.length);
Zeke Chin2d3b7e22015-07-14 12:55:44 -070056 NSParameterAssert(maxFileSize);
57 if (self = [super init]) {
tkchin28bae022015-07-23 12:27:02 -070058 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;
Zeke Chin2d3b7e22015-07-14 12:55:44 -070075 _maxFileSize = maxFileSize;
tkchin8b9ca952016-03-31 12:08:03 -070076 _severity = RTCFileLoggerSeverityInfo;
Zeke Chin2d3b7e22015-07-14 12:55:44 -070077 }
78 return self;
79}
80
81- (void)dealloc {
82 [self stop];
83}
84
85- (void)start {
86 if (_hasStarted) {
87 return;
88 }
hayscd02b0fa2015-12-08 13:59:05 -080089 switch (_rotationType) {
tkchin8b9ca952016-03-31 12:08:03 -070090 case RTCFileLoggerTypeApp:
hayscd02b0fa2015-12-08 13:59:05 -080091 _logSink.reset(
92 new rtc::FileRotatingLogSink(_dirPath.UTF8String,
93 kRTCFileLoggerRotatingLogPrefix,
94 _maxFileSize,
95 _maxFileSize / 10));
96 break;
tkchin8b9ca952016-03-31 12:08:03 -070097 case RTCFileLoggerTypeCall:
hayscd02b0fa2015-12-08 13:59:05 -080098 _logSink.reset(
99 new rtc::CallSessionFileRotatingLogSink(_dirPath.UTF8String,
100 _maxFileSize));
101 break;
102 }
tkchin28bae022015-07-23 12:27:02 -0700103 if (!_logSink->Init()) {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100104 RTC_LOG(LS_ERROR) << "Failed to open log files at path: " << _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
Peter Hanspersd9b64cd2018-01-12 16:16:18 +0100127- (nullable NSData *)logData {
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700128 if (_hasStarted) {
129 return nil;
130 }
tkchin28bae022015-07-23 12:27:02 -0700131 NSMutableData* logData = [NSMutableData data];
Niels Möllerd9ac0582019-01-03 14:21:38 +0100132 std::unique_ptr<rtc::FileRotatingStreamReader> stream;
hayscd02b0fa2015-12-08 13:59:05 -0800133 switch(_rotationType) {
tkchin8b9ca952016-03-31 12:08:03 -0700134 case RTCFileLoggerTypeApp:
Niels Möllerd9ac0582019-01-03 14:21:38 +0100135 stream = absl::make_unique<rtc::FileRotatingStreamReader>(_dirPath.UTF8String,
136 kRTCFileLoggerRotatingLogPrefix);
hayscd02b0fa2015-12-08 13:59:05 -0800137 break;
tkchin8b9ca952016-03-31 12:08:03 -0700138 case RTCFileLoggerTypeCall:
Niels Möllerd9ac0582019-01-03 14:21:38 +0100139 stream = absl::make_unique<rtc::CallSessionFileRotatingStreamReader>(_dirPath.UTF8String);
hayscd02b0fa2015-12-08 13:59:05 -0800140 break;
141 }
Niels Möllerd9ac0582019-01-03 14:21:38 +0100142 size_t bufferSize = stream->GetSize();
143 if (bufferSize == 0) {
tkchin28bae022015-07-23 12:27:02 -0700144 return logData;
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700145 }
tkchin28bae022015-07-23 12:27:02 -0700146 // Allocate memory using malloc so we can pass it direcly to NSData without
147 // copying.
kwibergbfefb032016-05-01 14:53:46 -0700148 std::unique_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(bufferSize)));
Niels Möllerd9ac0582019-01-03 14:21:38 +0100149 size_t read = stream->ReadAll(buffer.get(), bufferSize);
tkchin28bae022015-07-23 12:27:02 -0700150 logData = [[NSMutableData alloc] initWithBytesNoCopy:buffer.release()
151 length:read];
152 return logData;
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700153}
154
155#pragma mark - Private
156
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700157- (rtc::LoggingSeverity)rtcSeverity {
158 switch (_severity) {
tkchin8b9ca952016-03-31 12:08:03 -0700159 case RTCFileLoggerSeverityVerbose:
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700160 return rtc::LS_VERBOSE;
tkchin8b9ca952016-03-31 12:08:03 -0700161 case RTCFileLoggerSeverityInfo:
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700162 return rtc::LS_INFO;
tkchin8b9ca952016-03-31 12:08:03 -0700163 case RTCFileLoggerSeverityWarning:
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700164 return rtc::LS_WARNING;
tkchin8b9ca952016-03-31 12:08:03 -0700165 case RTCFileLoggerSeverityError:
Zeke Chin2d3b7e22015-07-14 12:55:44 -0700166 return rtc::LS_ERROR;
167 }
168}
169
170@end