135f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat// Copyright 2015 The Android Open Source Project
235f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat//
335f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat// Licensed under the Apache License, Version 2.0 (the "License");
435f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat// you may not use this file except in compliance with the License.
535f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat// You may obtain a copy of the License at
635f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat//
735f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat//      http://www.apache.org/licenses/LICENSE-2.0
835f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat//
935f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat// Unless required by applicable law or agreed to in writing, software
1035f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat// distributed under the License is distributed on an "AS IS" BASIS,
1135f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1235f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat// See the License for the specific language governing permissions and
1335f6587840b71c8bd3e3655508b6f05cb2593ba9Daniel Erat// limitations under the License.
14c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
15f719c98766c61db6faecde5808c6af26d993ce44Daniel Erat#include "webservd/log_manager.h"
16c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
17c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko#include <arpa/inet.h>
18c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko#include <cinttypes>
19c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko#include <netinet/in.h>
20c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko#include <set>
21c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
22c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko#include <base/bind.h>
23c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko#include <base/files/file_enumerator.h>
24c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko#include <base/files/file_util.h>
25c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko#include <base/lazy_instance.h>
26c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko#include <base/strings/stringprintf.h>
2775d6da24dedcbc090d23de60c4f1637c3a54b392Alex Vakulenko#include <brillo/strings/string_utils.h>
28c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
29c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkonamespace webservd {
30c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
31c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkonamespace {
32c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
33c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko// Singleton instance of LogManager class.
34c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkobase::LazyInstance<LogManager> g_log_manager = LAZY_INSTANCE_INITIALIZER;
35c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
36c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko// The number of files to keep in the log directory. Since there is one log
37c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko// file per day of logging, this is essentially how many days' worth of logs
38c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko// to keep. This also controls the total maximum size of the log data, which
39c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko// is (kLogFilesToKeep * kMaxLogFileSize).
40c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkoconst size_t kLogFilesToKeep = 7;
41c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
42c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko// Maximum log file size.
43c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkoconst int64_t kMaxLogFileSize = 1024 * 1024;  // 1 MB
44c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
45c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko// Obtain an IP address as a human-readable string for logging.
46c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkostd::string GetIPAddress(const sockaddr* addr) {
47c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  static_assert(INET6_ADDRSTRLEN > INET_ADDRSTRLEN, "Unexpected IP addr len.");
48c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  char buf[INET6_ADDRSTRLEN] = "-";
49f38547e749b2feee717aa13066000e3cdfd6e183Alex Vakulenko  if (!addr)
50f38547e749b2feee717aa13066000e3cdfd6e183Alex Vakulenko    return buf;
51f38547e749b2feee717aa13066000e3cdfd6e183Alex Vakulenko
52c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  switch (addr->sa_family) {
53c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    case AF_INET: {
54c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      auto addr_in = reinterpret_cast<const sockaddr_in*>(addr);
55c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      if (!inet_ntop(AF_INET, &addr_in->sin_addr, buf, sizeof(buf)))
56c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        PLOG(ERROR) << "Unable to get IP address string (IPv4)";
57c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    }
58c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    break;
59c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
60c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    case AF_INET6: {
61c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      auto addr_in6 = reinterpret_cast<const sockaddr_in6*>(addr);
62c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
63c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      // Note that inet_ntop(3) doesn't handle IPv4-mapped IPv6
64c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      // addresses [1] the way you'd expect .. for example, it returns
65c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      // "::ffff:172.22.72.163" instead of the more traditional IPv4
66c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      // notation "172.22.72.163". Fortunately, this is pretty easy to
67c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      // fix ourselves.
68c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      //
69c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      // [1] : see RFC 4291, section 2.5.5.2 for what that means
70c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      //       http://tools.ietf.org/html/rfc4291#section-2.5.5
71c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      //
72c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      auto dwords = reinterpret_cast<const uint32_t*>(&addr_in6->sin6_addr);
73c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      if (dwords[0] == 0x00000000 && dwords[1] == 0x00000000 &&
74c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko          dwords[2] == htonl(0x0000ffff)) {
75c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        auto bytes = reinterpret_cast<const uint8_t*>(&addr_in6->sin6_addr);
76c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        return base::StringPrintf("%d.%d.%d.%d",
77c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                                  bytes[12], bytes[13], bytes[14], bytes[15]);
78c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      } else if (!inet_ntop(AF_INET6, &addr_in6->sin6_addr, buf, sizeof(buf))) {
79c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        PLOG(ERROR) << "Unable to get IP address string (IPv6)";
80c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      }
81c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    }
82c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    break;
83c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
84c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    default:
85c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      LOG(ERROR) << "Unsupported address family " << addr->sa_family;
86c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      break;
87c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  }
88c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  return buf;
89c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko}
90c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
91c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko}  // Anonymous namespace
92c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
93c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko// Logger class to write the log data to a log file.
9415c915985a72d77125746bcdb576bb4fb8d97834Alex Vakulenkoclass FileLogger final : public LogManager::LoggerInterface {
95c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko public:
96c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  FileLogger(const base::FilePath& log_directory, LogManager* log_manager)
97c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      : log_directory_(log_directory), log_manager_{log_manager} {}
98c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
99c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // Write the log entry to today's log file.
100c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  void Log(const base::Time& timestamp, const std::string& entry) override {
101c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    tm time_buf = {};
102c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    char file_name[32] = {};
103c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    // Create the file name in year-month-day format so that string sort would
104c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    // correspond to date sort.
105c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    time_t time = timestamp.ToTimeT();
106c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    strftime(file_name, sizeof(file_name), "%Y-%m-%d.log",
107c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko             localtime_r(&time, &time_buf));
108c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    base::FilePath file_path = log_directory_.Append(file_name);
109c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    bool success = false;
110c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    bool exists = base::PathExists(file_path);
111c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    // If the file already exists, check its size. If it is going to be larger
112c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    // then the maximum allowed log size, archive the current log file and
113c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    // create a new, empty one.
114c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    if (exists) {
115c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      int64_t file_size = 0;
116c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      bool got_size = base::GetFileSize(file_path, &file_size);
117c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      if (got_size && (file_size + entry.size() > kMaxLogFileSize))
118c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        exists = !ArchiveLogFile(file_name);
119c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    }
120c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
121c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    if (exists) {
122c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      success = base::AppendToFile(file_path, entry.data(), entry.size());
123c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    } else {
124c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      int size = static_cast<int>(entry.size());
125c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      success = (base::WriteFile(file_path, entry.data(), size) == size);
126c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      if (success) {
127c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        // We just created a new file, see if we need to purge old ones.
128c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        log_manager_->PerformLogMaintenance();
129c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      }
130c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    }
131c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    PLOG_IF(ERROR, !success) << "Failed to append a log entry to log file at "
132c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                             << file_path.value();
133c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  }
134c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
135c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko private:
136c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // Renames the log file to a next available suffix-appended archive when
137c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // the log file size starts to exceed the pre-defined maximum size.
138c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // The existing log file is renamed by changing the original |file_name| to
139c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // YYYY-MM-DD-<suffix>.log where suffix is characters 'a', 'b', ...
140c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // Since '-' comes before '.', "2015-02-25-a.log" will come before
141c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // "2015-02-25.log" in sort order and the previously-renamed files will be
142c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // considered "older" than the current one, which is what we need.
143c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // Returns true if the file has been successfully renamed.
144c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  bool ArchiveLogFile(const std::string& file_name) {
145c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    char suffix = 'a';
14675d6da24dedcbc090d23de60c4f1637c3a54b392Alex Vakulenko    auto pair = brillo::string_utils::SplitAtFirst(file_name, ".");
147c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    // If we try all the suffixes from 'a' to 'z' and still can't find a name,
148c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    // abandon this strategy and keep appending to the current file.
149c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    while (suffix <= 'z') {
150c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      base::FilePath archive_file_path = log_directory_.Append(
151c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko          base::StringPrintf("%s-%c.%s", pair.first.c_str(),
152c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                              suffix, pair.second.c_str()));
153c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      if (!base::PathExists(archive_file_path)) {
154c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        base::FilePath file_path = log_directory_.Append(file_name);
155c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        if (base::Move(file_path, archive_file_path)) {
156c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko          // Successfully renamed, start a new log file.
157c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko          return true;
158c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        } else {
159c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko          PLOG(ERROR) << "Failed to rename log file from "
160c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                      << file_path.value() << " to "
161c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                      << archive_file_path.value();
162c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        }
163c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        break;
164c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      }
165c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      suffix++;
166c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    }
167c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    return false;
168c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  }
169c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
170c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  base::FilePath log_directory_;
171c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  LogManager* log_manager_{nullptr};
172c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko};
173c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
174c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkovoid LogManager::Init(const base::FilePath& log_directory) {
175c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  LogManager* inst = GetInstance();
176c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  inst->log_directory_ = log_directory;
177c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  inst->SetLogger(
178c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko      std::unique_ptr<LoggerInterface>{new FileLogger{log_directory, inst}});
179c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  inst->PerformLogMaintenance();
180c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko}
181c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
182c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkovoid LogManager::OnRequestCompleted(const base::Time& timestamp,
183c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                                    const sockaddr* client_addr,
184c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                                    const std::string& method,
185c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                                    const std::string& url,
186c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                                    const std::string& version,
187c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                                    int status_code,
188830b1a87c3428b819eea1466557d3f058d5e2270Alex Vakulenko                                    int64_t response_size) {
189c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  std::string ip_address = GetIPAddress(client_addr);
190c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  tm time_buf = {};
191c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  char str_buf[32] = {};
192c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // Format the date/time as "25/Feb/2015:03:29:12 -0800".
193c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  time_t time = timestamp.ToTimeT();
194c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  strftime(str_buf, sizeof(str_buf), "%d/%b/%Y:%H:%M:%S %z",
195c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko           localtime_r(&time, &time_buf));
196c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
197c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // Log file entry for one HTTP request looking like this:
198c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // 127.0.0.1 - - [25/Feb/2015:03:29:12 -0800] "GET /test HTTP/1.1" 200 2326
199330a10fd406a37af8f9c530d9b51dc5e25fcdcebAlex Vakulenko  std::string size_string{"-"};
200330a10fd406a37af8f9c530d9b51dc5e25fcdcebAlex Vakulenko  if (response_size >= 0)
201330a10fd406a37af8f9c530d9b51dc5e25fcdcebAlex Vakulenko    size_string = std::to_string(response_size);
202c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  std::string log_entry = base::StringPrintf(
203330a10fd406a37af8f9c530d9b51dc5e25fcdcebAlex Vakulenko      "%s - - [%s] \"%s %s %s\" %d %s\n", ip_address.c_str(), str_buf,
204330a10fd406a37af8f9c530d9b51dc5e25fcdcebAlex Vakulenko      method.c_str(), url.c_str(), version.c_str(), status_code,
205330a10fd406a37af8f9c530d9b51dc5e25fcdcebAlex Vakulenko      size_string.c_str());
206c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  GetInstance()->logger_->Log(timestamp, log_entry);
207c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko}
208c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
209c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkovoid LogManager::SetLogger(std::unique_ptr<LoggerInterface> logger) {
210c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  GetInstance()->logger_ = std::move(logger);
211c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko}
212c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
213c16c6da6d632d6041997052edfbb97a674241517Alex VakulenkoLogManager* LogManager::GetInstance() {
214c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  return g_log_manager.Pointer();
215c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko}
216c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
217c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenkovoid LogManager::PerformLogMaintenance() {
218c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // Get the list of all the log files in the log directory and put them into
219c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // a set which will sort the files by name (and effectively by the date since
220c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // we chose the file naming scheme deliberately to guarantee proper sorting
221c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // order).
222c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  std::set<base::FilePath> log_files;
223c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  base::FileEnumerator enumerator{log_directory_,
224c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                                  false,
225c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                                  base::FileEnumerator::FILES,
226c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko                                  "*.log"};
227c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  base::FilePath file = enumerator.Next();
228c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  while (!file.empty()) {
229c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    log_files.insert(file);
230c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    file = enumerator.Next();
231c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  }
232c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
233c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  // Now, if we have more files than we want to keep, purge the old files.
234c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  while (log_files.size() > kLogFilesToKeep) {
235c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    auto front_it = log_files.begin();
236c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    PLOG_IF(WARNING, !base::DeleteFile(*front_it, false))
237c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko        << "Failed to delete an old log file: " << front_it->value();
238c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko    log_files.erase(front_it);
239c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko  }
240c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko}
241c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko
242c16c6da6d632d6041997052edfbb97a674241517Alex Vakulenko}  // namespace webservd
243