blob: ee5f08cc8e56e65597762a0b228e0bb050482593 [file] [edit]
/*
* Copyright 2024 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "common/log.h"
#include <errno.h>
#include <syslog.h>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <vector>
#include "absl/cleanup/cleanup.h"
#include "absl/strings/str_format.h"
#include "tensorflow/lite/tools/command_line_flags.h"
namespace tflite::cros::log_impl {
namespace {
const char* GetProgramName() {
// This is defined in <errno.h>.
const char* name = program_invocation_short_name;
return name != nullptr ? name : "";
}
int GetSyslogLevel(const absl::LogEntry& entry) {
if (entry.verbosity() != absl::LogEntry::kNoVerbosityLevel) {
return LOG_DEBUG;
}
switch (entry.log_severity()) {
case absl::LogSeverity::kInfo:
return LOG_INFO;
case absl::LogSeverity::kWarning:
return LOG_WARNING;
case absl::LogSeverity::kError:
return LOG_ERR;
case absl::LogSeverity::kFatal:
return LOG_CRIT;
}
// Safe fallback. Should not happen normally.
return LOG_DEBUG;
}
// Gets the `--v=` flag from command line.
int GetVLogLevelFromCmdline() {
// We can not call absl::ParseCommandLine() in main because the logging
// library might be used in stable delegate, which will be loaded dynamically
// with dlopen(). Reconstruct argv from proc fs instead.
FILE* fp = fopen("/proc/self/cmdline", "r");
if (fp == nullptr) {
return 0;
}
absl::Cleanup close_file = [&] { fclose(fp); };
std::vector<std::string> args;
char* arg = nullptr;
size_t size = 0;
while (getdelim(&arg, &size, '\0', fp) != -1) {
args.push_back(arg);
}
free(arg);
int vlog_level = 0;
std::vector<Flag> flags = {
Flag::CreateFlag("v", &vlog_level,
"Show all VLOG(m) messages for m <= this."),
};
// We need to copy the arguments to a non-owning temporary vector since
// Flags::Parse() will try to remove the parsed arguments from argv.
int argc = args.size();
std::vector<const char*> argv;
for (const auto& arg : args) {
argv.push_back(arg.data());
}
Flags::Parse(&argc, argv.data(), flags);
return vlog_level;
}
std::string FormatPidTid(int pid, int tid) {
// Only show thread id when it's different from the process id.
return pid == tid ? absl::StrFormat("%d", pid)
: absl::StrFormat("%d:%d", pid, tid);
}
} // namespace
SyslogStderrSink* SyslogStderrSink::GetInstance() {
// Leaky singleton.
using Self = SyslogStderrSink;
alignas(Self) static uint8_t storage[sizeof(Self)];
static Self* instance = [&] {
int vlog_level = GetVLogLevelFromCmdline();
return new (storage) Self(vlog_level);
}();
return instance;
}
void SyslogStderrSink::Send(const absl::LogEntry& entry) {
if (entry.verbosity() > global_vlog_level_) {
return;
}
syslog(GetSyslogLevel(entry), "%s",
entry.text_message_with_prefix_and_newline_c_str());
std::string timestamp = absl::FormatTime(
"%Y-%m-%d%ET%H:%M:%E6SZ", entry.timestamp(), absl::UTCTimeZone());
const char* severity = absl::LogSeverityName(entry.log_severity());
// A syslog compatible format used on ChromeOS. Reference:
// https://source.chromium.org/chromium/chromium/src/+/main:base/logging_chromeos.cc
//
// We don't provide control knobs for simplicity. The format looks like:
// <timestamp> <severity> <program>[<pid>:<tid>]: [<file>(<line>)] <message>
//
// TODO(shik): Only log to stderr if stdin is a tty.
std::string body = absl::StrFormat(
"%v %s %s[%s]: [%v(%d)] %v", timestamp, severity, GetProgramName(),
FormatPidTid(getpid(), entry.tid()), entry.source_basename(),
entry.source_line(), entry.text_message_with_newline());
fwrite(body.data(), body.size(), 1, stderr);
// TODO(shik): Print stacktrace if available.
}
} // namespace tflite::cros::log_impl