Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(10)

Side by Side Diff: webrtc/base/stream.cc

Issue 1230823009: Add rotating log file stream. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Cleanup Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 * 3 *
4 * Use of this source code is governed by a BSD-style license 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 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 6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may 7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree. 8 * be found in the AUTHORS file in the root of the source tree.
9 */ 9 */
10 10
11 #if defined(WEBRTC_POSIX) 11 #if defined(WEBRTC_POSIX)
12 #include <sys/file.h> 12 #include <sys/file.h>
13 #endif // WEBRTC_POSIX 13 #endif // WEBRTC_POSIX
14 #include <sys/types.h> 14 #include <sys/types.h>
15 #include <sys/stat.h> 15 #include <sys/stat.h>
16 #include <errno.h> 16 #include <errno.h>
17 17
18 #include <algorithm> 18 #include <algorithm>
19 #include <string> 19 #include <string>
20 20
21 #include "webrtc/base/basictypes.h" 21 #include "webrtc/base/basictypes.h"
22 #include "webrtc/base/common.h" 22 #include "webrtc/base/common.h"
23 #include "webrtc/base/logging.h" 23 #include "webrtc/base/logging.h"
24 #include "webrtc/base/messagequeue.h" 24 #include "webrtc/base/messagequeue.h"
25 #include "webrtc/base/pathutils.h"
25 #include "webrtc/base/stream.h" 26 #include "webrtc/base/stream.h"
26 #include "webrtc/base/stringencode.h" 27 #include "webrtc/base/stringencode.h"
27 #include "webrtc/base/stringutils.h" 28 #include "webrtc/base/stringutils.h"
28 #include "webrtc/base/thread.h" 29 #include "webrtc/base/thread.h"
29 #include "webrtc/base/timeutils.h" 30 #include "webrtc/base/timeutils.h"
30 31
31 #if defined(WEBRTC_WIN) 32 #if defined(WEBRTC_WIN)
32 #include "webrtc/base/win32.h" 33 #include "webrtc/base/win32.h"
33 #define fileno _fileno 34 #define fileno _fileno
34 #endif 35 #endif
(...skipping 475 matching lines...) Expand 10 before | Expand all | Expand 10 after
510 511
511 return flock(fileno(file_), LOCK_UN) == 0; 512 return flock(fileno(file_), LOCK_UN) == 0;
512 } 513 }
513 514
514 #endif 515 #endif
515 516
516 void FileStream::DoClose() { 517 void FileStream::DoClose() {
517 fclose(file_); 518 fclose(file_);
518 } 519 }
519 520
521 ///////////////////////////////////////////////////////////////////////////////
522 // FileRotatingStream
523 ///////////////////////////////////////////////////////////////////////////////
524
525 FileRotatingStream::FileRotatingStream(const std::string& dir_path,
526 const std::string& file_prefix)
527 : FileRotatingStream(dir_path, file_prefix, 0, 0, kRead) {
528 }
529
530 FileRotatingStream::FileRotatingStream(const std::string& dir_path,
531 const std::string& file_prefix,
532 size_t max_file_size,
533 size_t num_files)
534 : FileRotatingStream(dir_path,
535 file_prefix,
536 max_file_size,
537 num_files,
538 kWrite) {
539 DCHECK_GT(max_file_size, 0u);
540 DCHECK_GT(num_files, 1u);
541 }
542
543 FileRotatingStream::FileRotatingStream(const std::string& dir_path,
544 const std::string& file_prefix,
545 size_t max_file_size,
546 size_t num_files,
547 Mode mode)
548 : dir_path_(dir_path),
549 file_prefix_(file_prefix),
550 mode_(mode),
551 file_stream_(nullptr),
552 max_file_size_(max_file_size),
553 current_file_index_(0),
554 last_file_index_(0),
555 current_bytes_written_(0),
556 disable_buffering_(false) {
557 DCHECK(Filesystem::IsFolder(dir_path));
558 switch (mode) {
559 case kWrite: {
560 file_names_.clear();
561 for (size_t i = 0; i < num_files; ++i) {
562 file_names_.push_back(GetFilePath(i, num_files));
563 }
564 last_file_index_ = num_files - 1;
565 break;
566 }
567 case kRead: {
568 // We want the files in reverse sorted order. The last log file contains
569 // the earliest entries.
570 file_names_ = GetFilesWithPrefix();
571 std::sort(file_names_.begin(), file_names_.end());
572 std::reverse(file_names_.begin(), file_names_.end());
573 break;
574 }
575 }
576 }
577
578 FileRotatingStream::~FileRotatingStream() {
579 }
580
581 StreamState FileRotatingStream::GetState() const {
582 if (mode_ == kRead && current_file_index_ < file_names_.size()) {
583 return SS_OPEN;
584 }
585 if (!file_stream_) {
586 return SS_CLOSED;
587 }
588 return file_stream_->GetState();
589 }
590
591 StreamResult FileRotatingStream::Read(void* buffer,
592 size_t buffer_len,
593 size_t* read,
594 int* error) {
595 DCHECK(buffer);
596 if (current_file_index_ >= file_names_.size()) {
597 return SR_EOS;
598 }
599 // We will have no file stream initially, and when we are finished with the
600 // previous file.
601 if (!file_stream_) {
602 if (!Open(false)) {
603 return SR_ERROR;
jiayl2 2015/07/20 23:27:06 Add logging for error cases?
tkchin_webrtc 2015/07/21 20:39:12 I didn't add logging because I thought there could
jiayl2 2015/07/21 23:36:19 std::err for write seems fine. For read, we can lo
604 }
605 }
606 StreamResult result = file_stream_->Read(buffer, buffer_len, read, error);
607 if (result == SR_EOS || result == SR_ERROR) {
608 // Reached the end of the file or error-ed, read next file.
609 Close();
610 ++current_file_index_;
611 if (read) {
612 *read = 0;
613 }
614 return SR_SUCCESS;
jiayl2 2015/07/20 23:27:05 I think we should surface the error to the client.
tkchin_webrtc 2015/07/21 20:39:12 Done.
615 } else if (result == SR_SUCCESS) {
616 // Succeeded, continue reading from this file.
617 return SR_SUCCESS;
618 } else {
619 RTC_NOTREACHED();
620 }
621 return result;
622 }
623
624 StreamResult FileRotatingStream::Write(const void* data,
625 size_t data_len,
626 size_t* written,
627 int* error) {
628 if (!file_stream_) {
629 return SR_ERROR;
jiayl2 2015/07/20 23:27:06 ditto logging
tkchin_webrtc 2015/07/21 20:39:12 Acknowledged.
630 }
631 // Write as much as will fit in to the current file.
632 DCHECK_LT(current_bytes_written_, max_file_size_);
633 size_t remaining_bytes = max_file_size_ - current_bytes_written_;
634 size_t write_length = std::min(data_len, remaining_bytes);
635 size_t local_written = 0;
636 if (!written) {
637 written = &local_written;
638 }
639 StreamResult result = file_stream_->Write(data, write_length, written, error);
640 current_bytes_written_ += *written;
641
642 // If we're done with this file, rotate it out.
643 if (current_bytes_written_ >= max_file_size_) {
644 DCHECK_EQ(current_bytes_written_, max_file_size_);
645 RotateFiles();
646 }
647 return result;
648 }
649
650 bool FileRotatingStream::Flush() {
651 if (!file_stream_) {
652 return false;
653 }
654 return file_stream_->Flush();
655 }
656
657 void FileRotatingStream::Close() {
658 if (!file_stream_) {
jiayl2 2015/07/20 23:27:05 please move the impl into a private method like Cl
tkchin_webrtc 2015/07/21 20:39:12 Done.
659 return;
660 }
661 current_bytes_written_ = 0;
662 file_stream_.reset();
663 }
664
665 bool FileRotatingStream::Open() {
666 switch (mode_) {
667 case kRead:
668 // Defer opening to when we first read since we want to return read error
669 // if we fail to open next file.
670 return true;
671 case kWrite:
672 // Delete existing files when opening for write.
673 return Open(true);
674 }
675 return false;
676 }
677
678 bool FileRotatingStream::DisableBuffering() {
679 disable_buffering_ = true;
680 if (!file_stream_) {
681 return false;
682 }
683 return file_stream_->DisableBuffering();
684 }
685
686 std::string FileRotatingStream::GetFilePath(size_t index) const {
687 DCHECK_LT(index, file_names_.size());
688 return file_names_[index];
jiayl2 2015/07/20 23:27:05 The order is different in read mode and write mode
tkchin_webrtc 2015/07/21 20:39:12 Sure, putting them in the same order in both modes
689 }
690
691 bool FileRotatingStream::Open(bool delete_existing) {
jiayl2 2015/07/20 23:27:05 nit: overloading adds confusion. How about moving
tkchin_webrtc 2015/07/21 20:39:12 Done.
692 Close();
693 if (mode_ == kWrite && delete_existing) {
694 std::vector<std::string> matching_files = GetFilesWithPrefix();
695 for (auto matching_file : matching_files) {
696 Filesystem::DeleteFile(matching_file);
697 }
698 }
699
700 // Opens the appropriate file in the appropriate mode.
701 DCHECK_LT(current_file_index_, file_names_.size());
702 std::string file_path = file_names_[current_file_index_];
703 file_stream_.reset(new FileStream());
704 const char* mode = nullptr;
705 switch (mode_) {
706 case kWrite:
707 mode = "w+";
708 // We should always we writing to the zero-th file.
709 DCHECK_EQ(current_file_index_, 0u);
710 break;
711 case kRead:
712 mode = "r";
713 break;
714 }
715 int error = 0;
716 if (!file_stream_->Open(file_path, mode, &error)) {
717 file_stream_.reset();
jiayl2 2015/07/20 23:27:05 ditto logging.
tkchin_webrtc 2015/07/21 20:39:12 Acknowledged.
718 return false;
719 }
720 if (disable_buffering_) {
721 file_stream_->DisableBuffering();
722 }
723 return true;
724 }
725
726 void FileRotatingStream::RotateFiles() {
727 DCHECK_EQ(mode_, kWrite);
jiayl2 2015/07/20 23:27:05 nit: add logging for critial operations like rotat
tkchin_webrtc 2015/07/21 20:39:13 Acknowledged.
728 Close();
729 // Starting at |last_file_index_|, delete the log file and rename the
jiayl2 2015/07/20 23:27:05 the name of last_file_index_ seems confusing, sinc
tkchin_webrtc 2015/07/21 20:39:13 Changed to rotation_index
730 // previous file to be the current file.
jiayl2 2015/07/20 23:27:05 "the current file" may be confusing with |current_
tkchin_webrtc 2015/07/21 20:39:12 Done.
731 DCHECK_LE(last_file_index_, file_names_.size());
732 for (auto i = last_file_index_; i > 0; --i) {
733 // Delete the current file.
734 std::string current_file_name = file_names_[i];
735 if (Filesystem::IsFile(current_file_name)) {
736 Filesystem::DeleteFile(current_file_name);
737 }
738 // Rename the previous file to be current if it exists.
739 std::string prev_file_name = file_names_[i - 1];
740 if (Filesystem::IsFile(prev_file_name)) {
741 Filesystem::MoveFile(prev_file_name, current_file_name);
742 }
743 }
744 Open(false);
745 OnRotation();
746 }
747
748 std::vector<std::string> FileRotatingStream::GetFilesWithPrefix() const {
749 std::vector<std::string> files;
750 // Iterate over the files in the directory.
751 DirectoryIterator it;
752 if (!it.Iterate(dir_path_)) {
753 return files;
754 }
755 std::string current_name = it.Name();
756 while (current_name.size() > 0) {
757 if (!it.IsDirectory() &&
758 current_name.compare(0, file_prefix_.size(), file_prefix_) == 0) {
759 Pathname path(dir_path_);
760 path.SetFilename(current_name);
761 files.push_back(path.pathname());
762 }
763 if (!it.Next()) {
764 break;
765 }
766 current_name = it.Name();
767 }
jiayl2 2015/07/20 23:27:05 Why not do { current_name = it.Name(); if (..
tkchin_webrtc 2015/07/21 20:39:12 thx.
768 return files;
769 }
770
771 std::string FileRotatingStream::GetFilePath(size_t index,
772 size_t num_files) const {
773 DCHECK_LT(index, num_files);
774 std::ostringstream file_name;
775 // The format will be "_%<num_digits>zu". We want to zero pad the index so
776 // that it will sort nicely.
777 size_t max_digits = ((num_files - 1) / 10) + 1;
778 size_t num_digits = (index / 10) + 1;
779 DCHECK_LE(num_digits, max_digits);
780 size_t padding = max_digits - num_digits;
781
782 file_name << file_prefix_ << "_";
783 for (size_t i = 0; i < padding; ++i) {
784 file_name << "0";
785 }
786 file_name << index;
787
788 Pathname file_path(dir_path_);
789 file_path.SetFilename(file_name.str());
790 return file_path.pathname();
791 }
792
793 ///////////////////////////////////////////////////////////////////////////////
794 // MobileDeviceLogStream
795 ///////////////////////////////////////////////////////////////////////////////
796
797 MobileDeviceLogStream::MobileDeviceLogStream(const std::string& dir_path)
798 : FileRotatingStream(dir_path, kLogPrefix),
799 max_total_log_size_(0),
800 num_rotations_(0) {
801 }
802
803 MobileDeviceLogStream::MobileDeviceLogStream(const std::string& dir_path,
804 size_t max_total_log_size)
805 : FileRotatingStream(dir_path,
806 kLogPrefix,
807 max_total_log_size / 2,
808 GetNumRotatingLogFiles(max_total_log_size) + 1),
809 max_total_log_size_(max_total_log_size),
810 num_rotations_(0) {
811 DCHECK_GE(max_total_log_size, 4u);
jiayl2 2015/07/20 23:27:06 nit: document this in the .h file
tkchin_webrtc 2015/07/21 20:39:12 Done.
812 }
813
814 const char* MobileDeviceLogStream::kLogPrefix = "webrtc_log";
815 const size_t MobileDeviceLogStream::kRotatingLogFileDefaultSize = 1024 * 1024;
816
817 void MobileDeviceLogStream::OnRotation() {
818 ++num_rotations_;
819 if (num_rotations_ == 1) {
820 // On the first rotation adjust the max file size so subsequent files after
821 // the first are smaller.
822 SetMaxFileSize(GetRotatingLogSize(max_total_log_size_));
823 } else if (num_rotations_ == (GetNumFiles() - 1)) {
824 // On the next rotation the very first file is going to be deleted. Change
825 // the last index so this doesn't happen.
826 SetLastFileIndex(GetLastFileIndex() - 1);
827 }
828 }
829
830 size_t MobileDeviceLogStream::GetRotatingLogSize(size_t max_total_log_size) {
831 size_t num_rotating_log_files = GetNumRotatingLogFiles(max_total_log_size);
832 size_t rotating_log_size = num_rotating_log_files > 2
833 ? kRotatingLogFileDefaultSize
834 : max_total_log_size / 4;
835 return rotating_log_size;
836 }
837
838 size_t MobileDeviceLogStream::GetNumRotatingLogFiles(
839 size_t max_total_log_size) {
840 // At minimum have two rotating files. Otherwise split the available log size
841 // evenly across 1MB files.
842 return std::max((size_t)2,
843 (max_total_log_size / 2) / kRotatingLogFileDefaultSize);
844 }
845
520 CircularFileStream::CircularFileStream(size_t max_size) 846 CircularFileStream::CircularFileStream(size_t max_size)
521 : max_write_size_(max_size), 847 : max_write_size_(max_size),
522 position_(0), 848 position_(0),
523 marked_position_(max_size / 2), 849 marked_position_(max_size / 2),
524 last_write_position_(0), 850 last_write_position_(0),
525 read_segment_(READ_LATEST), 851 read_segment_(READ_LATEST),
526 read_segment_available_(0) { 852 read_segment_available_(0) {
527 } 853 }
528 854
529 bool CircularFileStream::Open(const std::string& filename, 855 bool CircularFileStream::Open(const std::string& filename,
(...skipping 694 matching lines...) Expand 10 before | Expand all | Expand 10 after
1224 1550
1225 if (data_len) { 1551 if (data_len) {
1226 *data_len = 0; 1552 *data_len = 0;
1227 } 1553 }
1228 return SR_SUCCESS; 1554 return SR_SUCCESS;
1229 } 1555 }
1230 1556
1231 /////////////////////////////////////////////////////////////////////////////// 1557 ///////////////////////////////////////////////////////////////////////////////
1232 1558
1233 } // namespace rtc 1559 } // namespace rtc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698