| | #include "common.h"
|
| | #include "log.h"
|
| |
|
| | #include <chrono>
|
| | #include <condition_variable>
|
| | #include <cstdarg>
|
| | #include <cstdio>
|
| | #include <cstdlib>
|
| | #include <cstring>
|
| | #include <mutex>
|
| | #include <sstream>
|
| | #include <thread>
|
| | #include <vector>
|
| |
|
| | #if defined(_WIN32)
|
| | # include <io.h>
|
| | # include <windows.h>
|
| | # define isatty _isatty
|
| | # define fileno _fileno
|
| | #else
|
| | # include <unistd.h>
|
| | #endif
|
| |
|
| | int common_log_verbosity_thold = LOG_DEFAULT_LLAMA;
|
| |
|
| | void common_log_set_verbosity_thold(int verbosity) {
|
| | common_log_verbosity_thold = verbosity;
|
| | }
|
| |
|
| | static int64_t t_us() {
|
| | return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
| | }
|
| |
|
| |
|
| | enum common_log_col : int {
|
| | COMMON_LOG_COL_DEFAULT = 0,
|
| | COMMON_LOG_COL_BOLD,
|
| | COMMON_LOG_COL_RED,
|
| | COMMON_LOG_COL_GREEN,
|
| | COMMON_LOG_COL_YELLOW,
|
| | COMMON_LOG_COL_BLUE,
|
| | COMMON_LOG_COL_MAGENTA,
|
| | COMMON_LOG_COL_CYAN,
|
| | COMMON_LOG_COL_WHITE,
|
| | };
|
| |
|
| |
|
| | static std::vector<const char *> g_col = {
|
| | "",
|
| | "",
|
| | "",
|
| | "",
|
| | "",
|
| | "",
|
| | "",
|
| | "",
|
| | "",
|
| | };
|
| |
|
| | struct common_log_entry {
|
| | enum ggml_log_level level;
|
| |
|
| | bool prefix;
|
| |
|
| | int64_t timestamp;
|
| |
|
| | std::vector<char> msg;
|
| |
|
| |
|
| | bool is_end;
|
| |
|
| | void print(FILE * file = nullptr) const {
|
| | FILE * fcur = file;
|
| | if (!fcur) {
|
| |
|
| |
|
| | if (level == GGML_LOG_LEVEL_DEBUG && common_log_verbosity_thold < LOG_DEFAULT_DEBUG) {
|
| | return;
|
| | }
|
| |
|
| | fcur = stdout;
|
| |
|
| | if (level != GGML_LOG_LEVEL_NONE) {
|
| | fcur = stderr;
|
| | }
|
| | }
|
| |
|
| | if (level != GGML_LOG_LEVEL_NONE && level != GGML_LOG_LEVEL_CONT && prefix) {
|
| | if (timestamp) {
|
| |
|
| | fprintf(fcur, "%s%d.%02d.%03d.%03d%s ",
|
| | g_col[COMMON_LOG_COL_BLUE],
|
| | (int) (timestamp / 1000000 / 60),
|
| | (int) (timestamp / 1000000 % 60),
|
| | (int) (timestamp / 1000 % 1000),
|
| | (int) (timestamp % 1000),
|
| | g_col[COMMON_LOG_COL_DEFAULT]);
|
| | }
|
| |
|
| | switch (level) {
|
| | case GGML_LOG_LEVEL_INFO: fprintf(fcur, "%sI %s", g_col[COMMON_LOG_COL_GREEN], g_col[COMMON_LOG_COL_DEFAULT]); break;
|
| | case GGML_LOG_LEVEL_WARN: fprintf(fcur, "%sW %s", g_col[COMMON_LOG_COL_MAGENTA], "" ); break;
|
| | case GGML_LOG_LEVEL_ERROR: fprintf(fcur, "%sE %s", g_col[COMMON_LOG_COL_RED], "" ); break;
|
| | case GGML_LOG_LEVEL_DEBUG: fprintf(fcur, "%sD %s", g_col[COMMON_LOG_COL_YELLOW], "" ); break;
|
| | default:
|
| | break;
|
| | }
|
| | }
|
| |
|
| | fprintf(fcur, "%s", msg.data());
|
| |
|
| | if (level == GGML_LOG_LEVEL_WARN || level == GGML_LOG_LEVEL_ERROR || level == GGML_LOG_LEVEL_DEBUG) {
|
| | fprintf(fcur, "%s", g_col[COMMON_LOG_COL_DEFAULT]);
|
| | }
|
| |
|
| | fflush(fcur);
|
| | }
|
| | };
|
| |
|
| | struct common_log {
|
| |
|
| | common_log() : common_log(256) {}
|
| |
|
| | common_log(size_t capacity) {
|
| | file = nullptr;
|
| | prefix = false;
|
| | timestamps = false;
|
| | running = false;
|
| | t_start = t_us();
|
| |
|
| |
|
| | entries.resize(capacity);
|
| | for (auto & entry : entries) {
|
| | entry.msg.resize(256);
|
| | }
|
| |
|
| | head = 0;
|
| | tail = 0;
|
| |
|
| | resume();
|
| | }
|
| |
|
| | ~common_log() {
|
| | pause();
|
| | if (file) {
|
| | fclose(file);
|
| | }
|
| | }
|
| |
|
| | private:
|
| | std::mutex mtx;
|
| | std::thread thrd;
|
| | std::condition_variable cv;
|
| |
|
| | FILE * file;
|
| |
|
| | bool prefix;
|
| | bool timestamps;
|
| | bool running;
|
| |
|
| | int64_t t_start;
|
| |
|
| |
|
| | std::vector<common_log_entry> entries;
|
| | size_t head;
|
| | size_t tail;
|
| |
|
| |
|
| | common_log_entry cur;
|
| |
|
| | public:
|
| | void add(enum ggml_log_level level, const char * fmt, va_list args) {
|
| | std::lock_guard<std::mutex> lock(mtx);
|
| |
|
| | if (!running) {
|
| |
|
| | return;
|
| | }
|
| |
|
| | auto & entry = entries[tail];
|
| |
|
| | {
|
| |
|
| | va_list args_copy;
|
| | va_copy(args_copy, args);
|
| |
|
| | #if 1
|
| | const size_t n = vsnprintf(entry.msg.data(), entry.msg.size(), fmt, args);
|
| | if (n >= entry.msg.size()) {
|
| | entry.msg.resize(n + 1);
|
| | vsnprintf(entry.msg.data(), entry.msg.size(), fmt, args_copy);
|
| | }
|
| | #else
|
| |
|
| |
|
| | std::stringstream ss;
|
| | for (int i = 0; fmt[i] != 0; i++) {
|
| | if (fmt[i] == '%') {
|
| | ss << LOG_COL_BOLD;
|
| | while (fmt[i] != ' ' && fmt[i] != ')' && fmt[i] != ']' && fmt[i] != 0) ss << fmt[i++];
|
| | ss << LOG_COL_DEFAULT;
|
| | if (fmt[i] == 0) break;
|
| | }
|
| | ss << fmt[i];
|
| | }
|
| | const size_t n = vsnprintf(entry.msg.data(), entry.msg.size(), ss.str().c_str(), args);
|
| | if (n >= entry.msg.size()) {
|
| | entry.msg.resize(n + 1);
|
| | vsnprintf(entry.msg.data(), entry.msg.size(), ss.str().c_str(), args_copy);
|
| | }
|
| | #endif
|
| | va_end(args_copy);
|
| | }
|
| |
|
| | entry.level = level;
|
| | entry.prefix = prefix;
|
| | entry.timestamp = 0;
|
| | if (timestamps) {
|
| | entry.timestamp = t_us() - t_start;
|
| | }
|
| | entry.is_end = false;
|
| |
|
| | tail = (tail + 1) % entries.size();
|
| | if (tail == head) {
|
| |
|
| | std::vector<common_log_entry> new_entries(2*entries.size());
|
| |
|
| | size_t new_tail = 0;
|
| |
|
| | do {
|
| | new_entries[new_tail] = std::move(entries[head]);
|
| |
|
| | head = (head + 1) % entries.size();
|
| | new_tail = (new_tail + 1);
|
| | } while (head != tail);
|
| |
|
| | head = 0;
|
| | tail = new_tail;
|
| |
|
| | for (size_t i = tail; i < new_entries.size(); i++) {
|
| | new_entries[i].msg.resize(256);
|
| | }
|
| |
|
| | entries = std::move(new_entries);
|
| | }
|
| |
|
| | cv.notify_one();
|
| | }
|
| |
|
| | void resume() {
|
| | std::lock_guard<std::mutex> lock(mtx);
|
| |
|
| | if (running) {
|
| | return;
|
| | }
|
| |
|
| | running = true;
|
| |
|
| | thrd = std::thread([this]() {
|
| | while (true) {
|
| | {
|
| | std::unique_lock<std::mutex> lock(mtx);
|
| | cv.wait(lock, [this]() { return head != tail; });
|
| |
|
| | cur = entries[head];
|
| |
|
| | head = (head + 1) % entries.size();
|
| | }
|
| |
|
| | if (cur.is_end) {
|
| | break;
|
| | }
|
| |
|
| | cur.print();
|
| |
|
| | if (file) {
|
| | cur.print(file);
|
| | }
|
| | }
|
| | });
|
| | }
|
| |
|
| | void pause() {
|
| | {
|
| | std::lock_guard<std::mutex> lock(mtx);
|
| |
|
| | if (!running) {
|
| | return;
|
| | }
|
| |
|
| | running = false;
|
| |
|
| |
|
| | {
|
| | auto & entry = entries[tail];
|
| | entry.is_end = true;
|
| |
|
| | tail = (tail + 1) % entries.size();
|
| | }
|
| |
|
| | cv.notify_one();
|
| | }
|
| |
|
| | thrd.join();
|
| | }
|
| |
|
| | void set_file(const char * path) {
|
| | pause();
|
| |
|
| | if (file) {
|
| | fclose(file);
|
| | }
|
| |
|
| | if (path) {
|
| | file = fopen(path, "w");
|
| | } else {
|
| | file = nullptr;
|
| | }
|
| |
|
| | resume();
|
| | }
|
| |
|
| | void set_colors(bool colors) {
|
| | pause();
|
| |
|
| | if (colors) {
|
| | g_col[COMMON_LOG_COL_DEFAULT] = LOG_COL_DEFAULT;
|
| | g_col[COMMON_LOG_COL_BOLD] = LOG_COL_BOLD;
|
| | g_col[COMMON_LOG_COL_RED] = LOG_COL_RED;
|
| | g_col[COMMON_LOG_COL_GREEN] = LOG_COL_GREEN;
|
| | g_col[COMMON_LOG_COL_YELLOW] = LOG_COL_YELLOW;
|
| | g_col[COMMON_LOG_COL_BLUE] = LOG_COL_BLUE;
|
| | g_col[COMMON_LOG_COL_MAGENTA] = LOG_COL_MAGENTA;
|
| | g_col[COMMON_LOG_COL_CYAN] = LOG_COL_CYAN;
|
| | g_col[COMMON_LOG_COL_WHITE] = LOG_COL_WHITE;
|
| | } else {
|
| | for (size_t i = 0; i < g_col.size(); i++) {
|
| | g_col[i] = "";
|
| | }
|
| | }
|
| |
|
| | resume();
|
| | }
|
| |
|
| | void set_prefix(bool prefix) {
|
| | std::lock_guard<std::mutex> lock(mtx);
|
| |
|
| | this->prefix = prefix;
|
| | }
|
| |
|
| | void set_timestamps(bool timestamps) {
|
| | std::lock_guard<std::mutex> lock(mtx);
|
| |
|
| | this->timestamps = timestamps;
|
| | }
|
| | };
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | struct common_log * common_log_init() {
|
| | return new common_log;
|
| | }
|
| |
|
| | struct common_log * common_log_main() {
|
| | static struct common_log log;
|
| | static std::once_flag init_flag;
|
| | std::call_once(init_flag, [&]() {
|
| |
|
| | log.set_colors(tty_can_use_colors());
|
| | });
|
| |
|
| | return &log;
|
| | }
|
| |
|
| | void common_log_pause(struct common_log * log) {
|
| | log->pause();
|
| | }
|
| |
|
| | void common_log_resume(struct common_log * log) {
|
| | log->resume();
|
| | }
|
| |
|
| | void common_log_free(struct common_log * log) {
|
| | delete log;
|
| | }
|
| |
|
| | void common_log_add(struct common_log * log, enum ggml_log_level level, const char * fmt, ...) {
|
| | va_list args;
|
| | va_start(args, fmt);
|
| | log->add(level, fmt, args);
|
| | va_end(args);
|
| | }
|
| |
|
| | void common_log_set_file(struct common_log * log, const char * file) {
|
| | log->set_file(file);
|
| | }
|
| |
|
| | void common_log_set_colors(struct common_log * log, log_colors colors) {
|
| | if (colors == LOG_COLORS_AUTO) {
|
| | log->set_colors(tty_can_use_colors());
|
| | return;
|
| | }
|
| |
|
| | if (colors == LOG_COLORS_DISABLED) {
|
| | log->set_colors(false);
|
| | return;
|
| | }
|
| |
|
| | GGML_ASSERT(colors == LOG_COLORS_ENABLED);
|
| | log->set_colors(true);
|
| | }
|
| |
|
| | void common_log_set_prefix(struct common_log * log, bool prefix) {
|
| | log->set_prefix(prefix);
|
| | }
|
| |
|
| | void common_log_set_timestamps(struct common_log * log, bool timestamps) {
|
| | log->set_timestamps(timestamps);
|
| | }
|
| |
|
| | void common_log_flush(struct common_log * log) {
|
| | log->pause();
|
| | log->resume();
|
| | }
|
| |
|
| | static int common_get_verbosity(enum ggml_log_level level) {
|
| | switch (level) {
|
| | case GGML_LOG_LEVEL_DEBUG: return LOG_LEVEL_DEBUG;
|
| | case GGML_LOG_LEVEL_INFO: return LOG_LEVEL_INFO;
|
| | case GGML_LOG_LEVEL_WARN: return LOG_LEVEL_WARN;
|
| | case GGML_LOG_LEVEL_ERROR: return LOG_LEVEL_ERROR;
|
| | case GGML_LOG_LEVEL_CONT: return LOG_LEVEL_INFO;
|
| | case GGML_LOG_LEVEL_NONE:
|
| | default:
|
| | return LOG_LEVEL_OUTPUT;
|
| | }
|
| | }
|
| |
|
| | void common_log_default_callback(enum ggml_log_level level, const char * text, void * ) {
|
| | auto verbosity = common_get_verbosity(level);
|
| | if (verbosity <= common_log_verbosity_thold) {
|
| | common_log_add(common_log_main(), level, "%s", text);
|
| | }
|
| | }
|
| |
|