From 7a17ff02113664e466ba8cd35e6d2ae0049f0bc9 Mon Sep 17 00:00:00 2001
From: i7-8700 <1281261856@qq.com>
Date: Mon, 23 Sep 2019 22:44:27 +0800
Subject: [PATCH] basic log

---
 jlib/base/logging.h                    | 101 ++++++++++++++++++++-----
 jlib/base/logstream.h                  |  13 ++--
 jlib/base/timezone.h                   |  44 +++++------
 test/test_logging/test_logging.cpp     |  12 +++
 test/test_logging/test_logging.vcxproj |   1 +
 5 files changed, 126 insertions(+), 45 deletions(-)

diff --git a/jlib/base/logging.h b/jlib/base/logging.h
index d3d6d5b..de1a46d 100644
--- a/jlib/base/logging.h
+++ b/jlib/base/logging.h
@@ -3,14 +3,13 @@
 #include "config.h"
 #include "logstream.h"
 #include "timestamp.h"
+#include "timezone.h"
 #include "currentthread.h"
 #include <stdlib.h> // getenv
 
 namespace jlib
 {
 
-class TimeZone;
-
 class Logger
 {
 public:
@@ -29,13 +28,21 @@ public:
 	{
 		template <int N>
 		SourceFile(const char(&arr)[N]) : data_(arr), size_(N - 1) {
+#ifdef JLIB_WINDOWS
+			const char* slash = strrchr(data_, '\\');
+#else
 			const char* slash = strrchr(data_, '/');
+#endif
 			if (slash) { data_ = slash + 1; }
 			size_ -= static_cast<int>(data_ - arr);
 		}
 
 		explicit SourceFile(const char* filename) : data_(filename) {
-			const char* slash = strrchr(filename, '/');
+#ifdef JLIB_WINDOWS
+			const char* slash = strrchr(data_, '\\');
+#else
+			const char* slash = strrchr(data_, '/');
+#endif
 			if (slash) { data_ = slash + 1; }
 			size_ = static_cast<int>(strlen(data_));
 		}
@@ -44,19 +51,29 @@ public:
 		int size_;
 	};
 
-	Logger(SourceFile file, int line);
-	Logger(SourceFile file, int line, LogLevel level);
-	Logger(SourceFile file, int line, LogLevel level, const char* func);
-	Logger(SourceFile file, int line, bool toAbort);
-	~Logger();
+	Logger(SourceFile file, int line) : impl_(LogLevel::LOGLEVEL_INFO, 0, file, line) {}
+	Logger(SourceFile file, int line, LogLevel level) : impl_(level, 0, file, line) {}
+	Logger(SourceFile file, int line, LogLevel level, const char* func) : impl_(level, 0, file, line) { impl_.stream_ << func << ' '; }
+	Logger(SourceFile file, int line, bool toAbort) : impl_(toAbort ? LogLevel::LOGLEVEL_FATAL : LOGLEVEL_ERROR, errno, file, line) {}
+
+	~Logger() {
+		impl_.finish();
+		const auto& buf = impl_.stream_.buffer();
+		outputFunc_(buf.data(), buf.length());
+		if (impl_.level_ == LOGLEVEL_FATAL) {
+			flushFunc_();
+			abort();
+		}
+	}
 
 	LogStream& stream() { return impl_.stream_; }
 
 	static LogLevel logLevel() { return logLevel_; }
-	static void setLogLevel(LogLevel level);
+	static void setLogLevel(LogLevel level) { logLevel_ = level; }
 
 	typedef void (*OutputFunc)(const char* msg, int len);
 	typedef void (*FlushFunc)();
+
 	static void setOutput(OutputFunc);
 	static void setFlush(FlushFunc);
 	static void setTimeZone(const TimeZone& tz);
@@ -79,24 +96,41 @@ private:
 		SourceFile basename_;
 	};
 
+	static OutputFunc outputFunc_;
+	static FlushFunc flushFunc_;
 	static LogLevel logLevel_;
+	static TimeZone timeZone_;
 
 	Impl impl_;
 };
 
 
-static void defaultOutput(const char* msg, int len) { fwrite(msg, 1, len, stdout); }
-static void defaultFlush() { fflush(stdout); }
+/******** log micros *********/
 
