📄 log.cc
字号:
#include <assert.h>#include <ctype.h>#include <errno.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include <time.h>#include "module.hh"#include "serial.hh"#include "sulima.hh"// Log-related variables.//// log_enabled, if non-zero, enables logging facilities//// rotate_log_files specifies the maximum number of old log files to retain.// Setting this to 0 will disable backing up of old log files.//// log_file_name specifies the name of the log file.//// log_created and log_fp are internal variables. log_created is set by the// first call to open_log(). log_fp is valid if log_created is non-zero and// open_log() succeeded.//// log_bol and print_bol, when true, indicate that the last character written// to the log file or the standard output was a newline.//// log_console points to the last module to write to the log file.static int log_enabled = true; // logging to file enabled/disabled static int rotate_log_files = 9; // maximum number of log files to keep static char *log_file_name = 0; // log file name static bool log_created = false; // set after log is created static FILE *log_fp = 0; // log stream static bool log_bol = true;static bool print_bol = true;static const BasicModule *log_console = 0;// Return a log name with a numeric suffix attached.static char *generate_log_name(int s){ // Don't do anything fancy if s is 0. if (!s) return copy(log_file_name); // The buffer size uses log10(2) == 0.30103. char buffer[sizeof(int) * 8 * 30103L / 100000L + 2]; size_t n; int prec = 0; // Grab the precision of the suffix. int i = rotate_log_files; do { ++prec; i /= 10; } while (i); n += prec + 1; // Generate the string. sprintf(buffer, ".%0*d", prec, s); return copy(log_file_name, buffer, 0);}// Create the log file. The log rotation code isn't particularly efficient if// the rotation period is large (it's fine if the actual number of existing// logs aproaches the rotation period.) This should probably be fixed; for// now, I set the default period to a somewhat unsatisfactory 9 logs.static intopen_log(void){ log_created = true; if (log_enabled) { if (rotate_log_files) { // Rotate the log files, ignoring any errors. char *name1 = 0, *name2 = 0; int i = rotate_log_files; if ((name1 = generate_log_name(i)) == 0) return -1; remove(name1); while (i) { if ((name2 = generate_log_name(--i)) == 0) { delete name1; delete name2; return -1; } rename(name2, name1); delete name1; name1 = name2; } delete name1; } // This cannot be called from the Sulima constructor. assert(sulima); // Create the new log file. if ((log_fp = fopen(log_file_name, "w")) == 0) { sulima->msg("Cannot open log file \"%#s\" (%s)", log_file_name, strerror(errno)); return -1; } // Write the banner and the time stamp. time_t t; time(&t); fprintf(log_fp, "%s\n" "%s\n" "Log file created on %s\n", sulima->banner, sulima->copyright, ctime(&t)); if (ferror(log_fp)) { sulima->msg("Cannot write to the log file \"%#s\" (%s)", log_file_name, strerror(errno)); return -1; } setbuf(log_fp, NULL); } return 0;}// Write a character to the log file. If we've just switched consoles, start a// new line in the log file.voidBasicModule::log(char c) const{ if (!log_created) open_log(); if (log_fp && !ferror(log_fp)) { if (this != log_console && log_console) { putc('\n', log_fp); log_bol = true; log_console = this; } if (log_bol) { fputs(name(), log_fp); putc(':', log_fp); putc(' ', log_fp); } if (c == '\n') { putc(c, log_fp); log_bol = true; } else { if (c == '\\') { putc('\\', log_fp); putc(c, log_fp); } else if (isprint(c) || c == '\t') { putc(c, log_fp); } else { putc('\\', log_fp); putc(((c >> 6) & 7) + '0', log_fp); putc(((c >> 3) & 7) + '0', log_fp); putc(((c) & 7) + '0', log_fp); } log_bol = false; } if (ferror(log_fp)) { msg("Cannot write to log file \"%#s\" (%s)", log_file_name, strerror(errno)); } }}// Write a character to the log file, maintaining the print_bol variable.voidBasicModule::msg(char c) const{ log(c); putchar(c); print_bol = (c == '\n');}// Print and log the module name, followed by : ", followed by a printf-like// one-line message. The message is always started on a new line and followed// by a '\n'. There's an upper limit on the total length of the message, but// what do we care?static voidprint_message(FILE *fp, const BasicModule *mod, const char *templ, va_list ap){ char buf[4096]; fputs(mod->name(), fp); putc(':', fp); putc(' ', fp); size_t i = vecho(buf, sizeof(buf), templ, ap); fputs(buf, fp); if (i >= sizeof(buf)) fputs("...", fp); putc('\n', fp);}// Log a message.voidBasicModule::log(const char *templ, ...) const{ va_list ap; if (!log_created) open_log(); if (log_fp && !ferror(log_fp)) { va_start(ap, templ); if (!log_bol) putc('\n', log_fp); print_message(log_fp, this, templ, ap); va_end(ap); if (ferror(log_fp)) { msg("Cannot write to log file \"%#s\" (%s)", log_file_name, strerror(errno)); } } log_bol = true;}// Print a message to the standard output.voidBasicModule::msg(const char *templ, ...) const{ va_list ap; va_start(ap, templ); if (!print_bol) putc('\n', log_fp); print_message(stdout, this, templ, ap); va_end(ap); print_bol = true; if (!log_created) open_log(); if (log_fp && !ferror(log_fp)) { va_start(ap, templ); if (!log_bol) putc('\n', log_fp); print_message(log_fp, this, templ, ap); va_end(ap); if (ferror(log_fp)) { msg("Cannot write to log file \"%#s\" (%s)", log_file_name, strerror(errno)); } } log_bol = true;}// The "sim::log" and "sim::msg" commands.static SimArglog_func(const SimArgs &args){ for (int i = 0; i < args.length(); ++i) { for (const char *p = args[i]; *p; ++p) sulima->log(*p); if (i < args.length()) sulima->log(' '); } sulima->log('\n'); return "";}static SimArgmsg_func(const SimArgs &args){ for (int i = 0; i < args.length(); ++i) { for (const char *p = args[i]; *p; ++p) { sulima->msg(*p); } if (i < args.length()) { sulima->msg(' '); } } sulima->msg('\n'); return "";}// Initialize the logging subsystem.voidSulima::create_logging_subsystem(){ // Link related variables. define("log_enabled", log_enabled, true); define("rotate_log_files", rotate_log_files, 9); define("log_file_name", log_file_name, "sulima.log"); define("log", log_func); define("msg", msg_func);}// Destroy the logging subsystem.voidSulima::destroy_logging_subsystem(){ if (!log_file_name) log_file_name = "?"; if (log_created && log_fp) { if (fclose(log_fp)) { msg("Cannot write to log file \"%#s\" (%s)", log_file_name, strerror(errno)); } log_fp = 0; }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -