blob: 2089e7f6e5d14a901ff17e0059b3124fa914919d [file] [log] [blame]
// 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_