blob: 71a6197f0d41645ebd916351abe85dc7a4cf0ef8 [file] [log] [blame]
// Copyright 2020 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.
// This is a very basic direct output log implementation with no buffering.
#include "pw_log_basic/log_basic.h"
#include <cstring>
#include "pw_log/levels.h"
#include "pw_log_basic_private/config.h"
#include "pw_string/string_builder.h"
#include "pw_sys_io/sys_io.h"
// ANSI color constants to control the terminal. Not Windows compatible.
// clang-format off
#define MAGENTA "\033[35m"
#define YELLOW "\033[33m"
#define RED "\033[31m"
#define GREEN "\033[32m"
#define BLUE "\033[96m"
#define BLACK "\033[30m"
#define YELLOW_BG "\033[43m"
#define WHITE_BG "\033[47m"
#define RED_BG "\033[41m"
#define BOLD "\033[1m"
#define RESET "\033[0m"
// clang-format on
namespace pw::log_basic {
namespace {
const char* LogLevelToLogLevelName(int level) {
switch (level) {
// clang-format off
#if PW_EMOJI
case PW_LOG_LEVEL_DEBUG : return "👾" RESET;
case PW_LOG_LEVEL_INFO : return "ℹī¸ " RESET;
case PW_LOG_LEVEL_WARN : return "⚠ī¸ " RESET;
case PW_LOG_LEVEL_ERROR : return "❌" RESET;
case PW_LOG_LEVEL_CRITICAL : return "☠ī¸ " RESET;
default: return "❔" RESET;
#else
case PW_LOG_LEVEL_DEBUG : return BLUE BOLD "DBG" RESET;
case PW_LOG_LEVEL_INFO : return MAGENTA BOLD "INF" RESET;
case PW_LOG_LEVEL_WARN : return YELLOW BOLD "WRN" RESET;
case PW_LOG_LEVEL_ERROR : return RED BOLD "ERR" RESET;
case PW_LOG_LEVEL_CRITICAL : return BLACK BOLD RED_BG "FTL" RESET;
default : return GREEN BOLD "UNK" RESET;
#endif
// clang-format on
}
}
#if PW_LOG_SHOW_FILENAME
const char* GetFileBasename(const char* filename) {
int length = std::strlen(filename);
if (length == 0) {
return filename;
}
// Start on the last character.
// TODO(pwbug/38): This part of the function doesn't work for Windows paths.
const char* basename = filename + std::strlen(filename) - 1;
while (basename != filename && *basename != '/') {
basename--;
}
if (*basename == '/') {
basename++;
}
return basename;
}
#endif // PW_LOG_SHOW_FILENAME
void (*write_log)(std::string_view) = [](std::string_view log) {
sys_io::WriteLine(log)
.IgnoreError(); // TODO(pwbug/387): Handle Status properly
};
} // namespace
// This is a fully loaded, inefficient-at-the-callsite, log implementation.
extern "C" void pw_Log(int level,
unsigned int flags,
const char* module_name,
const char* file_name,
int line_number,
const char* function_name,
const char* message,
...) {
// Accumulate the log message in this buffer, then output it.
pw::StringBuffer<150> buffer;
// Column: Timestamp
// Note that this macro method defaults to a no-op.
PW_LOG_APPEND_TIMESTAMP(buffer);
// Column: Filename
#if PW_LOG_SHOW_FILENAME
buffer.Format(" %-30s:%4d |", GetFileBasename(file_name), line_number);
#else
static_cast<void>(file_name);
static_cast<void>(line_number);
#endif
// Column: Function
#if PW_LOG_SHOW_FUNCTION
buffer.Format(" %20s |", function_name);
#else
static_cast<void>(function_name);
#endif
// Column: Module
#if PW_LOG_SHOW_MODULE
buffer << " " BOLD;
buffer.Format("%3s", module_name);
buffer << RESET " ";
#else
static_cast<void>(module_name);
#endif // PW_LOG_SHOW_MODULE
// Column: Flag
#if PW_LOG_SHOW_FLAG
#if PW_EMOJI
buffer << (flags ? "🚩" : " ");
#else
buffer << (flags ? "*" : "|");
#endif // PW_EMOJI
buffer << " ";
#else
static_cast<void>(flags);
#endif // PW_LOG_SHOW_FLAG
// Column: Level
buffer << LogLevelToLogLevelName(level) << " ";
// Column: Message
va_list args;
va_start(args, message);
buffer.FormatVaList(message, args);
va_end(args);
// All done; flush the log.
write_log(buffer);
}
void SetOutput(void (*log_output)(std::string_view log)) {
write_log = log_output;
}
} // namespace pw::log_basic