2 Star 2 Fork 2

yuyugong/nanolog

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
nanolog.hpp 38.16 KB
一键复制 编辑 原始数据 按行查看 历史
yuyugong 提交于 2023-11-01 08:51 . 代码上传
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987
/*
# 支持linux/windows
基于nanolog代码进行优化
v1.1:
1.maxLogFileSize:文件最大支持内容,单位:MB
2.KeepLogCount:保留文件份数 0:全保留。
3.增加日、时、分三种生成文件的方式。
4.isCache:是否启动写缓存
```
#include "nanolog.hpp"
int main() {
// 初始化日志库
string logDir = "./"; // 日志目录
int maxLogFileSize = 100;// 文件最大支持内容,单位:MB
int keepLogCount = 30;// 保留文件份数 0:全保留。
bool isCache = false;// 是否启动写缓存
std::string logFileName ="logback.{yyyy-MM-dd}.log"; // 每天生成一个文件
//std::string logFileName ="logback.{yyyy-MM-dd_HH}.log"; // 每小时生成一个文件
//std::string logFileName ="logback.{yyyy-MM-dd_HH-mm}.log"; // 每分钟生成一个文件
nanolog::Logger::initialize(nanolog::GuaranteedLogger(), logDir, logFileName, maxLogFileSize, keepLogCount, isCache);
log_info << "nanolog v1.1 is initialize success.";
// 打印格式:2023-10-31 22:27:23.250 1 INFO nanolog v1.1 is initialize success. nanolog.hpp:941
}
```
*/
/**
* @Description: 组件-日志库
* @Author: yuyugong
* @Date: 2023-08-31
* @Version: V1.1
*/
#pragma once
#define NANO_LOG(LEVEL) nanolog::Logger() == nanolog::NanoLogLine(LEVEL, __FILE__, __func__, __LINE__)
#define log_debug nanolog::Logger::is_logged(nanolog::LogLevel::DEBUG) && NANO_LOG(nanolog::LogLevel::DEBUG)
#define log_info nanolog::Logger::is_logged(nanolog::LogLevel::INFO) && NANO_LOG(nanolog::LogLevel::INFO)
#define log_warn nanolog::Logger::is_logged(nanolog::LogLevel::WARN) && NANO_LOG(nanolog::LogLevel::WARN)
#define log_crit nanolog::Logger::is_logged(nanolog::LogLevel::CRIT) && NANO_LOG(nanolog::LogLevel::CRIT)
#define log_error nanolog::Logger::is_logged(nanolog::LogLevel::_ERROR) && NANO_LOG(nanolog::LogLevel::_ERROR)
#include <cstdint>
#include <memory>
#include <string>
#include <iosfwd>
#include <type_traits>
#include <cstring>
#include <chrono>
#include <ctime>
#include <thread>
#include <tuple>
#include <atomic>
#include <queue>
#include <fstream>
#include <sstream>
#include <iostream>
#include <functional>
#include <algorithm>
#include <dirent.h>
#define YEAR (1900)
#define MONTH (1)
#define CCT (+8)
namespace nanolog {
class NanoLogger;
class NanoLogger;
inline std::atomic<unsigned int> &get_loglevel() {
static std::atomic<unsigned int> loglevel;
return loglevel;
}
inline std::unique_ptr<NanoLogger> &get_nanologger() {
static std::unique_ptr<NanoLogger> nanologger;
return nanologger;
}
inline std::atomic<NanoLogger *> &get_atomic_nanologger() {
static std::atomic<NanoLogger *> atomic_nanologger;
return atomic_nanologger;
}
class NanologBase {
public:
/* Returns microseconds since epoch */
static uint64_t timestamp_now() {
// return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
return std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()).count();
}
static std::string get_datetime(uint64_t timestamp, const string& format) {
char miliseconds[4];
sprintf(miliseconds, "%03llu", (long long unsigned int) (timestamp % 1000));
std::time_t time_t = timestamp / 1000;
tm *gmtime = std::localtime(&time_t);
std::ostringstream ostr;
if (nullptr == gmtime) {
ostr << miliseconds;
} else {
char datetime[32];
//sprintf(datetime, "%d%02d%02d%02d%02d%02d", gmtime->tm_year+YEAR, gmtime->tm_mon+MONTH,
// gmtime->tm_mday, gmtime->tm_hour + CCT, gmtime->tm_min, gmtime->tm_sec);
if (format == "yyyy-MM-dd") {
sprintf(datetime, "%d-%02d-%02d", gmtime->tm_year + YEAR, gmtime->tm_mon + MONTH, gmtime->tm_mday);
} else if (format == "yyyy-MM-dd HH") {
sprintf(datetime, "%d-%02d-%02d_%02d", gmtime->tm_year + YEAR, gmtime->tm_mon + MONTH, gmtime->tm_mday, gmtime->tm_hour);
} else if (format == "yyyy-MM-dd HH:mm") {
sprintf(datetime, "%d-%02d-%02d_%02d-%02d", gmtime->tm_year + YEAR, gmtime->tm_mon + MONTH, gmtime->tm_mday, gmtime->tm_hour, gmtime->tm_min);
} else {
sprintf(datetime, "%d-%02d-%02d_%02d-%02d-%02d", gmtime->tm_year + YEAR, gmtime->tm_mon + MONTH, gmtime->tm_mday, gmtime->tm_hour, gmtime->tm_min, gmtime->tm_sec);
}
ostr << datetime;
}
return ostr.str();
}
/* I want [2016-10-13 00:01:23.528514] */
static void format_timestamp(std::ostream &os, std::stringstream &strstream, uint64_t timestamp) {
// The next 3 lines do not work on MSVC!
// auto duration = std::chrono::microseconds(timestamp);
// std::chrono::high_resolution_clock::time_point time_point(duration);
// std::time_t time_t = std::chrono::high_resolution_clock::to_time_t(time_point);
char miliseconds[7];
sprintf(miliseconds, "%03llu", (long long unsigned int) (timestamp % 1000));
std::time_t time_t = timestamp / 1000;
tm *gmtime = std::localtime(&time_t);
if (nullptr == gmtime) {
os << '[' << miliseconds << ']';
} else {
char datetime[32];
sprintf(datetime, "%d-%02d-%02d %02d:%02d:%02d", gmtime->tm_year + YEAR, gmtime->tm_mon + MONTH,
gmtime->tm_mday, gmtime->tm_hour, gmtime->tm_min, gmtime->tm_sec);
//os << '[' << datetime << "." << miliseconds << ']';
os << datetime << "." << miliseconds;
strstream << datetime << "." << miliseconds;
}
}
static std::thread::id this_thread_id() {
static thread_local const std::thread::id id = std::this_thread::get_id();
return id;
}
};
enum class LogLevel : uint8_t {
DEBUG, INFO, WARN, CRIT, _ERROR
};
class NanoLogLine {
public:
template<typename T, typename Tuple>
struct TupleIndex;
template<typename T, typename ... Types>
struct TupleIndex<T, std::tuple<T, Types...> > {
static constexpr const std::size_t value = 0;
};
template<typename T, typename U, typename ... Types>
struct TupleIndex<T, std::tuple<U, Types...> > {
static constexpr const std::size_t value = 1 + TupleIndex<T, std::tuple<Types...> >::value;
};
struct string_literal_t {
explicit string_literal_t(char const *s) : m_s(s) {}
char const *m_s;
};
typedef std::tuple<char, uint32_t, uint64_t, int32_t, int64_t, double, NanoLogLine::string_literal_t, char *> SupportedTypes;
NanoLogLine(LogLevel level, char const *file, char const *function, uint32_t line)
: m_bytes_used(0), m_buffer_size(sizeof(m_stack_buffer)) {
encode<uint64_t>(NanologBase::timestamp_now());
encode<std::thread::id>(NanologBase::this_thread_id());
encode<string_literal_t>(string_literal_t(file));
encode<string_literal_t>(string_literal_t(function));
encode<uint32_t>(line);
encode<LogLevel>(level);
}
~NanoLogLine() = default;
NanoLogLine(NanoLogLine &&) = default;
NanoLogLine &operator=(NanoLogLine &&) = default;
// 打印样式
void stringify(std::ostream &os, bool isCache) {
char *b = !m_heap_buffer ? m_stack_buffer : m_heap_buffer.get();
char const *const end = b + m_bytes_used;
uint64_t timestamp = *reinterpret_cast <uint64_t *>(b);
b += sizeof(uint64_t);
std::thread::id threadid = *reinterpret_cast <std::thread::id *>(b);
b += sizeof(std::thread::id);
string_literal_t file = *reinterpret_cast <string_literal_t *>(b);
b += sizeof(string_literal_t);
string_literal_t function = *reinterpret_cast <string_literal_t *>(b);
b += sizeof(string_literal_t);
uint32_t line = *reinterpret_cast <uint32_t *>(b);
b += sizeof(uint32_t);
LogLevel loglevel = *reinterpret_cast <LogLevel *>(b);
b += sizeof(LogLevel);
std::stringstream strstream;
NanologBase::format_timestamp(os, strstream, timestamp);
os << " " << threadid << " " << to_string(loglevel) << " ";
strstream << " " << threadid << " " << to_string(loglevel) << " ";
stringify(os, strstream, b, end);
// 只打印文件名
std::string pathFileName = file.m_s;
std::size_t pos = pathFileName.find_last_of("/\\");
//std::string path = pathFileName.substr(0, pos);
std::string fileName = pathFileName.substr(pos + 1);
//os << " " << file.m_s << ':' << function.m_s << ':' << line << std::endl;
os << " " << fileName << ':' << line << std::endl;
strstream << " " << fileName << ':' << line << std::endl;
//os << " " << fileName << ':' << function.m_s << ':' << line << std::endl;
//strstream << " " << fileName << ':' << function.m_s << ':' << line << std::endl;
// 输出到终端
std::cout << strstream.str();
// 缓存选项为false时实时写,其它为缓存写
if (!isCache) {
os.flush(); // 实时写
}
}
NanoLogLine &operator<<(char arg) {
encode<char>(arg, TupleIndex<char, SupportedTypes>::value);
return *this;
}
NanoLogLine &operator<<(int32_t arg) {
encode<int32_t>(arg, TupleIndex<int32_t, SupportedTypes>::value);
return *this;
}
NanoLogLine &operator<<(uint32_t arg) {
encode<uint32_t>(arg, TupleIndex<uint32_t, SupportedTypes>::value);
return *this;
}
NanoLogLine &operator<<(int64_t arg) {
encode<int64_t>(arg, TupleIndex<int64_t, SupportedTypes>::value);
return *this;
}
NanoLogLine &operator<<(uint64_t arg) {
encode<uint64_t>(arg, TupleIndex<uint64_t, SupportedTypes>::value);
return *this;
}
NanoLogLine &operator<<(double arg) {
encode<double>(arg, TupleIndex<double, SupportedTypes>::value);
return *this;
}
NanoLogLine &operator<<(std::string const &arg) {
encode_c_string(arg.c_str(), arg.length());
return *this;
}
template<size_t N>
NanoLogLine &operator<<(const char(&arg)[N]) {
encode(string_literal_t(arg));
return *this;
}
template<typename Arg>
typename std::enable_if<std::is_same<Arg, char const *>::value, NanoLogLine &>::type
operator<<(Arg const &arg) {
encode(arg);
return *this;
}
template<typename Arg>
typename std::enable_if<std::is_same<Arg, char *>::value, NanoLogLine &>::type
operator<<(Arg const &arg) {
encode(arg);
return *this;
}
private:
char const *to_string(LogLevel loglevel) {
switch (loglevel) {
case LogLevel::DEBUG:
return "DEBUG";
case LogLevel::INFO:
return "INFO";
case LogLevel::WARN:
return "WARN";
case LogLevel::CRIT:
return "CRIT";
case LogLevel::_ERROR:
return "ERROR";
}
return "XXXX";
}
char *buffer() {
return !m_heap_buffer ? &m_stack_buffer[m_bytes_used] : &(m_heap_buffer.get())[m_bytes_used];
}
template<typename Arg>
void encode(Arg arg) {
*reinterpret_cast<Arg *>(buffer()) = arg;
m_bytes_used += sizeof(Arg);
}
template<typename Arg>
void encode(Arg arg, uint8_t type_id) {
resize_buffer_if_needed(sizeof(Arg) + sizeof(uint8_t));
encode<uint8_t>(type_id);
encode<Arg>(arg);
}
void encode(char *arg) {
if (arg != nullptr)
encode_c_string(arg, strlen(arg));
}
void encode(char const *arg) {
if (arg != nullptr)
encode_c_string(arg, strlen(arg));
}
void encode(string_literal_t arg) {
encode<string_literal_t>(arg, TupleIndex<string_literal_t, SupportedTypes>::value);
}
void encode_c_string(char const *arg, size_t length) {
if (length == 0)
return;
resize_buffer_if_needed(1 + length + 1);
char *b = buffer();
auto type_id = TupleIndex<char *, SupportedTypes>::value;
*reinterpret_cast<uint8_t *>(b++) = static_cast<uint8_t>(type_id);
memcpy(b, arg, length + 1);
m_bytes_used += 1 + length + 1;
}
void resize_buffer_if_needed(size_t additional_bytes) {
size_t const required_size = m_bytes_used + additional_bytes;
if (required_size <= m_buffer_size)
return;
if (!m_heap_buffer) {
m_buffer_size = std::max(static_cast<size_t>(512), required_size);
m_heap_buffer.reset(new char[m_buffer_size]);
memcpy(m_heap_buffer.get(), m_stack_buffer, m_bytes_used);
return;
} else {
m_buffer_size = std::max(static_cast<size_t>(2 * m_buffer_size), required_size);
std::unique_ptr<char[]> new_heap_buffer(new char[m_buffer_size]);
memcpy(new_heap_buffer.get(), m_heap_buffer.get(), m_bytes_used);
m_heap_buffer.swap(new_heap_buffer);
}
}
void stringify(std::ostream &os, std::stringstream &strstream, char *start, char const *const end) {
if (start == end)
return;
int type_id = static_cast <int>(*start);
start++;
switch (type_id) {
case 0:
stringify(os, strstream,
decode(os, strstream, start,
static_cast<std::tuple_element<0, SupportedTypes>::type *>(nullptr)),
end);
return;
case 1:
stringify(os, strstream,
decode(os, strstream, start,
static_cast<std::tuple_element<1, SupportedTypes>::type *>(nullptr)),
end);
return;
case 2:
stringify(os, strstream,
decode(os, strstream, start,
static_cast<std::tuple_element<2, SupportedTypes>::type *>(nullptr)),
end);
return;
case 3:
stringify(os, strstream,
decode(os, strstream, start,
static_cast<std::tuple_element<3, SupportedTypes>::type *>(nullptr)),
end);
return;
case 4:
stringify(os, strstream,
decode(os, strstream, start,
static_cast<std::tuple_element<4, SupportedTypes>::type *>(nullptr)),
end);
return;
case 5:
stringify(os, strstream,
decode(os, strstream, start,
static_cast<std::tuple_element<5, SupportedTypes>::type *>(nullptr)),
end);
return;
case 6:
stringify(os, strstream,
decode(os, strstream, start,
static_cast<std::tuple_element<6, SupportedTypes>::type *>(nullptr)),
end);
return;
case 7:
stringify(os, strstream,
decode(os, strstream, start,
static_cast<std::tuple_element<7, SupportedTypes>::type *>(nullptr)),
end);
return;
}
}
template<typename Arg>
char *decode(std::ostream &os, std::stringstream &strstream, char *b, Arg *dummy) {
Arg arg = *reinterpret_cast <Arg *>(b);
os << arg;
strstream << arg;
return b + sizeof(Arg);
}
char *decode(std::ostream &os, std::stringstream &strstream, char *b, NanoLogLine::string_literal_t *dummy) {
NanoLogLine::string_literal_t s = *reinterpret_cast <NanoLogLine::string_literal_t *>(b);
os << s.m_s;
strstream << s.m_s;
return b + sizeof(NanoLogLine::string_literal_t);
}
char *decode(std::ostream &os, std::stringstream &strstream, char *b, char **dummy) {
while (*b != '\0') {
os << *b;
strstream << *b;
++b;
}
return ++b;
}
private:
size_t m_bytes_used;
size_t m_buffer_size;
std::unique_ptr<char[]> m_heap_buffer;
char m_stack_buffer[256 - 2 * sizeof(size_t) - sizeof(decltype(m_heap_buffer)) - 8 /* Reserved */];
};
struct BufferBase {
virtual ~BufferBase() = default;
virtual void push(NanoLogLine &&logline) = 0;
virtual bool try_pop(NanoLogLine &logline) = 0;
};
class SpinLock {
public:
SpinLock(std::atomic_flag &flag) : m_flag(flag) {
while (m_flag.test_and_set(std::memory_order_acquire));
}
~SpinLock() {
m_flag.clear(std::memory_order_release);
}
private:
std::atomic_flag &m_flag;
};
/* Multi Producer Single Consumer Ring Buffer */
class RingBuffer : public BufferBase {
public:
struct alignas(64) Item {
Item()
: written(0), logline(LogLevel::INFO, nullptr, nullptr, 0) {
}
std::atomic_flag flag = ATOMIC_FLAG_INIT;
char written;
char padding[256 - sizeof(std::atomic_flag) - sizeof(char) - sizeof(NanoLogLine)];
NanoLogLine logline;
};
RingBuffer(size_t const size)
: m_size(size), m_ring(static_cast<Item *>(std::malloc(size * sizeof(Item)))), m_write_index(0),
m_read_index(0) {
for (size_t i = 0; i < m_size; ++i) {
new(&m_ring[i]) Item();
}
static_assert(sizeof(Item) == 256, "Unexpected size != 256");
}
~RingBuffer() {
for (size_t i = 0; i < m_size; ++i) {
m_ring[i].~Item();
}
std::free(m_ring);
}
void push(NanoLogLine &&logline) override {
unsigned int write_index = m_write_index.fetch_add(1, std::memory_order_relaxed) % m_size;
Item &item = m_ring[write_index];
SpinLock spinlock(item.flag);
item.logline = std::move(logline);
item.written = 1;
}
bool try_pop(NanoLogLine &logline) override {
Item &item = m_ring[m_read_index % m_size];
SpinLock spinlock(item.flag);
if (item.written == 1) {
logline = std::move(item.logline);
item.written = 0;
++m_read_index;
return true;
}
return false;
}
RingBuffer(RingBuffer const &) = delete;
RingBuffer &operator=(RingBuffer const &) = delete;
private:
size_t const m_size;
Item *m_ring;
std::atomic<unsigned int> m_write_index;
char pad[64];
unsigned int m_read_index;
};
class Buffer {
public:
struct Item {
Item(NanoLogLine &&nanologline) : logline(std::move(nanologline)) {}
char padding[256 - sizeof(NanoLogLine)];
NanoLogLine logline;
};
static constexpr const size_t size = 32768; // 8MB. Helps reduce memory fragmentation
Buffer() : m_buffer(static_cast<Item *>(std::malloc(size * sizeof(Item)))) {
for (size_t i = 0; i <= size; ++i) {
m_write_state[i].store(0, std::memory_order_relaxed);
}
static_assert(sizeof(Item) == 256, "Unexpected size != 256");
}
~Buffer() {
unsigned int write_count = m_write_state[size].load();
for (size_t i = 0; i < write_count; ++i) {
m_buffer[i].~Item();
}
std::free(m_buffer);
}
// Returns true if we need to switch to next buffer
bool push(NanoLogLine &&logline, unsigned int const write_index) {
new(&m_buffer[write_index]) Item(std::move(logline));
m_write_state[write_index].store(1, std::memory_order_release);
return m_write_state[size].fetch_add(1, std::memory_order_acquire) + 1 == size;
}
bool try_pop(NanoLogLine &logline, unsigned int const read_index) {
if (m_write_state[read_index].load(std::memory_order_acquire)) {
Item &item = m_buffer[read_index];
logline = std::move(item.logline);
return true;
}
return false;
}
Buffer(Buffer const &) = delete;
Buffer &operator=(Buffer const &) = delete;
private:
Item *m_buffer;
std::atomic<unsigned int> m_write_state[size + 1];
};
class QueueBuffer : public BufferBase {
public:
QueueBuffer(QueueBuffer const &) = delete;
QueueBuffer &operator=(QueueBuffer const &) = delete;
QueueBuffer() : m_current_read_buffer{nullptr}, m_write_index(0), m_read_index(0) {
setup_next_write_buffer();
}
void push(NanoLogLine &&logline) override {
unsigned int write_index = m_write_index.fetch_add(1, std::memory_order_relaxed);
if (write_index < Buffer::size) {
if (m_current_write_buffer.load(std::memory_order_acquire)->push(std::move(logline), write_index)) {
setup_next_write_buffer();
}
} else {
while (m_write_index.load(std::memory_order_acquire) >= Buffer::size);
push(std::move(logline));
}
}
bool try_pop(NanoLogLine &logline) override {
if (m_current_read_buffer == nullptr)
m_current_read_buffer = get_next_read_buffer();
Buffer *read_buffer = m_current_read_buffer;
if (read_buffer == nullptr)
return false;
if (bool success = read_buffer->try_pop(logline, m_read_index)) {
m_read_index++;
if (m_read_index == Buffer::size) {
m_read_index = 0;
m_current_read_buffer = nullptr;
SpinLock spinlock(m_flag);
m_buffers.pop();
}
return true;
}
return false;
}
private:
void setup_next_write_buffer() {
std::unique_ptr<Buffer> next_write_buffer(new Buffer());
m_current_write_buffer.store(next_write_buffer.get(), std::memory_order_release);
SpinLock spinlock(m_flag);
m_buffers.push(std::move(next_write_buffer));
m_write_index.store(0, std::memory_order_relaxed);
}
Buffer *get_next_read_buffer() {
SpinLock spinlock(m_flag);
return m_buffers.empty() ? nullptr : m_buffers.front().get();
}
private:
std::queue<std::unique_ptr<Buffer> > m_buffers;
std::atomic<Buffer *> m_current_write_buffer;
Buffer *m_current_read_buffer;
std::atomic<unsigned int> m_write_index;
unsigned int m_read_index;
std::atomic_flag m_flag = ATOMIC_FLAG_INIT;
};
class FileWriter {
public:
// 判断是否是文件夹
inline bool is_folder(const char* dir_name){
auto dir =opendir(dir_name);
if(dir){
closedir(dir);
return true;
}
return false;
}
inline char file_sepator(){
return '/';
}
/*
* 列出指定目录的所有文件(不包含目录)执行,对每个文件执行filter过滤器,
* filter返回true时将文件名全路径加入std::vector
* sub为true时为目录递归
* 返回每个文件的全路径名
*/
std::vector<std::string> for_each_file(const std::string&dir_name,bool sub=false){
std::vector<std::string> v;
auto dir =opendir(dir_name.data());
struct dirent *ent;
if(dir){
while ((ent = readdir (dir)) != NULL) {
auto p = std::string(dir_name).append({ "/" }).append(ent->d_name);
//auto p = std::string(dir_name).append({ "/" }).append(ent->d_name);
if(sub){
if ( 0== strcmp (ent->d_name, "..") || 0 == strcmp (ent->d_name, ".")){
continue;
}else if(is_folder(p.c_str())){
auto r= for_each_file(p,sub);
v.insert(v.end(),r.begin(),r.end());
continue;
}
}
//如果是文件
if (sub||!is_folder(p.c_str())){
//if(filter(dir_name.data(),ent->d_name))
v.emplace_back(p);
}
}
closedir(dir);
}
return v;
}
// 获取文件大小
std::streampos fileSize( const char* filePath ){
std::streampos fsize = 0;
std::ifstream file( filePath, std::ios::binary );
fsize = file.tellg();
file.seekg( 0, std::ios::end );
fsize = file.tellg() - fsize;
file.close();
return fsize;
}
FileWriter(std::string const &log_directory, std::string const &log_file_name, uint32_t log_file_roll_size_mb)
: m_log_file_roll_size_bytes(log_file_roll_size_mb * 1024 * 1024),
m_name(log_directory + log_file_name) {
// 判断文件存在
std::string file_name = get_real_logfilename();
std::streampos fsize = fileSize(file_name.c_str());
//std::cout << file_name << " fsize: "<<fsize <<std::endl;
while (fsize>(log_file_roll_size_mb * 1024 * 1024)) {
file_name = get_real_logfilename();
fsize = fileSize(file_name.c_str());
//std::cout << file_name << " fsize: "<<fsize <<std::endl;
}
//std::cout << file_name << " fsize: "<<fsize <<std::endl;
m_os.reset(new std::ofstream());
m_os->open(file_name, std::ofstream::out | std::ofstream::app);
}
void write(NanoLogLine &logline, bool isCache, std::string& log_directory, int keepLogCount) {
// 判断是否同一天
//std::cout << "m_name:" << m_name << std::endl;
std::string date_time = "";
if (m_name.find("{yyyy-MM-dd}") != string::npos) {
date_time = NanologBase::get_datetime(NanologBase::timestamp_now(), "yyyy-MM-dd");
} else if (m_name.find("{yyyy-MM-dd_HH}") != string::npos) {
date_time = NanologBase::get_datetime(NanologBase::timestamp_now(), "yyyy-MM-dd HH");
} else if (m_name.find("{yyyy-MM-dd_HH-mm}") != string::npos) {
date_time = NanologBase::get_datetime(NanologBase::timestamp_now(), "yyyy-MM-dd HH:mm");
} else {
throw runtime_error("文件格式错误");
}
//std::cout << "date_time:" << date_time << ",m_date_time:" << m_date_time << std::endl;
// 名称不相同时,创建新文件
if (date_time.compare(m_date_time) != 0) {
//std::cout << "create_file..." << std::endl;
// 创建新的文件
create_file();
// 保留文件份数 0:全保留
if (keepLogCount != 0) {
std::vector<std::string> vector = for_each_file(log_directory,false);
if (vector.size() > keepLogCount) {
std::sort(vector.begin(),vector.end());
for (int i=0; i < (vector.size()-keepLogCount); i++) {
//std::cout << "删除,del,i:" << i << ",vector[i]:" << vector[i] << std::endl;
if (remove(vector[i].c_str()) != 0) {
std::cout << "Failed to delete file." << vector[i] << std::endl;
}
}
}
}
}
// 正常写日志逻辑
// auto pos = m_os->tellp();
// logline.stringify(*m_os, isCache);
// m_bytes_written += m_os->tellp() - pos;
// if (m_bytes_written > m_log_file_roll_size_bytes) {
// roll_file();
// }
// 支持写日志最大MB,超出不写日志
if (m_bytes_written < m_log_file_roll_size_bytes) {
auto pos = m_os->tellp();
logline.stringify(*m_os, isCache);
m_bytes_written += m_os->tellp() - pos;
}
}
private:
std::string get_real_logfilename() {
std::string log_file_name = m_name;
//std::cout << "m_name:" << m_name << std::endl;
std::string date_time = "";
if (log_file_name.find("{yyyy-MM-dd}") != string::npos) {
date_time = NanologBase::get_datetime(NanologBase::timestamp_now(), "yyyy-MM-dd");
log_file_name.replace(log_file_name.find("{yyyy-MM-dd}"),strlen("{yyyy-MM-dd}"),date_time);
} else if (log_file_name.find("{yyyy-MM-dd_HH}") != string::npos) {
date_time = NanologBase::get_datetime(NanologBase::timestamp_now(), "yyyy-MM-dd HH");
log_file_name.replace(log_file_name.find("{yyyy-MM-dd_HH}"),strlen("{yyyy-MM-dd_HH}"),date_time);
} else if (log_file_name.find("{yyyy-MM-dd_HH-mm}") != string::npos) {
date_time = NanologBase::get_datetime(NanologBase::timestamp_now(), "yyyy-MM-dd HH:mm");
log_file_name.replace(log_file_name.find("{yyyy-MM-dd_HH-mm}"),strlen("{yyyy-MM-dd_HH-mm}"),date_time);
} else {
throw runtime_error("文件格式错误");
}
m_date_time = date_time;
//std::cout << "log_file_name:" << log_file_name << std::endl;
return log_file_name;
}
void create_file() {
m_file_number = 0;
m_bytes_written = 0;
std::string file_name = get_real_logfilename();
m_os.reset(new std::ofstream());
m_os->open(file_name, std::ofstream::out | std::ofstream::app);
}
void roll_file() {
if (m_os) {
m_os->flush();
m_os->close();
}
m_bytes_written = 0;
std::string file_name = get_real_logfilename();
m_os.reset(new std::ofstream());
m_os->open(file_name, std::ofstream::out | std::ofstream::trunc);
}
private:
uint32_t m_file_number = 0;
std::streamoff m_bytes_written = 0;
uint32_t const m_log_file_roll_size_bytes;
std::string const m_name;
std::unique_ptr<std::ofstream> m_os;
std::string m_date_time = "";
};
/*
* Non guaranteed logging. Uses a ring buffer to hold log lines.
* When the ring gets full, the previous log line in the slot will be dropped.
* Does not block producer even if the ring buffer is full.
* ring_buffer_size_mb - LogLines are pushed into a mpsc ring buffer whose size
* is determined by this parameter. Since each LogLine is 256 bytes,
* ring_buffer_size = ring_buffer_size_mb * 1024 * 1024 / 256
*/
struct NonGuaranteedLogger {
NonGuaranteedLogger(uint32_t ring_buffer_size_mb_) : ring_buffer_size_mb(ring_buffer_size_mb_) {}
uint32_t ring_buffer_size_mb;
};
/*
* Provides a guarantee log lines will not be dropped.
*/
struct GuaranteedLogger {
};
class NanoLogger {
public:
NanoLogger(NonGuaranteedLogger ngl, std::string const &log_directory, std::string const &log_file_name,
uint32_t log_file_roll_size_mb)
: m_state(State::INIT), m_buffer_base(new RingBuffer(std::max(1u, ngl.ring_buffer_size_mb) * 1024 * 4)),
m_file_writer(log_directory, log_file_name, std::max(1u, log_file_roll_size_mb)),
m_thread(&NanoLogger::pop, this) {
m_state.store(State::READY, std::memory_order_release);
}
// NanoLogger(GuaranteedLogger gl, std::string const &log_directory, std::string const &log_file_name,
// uint32_t log_file_roll_size_mb)
// : m_state(State::INIT), m_buffer_base(new QueueBuffer()),
// m_file_writer(log_directory, log_file_name, std::max(1u, log_file_roll_size_mb)),
// m_thread(&NanoLogger::pop, this) {
// m_state.store(State::READY, std::memory_order_release);
// }
NanoLogger(GuaranteedLogger gl, std::string const &log_directory, std::string const &log_file_name,
uint32_t log_file_roll_size_mb, int keepLogCount, bool isCache)
: m_state(State::INIT), m_buffer_base(new QueueBuffer()),
m_file_writer(log_directory, log_file_name, std::max(1u, log_file_roll_size_mb)),
m_thread(&NanoLogger::pop, this) {
m_log_directory = log_directory;
m_keepLogCount = keepLogCount;
m_isCache = isCache;
m_state.store(State::READY, std::memory_order_release);
}
~NanoLogger() {
m_state.store(State::SHUTDOWN);
m_thread.join();
}
void add(NanoLogLine &&logline) {
m_buffer_base->push(std::move(logline));
}
void pop() {
// Wait for constructor to complete and pull all stores done there to this thread / core.
while (m_state.load(std::memory_order_acquire) == State::INIT)
std::this_thread::sleep_for(std::chrono::microseconds(50));
NanoLogLine logline(LogLevel::INFO, nullptr, nullptr, 0);
while (m_state.load() == State::READY) {
if (m_buffer_base->try_pop(logline))
m_file_writer.write(logline, m_isCache, m_log_directory, m_keepLogCount);
else
std::this_thread::sleep_for(std::chrono::microseconds(50));
}
// Pop and log all remaining entries
while (m_buffer_base->try_pop(logline)) {
m_file_writer.write(logline, m_isCache, m_log_directory, m_keepLogCount);
}
}
private:
enum class State {
INIT,
READY,
SHUTDOWN
};
std::atomic<State> m_state;
std::unique_ptr<BufferBase> m_buffer_base;
FileWriter m_file_writer;
std::thread m_thread;
bool m_isCache;
std::string m_log_directory;
int m_keepLogCount;
};
// 对外接口类
class Logger{
public:
/*
* Ensure initialize() is called prior to any log statements.
* log_directory - where to create the logs. For example - "/tmp/"
* log_file_name - root of the file name. For example - "nanolog"
* This will create log files of the form -
* /tmp/nanolog.1.txt
* /tmp/nanolog.2.txt
* etc.
* log_file_roll_size_mb - mega bytes after which we roll to next log file.
*/
static void initialize(GuaranteedLogger gl, std::string const &logDirectory, std::string const &log_file_name,
uint32_t log_file_roll_size_mb, int keepLogCount, bool isCache) {
string log_directory = logDirectory;
if (*(--log_directory.end()) != '/' || *(--log_directory.end()) != '\\') {
log_directory += "/";
}
//cout << "log_directory:" << log_directory <<endl;
get_nanologger().reset(new NanoLogger(gl, log_directory, log_file_name, log_file_roll_size_mb, keepLogCount, isCache));
get_atomic_nanologger().store(get_nanologger().get(), std::memory_order_seq_cst);
log_info << "nanolog v1.1 is initialize success.";
}
static void
initialize(NonGuaranteedLogger ngl, std::string const &logDirectory, std::string const &log_file_name,
uint32_t log_file_roll_size_mb) {
string log_directory = logDirectory;
if (*(--log_directory.end()) != '/' || *(--log_directory.end()) != '\\') {
log_directory += "/";
}
get_nanologger().reset(new NanoLogger(ngl, log_directory, log_file_name, log_file_roll_size_mb));
get_atomic_nanologger().store(get_nanologger().get(), std::memory_order_seq_cst);
}
static void set_log_level(LogLevel level) {
get_loglevel().store(static_cast<unsigned int>(level), std::memory_order_release);
}
public:
static bool is_logged(LogLevel level) {
return static_cast<unsigned int>(level) >= get_loglevel().load(std::memory_order_relaxed);
}
bool operator==(NanoLogLine &logline) {
get_atomic_nanologger().load(std::memory_order_acquire)->add(std::move(logline));
return true;
}
};
} // namespace nanolog
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/yuyugong/nanolog.git
[email protected]:yuyugong/nanolog.git
yuyugong
nanolog
nanolog
master

搜索帮助