chromium/ash/system/diagnostics/routine_log.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/system/diagnostics/routine_log.h"

#include <sstream>
#include <string>

#include "base/i18n/time_formatting.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/sequenced_task_runner.h"
#include "base/time/time.h"

namespace ash {
namespace diagnostics {
namespace {

const char kCancelledDescription[] = "Inflight Routine Cancelled";
const char kNewline[] = "\n";
const char kSeparator[] = " - ";
const char kStartedDescription[] = "Started";

std::string GetCurrentDateTimeAsString() {
  return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(base::Time::Now()));
}

std::string GetRoutineLogCategoryString(RoutineLog::RoutineCategory category) {
  switch (category) {
    case RoutineLog::RoutineCategory::kNetwork:
      return "network";
    case RoutineLog::RoutineCategory::kSystem:
      return "system";
  }
}

std::string getRoutineResultString(mojom::StandardRoutineResult result) {
  switch (result) {
    case mojom::StandardRoutineResult::kTestPassed:
      return "Passed";
    case mojom::StandardRoutineResult::kTestFailed:
      return "Failed";
    case mojom::StandardRoutineResult::kExecutionError:
      return "Execution error";
    case mojom::StandardRoutineResult::kUnableToRun:
      return "Unable to run";
  }
}

std::string getRoutineTypeString(mojom::RoutineType type) {
  std::stringstream s;
  s << type;
  const std::string routineName = s.str();

  // Remove leading "k" ex: "kCpuStress" -> "CpuStress".
  DCHECK_GE(routineName.size(), 1U);
  DCHECK_EQ(routineName[0], 'k');
  return routineName.substr(1, routineName.size() - 1);
}

// Get the category for the routine `type`.
RoutineLog::RoutineCategory GetRoutineCategory(mojom::RoutineType type) {
  switch (type) {
    case mojom::RoutineType::kBatteryCharge:
    case mojom::RoutineType::kBatteryDischarge:
    case mojom::RoutineType::kCpuCache:
    case mojom::RoutineType::kCpuStress:
    case mojom::RoutineType::kCpuFloatingPoint:
    case mojom::RoutineType::kCpuPrime:
    case mojom::RoutineType::kMemory:
      return RoutineLog::RoutineCategory::kSystem;
    case mojom::RoutineType::kLanConnectivity:
    case mojom::RoutineType::kSignalStrength:
    case mojom::RoutineType::kGatewayCanBePinged:
    case mojom::RoutineType::kHasSecureWiFiConnection:
    case mojom::RoutineType::kDnsResolverPresent:
    case mojom::RoutineType::kDnsLatency:
    case mojom::RoutineType::kDnsResolution:
    case mojom::RoutineType::kCaptivePortal:
    case mojom::RoutineType::kHttpFirewall:
    case mojom::RoutineType::kHttpsFirewall:
    case mojom::RoutineType::kHttpsLatency:
    case mojom::RoutineType::kArcHttp:
    case mojom::RoutineType::kArcPing:
    case mojom::RoutineType::kArcDnsResolution:
      return RoutineLog::RoutineCategory::kNetwork;
  };
}

}  // namespace

RoutineLog::RoutineLog(const base::FilePath& log_base_path)
    : log_base_path_(log_base_path) {}

RoutineLog::~RoutineLog() = default;

void RoutineLog::LogRoutineStarted(mojom::RoutineType type) {
  std::stringstream log_line;
  log_line << GetCurrentDateTimeAsString() << kSeparator
           << getRoutineTypeString(type) << kSeparator << kStartedDescription
           << kNewline;
  Append(type, log_line.str());
}

void RoutineLog::LogRoutineCompleted(mojom::RoutineType type,
                                     mojom::StandardRoutineResult result) {
  std::stringstream log_line;
  log_line << GetCurrentDateTimeAsString() << kSeparator
           << getRoutineTypeString(type) << kSeparator
           << getRoutineResultString(result) << kNewline;
  Append(type, log_line.str());
}

void RoutineLog::LogRoutineCancelled(mojom::RoutineType type) {
  std::stringstream log_line;
  log_line << GetCurrentDateTimeAsString() << kSeparator
           << kCancelledDescription << kNewline;
  Append(type, log_line.str());
}

std::string RoutineLog::GetContentsForCategory(
    const RoutineCategory category) const {
  const auto iter = logs_.find(category);
  if (iter == logs_.end()) {
    return "";
  }

  return iter->second->GetContents();
}

void RoutineLog::Append(mojom::RoutineType type, const std::string& text) {
  RoutineCategory category = GetRoutineCategory(type);

  // Insert a new log if it doesn't exist then append to it.
  base::FilePath log_path = GetCategoryLogFilePath(category);
  auto iter = logs_.find(category);
  if (iter == logs_.end()) {
    iter = logs_.emplace(category, std::make_unique<AsyncLog>(log_path)).first;
  }

  iter->second->Append(text);
}

base::FilePath RoutineLog::GetCategoryLogFilePath(
    const RoutineCategory category) {
  std::string name =
      "diagnostics_routines_" + GetRoutineLogCategoryString(category) + ".log";
  return log_base_path_.Append(name);
}

}  // namespace diagnostics
}  // namespace ash