-static Logger::OutputFunc g_output = defaultOutput;
-static Logger::FlushFunc g_flush = defaultFlush;
+#define LOG_TRACE if (jlib::Logger::logLevel() <= jlib::Logger::LogLevel::LOGLEVEL_TRACE) \
+	jlib::Logger(__FILE__, __LINE__, jlib::Logger::LogLevel::LOGLEVEL_TRACE, __func__).stream()
+
+#define LOG_DEBUG if (jlib::Logger::logLevel() <= jlib::Logger::LogLevel::LOGLEVEL_DEBUG) \
+	jlib::Logger(__FILE__, __LINE__, jlib::Logger::LogLevel::LOGLEVEL_DEBUG, __func__).stream()
+
+#define LOG_INFO if (jlib::Logger::logLevel() <= jlib::Logger::LogLevel::LOGLEVEL_INFO) \
+	jlib::Logger(__FILE__, __LINE__, jlib::Logger::LogLevel::LOGLEVEL_INFO).stream()
+
+
+#define LOG_WARN jlib::Logger(__FILE__, __LINE__, jlib::Logger::LogLevel::LOGLEVEL_WARN).stream()
+#define LOG_ERROR jlib::Logger(__FILE__, __LINE__, jlib::Logger::LogLevel::LOGLEVEL_ERROR).stream()
+#define LOG_FATAL jlib::Logger(__FILE__, __LINE__, jlib::Logger::LogLevel::LOGLEVEL_FATAL).stream()
+#define LOG_SYSERR jlib::Logger(__FILE__, __LINE__, false).stream()
+#define LOG_SYSFATAL jlib::Logger(__FILE__, __LINE__, true).stream()
 
 
 namespace detail
 {
 
-static const char* LogLevelName[Logger::LogLevel::LOGLEVEL_COUNT] =
-{
+static void defaultOutput(const char* msg, int len) { fwrite(msg, 1, len, stdout); }
+static void defaultFlush() { fflush(stdout); }
+
+static const char* LogLevelName[Logger::LogLevel::LOGLEVEL_COUNT] = {
 	"TRACE ",
 	"DEBUG ",
 	"INFO  ",
@@ -131,11 +165,20 @@ struct T
 thread_local char t_errnobuf[512] = { 0 };
 thread_local char t_time[64] = { 0 };
 thread_local time_t t_lastSecond = 0;
+static constexpr unsigned int T_TIME_STR_LEN = 17;
 
 } // detail
 
 
+/******** static initializer *********/
+
+Logger::OutputFunc Logger::outputFunc_ = detail::defaultOutput;
+Logger::FlushFunc Logger::flushFunc_ = detail::defaultFlush;
 Logger::LogLevel Logger::logLevel_ = detail::initLogLevel();
+TimeZone Logger::timeZone_ = {};
+
+
+/******** LogStream operators *********/
 
 inline LogStream& operator<<(LogStream& s, const Logger::SourceFile& f) {
 	s.append(f.data_, f.size_); return s;
@@ -151,6 +194,8 @@ static const char* strerror_t(int savedErrno) {
 }
 
 
+/******** Logger::Impl *********/
+
 Logger::Impl::Impl(LogLevel level, int old_errno, const SourceFile& file, int line)
 	: time_(Timestamp::now())
 	, stream_()
@@ -170,12 +215,34 @@ Logger::Impl::Impl(LogLevel level, int old_errno, const SourceFile& file, int li
 void Logger::Impl::formatTime()
 {
 	int64_t microSecsSinceEpoch = time_.microSecondsSinceEpoch();
-	time_t seconds = static_cast<time_t>(microSecsSinceEpoch / Timestamp::MICRO_SECONDS_PER_SECOND);
-	int microsecs = static_cast<int>(microSecsSinceEpoch % Timestamp::MICRO_SECONDS_PER_SECOND);
+	time_t seconds = static_cast<time_t>(microSecsSinceEpoch / MICRO_SECONDS_PER_SECOND);
+	int microsecs = static_cast<int>(microSecsSinceEpoch % MICRO_SECONDS_PER_SECOND);
 	if (seconds != detail::t_lastSecond) {
 		detail::t_lastSecond = seconds;
+		struct tm tm_time;
+		if (timeZone_.valid()) {
+			// TODO
+		} else {
+			gmtime_r(&seconds, &tm_time);
+		}
 
+		int len = snprintf(detail::t_time, sizeof(detail::t_time), "%4d%02d%02d %02d:%02d:%02d",
+						   tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
+						   tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
+		assert(len == detail::T_TIME_STR_LEN); (void)len;
 	}
+
+	if (timeZone_.valid()) {
+		// TODO
+	} else {
+		Format us(".%06dZ ", microsecs); assert(us.length() == 9);
+		stream_ << detail::T(detail::t_time, detail::T_TIME_STR_LEN) << detail::T(us.data(), 9);
+	}
+}
+
+void Logger::Impl::finish()
+{
+	stream_ << " - " << basename_ << ':' << line_ << '\n';
 }
 
 
diff --git a/jlib/base/logstream.h b/jlib/base/logstream.h
index 6946d9c..2837d0f 100644
--- a/jlib/base/logstream.h
+++ b/jlib/base/logstream.h
@@ -346,14 +346,15 @@ static std::string formatSI(int64_t s)
 */
 static std::string formatIEC(int64_t s)
 {
+	static constexpr double Ki = 1024.0;
+	static constexpr double Mi = Ki * 1024.0;
+	static constexpr double Gi = Mi * 1024.0;
+	static constexpr double Ti = Gi * 1024.0;
+	static constexpr double Pi = Ti * 1024.0;
+	static constexpr double Ei = Pi * 1024.0;
+
     double n = static_cast<double>(s);
     char buf[64];
-    const double Ki = 1024.0;
-    const double Mi = Ki * 1024.0;
-    const double Gi = Mi * 1024.0;
-    const double Ti = Gi * 1024.0;
-    const double Pi = Ti * 1024.0;
-    const double Ei = Pi * 1024.0;
 
     if (n < Ki)
         snprintf(buf, sizeof buf, "%" PRId64, s);
diff --git a/jlib/base/timezone.h b/jlib/base/timezone.h
index 7bec454..b56ef70 100644
--- a/jlib/base/timezone.h
+++ b/jlib/base/timezone.h
@@ -71,46 +71,46 @@ struct LocalTime
 static constexpr int SECONDS_PER_DAY = 24 * 60 * 60;
     
 //! TimeZone for 1970~2030
-class Timezone : public copyable
+class TimeZone : public copyable
 {
 public:
-	explicit Timezone(const char* zonename) {
+	explicit TimeZone(const char* zonename) {
 
 	}
 
-	Timezone(int bias, const char* tzname) {
+	TimeZone(int bias, const char* tzname) {
 
 	}
 
-    Timezone() = default; // invalid timezone
+    TimeZone() = default; // invalid timezone
 
     bool valid() const{
         return static_cast<bool>(data_);
     }
 
-	struct tm toLocalTime(time_t secondsSinceEpoch) const {
+	//struct tm toLocalTime(time_t secondsSinceEpoch) const {
 
-	}
+	//}
 
-	time_t fromLocalTime(const struct tm& tm_time) const {
+	//time_t fromLocalTime(const struct tm& tm_time) const {
 
-	}
+	//}
 
-	static struct tm toUtcTime(time_t secondsSinceEpoch, bool yday = false);
-	static time_t fromUtcTime(const struct tm& tm_time);
-	
-	/**
-	* @param year [1900, 2500]
-	* @param month [1, 12]
-	* @param day [1, 31]
-	* @param hour [0, 23]
-	* @param minute [0, 59]
-	* @param seconds [0, 59]
-	*/
-	static time_t fromUtcTime(int year, int month, int day,
-							  int hour, int minute, int seconds) {
+	//static struct tm toUtcTime(time_t secondsSinceEpoch, bool yday = false);
+	//static time_t fromUtcTime(const struct tm& tm_time);
+	//
+	///**
+	//* @param year [1900, 2500]
+	//* @param month [1, 12]
+	//* @param day [1, 31]
+	//* @param hour [0, 23]
+	//* @param minute [0, 59]
+	//* @param seconds [0, 59]
+	//*/
+	//static time_t fromUtcTime(int year, int month, int day,
+	//						  int hour, int minute, int seconds) {
 
-	}
+	//}
 
 	struct Data {
 		std::vector<detail::Transition> transitions;
diff --git a/test/test_logging/test_logging.cpp b/test/test_logging/test_logging.cpp
index 78a3a91..b3014d4 100644
--- a/test/test_logging/test_logging.cpp
+++ b/test/test_logging/test_logging.cpp
@@ -1,6 +1,18 @@
 #include "../../jlib/base/logging.h"
 
+using namespace jlib;
+
 int main()
 {
+	Logger::setLogLevel(Logger::LOGLEVEL_TRACE);
 
+	LOG_TRACE << "trace";
+	LOG_DEBUG << "debug";
+	LOG_INFO << "Hello";
+	LOG_WARN << "World";
+	LOG_ERROR << "Error";
+	LOG_INFO << sizeof(Logger);
+	LOG_INFO << sizeof(LogStream);
+	LOG_INFO << sizeof(Format);
+	LOG_INFO << sizeof(LogStream::Buffer);
 }
\ No newline at end of file
diff --git a/test/test_logging/test_logging.vcxproj b/test/test_logging/test_logging.vcxproj
index 512b765..22693e6 100644
--- a/test/test_logging/test_logging.vcxproj
+++ b/test/test_logging/test_logging.vcxproj
@@ -88,6 +88,7 @@
       <Optimization>Disabled</Optimization>
       <SDLCheck>true</SDLCheck>
       <ConformanceMode>true</ConformanceMode>
+      <AdditionalIncludeDirectories>$(BOOST);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <SubSystem>Console</SubSystem>
-- 
2.18.1