1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/extensions/api/log_private/log_private_api.h"
10 #include "base/command_line.h"
11 #include "base/files/file_util.h"
12 #include "base/json/json_writer.h"
13 #include "base/lazy_instance.h"
14 #include "base/logging.h"
15 #include "base/memory/linked_ptr.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/download/download_prefs.h"
19 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
20 #include "chrome/browser/extensions/api/log_private/filter_handler.h"
21 #include "chrome/browser/extensions/api/log_private/log_parser.h"
22 #include "chrome/browser/extensions/api/log_private/syslog_parser.h"
23 #include "chrome/browser/feedback/system_logs/scrubbed_system_logs_fetcher.h"
24 #include "chrome/browser/io_thread.h"
25 #include "chrome/browser/net/chrome_net_log.h"
26 #include "chrome/browser/profiles/profile.h"
27 #include "chrome/browser/profiles/profile_manager.h"
28 #include "chrome/common/extensions/api/log_private.h"
29 #include "chrome/common/logging_chrome.h"
30 #include "content/public/browser/render_frame_host.h"
31 #include "content/public/browser/render_process_host.h"
32 #include "extensions/browser/event_router.h"
33 #include "extensions/browser/extension_function.h"
34 #include "extensions/browser/extension_registry.h"
35 #include "extensions/browser/granted_file_entry.h"
37 #if defined(OS_CHROMEOS)
38 #include "chrome/browser/chromeos/system_logs/debug_log_writer.h"
41 using content::BrowserThread
;
44 const char kOnCapturedEvents
[] = "logPrivate.onCapturedEvents";
47 namespace extensions
{
50 const char kAppLogsSubdir
[] = "apps";
51 const char kLogDumpsSubdir
[] = "log_dumps";
52 const char kLogFileNameBase
[] = "net-internals";
53 const int kNetLogEventDelayMilliseconds
= 100;
55 // Gets sequenced task runner for file specific calls within this API.
56 scoped_refptr
<base::SequencedTaskRunner
> GetSequencedTaskRunner() {
57 base::SequencedWorkerPool
* pool
= BrowserThread::GetBlockingPool();
58 return pool
->GetSequencedTaskRunnerWithShutdownBehavior(
59 pool
->GetNamedSequenceToken(FileResource::kSequenceToken
),
60 base::SequencedWorkerPool::BLOCK_SHUTDOWN
);
63 // Checks if we are running on sequenced task runner thread.
64 bool IsRunningOnSequenceThread() {
65 base::SequencedWorkerPool
* pool
= content::BrowserThread::GetBlockingPool();
66 return pool
->IsRunningSequenceOnCurrentThread(
67 pool
->GetNamedSequenceToken(FileResource::kSequenceToken
));
70 scoped_ptr
<LogParser
> CreateLogParser(const std::string
& log_type
) {
71 if (log_type
== "syslog")
72 return scoped_ptr
<LogParser
>(new SyslogParser());
73 // TODO(shinfan): Add more parser here
75 NOTREACHED() << "Invalid log type: " << log_type
;
76 return scoped_ptr
<LogParser
>();
80 FilterHandler
* filter_handler
,
81 system_logs::SystemLogsResponse
* logs
,
82 std::vector
<linked_ptr
<api::log_private::LogEntry
> >* output
) {
83 for (system_logs::SystemLogsResponse::const_iterator
84 request_it
= logs
->begin(); request_it
!= logs
->end(); ++request_it
) {
85 if (!filter_handler
->IsValidSource(request_it
->first
)) {
88 scoped_ptr
<LogParser
> parser(CreateLogParser(request_it
->first
));
90 parser
->Parse(request_it
->second
, output
, filter_handler
);
95 // Returns directory location of app-specific logs that are initiated with
96 // logPrivate.startEventRecorder() calls - /home/chronos/user/log/apps
97 base::FilePath
GetAppLogDirectory() {
98 return logging::GetSessionLogDir(*base::CommandLine::ForCurrentProcess())
99 .Append(kAppLogsSubdir
);
102 // Returns directory location where logs dumps initiated with chrome.dumpLogs
103 // will be stored - /home/chronos/<user_profile_dir>/Downloads/log_dumps
104 base::FilePath
GetLogDumpDirectory(content::BrowserContext
* context
) {
105 const DownloadPrefs
* const prefs
= DownloadPrefs::FromBrowserContext(context
);
106 return prefs
->DownloadPath().Append(kLogDumpsSubdir
);
109 // Removes direcotry content of |logs_dumps| and |app_logs_dir| (only for the
111 void CleanUpLeftoverLogs(bool is_primary_profile
,
112 const base::FilePath
& app_logs_dir
,
113 const base::FilePath
& logs_dumps
) {
114 LOG(WARNING
) << "Deleting " << app_logs_dir
.value();
115 LOG(WARNING
) << "Deleting " << logs_dumps
.value();
117 DCHECK(IsRunningOnSequenceThread());
118 base::DeleteFile(logs_dumps
, true);
120 // App-specific logs are stored in /home/chronos/user/log/apps directory that
121 // is shared between all profiles in multi-profile case. We should not
122 // nuke it for non-primary profiles.
123 if (!is_primary_profile
)
126 base::DeleteFile(app_logs_dir
, true);
131 const char FileResource::kSequenceToken
[] = "log_api_files";
133 FileResource::FileResource(const std::string
& owner_extension_id
,
134 const base::FilePath
& path
)
135 : ApiResource(owner_extension_id
), path_(path
) {
138 FileResource::~FileResource() {
139 base::DeleteFile(path_
, true);
142 bool FileResource::IsPersistent() const {
147 LogPrivateAPI
* LogPrivateAPI::Get(content::BrowserContext
* context
) {
148 LogPrivateAPI
* api
= GetFactoryInstance()->Get(context
);
153 LogPrivateAPI::LogPrivateAPI(content::BrowserContext
* context
)
154 : browser_context_(context
),
155 logging_net_internals_(false),
156 event_sink_(api::log_private::EVENT_SINK_CAPTURE
),
157 extension_registry_observer_(this),
158 log_file_resources_(context
),
159 initialized_(false) {
162 LogPrivateAPI::~LogPrivateAPI() {
165 void LogPrivateAPI::StartNetInternalsWatch(
166 const std::string
& extension_id
,
167 api::log_private::EventSink event_sink
,
168 const base::Closure
& closure
) {
169 net_internal_watches_
.insert(extension_id
);
171 // Nuke any leftover app-specific or dumped log files from previous sessions.
172 BrowserThread::PostTaskAndReply(
175 base::Bind(&LogPrivateAPI::MaybeStartNetInternalLogging
,
176 base::Unretained(this),
178 g_browser_process
->io_thread(),
183 void LogPrivateAPI::StopNetInternalsWatch(const std::string
& extension_id
,
184 const base::Closure
& closure
) {
185 net_internal_watches_
.erase(extension_id
);
186 MaybeStopNetInternalLogging(closure
);
189 void LogPrivateAPI::StopAllWatches(const std::string
& extension_id
,
190 const base::Closure
& closure
) {
191 StopNetInternalsWatch(extension_id
, closure
);
194 void LogPrivateAPI::RegisterTempFile(const std::string
& owner_extension_id
,
195 const base::FilePath
& file_path
) {
196 if (!IsRunningOnSequenceThread()) {
197 GetSequencedTaskRunner()->PostTask(
199 base::Bind(&LogPrivateAPI::RegisterTempFile
,
200 base::Unretained(this),
206 log_file_resources_
.Add(new FileResource(owner_extension_id
, file_path
));
209 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<LogPrivateAPI
> >
210 g_factory
= LAZY_INSTANCE_INITIALIZER
;
213 BrowserContextKeyedAPIFactory
<LogPrivateAPI
>*
214 LogPrivateAPI::GetFactoryInstance() {
215 return g_factory
.Pointer();
218 void LogPrivateAPI::OnAddEntry(const net::NetLog::Entry
& entry
) {
219 // We could receive events on whatever thread they happen to be generated,
220 // since we are only interested in network events, we should ignore any
221 // other thread than BrowserThread::IO.
222 if (!BrowserThread::CurrentlyOn(BrowserThread::IO
)) {
226 if (!pending_entries_
.get()) {
227 pending_entries_
.reset(new base::ListValue());
228 BrowserThread::PostDelayedTask(
229 BrowserThread::IO
, FROM_HERE
,
230 base::Bind(&LogPrivateAPI::PostPendingEntries
, base::Unretained(this)),
231 base::TimeDelta::FromMilliseconds(kNetLogEventDelayMilliseconds
));
233 pending_entries_
->Append(entry
.ToValue());
236 void LogPrivateAPI::PostPendingEntries() {
237 BrowserThread::PostTask(
238 BrowserThread::UI
, FROM_HERE
,
239 base::Bind(&LogPrivateAPI:: AddEntriesOnUI
,
240 base::Unretained(this),
241 base::Passed(&pending_entries_
)));
244 void LogPrivateAPI::AddEntriesOnUI(scoped_ptr
<base::ListValue
> value
) {
245 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
247 for (std::set
<std::string
>::iterator ix
= net_internal_watches_
.begin();
248 ix
!= net_internal_watches_
.end(); ++ix
) {
249 // Create the event's arguments value.
250 scoped_ptr
<base::ListValue
> event_args(new base::ListValue());
251 event_args
->Append(value
->DeepCopy());
252 scoped_ptr
<Event
> event(
253 new Event(::extensions::events::LOG_PRIVATE_ON_CAPTURED_EVENTS
,
254 ::events::kOnCapturedEvents
, event_args
.Pass()));
255 EventRouter::Get(browser_context_
)
256 ->DispatchEventToExtension(*ix
, event
.Pass());
260 void LogPrivateAPI::CreateTempNetLogFile(const std::string
& owner_extension_id
,
261 base::ScopedFILE
* file
) {
262 DCHECK(IsRunningOnSequenceThread());
264 // Create app-specific subdirectory in session logs folder.
265 base::FilePath app_log_dir
= GetAppLogDirectory().Append(owner_extension_id
);
266 if (!base::DirectoryExists(app_log_dir
)) {
267 if (!base::CreateDirectory(app_log_dir
)) {
268 LOG(ERROR
) << "Could not create dir " << app_log_dir
.value();
273 base::FilePath file_path
= app_log_dir
.Append(kLogFileNameBase
);
274 file_path
= logging::GenerateTimestampedName(file_path
, base::Time::Now());
275 FILE* file_ptr
= fopen(file_path
.value().c_str(), "w");
276 if (file_ptr
== nullptr) {
277 LOG(ERROR
) << "Could not open " << file_path
.value();
281 RegisterTempFile(owner_extension_id
, file_path
);
282 return file
->reset(file_ptr
);
285 void LogPrivateAPI::StartObservingNetEvents(
287 base::ScopedFILE
* file
) {
288 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
292 write_to_file_observer_
.reset(new net::WriteToFileNetLogObserver());
293 write_to_file_observer_
->set_capture_mode(
294 net::NetLogCaptureMode::IncludeCookiesAndCredentials());
295 write_to_file_observer_
->StartObserving(io_thread
->net_log(), file
->Pass(),
299 void LogPrivateAPI::MaybeStartNetInternalLogging(
300 const std::string
& caller_extension_id
,
302 api::log_private::EventSink event_sink
) {
303 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
304 if (!logging_net_internals_
) {
305 logging_net_internals_
= true;
306 event_sink_
= event_sink
;
307 switch (event_sink_
) {
308 case api::log_private::EVENT_SINK_CAPTURE
: {
309 io_thread
->net_log()->DeprecatedAddObserver(
310 this, net::NetLogCaptureMode::IncludeCookiesAndCredentials());
313 case api::log_private::EVENT_SINK_FILE
: {
314 base::ScopedFILE
* file
= new base::ScopedFILE();
315 // Initialize a FILE on the blocking pool and start observing event
317 GetSequencedTaskRunner()->PostTaskAndReply(
319 base::Bind(&LogPrivateAPI::CreateTempNetLogFile
,
320 base::Unretained(this),
323 base::Bind(&LogPrivateAPI::StartObservingNetEvents
,
324 base::Unretained(this),
329 case api::log_private::EVENT_SINK_NONE
: {
337 void LogPrivateAPI::MaybeStopNetInternalLogging(const base::Closure
& closure
) {
338 if (net_internal_watches_
.empty()) {
339 if (closure
.is_null()) {
340 BrowserThread::PostTask(
343 base::Bind(&LogPrivateAPI::StopNetInternalLogging
,
344 base::Unretained(this)));
346 BrowserThread::PostTaskAndReply(
349 base::Bind(&LogPrivateAPI::StopNetInternalLogging
,
350 base::Unretained(this)),
356 void LogPrivateAPI::StopNetInternalLogging() {
357 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
358 if (net_log() && logging_net_internals_
) {
359 logging_net_internals_
= false;
360 switch (event_sink_
) {
361 case api::log_private::EVENT_SINK_CAPTURE
:
362 net_log()->DeprecatedRemoveObserver(this);
364 case api::log_private::EVENT_SINK_FILE
:
365 write_to_file_observer_
->StopObserving(nullptr);
366 write_to_file_observer_
.reset();
368 case api::log_private::EVENT_SINK_NONE
:
375 void LogPrivateAPI::Initialize() {
379 // Clean up temp files and folders from the previous sessions.
381 extension_registry_observer_
.Add(ExtensionRegistry::Get(browser_context_
));
382 GetSequencedTaskRunner()->PostTask(
384 base::Bind(&CleanUpLeftoverLogs
,
385 Profile::FromBrowserContext(browser_context_
) ==
386 ProfileManager::GetPrimaryUserProfile(),
387 GetAppLogDirectory(),
388 GetLogDumpDirectory(browser_context_
)));
391 void LogPrivateAPI::OnExtensionUnloaded(
392 content::BrowserContext
* browser_context
,
393 const Extension
* extension
,
394 UnloadedExtensionInfo::Reason reason
) {
395 StopNetInternalsWatch(extension
->id(), base::Closure());
398 LogPrivateGetHistoricalFunction::LogPrivateGetHistoricalFunction() {
401 LogPrivateGetHistoricalFunction::~LogPrivateGetHistoricalFunction() {
404 bool LogPrivateGetHistoricalFunction::RunAsync() {
406 scoped_ptr
<api::log_private::GetHistorical::Params
> params(
407 api::log_private::GetHistorical::Params::Create(*args_
));
408 EXTENSION_FUNCTION_VALIDATE(params
.get());
409 filter_handler_
.reset(new FilterHandler(params
->filter
));
411 system_logs::SystemLogsFetcherBase
* fetcher
;
412 if ((params
->filter
).scrub
) {
413 fetcher
= new system_logs::ScrubbedSystemLogsFetcher();
415 fetcher
= new system_logs::AboutSystemLogsFetcher();
418 base::Bind(&LogPrivateGetHistoricalFunction::OnSystemLogsLoaded
, this));
423 void LogPrivateGetHistoricalFunction::OnSystemLogsLoaded(
424 scoped_ptr
<system_logs::SystemLogsResponse
> sys_info
) {
425 std::vector
<linked_ptr
<api::log_private::LogEntry
> > data
;
427 CollectLogInfo(filter_handler_
.get(), sys_info
.get(), &data
);
430 api::log_private::Result result
;
432 api::log_private::Filter::Populate(
433 *((filter_handler_
->GetFilter())->ToValue()), &result
.filter
);
434 SetResult(result
.ToValue().release());
438 LogPrivateStartEventRecorderFunction::LogPrivateStartEventRecorderFunction() {
441 LogPrivateStartEventRecorderFunction::~LogPrivateStartEventRecorderFunction() {
444 bool LogPrivateStartEventRecorderFunction::RunAsync() {
445 scoped_ptr
<api::log_private::StartEventRecorder::Params
> params(
446 api::log_private::StartEventRecorder::Params::Create(*args_
));
447 EXTENSION_FUNCTION_VALIDATE(params
.get());
448 switch (params
->event_type
) {
449 case api::log_private::EVENT_TYPE_NETWORK
:
450 LogPrivateAPI::Get(Profile::FromBrowserContext(browser_context()))
451 ->StartNetInternalsWatch(
455 &LogPrivateStartEventRecorderFunction::OnEventRecorderStarted
,
458 case api::log_private::EVENT_TYPE_NONE
:
466 void LogPrivateStartEventRecorderFunction::OnEventRecorderStarted() {
470 LogPrivateStopEventRecorderFunction::LogPrivateStopEventRecorderFunction() {
473 LogPrivateStopEventRecorderFunction::~LogPrivateStopEventRecorderFunction() {
476 bool LogPrivateStopEventRecorderFunction::RunAsync() {
477 scoped_ptr
<api::log_private::StopEventRecorder::Params
> params(
478 api::log_private::StopEventRecorder::Params::Create(*args_
));
479 EXTENSION_FUNCTION_VALIDATE(params
.get());
480 switch (params
->event_type
) {
481 case api::log_private::EVENT_TYPE_NETWORK
:
482 LogPrivateAPI::Get(Profile::FromBrowserContext(browser_context()))
483 ->StopNetInternalsWatch(
486 &LogPrivateStopEventRecorderFunction::OnEventRecorderStopped
,
489 case api::log_private::EVENT_TYPE_NONE
:
496 void LogPrivateStopEventRecorderFunction::OnEventRecorderStopped() {
500 LogPrivateDumpLogsFunction::LogPrivateDumpLogsFunction() {
503 LogPrivateDumpLogsFunction::~LogPrivateDumpLogsFunction() {
506 bool LogPrivateDumpLogsFunction::RunAsync() {
507 LogPrivateAPI::Get(Profile::FromBrowserContext(browser_context()))
510 base::Bind(&LogPrivateDumpLogsFunction::OnStopAllWatches
, this));
514 void LogPrivateDumpLogsFunction::OnStopAllWatches() {
515 chromeos::DebugLogWriter::StoreCombinedLogs(
516 GetLogDumpDirectory(browser_context()).Append(extension_id()),
517 FileResource::kSequenceToken
,
518 base::Bind(&LogPrivateDumpLogsFunction::OnStoreLogsCompleted
, this));
521 void LogPrivateDumpLogsFunction::OnStoreLogsCompleted(
522 const base::FilePath
& log_path
,
525 LogPrivateAPI::Get(Profile::FromBrowserContext(browser_context()))
526 ->RegisterTempFile(extension_id(), log_path
);
529 scoped_ptr
<base::DictionaryValue
> response(new base::DictionaryValue());
530 extensions::GrantedFileEntry file_entry
=
531 extensions::app_file_handler_util::CreateFileEntry(
532 Profile::FromBrowserContext(browser_context()),
534 render_frame_host()->GetProcess()->GetID(),
538 base::DictionaryValue
* entry
= new base::DictionaryValue();
539 entry
->SetString("fileSystemId", file_entry
.filesystem_id
);
540 entry
->SetString("baseName", file_entry
.registered_name
);
541 entry
->SetString("id", file_entry
.id
);
542 entry
->SetBoolean("isDirectory", false);
543 base::ListValue
* entry_list
= new base::ListValue();
544 entry_list
->Append(entry
);
545 response
->Set("entries", entry_list
);
546 response
->SetBoolean("multiple", false);
547 SetResult(response
.release());
548 SendResponse(succeeded
);
551 } // namespace extensions