#include "logger.h"
#include <windows.h>
#define OBS_UNIX_STRUCTURE 0
#define BASE_PATH "../.."
#define CONFIG_PATH BASE_PATH "/config"
#define MAX_REPEATED_LINES 30
#define MAX_CHAR_VARIATION (255 * 3)
bool get_token(lexer* lex, string& str, base_token_type type)
{
base_token token;
if (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))
return false;
if (token.type != type)
return false;
str.assign(token.text.array, token.text.len);
return true;
}
bool expect_token(lexer* lex, const char* str, base_token_type type)
{
base_token token;
if (!lexer_getbasetoken(lex, &token, IGNORE_WHITESPACE))
return false;
if (token.type != type)
return false;
return strref_cmp(&token.text, str) == 0;
}
uint64_t convert_log_name(bool has_prefix, const char* name)
{
BaseLexer lex;
string year, month, day, hour, minute, second;
lexer_start(lex, name);
if (has_prefix) {
string temp;
if (!get_token(lex, temp, BASETOKEN_ALPHA))
return 0;
}
if (!get_token(lex, year, BASETOKEN_DIGIT))
return 0;
if (!expect_token(lex, "-", BASETOKEN_OTHER))
return 0;
if (!get_token(lex, month, BASETOKEN_DIGIT))
return 0;
if (!expect_token(lex, "-", BASETOKEN_OTHER))
return 0;
if (!get_token(lex, day, BASETOKEN_DIGIT))
return 0;
if (!get_token(lex, hour, BASETOKEN_DIGIT))
return 0;
if (!expect_token(lex, "-", BASETOKEN_OTHER))
return 0;
if (!get_token(lex, minute, BASETOKEN_DIGIT))
return 0;
if (!expect_token(lex, "-", BASETOKEN_OTHER))
return 0;
if (!get_token(lex, second, BASETOKEN_DIGIT))
return 0;
stringstream timestring;
timestring << year << month << day << hour << minute << second;
return std::stoull(timestring.str());
}
void get_last_log(bool has_prefix, const char* subdir_to_use,//log绝对路径
std::string& last)
{
/* BPtr<char> logDir(GetConfigPathPtr(subdir_to_use));*/
struct os_dirent* entry;
os_dir_t* dir = os_opendir(subdir_to_use);
uint64_t highest_ts = 0;
if (dir) {
while ((entry = os_readdir(dir)) != NULL) {
if (entry->directory || *entry->d_name == '.')
continue;
uint64_t ts =
convert_log_name(has_prefix, entry->d_name);//去除后缀
if (ts > highest_ts) {
last = entry->d_name;
highest_ts = ts;
}
}
os_closedir(dir);
}
}
string GenerateTimeDateFilename(const char* extension, bool noSpace)
{
time_t now = time(0);
char file[256] = {};
struct tm* cur_time;
cur_time = localtime(&now);
snprintf(file, sizeof(file), "%d-%02d-%02d%c%02d-%02d-%02d.%s",
cur_time->tm_year + 1900, cur_time->tm_mon + 1,
cur_time->tm_mday, noSpace ? '_' : ' ', cur_time->tm_hour,
cur_time->tm_min, cur_time->tm_sec, extension);
return string(file);
}
int GetConfigPath(char* path, size_t size, const char* name)
{
if (!OBS_UNIX_STRUCTURE && false) {
if (name && *name) {
return snprintf(path, size, CONFIG_PATH "/%s", name);
}
else {
return snprintf(path, size, CONFIG_PATH);
}
}
else {
return os_get_config_path(path, size, name);
}
}
char* GetConfigPathPtr(const char* name)
{
if (!OBS_UNIX_STRUCTURE && false) {//portable_mode
char path[512];
if (snprintf(path, sizeof(path), CONFIG_PATH "/%s", name) > 0) {
return bstrdup(path);
}
else {
return NULL;
}
}
else {
return os_get_config_path_ptr(name);
}
}
void delete_oldest_file(bool has_prefix, const char* location)
{
BPtr<char> logDir(GetConfigPathPtr(location));
string oldestLog;
uint64_t oldest_ts = (uint64_t)-1;
struct os_dirent* entry;
os_dir_t* dir = os_opendir(logDir);
if (dir) {
unsigned int count = 0;
while ((entry = os_readdir(dir)) != NULL) {
if (entry->directory || *entry->d_name == '.')
continue;
uint64_t ts =
convert_log_name(has_prefix, entry->d_name);
if (ts) {
if (ts < oldest_ts) {
oldestLog = entry->d_name;
oldest_ts = ts;
}
count++;
}
}
os_closedir(dir);
}
}
int sum_chars(const char* str)
{
int val = 0;
for (; *str != 0; str++)
val += *str;
return val;
}
bool too_many_repeated_entries(fstream& logFile, const char* msg,
const char* output_str)
{
static mutex log_mutex;
static const char* last_msg_ptr = nullptr;
static int last_char_sum = 0;
static char cmp_str[4096];
static int rep_count = 0;
int new_sum = sum_chars(output_str);
lock_guard<mutex> guard(log_mutex);
if (false) {//unfiltered_log
return false;
}
if (last_msg_ptr == msg) {
int diff = std::abs(new_sum - last_char_sum);
if (diff < MAX_CHAR_VARIATION) {
return (rep_count++ >= MAX_REPEATED_LINES);
}
}
if (rep_count > MAX_REPEATED_LINES) {
logFile << CurrentTimeString()
<< ": Last log entry repeated for "
<< to_string(rep_count - MAX_REPEATED_LINES)
<< " more lines" << endl;
}
last_msg_ptr = msg;
strcpy(cmp_str, output_str);
last_char_sum = new_sum;
rep_count = 0;
return false;
}
void LogString(fstream& logFile, const char* timeString,
char* str, int log_level)
{
string msg;
msg += timeString;
msg += str;
logFile << msg << endl;
}
string CurrentTimeString()
{
using namespace std::chrono;
struct tm tstruct;
char buf[80];
auto tp = system_clock::now();
auto now = system_clock::to_time_t(tp);
tstruct = *localtime(&now);
size_t written = strftime(buf, sizeof(buf), "%X", &tstruct);
if (ratio_less<system_clock::period, seconds::period>::value &&
written && (sizeof(buf) - written) > 5) {
auto tp_secs = time_point_cast<seconds>(tp);
auto millis = duration_cast<milliseconds>(tp - tp_secs).count();
snprintf(buf + written, sizeof(buf) - written, ".%03u",
static_cast<unsigned>(millis));
}
return buf;
}
void LogStringChunk(fstream& logFile, char* str, int log_level)
{
char* nextLine = str;
string timeString = CurrentTimeString();
timeString += ": ";
while (*nextLine) {
char* nextLine = strchr(str, '\n');
if (!nextLine)
break;
if (nextLine != str && nextLine[-1] == '\r') {
nextLine[-1] = 0;
}
else {
nextLine[0] = 0;
}
LogString(logFile, timeString.c_str(), str, log_level);
nextLine++;
str = nextLine;
}
LogString(logFile, timeString.c_str(), str, log_level);
}
void do_log(int log_level, const char* msg, va_list args, void* param)
{
fstream& logFile = *static_cast<fstream*>(param);
char str[4096];
#ifndef _WIN32
va_list args2;
va_copy(args2, args);
#endif
vsnprintf(str, 4095, msg, args);
#ifdef _WIN32
if (IsDebuggerPresent()) {
int wNum = MultiByteToWideChar(CP_UTF8, 0, str, -1, NULL, 0);
if (wNum > 1) {
static wstring wide_buf;
static mutex wide_mutex;
lock_guard<mutex> lock(wide_mutex);
wide_buf.reserve(wNum + 1);
wide_buf.resize(wNum - 1);
MultiByteToWideChar(CP_UTF8, 0, str, -1, &wide_b