| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SHILL_MEMORY_LOG_H_ |
| #define SHILL_MEMORY_LOG_H_ |
| |
| #include <deque> |
| #include <string> |
| #include <sstream> |
| #include <unistd.h> |
| |
| #include <base/basictypes.h> |
| #include <base/lazy_instance.h> |
| #include <base/logging.h> |
| #include <gtest/gtest_prod.h> |
| |
| namespace base { |
| |
| class FilePath; |
| |
| } // namespace base |
| |
| // MemoryLog is nothing but a memory buffer of the most recent messages, capped |
| // by a configurable limit of how many message bytes to remember at a time. |
| // When a new message comes in, we add it to the buffer, then drop the oldest |
| // messages until the size of the buffer is under the byte limit. The number of |
| // bytes in the buffer does not include std::string overhead, nor overhead from |
| // the buffer implementation. Only bytes in messages are counted. |
| // |
| // When something 'interesting' happens (e.g. connectivity event or crash), the |
| // logic reacting to that event can dump the contents of the MemoryLog to disk. |
| // This gives us a verbose log of the most recent events up until the event, |
| // which may be useful for further debugging. |
| |
| namespace shill { |
| |
| class MemoryLog { |
| public: |
| // Returns a singleton of this class. |
| static MemoryLog *GetInstance(); |
| |
| ~MemoryLog(); |
| // Appends this message to the log, dropping the oldest messages until the log |
| // is under the byte limit. |
| void Append(const std::string &msg); |
| // Removes all messages from the log. |
| void Clear(); |
| // Sets the maximum size for the log and drops messages until we get under it. |
| void SetMaximumSize(size_t size_in_bytes); |
| // See FlushToDiskImpl(). |
| void FlushToDisk(); |
| |
| size_t maximum_size_bytes() const { return maximum_size_bytes_; } |
| size_t current_size_bytes() const { return current_size_bytes_; } |
| |
| private: |
| friend class MemoryLogTest; |
| // Required for constructing LazyInstance<MemoryLog>. |
| friend struct base::DefaultLazyInstanceTraits<MemoryLog>; |
| FRIEND_TEST(ManagerTest, PopProfileShouldClearMemoryLog); |
| FRIEND_TEST(MemoryLogTest, MemoryLogFlushToDiskCannotCreateFile); |
| FRIEND_TEST(MemoryLogTest, MemoryLogFlushToDiskRotateWorks); |
| FRIEND_TEST(MemoryLogTest, MemoryLogFlushToDiskWorks); |
| FRIEND_TEST(MemoryLogTest, MemoryLogFlushToFileWorks); |
| FRIEND_TEST(MemoryLogTest, MemoryLogIsLogging); |
| FRIEND_TEST(MemoryLogTest, MemoryLogLimitingWorks); |
| FRIEND_TEST(MemoryLogTest, MemoryLogMessageInterceptorWorks); |
| |
| // Arbitrary default verbose log capacity is an even megabyte. |
| static const size_t kDefaultMaximumMemoryLogSizeInBytes = 1 << 20; |
| // Default log dump path used with FlushToDisk() when a user is logged in. |
| static const char kDefaultLoggedInDumpPath[]; |
| // Default log dump path used when no user is logged in. |
| static const char kDefaultLoggedOutDumpPath[]; |
| // If this file exists, then we say that a user is logged in |
| static const char kLoggedInTokenPath[]; |
| // The on disk log file may only be this big before we'll forcibly rotate it. |
| // This means we may have this number * 2 bytes on disk at any time. |
| static const size_t kDefaultMaxDiskLogSizeInBytes = |
| kDefaultMaximumMemoryLogSizeInBytes * 20; |
| |
| std::deque<std::string> log_; |
| |
| void ShrinkToTargetSize(size_t number_bytes); |
| size_t TestGetNumberMessages() { return log_.size(); } |
| bool TestContainsMessageWithText(const char *msg); |
| void TestSetMaxDiskLogSize(size_t number_bytes) { |
| maximum_disk_log_size_bytes_ = number_bytes; |
| } |
| // Appends the current contents of the memory buffer to a specified file on |
| // disk. Returns the number of bytes written to disk, or -1 on failure. -1 is |
| // returned on failure even if some bytes have already made it to disk. |
| // Attempts to create the parent directories of |file_path| if it does not |
| // already exist. If the resulting log file is too large (> kMaxDiskLogSize), |
| // tries to rotate logs. |
| ssize_t FlushToFile(const base::FilePath &file_path); |
| // Flushes the log to disk via FlushToFile, then clears the log, and tries to |
| // rotate our logs if |file_path| is larger than |
| // |maximum_disk_log_size_bytes_|. |
| // |
| // We rotate here rather than through logrotate because we fear situations |
| // where we experience a lot of connectivity problems in a short span of time |
| // before logrotate has a chance to run. |
| void FlushToDiskImpl(const base::FilePath &file_path); |
| |
| size_t maximum_size_bytes_; |
| size_t current_size_bytes_; |
| size_t maximum_disk_log_size_bytes_; |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(MemoryLog); |
| }; |
| |
| class MemoryLogMessage { |
| public: |
| MemoryLogMessage(const char *file, |
| int line, |
| logging::LogSeverity severity, |
| bool propagate_down); |
| ~MemoryLogMessage(); |
| |
| std::ostream &stream() { return stream_; } |
| |
| private: |
| const char *file_; |
| const int line_; |
| const logging::LogSeverity severity_; |
| bool propagate_down_; |
| std::ostringstream stream_; |
| size_t message_start_; |
| |
| void Init(); |
| |
| DISALLOW_IMPLICIT_CONSTRUCTORS(MemoryLogMessage); |
| }; |
| |
| } // namespace shill |
| |
| #endif // SHILL_MEMORY_LOG_H_ |