• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 *  Copyright 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 *  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.
9 */
10
11#import "RTCFileLogger.h"
12
13#include <memory>
14
15#include "rtc_base/checks.h"
16#include "rtc_base/file_rotating_stream.h"
17#include "rtc_base/log_sinks.h"
18#include "rtc_base/logging.h"
19
20NSString *const kDefaultLogDirName = @"webrtc_logs";
21NSUInteger const kDefaultMaxFileSize = 10 * 1024 * 1024; // 10MB.
22const char *kRTCFileLoggerRotatingLogPrefix = "rotating_log";
23
24@implementation RTC_OBJC_TYPE (RTCFileLogger) {
25  BOOL _hasStarted;
26  NSString *_dirPath;
27  NSUInteger _maxFileSize;
28  std::unique_ptr<rtc::FileRotatingLogSink> _logSink;
29}
30
31@synthesize severity = _severity;
32@synthesize rotationType = _rotationType;
33@synthesize shouldDisableBuffering = _shouldDisableBuffering;
34
35- (instancetype)init {
36  NSArray *paths = NSSearchPathForDirectoriesInDomains(
37      NSDocumentDirectory, NSUserDomainMask, YES);
38  NSString *documentsDirPath = [paths firstObject];
39  NSString *defaultDirPath =
40      [documentsDirPath stringByAppendingPathComponent:kDefaultLogDirName];
41  return [self initWithDirPath:defaultDirPath
42                   maxFileSize:kDefaultMaxFileSize];
43}
44
45- (instancetype)initWithDirPath:(NSString *)dirPath
46                    maxFileSize:(NSUInteger)maxFileSize {
47  return [self initWithDirPath:dirPath
48                   maxFileSize:maxFileSize
49                  rotationType:RTCFileLoggerTypeCall];
50}
51
52- (instancetype)initWithDirPath:(NSString *)dirPath
53                    maxFileSize:(NSUInteger)maxFileSize
54                   rotationType:(RTCFileLoggerRotationType)rotationType {
55  NSParameterAssert(dirPath.length);
56  NSParameterAssert(maxFileSize);
57  if (self = [super init]) {
58    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;
75    _maxFileSize = maxFileSize;
76    _severity = RTCFileLoggerSeverityInfo;
77  }
78  return self;
79}
80
81- (void)dealloc {
82  [self stop];
83}
84
85- (void)start {
86  if (_hasStarted) {
87    return;
88  }
89  switch (_rotationType) {
90    case RTCFileLoggerTypeApp:
91      _logSink.reset(
92          new rtc::FileRotatingLogSink(_dirPath.UTF8String,
93                                       kRTCFileLoggerRotatingLogPrefix,
94                                       _maxFileSize,
95                                       _maxFileSize / 10));
96      break;
97    case RTCFileLoggerTypeCall:
98      _logSink.reset(
99          new rtc::CallSessionFileRotatingLogSink(_dirPath.UTF8String,
100                                                  _maxFileSize));
101      break;
102  }
103  if (!_logSink->Init()) {
104    RTC_LOG(LS_ERROR) << "Failed to open log files at path: " << _dirPath.UTF8String;
105    _logSink.reset();
106    return;
107  }
108  if (_shouldDisableBuffering) {
109    _logSink->DisableBuffering();
110  }
111  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  }
121  RTC_DCHECK(_logSink);
122  rtc::LogMessage::RemoveLogToStream(_logSink.get());
123  _hasStarted = NO;
124  _logSink.reset();
125}
126
127- (nullable NSData *)logData {
128  if (_hasStarted) {
129    return nil;
130  }
131  NSMutableData* logData = [NSMutableData data];
132  std::unique_ptr<rtc::FileRotatingStreamReader> stream;
133  switch(_rotationType) {
134    case RTCFileLoggerTypeApp:
135      stream = std::make_unique<rtc::FileRotatingStreamReader>(_dirPath.UTF8String,
136                                                               kRTCFileLoggerRotatingLogPrefix);
137      break;
138    case RTCFileLoggerTypeCall:
139      stream = std::make_unique<rtc::CallSessionFileRotatingStreamReader>(_dirPath.UTF8String);
140      break;
141  }
142  size_t bufferSize = stream->GetSize();
143  if (bufferSize == 0) {
144    return logData;
145  }
146  // Allocate memory using malloc so we can pass it direcly to NSData without
147  // copying.
148  std::unique_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(bufferSize)));
149  size_t read = stream->ReadAll(buffer.get(), bufferSize);
150  logData = [[NSMutableData alloc] initWithBytesNoCopy:buffer.release()
151                                                length:read];
152  return logData;
153}
154
155#pragma mark - Private
156
157- (rtc::LoggingSeverity)rtcSeverity {
158  switch (_severity) {
159    case RTCFileLoggerSeverityVerbose:
160      return rtc::LS_VERBOSE;
161    case RTCFileLoggerSeverityInfo:
162      return rtc::LS_INFO;
163    case RTCFileLoggerSeverityWarning:
164      return rtc::LS_WARNING;
165    case RTCFileLoggerSeverityError:
166      return rtc::LS_ERROR;
167  }
168}
169
170@end
171