blob: e5e9ff15bde86a32c240006f3af871d633d947b7 [file] [log] [blame]
// Copyright 2020 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.
#include "croslog/log_parser_syslog.h"
#include <memory>
#include <string>
#include <utility>
#include "base/strings/string_number_conversions.h"
namespace {
// The length of time string like "2020-05-25T00:00:00.000000+00:00".
constexpr size_t kTimeStringLengthWithTimeZone = 32;
// The length of time string like "2020-05-25T00:00:00.000000Z".
constexpr size_t kTimeStringLengthUTC = 27;
int ParseTime(const std::string& entire_line, base::Time* time) {
DCHECK_NE(nullptr, time);
if (entire_line[26] == 'Z') {
// Case of UTC time format like "2020-05-25T00:00:00.000000Z".
std::string log_time = entire_line.substr(0, kTimeStringLengthUTC);
bool result = base::Time::FromString(log_time.c_str(), time);
if (!result)
return -1;
return kTimeStringLengthUTC;
} else if (entire_line[26] == '+' || entire_line[26] == '-') {
// Case of format with time-zone like "2020-05-25T00:00:00.000000+00:00".
std::string log_time = entire_line.substr(0, kTimeStringLengthWithTimeZone);
bool result = base::Time::FromString(log_time.c_str(), time);
if (!result)
return -1;
return kTimeStringLengthWithTimeZone;
}
return -1;
}
} // namespace
namespace croslog {
LogParserSyslog::LogParserSyslog() = default;
MaybeLogEntry LogParserSyslog::ParseInternal(std::string&& entire_line) {
if (entire_line.empty()) {
// Returns an invalid value if the line is invalid or empty.
return base::nullopt;
}
if (entire_line.size() < kTimeStringLengthUTC) {
// Parse failed. Maybe this line doesn't contains a header.
return base::nullopt;
}
base::Time time;
int message_start_pos = ParseTime(entire_line, &time);
if (message_start_pos < 0) {
// Parse failed. Maybe this line doesn't contains a header.
return base::nullopt;
}
int pos = message_start_pos;
if (entire_line[pos] != ' ') {
// Parse failed. Maybe this line doesn't contains a header.
return base::nullopt;
}
std::string severity_str;
if (entire_line[pos] == ' ') {
for (int i = pos + 1; i < entire_line.size(); i++) {
if (entire_line[i] == ' ') {
severity_str = entire_line.substr(pos + 1, i - pos - 1);
pos = i;
break;
}
}
}
Severity severity = Severity::UNSPECIFIED;
if (!severity_str.empty()) {
severity = SeverityFromString(severity_str);
}
std::string tag;
if (entire_line[pos] == ' ') {
for (int i = pos + 1; i < entire_line.size(); i++) {
if (entire_line[i] == '[' || entire_line[i] == ':' ||
entire_line[i] == ' ') {
tag = entire_line.substr(pos + 1, i - pos - 1);
pos = i;
break;
}
}
}
int pid = -1;
if (entire_line[pos] == '[') {
for (int i = pos + 1; i < entire_line.size(); i++) {
if (entire_line[i] == ']') {
std::string pid_str = entire_line.substr(pos + 1, i - pos - 1);
if (!base::StringToInt(pid_str, &pid))
pid = -1;
pos = i;
break;
}
}
DCHECK_EQ(']', entire_line[pos]);
pos++;
}
if (entire_line.size() > pos && entire_line[pos] == ':')
pos++;
std::string message;
if (entire_line.size() > pos) {
if (entire_line[pos] != ' ') {
// Parse failed. Maybe this line doesn't contains a header.
return base::nullopt;
}
pos++;
message = entire_line.substr(pos, entire_line.size() - pos);
}
return LogEntry{time, severity, std::move(tag),
pid, std::move(message), std::move(entire_line)};
}
} // namespace croslog