Index: talk/app/webrtc/objc/RTCFileLogger.mm |
diff --git a/talk/app/webrtc/objc/RTCFileLogger.mm b/talk/app/webrtc/objc/RTCFileLogger.mm |
index b474d7a5c65dec7f9b0673300998980009e93b4c..3080ebc080ec9b13fe421afdcfb8e7930cb38891 100644 |
--- a/talk/app/webrtc/objc/RTCFileLogger.mm |
+++ b/talk/app/webrtc/objc/RTCFileLogger.mm |
@@ -28,45 +28,19 @@ |
#import "RTCFileLogger.h" |
#include "webrtc/base/checks.h" |
+#include "webrtc/base/filerotatingstream.h" |
#include "webrtc/base/logging.h" |
+#include "webrtc/base/logsinks.h" |
#include "webrtc/base/scoped_ptr.h" |
-#include "webrtc/base/stream.h" |
-NSString *const kDefaultLogFileName = @"webrtc.log"; |
+NSString *const kDefaultLogDirName = @"webrtc_logs"; |
NSUInteger const kDefaultMaxFileSize = 10 * 1024 * 1024; // 10MB. |
-namespace rtc { |
- |
-class CircularFileStreamLogSink : public LogSink { |
- public: |
- // Creates a log sink that writes to the given stream. This log sink takes |
- // ownership of |stream|. |
- CircularFileStreamLogSink(CircularFileStream *stream) { |
- DCHECK(stream); |
- _stream.reset(stream); |
- } |
- |
- ~CircularFileStreamLogSink() override {} |
- |
- void OnLogMessage(const std::string &message) override { |
- if (_stream) { |
- _stream->WriteAll(message.data(), message.size(), nullptr, nullptr); |
- } |
- } |
- |
- CircularFileStream *GetStream() { return _stream.get(); } |
- |
- private: |
- scoped_ptr<CircularFileStream> _stream; |
-}; |
- |
-} // namespace rtc |
- |
@implementation RTCFileLogger { |
BOOL _hasStarted; |
- NSString *_filePath; |
+ NSString *_dirPath; |
NSUInteger _maxFileSize; |
- rtc::scoped_ptr<rtc::CircularFileStreamLogSink> _logSink; |
+ rtc::scoped_ptr<rtc::CallSessionFileRotatingLogSink> _logSink; |
} |
@synthesize severity = _severity; |
@@ -75,18 +49,34 @@ class CircularFileStreamLogSink : public LogSink { |
NSArray *paths = NSSearchPathForDirectoriesInDomains( |
NSDocumentDirectory, NSUserDomainMask, YES); |
NSString *documentsDirPath = [paths firstObject]; |
- NSString *defaultFilePath = |
- [documentsDirPath stringByAppendingPathComponent:kDefaultLogFileName]; |
- return [self initWithFilePath:defaultFilePath |
- maxFileSize:kDefaultMaxFileSize]; |
+ NSString *defaultDirPath = |
+ [documentsDirPath stringByAppendingPathComponent:kDefaultLogDirName]; |
+ return [self initWithDirPath:defaultDirPath |
+ maxFileSize:kDefaultMaxFileSize]; |
} |
-- (instancetype)initWithFilePath:(NSString *)filePath |
- maxFileSize:(NSUInteger)maxFileSize { |
- NSParameterAssert(filePath.length); |
+- (instancetype)initWithDirPath:(NSString *)dirPath |
+ maxFileSize:(NSUInteger)maxFileSize { |
+ NSParameterAssert(dirPath.length); |
NSParameterAssert(maxFileSize); |
if (self = [super init]) { |
- _filePath = filePath; |
+ BOOL isDir = NO; |
+ NSFileManager *fileManager = [NSFileManager defaultManager]; |
+ if ([fileManager fileExistsAtPath:dirPath isDirectory:&isDir]) { |
+ if (!isDir) { |
+ // Bail if something already exists there. |
+ return nil; |
+ } |
+ } else { |
+ if (![fileManager createDirectoryAtPath:dirPath |
+ withIntermediateDirectories:NO |
+ attributes:nil |
+ error:nil]) { |
+ // Bail if we failed to create a directory. |
+ return nil; |
+ } |
+ } |
+ _dirPath = dirPath; |
_maxFileSize = maxFileSize; |
_severity = kRTCFileLoggerSeverityInfo; |
} |
@@ -101,19 +91,14 @@ class CircularFileStreamLogSink : public LogSink { |
if (_hasStarted) { |
return; |
} |
- rtc::scoped_ptr<rtc::CircularFileStream> stream; |
- stream.reset(new rtc::CircularFileStream(_maxFileSize)); |
- _logSink.reset(new rtc::CircularFileStreamLogSink(stream.release())); |
- int error = 0; |
- if (!_logSink->GetStream()->Open(_filePath.UTF8String, "wb", &error)) { |
- LOG(LS_ERROR) << "Failed to open log file at path: " |
- << _filePath.UTF8String |
- << " Error: " |
- << error; |
+ _logSink.reset(new rtc::CallSessionFileRotatingLogSink(_dirPath.UTF8String, |
+ _maxFileSize)); |
+ if (!_logSink->Init()) { |
+ LOG(LS_ERROR) << "Failed to open log files at path: " |
+ << _dirPath.UTF8String; |
_logSink.reset(); |
return; |
} |
- // TODO(tkchin): Log thead info on iOS, currently this doesn't do anything. |
rtc::LogMessage::LogThreads(true); |
rtc::LogMessage::LogTimestamps(true); |
rtc::LogMessage::AddLogToStream(_logSink.get(), [self rtcSeverity]); |
@@ -127,93 +112,35 @@ class CircularFileStreamLogSink : public LogSink { |
DCHECK(_logSink); |
rtc::LogMessage::RemoveLogToStream(_logSink.get()); |
_hasStarted = NO; |
- |
- // Read the ordered version of the log. |
- NSData *logData = [self reorderedLogData]; |
- NSError *error = nil; |
- // Write the ordered version back to disk. |
- if (![logData writeToFile:_filePath |
- options:NSDataWritingAtomic |
- error:&error]) { |
- LOG(LS_ERROR) << "Failed to rewrite log to disk at path: " |
- << _filePath.UTF8String; |
- if (error) { |
- LOG(LS_ERROR) << "Error: " << error.localizedDescription.UTF8String; |
- } |
- } else { |
- // If we succeeded in writing to disk we don't need to hold on to the |
- // stream anymore. |
- _logSink.reset(); |
- } |
+ _logSink.reset(); |
} |
- (NSData *)logData { |
if (_hasStarted) { |
return nil; |
} |
- if (!_logSink.get()) { |
- // If there isn't a previously used stream just return contents of file. |
- return [[self class] contentsOfFileAtPath:_filePath]; |
+ NSMutableData* logData = [NSMutableData data]; |
+ rtc::scoped_ptr<rtc::CallSessionFileRotatingStream> stream( |
+ new rtc::CallSessionFileRotatingStream(_dirPath.UTF8String)); |
+ if (!stream->Open()) { |
+ return logData; |
} |
- return [self reorderedLogData]; |
-} |
- |
-#pragma mark - Private |
- |
-+ (NSData *)contentsOfFileAtPath:(NSString *)path { |
- NSError *error = nil; |
- NSData *contents = [NSData dataWithContentsOfFile:path |
- options:0 |
- error:&error]; |
- if (error) { |
- LOG(LS_ERROR) << "Failed to read contents of file at path: " |
- << path.UTF8String |
- << " Error: " |
- << error.localizedDescription.UTF8String; |
- return nil; |
- } |
- return contents; |
-} |
- |
-- (NSData *)reorderedLogData { |
- if (_hasStarted || !_logSink.get()) { |
- return nil; |
- } |
- // We have a stream we used for writing in memory and we're not writing. The |
- // stream has a pointer to where the log boundary is so it can reorder the |
- // log correctly. We just need to reopen the file in read mode. |
- int error = 0; |
- rtc::CircularFileStream *stream = _logSink->GetStream(); |
- if (!stream->Open(_filePath.UTF8String, "r", &error)) { |
- LOG(LS_ERROR) << "Failed to open log file at path: " |
- << _filePath.UTF8String |
- << " Error: " |
- << error; |
- return nil; |
- } |
- size_t logSize = 0; |
- size_t bytesRead = 0; |
- error = 0; |
- if (!stream->GetSize(&logSize)) { |
- LOG(LS_ERROR) << "Failed to get log file size."; |
- return nil; |
+ size_t bufferSize = 0; |
+ if (!stream->GetSize(&bufferSize) || bufferSize == 0) { |
+ return logData; |
} |
+ size_t read = 0; |
// Allocate memory using malloc so we can pass it direcly to NSData without |
// copying. |
- rtc::scoped_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(logSize))); |
- if (stream->ReadAll(buffer.get(), logSize, &bytesRead, &error) |
- != rtc::SR_SUCCESS) { |
- LOG(LS_ERROR) << "Failed to read log file at path: " |
- << _filePath.UTF8String |
- << " Error: " |
- << error; |
- } |
- DCHECK_LE(bytesRead, logSize); |
- // NSData takes ownership of the bytes and frees it on dealloc. |
- return [NSData dataWithBytesNoCopy:buffer.release() |
- length:bytesRead]; |
+ rtc::scoped_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(bufferSize))); |
+ stream->ReadAll(buffer.get(), bufferSize, &read, nullptr); |
+ logData = [[NSMutableData alloc] initWithBytesNoCopy:buffer.release() |
+ length:read]; |
+ return logData; |
} |
+#pragma mark - Private |
+ |
- (rtc::LoggingSeverity)rtcSeverity { |
switch (_severity) { |
case kRTCFileLoggerSeverityVerbose: |