OLD | NEW |
---|---|
(Empty) | |
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/logging.h" | |
32 #include "webrtc/base/scoped_ptr.h" | |
33 #include "webrtc/base/stream.h" | |
34 | |
35 NSString *const kDefaultLogFileName = @"webrtc.log"; | |
36 NSUInteger const kDefaultMaxFileSize = 10 * 1024 * 1024; // 10MB. | |
37 | |
38 namespace rtc { | |
39 | |
40 class CircularFileStreamLogSink : public LogSink { | |
41 public: | |
42 // Creates a log sink that writes to the given stream. This log sink takes | |
43 // ownership of |stream|. | |
44 CircularFileStreamLogSink(CircularFileStream *stream) { | |
45 DCHECK(stream); | |
46 _stream.reset(stream); | |
47 } | |
48 | |
49 ~CircularFileStreamLogSink() override {} | |
50 | |
51 void OnLogMessage(const std::string &message) override { | |
52 if (_stream) { | |
53 _stream->WriteAll(message.data(), message.size(), nullptr, nullptr); | |
54 } | |
55 } | |
56 | |
57 CircularFileStream *GetStream() { return _stream.get(); } | |
58 | |
59 private: | |
60 scoped_ptr<CircularFileStream> _stream; | |
61 }; | |
62 | |
63 } // namespace rtc | |
64 | |
65 @implementation RTCFileLogger { | |
66 BOOL _hasStarted; | |
67 NSString *_filePath; | |
68 NSUInteger _maxFileSize; | |
69 rtc::scoped_ptr<rtc::CircularFileStreamLogSink> _logSink; | |
70 } | |
71 | |
72 @synthesize severity = _severity; | |
73 | |
74 - (instancetype)init { | |
75 NSArray *paths = NSSearchPathForDirectoriesInDomains( | |
76 NSDocumentDirectory, NSUserDomainMask, YES); | |
77 NSString *documentsDirPath = [paths firstObject]; | |
78 NSString *defaultFilePath = | |
79 [documentsDirPath stringByAppendingPathComponent:kDefaultLogFileName]; | |
80 return [self initWithFilePath:defaultFilePath | |
81 maxFileSize:kDefaultMaxFileSize]; | |
82 } | |
83 | |
84 - (instancetype)initWithFilePath:(NSString *)filePath | |
85 maxFileSize:(NSUInteger)maxFileSize { | |
86 NSParameterAssert(filePath.length); | |
87 NSParameterAssert(maxFileSize); | |
88 if (self = [super init]) { | |
89 _filePath = filePath; | |
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 rtc::scoped_ptr<rtc::CircularFileStream> stream; | |
105 stream.reset(new rtc::CircularFileStream(_maxFileSize)); | |
106 _logSink.reset(new rtc::CircularFileStreamLogSink(stream.release())); | |
107 int error = 0; | |
108 if (!_logSink->GetStream()->Open(_filePath.UTF8String, "wb", &error)) { | |
109 LOG(LS_ERROR) << "Failed to open log file at path: " | |
110 << _filePath.UTF8String | |
111 << " Error: " | |
112 << error; | |
113 _logSink.reset(); | |
114 return; | |
115 } | |
116 // TODO(tkchin): Log thead info on iOS, currently this doesn't do anything. | |
henrika_webrtc
2015/07/15 08:36:01
It actually does now :-) Please try it out.
| |
117 rtc::LogMessage::LogThreads(true); | |
118 rtc::LogMessage::LogTimestamps(true); | |
119 rtc::LogMessage::AddLogToStream(_logSink.get(), [self rtcSeverity]); | |
120 _hasStarted = YES; | |
121 } | |
122 | |
123 - (void)stop { | |
124 if (!_hasStarted) { | |
125 return; | |
126 } | |
127 DCHECK(_logSink); | |
128 rtc::LogMessage::RemoveLogToStream(_logSink.get()); | |
129 _hasStarted = NO; | |
130 | |
131 // Read the ordered version of the log. | |
132 NSData *logData = [self reorderedLogData]; | |
133 NSError *error = nil; | |
134 // Write the ordered version back to disk. | |
135 if (![logData writeToFile:_filePath | |
136 options:NSDataWritingAtomic | |
137 error:&error]) { | |
138 LOG(LS_ERROR) << "Failed to rewrite log to disk at path: " | |
139 << _filePath.UTF8String; | |
140 if (error) { | |
141 LOG(LS_ERROR) << "Error: " << error.localizedDescription.UTF8String; | |
142 } | |
143 } else { | |
144 // If we succeeded in writing to disk we don't need to hold on to the | |
145 // stream anymore. | |
146 _logSink.reset(); | |
147 } | |
148 } | |
149 | |
150 - (NSData *)logData { | |
151 if (_hasStarted) { | |
152 return nil; | |
153 } | |
154 if (!_logSink.get()) { | |
155 // If there isn't a previously used stream just return contents of file. | |
156 return [[self class] contentsOfFileAtPath:_filePath]; | |
157 } | |
158 return [self reorderedLogData]; | |
159 } | |
160 | |
161 #pragma mark - Private | |
162 | |
163 + (NSData *)contentsOfFileAtPath:(NSString *)path { | |
164 NSError *error = nil; | |
165 NSData *contents = [NSData dataWithContentsOfFile:path | |
166 options:0 | |
167 error:&error]; | |
168 if (error) { | |
169 LOG(LS_ERROR) << "Failed to read contents of file at path: " | |
170 << path.UTF8String | |
171 << " Error: " | |
172 << error.localizedDescription.UTF8String; | |
173 return nil; | |
174 } | |
175 return contents; | |
176 } | |
177 | |
178 - (NSData *)reorderedLogData { | |
179 if (_hasStarted || !_logSink.get()) { | |
180 return nil; | |
181 } | |
182 // We have a stream we used for writing in memory and we're not writing. The | |
183 // stream has a pointer to where the log boundary is so it can reorder the | |
184 // log correctly. We just need to reopen the file in read mode. | |
185 int error = 0; | |
186 rtc::CircularFileStream *stream = _logSink->GetStream(); | |
187 if (!stream->Open(_filePath.UTF8String, "r", &error)) { | |
188 LOG(LS_ERROR) << "Failed to open log file at path: " | |
189 << _filePath.UTF8String | |
190 << " Error: " | |
191 << error; | |
192 return nil; | |
193 } | |
194 size_t logSize = 0; | |
195 size_t bytesRead = 0; | |
196 error = 0; | |
197 if (!stream->GetSize(&logSize)) { | |
198 LOG(LS_ERROR) << "Failed to get log file size."; | |
199 return nil; | |
200 } | |
201 // Allocate memory using malloc so we can pass it direcly to NSData without | |
202 // copying. | |
203 rtc::scoped_ptr<uint8_t[]> buffer(static_cast<uint8_t*>(malloc(logSize))); | |
204 if (stream->ReadAll(buffer.get(), logSize, &bytesRead, &error) | |
205 != rtc::SR_SUCCESS) { | |
206 LOG(LS_ERROR) << "Failed to read log file at path: " | |
207 << _filePath.UTF8String | |
208 << " Error: " | |
209 << error; | |
210 } | |
211 DCHECK_LE(bytesRead, logSize); | |
212 // NSData takes ownership of the bytes and frees it on dealloc. | |
213 return [NSData dataWithBytesNoCopy:buffer.release() | |
214 length:bytesRead]; | |
215 } | |
216 | |
217 - (rtc::LoggingSeverity)rtcSeverity { | |
218 switch (_severity) { | |
219 case kRTCFileLoggerSeverityVerbose: | |
220 return rtc::LS_VERBOSE; | |
221 case kRTCFileLoggerSeverityInfo: | |
222 return rtc::LS_INFO; | |
223 case kRTCFileLoggerSeverityWarning: | |
224 return rtc::LS_WARNING; | |
225 case kRTCFileLoggerSeverityError: | |
226 return rtc::LS_ERROR; | |
227 } | |
228 } | |
229 | |
230 @end | |
OLD | NEW |