blob: 0d6645f61758ce76e5deaaf7882d3c0ec8a7ae22 [file] [log] [blame]
Christopher Wiley5781aa42012-07-30 14:42:23 -07001// Copyright (c) 2012 The Chromium OS 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.
4
Christopher Wileyb691efd2012-08-09 13:51:51 -07005#include "shill/memory_log.h"
6
Christopher Wiley5781aa42012-07-30 14:42:23 -07007#include <stdio.h>
8
9#include <ctime>
10#include <iomanip>
Christopher Wileyf11cebb2012-08-08 12:22:20 -070011#include <string>
Christopher Wiley5781aa42012-07-30 14:42:23 -070012
13#include <base/file_path.h>
14#include <base/file_util.h>
15
Christopher Wileyb691efd2012-08-09 13:51:51 -070016#include "shill/logging.h"
Christopher Wiley5781aa42012-07-30 14:42:23 -070017#include "shill/shill_time.h"
18
19namespace shill {
20
21namespace {
22
23// MemoryLog needs to be a 'leaky' singleton as it needs to survive to
24// handle logging till the very end of the shill process. Making MemoryLog
25// leaky is fine as it does not need to clean up or release any resource at
26// destruction.
27base::LazyInstance<MemoryLog>::Leaky g_memory_log = LAZY_INSTANCE_INITIALIZER;
28
29// Can't get to these in base/logging.c.
30const char *const kLogSeverityNames[logging::LOG_NUM_SEVERITIES] = {
31 "INFO", "WARNING", "ERROR", "ERROR_REPORT", "FATAL"
32};
33
Christopher Wileyf11cebb2012-08-08 12:22:20 -070034// Size is one bigger to hold the null at the end.
35const char kMemoryLogPrefix[] = "memlog: ";
36const size_t kMemoryLogPrefixLength = arraysize(kMemoryLogPrefix) - 1;
37
38logging::LogMessageHandlerFunction nested_message_handler = NULL;
39
40bool LogMessageInterceptor(int severity,
41 const char *file,
42 int line,
43 size_t message_start,
44 const std::string &str) {
45 if (message_start < str.size() &&
46 strncmp(str.c_str() + message_start,
47 kMemoryLogPrefix,
48 kMemoryLogPrefixLength)) {
49 // Stream this in to rewrite the prefix into memory log format.
50 MemoryLogMessage(file,
51 line,
52 severity,
53 false).stream() << (str.c_str() + message_start);
54 }
55
56 // Signal that logging should continue processing this message.
57 bool ret = false;
58 if (nested_message_handler) {
59 // If the nested handler wants to bypass logging, let it.
60 ret = nested_message_handler(severity, file, line, message_start, str);
61 }
62 return ret;
63}
64
Christopher Wiley5781aa42012-07-30 14:42:23 -070065} // namespace
66
Christopher Wiley5781aa42012-07-30 14:42:23 -070067
68// static
69MemoryLog *MemoryLog::GetInstance() {
70 return g_memory_log.Pointer();
71}
72
Christopher Wileyf11cebb2012-08-08 12:22:20 -070073// static
74void MemoryLog::InstallLogInterceptor() {
75 nested_message_handler = logging::GetLogMessageHandler();
76 logging::SetLogMessageHandler(LogMessageInterceptor);
77}
78
79// static
80void MemoryLog::UninstallLogInterceptor() {
81 if (logging::GetLogMessageHandler() == LogMessageInterceptor) {
82 logging::SetLogMessageHandler(nested_message_handler);
83 } else {
84 MLOG(ERROR) << "Tried to uninstall MemoryLog message handler, but found"
85 << " another handler installed over top of MemoryLog's"
86 << " handler. Leaving all handlers installed.";
87 }
88}
89
Christopher Wiley5781aa42012-07-30 14:42:23 -070090MemoryLog::MemoryLog()
91 : maximum_size_bytes_(kDefaultMaximumMemoryLogSizeInBytes),
92 current_size_bytes_(0) { }
93
94void MemoryLog::Append(const std::string &msg) {
95 current_size_bytes_ += msg.size();
96 log_.push_back(msg);
97 ShrinkToTargetSize(maximum_size_bytes_);
98}
99
100void MemoryLog::Clear() {
101 current_size_bytes_ = 0;
102 log_.clear();
103}
104
105ssize_t MemoryLog::FlushToDisk(const std::string &file_path) {
106 const FilePath file_name(file_path);
107 FILE *f = file_util::OpenFile(file_name, "w");
108 if (!f) {
109 return -1;
110 }
111
112 file_util::ScopedFILE file_closer(f);
113 ssize_t bytes_written = 0;
114 std::deque<std::string>::iterator it;
115 for (it = log_.begin(); it != log_.end(); it++) {
116 bytes_written += fwrite(it->c_str(), 1, it->size(), f);
117 if (ferror(f)) {
118 return -1;
119 }
120 }
121
122 return bytes_written;
123}
124
125void MemoryLog::SetMaximumSize(size_t size_in_bytes) {
126 ShrinkToTargetSize(size_in_bytes);
127 maximum_size_bytes_ = size_in_bytes;
128}
129
130void MemoryLog::ShrinkToTargetSize(size_t number_bytes) {
131 while (current_size_bytes_ > number_bytes) {
132 const std::string &front = log_.front();
133 current_size_bytes_ -= front.size();
134 log_.pop_front();
135 }
136}
137
138MemoryLogMessage::MemoryLogMessage(const char *file,
139 int line,
140 logging::LogSeverity severity,
141 bool propagate_down)
142 : file_(file),
143 line_(line),
144 severity_(severity),
145 propagate_down_(propagate_down),
146 message_start_(0) {
147 Init();
148}
149
150MemoryLogMessage::~MemoryLogMessage() {
151 if (propagate_down_) {
152 ::logging::LogMessage(file_, line_, severity_).stream()
153 << &(stream_.str()[message_start_]);
154 }
155 stream_ << std::endl;
156 MemoryLog::GetInstance()->Append(stream_.str());
157}
158
159// This owes heavily to the base/logging.cc:LogMessage implementation, but
160// without as much customization. Unfortunately, there isn't a good way to
161// get into that code without drastically changing how base/logging works. It
162// isn't exactly rocket science in any case.
163void MemoryLogMessage::Init() {
164 const char *filename = strrchr(file_, '/');
165 if (!filename) {
166 filename = file_;
167 }
168
169 // Just log a timestamp, number of ticks, severity, and a file name.
170 struct timeval tv = {0};
171 struct tm local_time = {0};
172 Time::GetInstance()->GetTimeOfDay(&tv, NULL);
173 localtime_r(&tv.tv_sec, &local_time);
174 stream_ << std::setfill('0')
175 << local_time.tm_year + 1900
176 << '-' << std::setw(2) << local_time.tm_mon + 1
177 << '-' << std::setw(2) << local_time.tm_mday
178 << 'T' << std::setw(2) << local_time.tm_hour
179 << ':' << std::setw(2) << local_time.tm_min
180 << ':' << std::setw(2) << local_time.tm_sec
181 << '.' << tv.tv_usec << ' ';
182
Christopher Wiley5781aa42012-07-30 14:42:23 -0700183 if (severity_ >= 0)
184 stream_ << kLogSeverityNames[severity_];
185 else
186 stream_ << "VERBOSE" << -severity_;
187 stream_ << ":" << filename << "(" << line_ << ") ";
Christopher Wileyf11cebb2012-08-08 12:22:20 -0700188
Christopher Wiley5781aa42012-07-30 14:42:23 -0700189 message_start_ = stream_.tellp();
Christopher Wileyf11cebb2012-08-08 12:22:20 -0700190
191 // Let the memlog prefix hit the real logging. This allows our interceptor
192 // to discard messages that have already gotten into the memlog.
193 stream_ << kMemoryLogPrefix;
Christopher Wiley5781aa42012-07-30 14:42:23 -0700194}
195
196} // namespace shill