【原创】windows api系统打印机打印JPG图片

使用windows api调用系统打印机打印图片,基本思路是获得打印机的DC然后在DC上绘制。

好处

  • 不需要依赖厂家提供的库

关键点

  • 打印机脱机状态判断
  • 打印机的纸张尺寸

源码

// syscallprinter.cpp : 定义 DLL 的初始化例程。
//

#include "stdafx.h"
#include "syscallprinter.h"

#include "fn_log.h"

#include <atlimage.h>

#pragma comment(lib, "winspool.lib")
#include <winspool.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//
//TODO:  如果此 DLL 相对于 MFC DLL 是动态链接的,
//		则从此 DLL 导出的任何调入
//		MFC 的函数必须将 AFX_MANAGE_STATE 宏添加到
//		该函数的最前面。
//
//		例如: 
//
//		extern "C" BOOL PASCAL EXPORT ExportedFunction()
//		{
//			AFX_MANAGE_STATE(AfxGetStaticModuleState());
//			// 此处为普通函数体
//		}
//
//		此宏先于任何 MFC 调用
//		出现在每个函数中十分重要。  这意味着
//		它必须作为函数中的第一个语句
//		出现,甚至先于所有对象变量声明,
//		这是因为它们的构造函数可能生成 MFC
//		DLL 调用。
//
//		有关其他详细信息,
//		请参阅 MFC 技术说明 33 和 58。
//

// CsyscallprinterApp

BEGIN_MESSAGE_MAP(CsyscallprinterApp, CWinApp)
END_MESSAGE_MAP()


// CsyscallprinterApp 构造

CsyscallprinterApp::CsyscallprinterApp()
{
	// TODO:  在此处添加构造代码,
	// 将所有重要的初始化放置在 InitInstance 中
}


// 唯一的一个 CsyscallprinterApp 对象

CsyscallprinterApp theApp;


// CsyscallprinterApp 初始化

BOOL CsyscallprinterApp::InitInstance()
{
	CWinApp::InitInstance();

	return TRUE;
}

bool bLogInited = false;

void logInit() {
	if (bLogInited)
		return;

	int ret = FNLog::LoadAndStartDefaultLogger("./log.yaml");
	if (ret != 0)
	{
		return;
	}

	bLogInited = true;
}

std::string GetFormatTime()
{
	time_t currentTime;
	time(&currentTime);
	tm* t_tm = localtime(&currentTime);

	char formatTime[64] = { 0 };
	snprintf(formatTime, 64, "%04d%02d%02d%02d%02d%02d",
		t_tm->tm_year + 1900,
		t_tm->tm_mon + 1,
		t_tm->tm_mday,
		t_tm->tm_hour,
		t_tm->tm_min,
		t_tm->tm_sec);
	return std::string(formatTime);
}

int __stdcall PrintImage(char* szPrinterName, char* szDriverName, char* szImageFile, char *szOutBuf)
{
	logInit();
	LogInfo() << "params: szPrinterName=[" << szPrinterName << "], " << "szDriverName=[" << szDriverName << "], szImageFile=[" << szImageFile << "]";

	int ret = 0;
	char szOutBufTmp[256] = { 0 };
	HANDLE   hPrinter;
	DOCINFO docInfo;
	DWORD   dwJob;
	DWORD   dwBytesWritten;
	BOOL bRet = FALSE;
	char szDocName[256] = { 0 };

	
	sprintf(szDocName, "%s_%s", GetFormatTime().c_str(), szImageFile);

	// open printer
	if (!OpenPrinter(szPrinterName, &hPrinter, NULL)) {
		int lastErrorCode = GetLastError();
		LogWarn() << "open printer err, " << lastErrorCode;
		sprintf(szOutBufTmp, "open printer err, %d", lastErrorCode);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 11; // 打开打印机失败
		return ret;
	}

	// get print info
	DWORD pcbNeeded = 0;

	bRet = GetPrinter(hPrinter, 2, NULL, 0, &pcbNeeded);
	DWORD dwErrorCode = ::GetLastError();
	if (dwErrorCode != ERROR_INSUFFICIENT_BUFFER) {
		LogWarn() << "GetPrinter err" << dwErrorCode;;
		sprintf(szOutBufTmp, "GetPrinter err, %d", dwErrorCode);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}

	// get printer info
	PRINTER_INFO_2* printer_info = NULL;
	printer_info = (PRINTER_INFO_2*)malloc(pcbNeeded);
	bRet = GetPrinter(hPrinter, 2, (LPBYTE)printer_info, pcbNeeded, &pcbNeeded);
	if (!bRet) {
		int lastErrorCode = GetLastError();
		LogWarn() << "GetPrinter err, " << lastErrorCode;
		sprintf(szOutBufTmp, "StartDoc err, %d", lastErrorCode);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}
	// offline check
	if ((printer_info->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE) > 0) {
		LogWarn() << "printer offline";
		sprintf(szOutBufTmp, "printer offline");
		strcpy(szOutBuf, szOutBufTmp);
		ret = 12;
		return ret;
	}

	// create printer dc
	HDC hDC = CreateDC(szDriverName, szPrinterName, NULL, NULL);

	// draw image to printer dc
	CDC* pDC = NULL;
	pDC = CDC::FromHandle(hDC);
	pDC->m_bPrinting = TRUE;

	// set print info
	::ZeroMemory(&docInfo, sizeof(DOCINFO));
	docInfo.cbSize = sizeof(DOCINFO);
	docInfo.lpszDocName = _T(szDocName);

	ret = pDC->StartDoc(&docInfo);
	if (ret <= 0) {
		int lastErrorCode = GetLastError();
		LogWarn() << "StartDoc err, " << lastErrorCode;
		sprintf(szOutBufTmp, "StartDoc err, %d", lastErrorCode);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}
	ret = pDC->StartPage();
	if (ret <= 0) {
		int lastErrorCode = GetLastError();
		LogWarn() << "StartPage err, " << lastErrorCode;
		sprintf(szOutBufTmp, "StartPage err, %d", lastErrorCode);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}

	// load image
	CImage img;
	img.Load(szImageFile);

	if (img.IsNull())
	{
		LogWarn() << "image file[" << szImageFile << "] load err";
		sprintf(szOutBufTmp, "image file [%s] load err", szImageFile);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}

	// get page size
	int nPageWidth = pDC->GetDeviceCaps(PHYSICALWIDTH);
	int nPageLength = pDC->GetDeviceCaps(PHYSICALHEIGHT);
	LogInfo() << "device: [" << szPrinterName << "], pageWidth=" << nPageWidth << "px, pageLength=" << nPageLength <<"px";
	// draw image to dc
	bRet = img.Draw(pDC->m_hDC, CRect(0, 0, nPageWidth, nPageLength));
	if (!bRet) {
		LogWarn() << "draw image err";
		sprintf(szOutBufTmp, "draw image err");
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}

	BOOL bPrinting = (pDC->EndPage() > 0);
	if (!bPrinting) {
		int lastErrorCode = GetLastError();
		LogWarn() << "EndPage err, " << lastErrorCode;
		sprintf(szOutBufTmp, "EndPage err, %d", lastErrorCode);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}

	ret = pDC->EndDoc();
	if (ret <= 0) {
		int lastErrorCode = GetLastError();
		LogWarn() << "EndDoc err, " << lastErrorCode;
		sprintf(szOutBufTmp, "EndDoc err, %d", lastErrorCode);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}
	bRet = pDC->DeleteDC();
	if (!ret) {
		int lastErrorCode = GetLastError();
		LogWarn() << "DeleteDC err, " << lastErrorCode;
		sprintf(szOutBufTmp, "DeleteDC err, %d", lastErrorCode);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}
	bRet = ClosePrinter(hPrinter);
	if (!bRet) {
		int lastErrorCode = GetLastError();
		LogWarn() << "ClosePrinter err, " << lastErrorCode;
		sprintf(szOutBufTmp, "ClosePrinter err, %d", lastErrorCode);
		strcpy(szOutBuf, szOutBufTmp);
		ret = 1;
		return ret;
	}
	ret = 0;
	return ret;
}

fn_log.h

#ifdef __GNUG__
#pragma GCC push_options
#pragma GCC optimize ("O2")
#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_FILE_H_
#define _FN_LOG_FILE_H_

#include <cstddef>
#include <cstring>
#include <stdio.h>
#include <iostream>
#include <vector>
#include <array>
#include <string>
#include <algorithm>
#include <array>
#include <mutex>
#include <thread>
#include <functional>
#include <regex>
#include <atomic>
#include <cmath>
#include <cfloat>
#include <list>
#include <deque>
#include <queue>
#include <map>
#include <unordered_map>
#include <set>
#include <unordered_set>
#include <memory>
#include <atomic>
#include <fcntl.h>
#ifdef WIN32
#ifndef KEEP_INPUT_QUICK_EDIT
#define KEEP_INPUT_QUICK_EDIT false
#endif

#define WIN32_LEAN_AND_MEAN
#include <WinSock2.h>
#include <Windows.h>
#include <io.h>
#include <shlwapi.h>
#include <process.h>
#include <ws2tcpip.h>
#pragma comment(lib, "shlwapi")
#pragma comment(lib, "User32.lib")
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)

#else
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <dirent.h>

#include <semaphore.h>
#include <sys/syscall.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <netdb.h>
#endif


#ifdef __APPLE__
#include "TargetConditionals.h"
#include <dispatch/dispatch.h>
#if !TARGET_OS_IPHONE
#define NFLOG_HAVE_LIBPROC
#include <libproc.h>
#endif
#endif


namespace FNLog
{
	static const int CHUNK_SIZE = 128;
	class FileHandler
	{
	public:
		inline FileHandler();
		inline ~FileHandler();
		inline bool is_open();
		inline long open(const char* path, const char* mod, struct stat& file_stat);
		inline void close();
		inline void write(const char* data, size_t len);
		inline void flush();

		inline std::string read_line();
		inline std::string read_content();

		static inline bool is_dir(const std::string & path);
		static inline bool is_file(const std::string & path);
		static inline bool create_dir(const std::string& path);
		static inline std::string process_id();
		static inline std::string process_name();
		static inline bool remove_file(const std::string& path);
		static inline struct tm time_to_tm(time_t t);

		static inline bool rollback(const std::string& path, int depth, int max_depth);
	public:
		char chunk_1_[128];
		FILE* file_;
	};





	long FileHandler::open(const char* path, const char* mod, struct stat& file_stat)
	{
		if (file_ != nullptr)
		{
			fclose(file_);
			file_ = nullptr;
		}
		file_ = fopen(path, mod);
		if (file_)
		{
			if (fstat(fileno(file_), &file_stat) != 0)
			{
				fclose(file_);
				file_ = nullptr;
				return -1;
			}
			return file_stat.st_size;
		}
		return -2;
	}
	void FileHandler::close()
	{
		if (file_ != nullptr)
		{
#if !defined(__APPLE__) && !defined(WIN32) 
			if (file_ != nullptr)
			{
				int fd = fileno(file_);
				fsync(fd);
				posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
				fsync(fd);
			}
#endif
			fclose(file_);
			file_ = nullptr;
		}
	}

	FileHandler::FileHandler()
	{
		file_ = nullptr;
	}
	FileHandler::~FileHandler()
	{
		close();
	}

	bool FileHandler::is_open()
	{
		return file_ != nullptr;
	}

	void FileHandler::write(const char* data, size_t len)
	{
		if (file_ && len > 0)
		{
			if (fwrite(data, 1, len, file_) != len)
			{
				close();
			}
		}
	}
	void FileHandler::flush()
	{
		if (file_)
		{
			fflush(file_);
		}
	}

	std::string FileHandler::read_line()
	{
		char buf[500] = { 0 };
		if (file_ && fgets(buf, 500, file_) != nullptr)
		{
			return std::string(buf);
		}
		return std::string();
	}
	std::string FileHandler::read_content()
	{
		std::string content;

		if (!file_)
		{
			return content;
		}
		char buf[BUFSIZ];
		size_t ret = 0;
		do
		{
			ret = fread(buf, sizeof(char), BUFSIZ, file_);
			content.append(buf, ret);
		} while (ret == BUFSIZ);

		return content;
	}

	bool FileHandler::is_dir(const std::string& path)
	{
#ifdef WIN32
		return PathIsDirectoryA(path.c_str()) ? true : false;
#else
		DIR* pdir = opendir(path.c_str());
		if (pdir == nullptr)
		{
			return false;
		}
		else
		{
			closedir(pdir);
			pdir = nullptr;
			return true;
		}
#endif
	}

	bool FileHandler::is_file(const std::string& path)
	{
#ifdef WIN32
		return ::_access(path.c_str(), 0) == 0;
#else
		return ::access(path.c_str(), F_OK) == 0;
#endif
	}

	bool FileHandler::create_dir(const std::string& path)
	{
		if (path.length() == 0)
		{
			return true;
		}
		std::string sub;

		std::string::size_type pos = path.find('/');
		while (pos != std::string::npos)
		{
			std::string cur = path.substr(0, pos - 0);
			if (cur.length() > 0 && !is_dir(cur))
			{
				bool ret = false;
#ifdef WIN32
				ret = CreateDirectoryA(cur.c_str(), nullptr) ? true : false;
#else
				ret = (mkdir(cur.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0);
#endif
				if (!ret)
				{
					return false;
				}
			}
			pos = path.find('/', pos + 1);
		}

		return true;
	}


	std::string FileHandler::process_id()
	{
		std::string pid = "0";
		char buf[260] = { 0 };
#ifdef WIN32
		DWORD winPID = GetCurrentProcessId();
		sprintf(buf, "%06u", winPID);
		pid = buf;
#else
		sprintf(buf, "%06d", getpid());
		pid = buf;
#endif
		return pid;
	}

	std::string FileHandler::process_name()
	{
		std::string name = "process";
		char buf[260] = { 0 };
#ifdef WIN32
		if (GetModuleFileNameA(nullptr, buf, 259) > 0)
		{
			name = buf;
		}
		std::string::size_type pos = name.rfind("\\");
		if (pos != std::string::npos)
		{
			name = name.substr(pos + 1, std::string::npos);
		}
		pos = name.rfind(".");
		if (pos != std::string::npos)
		{
			name = name.substr(0, pos - 0);
		}

#elif defined(__APPLE__)
		proc_name(getpid(), buf, 260);
		name = buf;
		return name;;
#else
		sprintf(buf, "/proc/%d/cmdline", (int)getpid());
		FileHandler i;
		struct stat file_stat;
		i.open(buf, "rb", file_stat);
		if (!i.is_open())
		{
			return name;
		}
		name = i.read_line();
		i.close();

		std::string::size_type pos = name.rfind("/");
		if (pos != std::string::npos)
		{
			name = name.substr(pos + 1, std::string::npos);
		}
#endif

		return name;
	}

	bool FileHandler::remove_file(const std::string & path)
	{
		return ::remove(path.c_str()) == 0;
	}

	struct tm FileHandler::time_to_tm(time_t t)
	{
#ifdef WIN32
#if _MSC_VER < 1400 //VS2003
		return *localtime(&t);
#else //vs2005->vs2013->
		struct tm tt = { 0 };
		localtime_s(&tt, &t);
		return tt;
#endif
#else //linux
		struct tm tt = { 0 };
		localtime_r(&t, &tt);
		return tt;
#endif
	}



	bool FileHandler::rollback(const std::string& path, int depth, int max_depth)
	{
		if (!is_file(path))
		{
			return true;
		}
		if (depth > max_depth)
		{
			return remove_file(path);
		}
		std::string next_path = path;
		size_t pos = path.find_last_not_of("0123456789");
		if (pos != std::string::npos && path.at(pos) == '.')
		{
			next_path = path.substr(0, pos - 0);
		}
		next_path += ".";
		next_path += std::to_string(depth);
		rollback(next_path, depth + 1, max_depth);
		int ret = ::rename(path.c_str(), next_path.c_str());
		(void)ret;
		return true;
	}

	inline int short_path(const char* path, int len)
	{
		int count = 3;
		if (path == nullptr || len <= 0)
		{
			return 0;
		}
		const char* last = path + len;
		while (last-- != path)
		{
			if (*last == '/' || *last == '\\')
			{
				if (--count <= 0)
				{
					return (int)(last - path + 1);
				}
			}
		}
		return 0;
	}

}





class UDPHandler
{
public:
#ifndef WIN32
	using FNLOG_SOCKET = int;
	static const int FNLOG_INVALID_SOCKET = -1;
#else
	using FNLOG_SOCKET = SOCKET;
	static const SOCKET FNLOG_INVALID_SOCKET = INVALID_SOCKET;
#endif 

public:
	UDPHandler()
	{
		chunk_1_[0] = '\0';
		handler_ = FNLOG_INVALID_SOCKET;
		open_ts_ = 0;
		memset(&addr_, 0, sizeof(addr_));
	}
	~UDPHandler()
	{
		if (handler_ != FNLOG_INVALID_SOCKET)
		{
			close();
		}
	}

	bool is_open()
	{
		return handler_ != FNLOG_INVALID_SOCKET;
	}

	int open()
	{
		if (time(NULL) <= open_ts_ + 2)
		{
			return 0; //but not open
		}
		open_ts_ = time(NULL);

		handler_ = socket(AF_INET, SOCK_DGRAM, 0);
		if (handler_ == FNLOG_INVALID_SOCKET)
		{
			//int ret = WSAGetLastError();
			return -1;
		}
		memset(&addr_, 0, sizeof(addr_));
#ifdef WIN32
		u_long argp = 1;
		int ret = ioctlsocket(handler_, FIONBIO, &argp);
		if (ret != NO_ERROR)
		{
			return -2;
		}
#else
		int oldf = fcntl(handler_, F_GETFL, 0);
		int newf = oldf | O_NONBLOCK;
		int ret = fcntl(handler_, F_SETFL, newf);
		if (ret == -1)
		{
			return -2;
		}
#endif

		return 0;
	}

	int bind(unsigned int ip, unsigned short port)
	{
		addr_.sin_family = AF_INET;
		addr_.sin_port = port;
		addr_.sin_addr.s_addr = ip;
		int ret = ::bind(handler_, (struct sockaddr*)&addr_, sizeof(addr_));
		(void)ret;
		if (ret != 0)
		{
			return ret;
		}
		return 0;
	}

	void close()
	{
		if (handler_ != FNLOG_INVALID_SOCKET)
		{
#ifndef WIN32
			::close(handler_);
#else
			closesocket(handler_);
#endif 
			handler_ = FNLOG_INVALID_SOCKET;
		}
	}


	int write(unsigned int ip, unsigned short port, const char* data, int len)
	{
		if (handler_ == FNLOG_INVALID_SOCKET)
		{
			return 0;
		}
		addr_.sin_family = AF_INET;
		addr_.sin_port = port;
		addr_.sin_addr.s_addr = ip;
		int ret = sendto(handler_, data, len, 0, (struct sockaddr*) &addr_, sizeof(addr_));
		(void)ret;
		return ret;
	}

	int read(char* data, int data_len)
	{
		if (handler_ == FNLOG_INVALID_SOCKET)
		{
			return 0;
		}

		int ret = recvfrom(handler_, data, data_len, 0, NULL, NULL);
		if (ret < 0)
		{
			return 0;
		}
		return ret;
	}
public:
	char chunk_1_[128];
	time_t open_ts_;
	struct sockaddr_in addr_;
	FNLOG_SOCKET handler_;
};


#endif
/*
*
* MIT License
*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* ===============================================================================
*
* (end of COPYRIGHT)
*/


/*
* AUTHORS:  YaweiZhang <yawei.zhang@foxmail.com>
* VERSION:  1.0.0
* PURPOSE:  fn-log is a cpp-based logging utility.
* CREATION: 2019.4.20
* RELEASED: 2019.6.27
* QQGROUP:  524700770
*/


#pragma once
#ifndef _FN_LOG_DATA_H_
#define _FN_LOG_DATA_H_


#ifndef FN_LOG_MAX_DEVICE_SIZE
#define FN_LOG_MAX_DEVICE_SIZE 20
#endif

#ifndef FN_LOG_MAX_CHANNEL_SIZE
#define FN_LOG_MAX_CHANNEL_SIZE 2
#endif

#ifndef FN_LOG_MAX_LOG_SIZE
#define FN_LOG_MAX_LOG_SIZE 10000
#endif

#ifndef FN_LOG_MAX_LOG_QUEUE_SIZE //the size need big than push log thread count
#define FN_LOG_MAX_LOG_QUEUE_SIZE 1000
#endif

#ifndef FN_LOG_MAX_ASYNC_SLEEP_MS 
#define FN_LOG_MAX_ASYNC_SLEEP_MS 10
#endif

#ifndef FN_LOG_FORCE_FLUSH_QUE 
#define FN_LOG_FORCE_FLUSH_QUE 10000
#endif

#ifndef FN_LOG_HOTUPDATE_INTERVEL
#define FN_LOG_HOTUPDATE_INTERVEL 5
#endif


//#define FN_LOG_USING_ATOM_CFG

namespace FNLog
{
	enum LogPriority
	{
		PRIORITY_TRACE = 0,
		PRIORITY_DEBUG,
		PRIORITY_INFO,
		PRIORITY_WARN,
		PRIORITY_ERROR,
		PRIORITY_ALARM,
		PRIORITY_FATAL,
		PRIORITY_MAX
	};
	enum LogPrefix
	{
		LOG_PREFIX_NULL = 0x0,
		LOG_PREFIX_TIMESTAMP = 0x1,
		LOG_PREFIX_PRIORITY = 0x2,
		LOG_PREFIX_THREAD = 0x4,
		LOG_PREFIX_NAME = 0x8,
		LOG_PREFIX_DESC = 0x10,
		LOG_PREFIX_FILE = 0x20,
		LOG_PREFIX_FUNCTION = 0x40,
		LOG_PREFIX_ALL = 0xff,
		//LOG_PREFIX_DEFAULT = LOG_PREFIX_ALL,
		LOG_PREFIX_DEFAULT = LOG_PREFIX_TIMESTAMP | LOG_PREFIX_PRIORITY | LOG_PREFIX_FILE | LOG_PREFIX_FUNCTION,
	};


	enum LogType
	{
		LOG_TYPE_NULL,
		LOG_TYPE_MAX,
	};

	enum LogState
	{
		MARK_INVALID,
		MARK_HOLD,
		MARK_READY
	};

	struct LogData
	{
	public:
		static const int LOG_SIZE = FN_LOG_MAX_LOG_SIZE;
	public:
		std::atomic_int    data_mark_; //0 invalid, 1 hold, 2 ready
		int     channel_id_;
		int     priority_;
		int     category_;
		long long     identify_;
		int     code_line_;
		int     code_func_len_;
		int     code_file_len_;
		const char* code_func_;
		const char* code_file_;
		long long timestamp_;        //create timestamp
		int precise_; //create time millionsecond suffix
		unsigned int thread_;
		int prefix_len_;
		int content_len_;
		char content_[LOG_SIZE]; //content
	};

	enum DeviceInType
	{
		DEVICE_IN_NULL,
		DEVICE_IN_UDP,
	};

	enum DeviceOutType
	{
		DEVICE_OUT_NULL,
		DEVICE_OUT_SCREEN,
		DEVICE_OUT_FILE,
		DEVICE_OUT_UDP,
		DEVICE_OUT_VIRTUAL,
		DEVICE_OUT_EMPTY,
	};


	enum DeviceConfigEnum
	{
		DEVICE_CFG_ABLE,
		DEVICE_CFG_PRIORITY,
		DEVICE_CFG_CATEGORY,
		DEVICE_CFG_CATEGORY_EXTEND,
		DEVICE_CFG_CATEGORY_MASK,
		DEVICE_CFG_IDENTIFY,
		DEVICE_CFG_IDENTIFY_EXTEND,
		DEVICE_CFG_IDENTIFY_MASK,
		DEVICE_CFG_FILE_LIMIT_SIZE,
		DEVICE_CFG_FILE_ROLLBACK,
		DEVICE_CFG_FILE_ROLLDAILY,
		DEVICE_CFG_FILE_ROLLHOURLY,
		DEVICE_CFG_FILE_STUFF_UP,
		DEVICE_CFG_UDP_IP,
		DEVICE_CFG_UDP_PORT,
		DEVICE_CFG_MAX_ID
	};

	enum DeviceLogEnum
	{
		DEVICE_LOG_CUR_FILE_SIZE,
		DEVICE_LOG_CUR_FILE_CREATE_TIMESTAMP,
		DEVICE_LOG_CUR_FILE_CREATE_DAY,
		DEVICE_LOG_CUR_FILE_CREATE_HOUR,
		DEVICE_LOG_LAST_TRY_CREATE_TIMESTAMP,
		DEVICE_LOG_LAST_TRY_CREATE_ERROR,
		DEVICE_LOG_LAST_TRY_CREATE_CNT,
		DEVICE_LOG_PRIORITY, //== PRIORITY_TRACE
		DEVICE_LOG_PRIORITY_MAX = DEVICE_LOG_PRIORITY + PRIORITY_MAX,
		DEVICE_LOG_TOTAL_WRITE_LINE,
		DEVICE_LOG_TOTAL_WRITE_BYTE,
		DEVICE_LOG_TOTAL_LOSE_LINE,
		DEVICE_LOG_MAX_ID
	};




	struct Device
	{
	public:
		static const int MAX_PATH_SYS_LEN = 255;
		static const int MAX_PATH_LEN = 200;
		static const int MAX_LOGGER_NAME_LEN = 50;
		static const int MAX_ROLLBACK_LEN = 4;
		static const int MAX_ROLLBACK_PATHS = 5;
		static_assert(MAX_PATH_LEN + MAX_LOGGER_NAME_LEN + MAX_ROLLBACK_LEN < MAX_PATH_SYS_LEN, "");
		static_assert(LogData::LOG_SIZE > MAX_PATH_SYS_LEN * 2, "unsafe size"); // promise format length: date, time, source file path, function length.
		static_assert(MAX_ROLLBACK_PATHS < 10, "");
#ifdef FN_LOG_USING_ATOM_CFG
		using ConfigFields = std::array<std::atomic_llong, DEVICE_CFG_MAX_ID>;
#else
		using ConfigFields = long long[DEVICE_CFG_MAX_ID];
#endif // FN_LOG_USING_ATOM_CFG

	public:
		int device_id_;
		unsigned int out_type_;
		unsigned int in_type_;
		char out_file_[MAX_LOGGER_NAME_LEN];
		char out_path_[MAX_PATH_LEN];
		ConfigFields config_fields_;
	};


	enum ChannelType
	{
		CHANNEL_ASYNC,
		CHANNEL_SYNC,
	};

	enum ChannelConfigEnum
	{
		CHANNEL_CFG_PRIORITY,
		CHANNEL_CFG_CATEGORY,
		CHANNEL_CFG_CATEGORY_EXTEND,
		CHANNEL_CFG_CATEGORY_MASK,
		CHANNEL_CFG_IDENTIFY,
		CHANNEL_CFG_IDENTIFY_EXTEND,
		CHANNEL_CFG_IDENTIFY_MASK,
		CHANNEL_CFG_MAX_ID
	};


	enum ChannelLogEnum
	{
		CHANNEL_LOG_WAIT_COUNT,
		CHANNEL_LOG_HOLD,
		CHANNEL_LOG_PUSH,
		CHANNEL_LOG_PRIORITY, //== PRIORITY_TRACE
		CHANNEL_LOG_PRIORITY_MAX = CHANNEL_LOG_PRIORITY + PRIORITY_MAX,

		CHANNEL_LOG_BOUND = CHANNEL_LOG_PRIORITY_MAX + 8, //ull*8 

		CHANNEL_LOG_PROCESSED,
		CHANNEL_LOG_PROCESSED_BYTES,
		CHANNEL_LOG_MAX_PROC_QUE_SIZE,
		CHANNEL_LOG_MAX_DELAY_TIME_S, //second 

		CHANNEL_LOG_MAX_ID
	};

	enum ChannelState
	{
		CHANNEL_STATE_NULL = 0,
		CHANNEL_STATE_RUNNING,
		CHANNEL_STATE_WAITING_FINISH,
		CHANNEL_STATE_FINISH,
	};

	struct RingBuffer
	{
	public:
		static const int BUFFER_LEN = FN_LOG_MAX_LOG_QUEUE_SIZE;
		static_assert(BUFFER_LEN > 10, "ring queue size too little");
	public:
		char chunk_1_[CHUNK_SIZE];
		std::atomic_int write_idx_;
		char chunk_2_[CHUNK_SIZE];
		std::atomic_int hold_idx_;
		char chunk_3_[CHUNK_SIZE];
		std::atomic_int read_idx_;
		char chunk_4_[CHUNK_SIZE];
		std::atomic_int proc_idx_;
		char chunk_5_[CHUNK_SIZE];
		LogData buffer_[BUFFER_LEN];
		char chunk_6_[CHUNK_SIZE];
		LogData udp_buffer_;
	};

	struct Channel
	{
	public:
#ifdef FN_LOG_USING_ATOM_CFG
		using ConfigFields = std::array<std::atomic_llong, CHANNEL_CFG_MAX_ID>;
#else
		using ConfigFields = long long[CHANNEL_CFG_MAX_ID];
#endif // FN_LOG_USING_ATOM_CFG

		using ChannelLogFields = std::array<std::atomic_llong, CHANNEL_LOG_MAX_ID>;
		static const int MAX_DEVICE_SIZE = FN_LOG_MAX_DEVICE_SIZE;
		using DeviceLogFields = std::array<std::atomic_llong, DEVICE_LOG_MAX_ID>;

	public:

		int  channel_id_;
		int  channel_type_;
		int virtual_device_id_;
		int device_size_;
		unsigned int channel_state_;
		ConfigFields config_fields_;
		Device devices_[MAX_DEVICE_SIZE];

		char chunk_1_[CHUNK_SIZE];

		time_t yaml_mtime_;
		time_t last_hot_check_;
		ChannelLogFields channel_log_fields_;
		DeviceLogFields device_log_fields_[MAX_DEVICE_SIZE];
	};


	enum LoggerState
	{
		LOGGER_STATE_UNINIT = 0,
		LOGGER_STATE_INITING,
		LOGGER_STATE_RUNNING,
		LOGGER_STATE_CLOSING,
	};

	struct SHMLogger
	{
		static const int MAX_CHANNEL_SIZE = FN_LOG_MAX_CHANNEL_SIZE;
		using Channels = std::array<Channel, MAX_CHANNEL_SIZE>;
		using RingBuffers = std::array<RingBuffer, MAX_CHANNEL_SIZE>;
		int shm_id_;
		int shm_size_;
		int channel_size_;
		Channels channels_;
		RingBuffers ring_buffers_;
	};

	template<class Mutex>
	class AutoGuard
	{
	public:
		using mutex_type = Mutex;
		inline explicit AutoGuard(Mutex& mtx, bool noop = false) : mutex_(mtx), noop_(noop)
		{
			if (!noop_)
			{
				mutex_.lock();
			}
		}

		inline ~AutoGuard() noexcept
		{
			if (!noop_)
			{
				mutex_.unlock();
			}
		}
		AutoGuard(const AutoGuard&) = delete;
		AutoGuard& operator=(const AutoGuard&) = delete;
	private:
		Mutex& mutex_;
		bool noop_;
	};

	class Logger
	{
	public:
		static const int MAX_CHANNEL_SIZE = SHMLogger::MAX_CHANNEL_SIZE;
		static const int HOTUPDATE_INTERVEL = FN_LOG_HOTUPDATE_INTERVEL;
		static const int MAX_LOGGER_DESC_LEN = 50;
		static const int MAX_LOGGER_NAME_LEN = 250;
		using ReadLocks = std::array<std::mutex, MAX_CHANNEL_SIZE>;
		using ReadGuard = AutoGuard<std::mutex>;

		using AsyncThreads = std::array<std::thread, MAX_CHANNEL_SIZE>;
		using FileHandles = std::array<FileHandler, MAX_CHANNEL_SIZE* Channel::MAX_DEVICE_SIZE>;
		using UDPHandles = std::array<UDPHandler, MAX_CHANNEL_SIZE* Channel::MAX_DEVICE_SIZE>;

	public:
		using StateLock = std::recursive_mutex;
		using StateLockGuard = AutoGuard<StateLock>;

		using ScreenLock = std::mutex;
		using ScreenLockGuard = AutoGuard<ScreenLock>;


	public:
		Logger();
		~Logger();
		bool hot_update_;
		std::string yaml_path_;
		unsigned int logger_state_;
		StateLock state_lock_;
		char desc_[MAX_LOGGER_DESC_LEN];
		int desc_len_;
		char name_[MAX_LOGGER_NAME_LEN];
		int name_len_;

		long long shm_key_;
		SHMLogger* shm_;

		ReadLocks read_locks_;
		AsyncThreads async_threads;
		ScreenLock screen_lock_;
		FileHandles file_handles_;
		UDPHandles udp_handles_;
	};



	template<class V>
	inline V FN_MIN(V x, V y)
	{
		return y < x ? y : x;
	}
	template<class V>
	inline V FN_MAX(V x, V y)
	{
		return x < y ? y : x;
	}


#ifdef FN_LOG_USING_ATOM_CFG
#define AtomicLoadC(m, eid) m.config_fields_[eid].load(std::memory_order_relaxed)
#else
#define AtomicLoadC(m, eid) m.config_fields_[eid]
#endif // FN_LOG_USING_ATOM_CFG

	inline long long AtomicLoadChannelLog(Channel& c, unsigned eid)
	{
		return c.channel_log_fields_[eid].load(std::memory_order_relaxed);
	}

	inline void AtomicIncChannelLog(Channel& c, unsigned eid, long long v)
	{
		c.channel_log_fields_[eid].fetch_add(v, std::memory_order_relaxed);
	}

	inline void AtomicStoreChannelLog(Channel& c, unsigned eid, long long v)
	{
		c.channel_log_fields_[eid].store(v, std::memory_order_relaxed);
	}



	inline long long AtomicLoadDeviceLog(Channel& c, int device_id, unsigned eid)
	{
		return c.device_log_fields_[device_id][eid].load(std::memory_order_relaxed);
	}

	inline void AtomicIncDeviceLog(Channel& c, int device_id, unsigned eid, long long v)
	{
		c.device_log_fields_[device_id][eid].fetch_add(v, std::memory_order_relaxed);
	}

	inline void AtomicStoreDeviceLog(Channel& c, int device_id, unsigned eid, long long v)
	{
		c.device_log_fields_[device_id][eid].store(v, std::memory_order_relaxed);
	}


	enum ErrNo
	{
		E_SUCCESS = 0,

		E_LOGGER_IN_USE,
		E_LOGGER_NOT_INIT,
		E_LOGGER_NOT_RUNNING,

		E_INVALID_CONFIG_PATH,

		E_INVALID_CHANNEL_SIZE,
		E_INVALID_CHANNEL_SYNC,
		E_INVALID_CHANNEL_STATE,
		E_CHANNEL_THREAD_FAILED,
		E_CHANNEL_NOT_SEQUENCE,


		E_INVALID_DEVICE_SIZE,
		E_DEVICE_NOT_SEQUENCE,





		E_SHMGET_PROBE_ERROR,
		E_SHMGET_CREATE_ERROR,
		E_SHMAT_ERROR,
		E_SHM_VERSION_WRONG,

		E_VERSION_MISMATCH,


		E_DISABLE_HOTUPDATE,
		E_NO_CONFIG_PATH,
		E_CONFIG_NO_CHANGE,
		E_OUT_RINGBUFFER,

		E_BASE_ERRNO_MAX

	};


}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_PARSE_H_
#define _FN_LOG_PARSE_H_



namespace FNLog
{
	enum ParseErrorCode
	{
		PEC_NONE = E_SUCCESS,
		PEC_ERROR = E_BASE_ERRNO_MAX,
		PEC_NONSUPPORT_SYNTAX,
		PEC_ILLEGAL_KEY,
		PEC_ILLEGAL_VAR_NAME,
		PEC_ILLEGAL_VAR_VALUE,
		PEC_NOT_CLOSURE,
		PEC_ILLEGAL_ADDR_IP,
		PEC_ILLEGAL_ADDR_PORT,
		PEC_DEFINED_TARGET_TOO_LONG,

		PEC_UNDEFINED_DEVICE_KEY,
		PEC_UNDEFINED_DEVICE_TYPE,
		PEC_UNDEFINED_CHANNEL_KEY,
		PEC_UNDEFINED_GLOBAL_KEY,

	};


	inline std::string DebugErrno(int error_code)
	{
		switch (error_code)
		{
		case E_SUCCESS:
			return "success";
		case E_LOGGER_IN_USE:
			return "logger alread in use";
		case E_LOGGER_NOT_INIT:
			return "logger not init";
		case E_LOGGER_NOT_RUNNING:
			return "logger not running";
		case E_INVALID_CONFIG_PATH:
			return "invalid config path";
		case E_INVALID_CHANNEL_SIZE:
			return "invalid channel size";
		case E_INVALID_CHANNEL_SYNC:
			return "invalid channel sync";
		case E_INVALID_CHANNEL_STATE:
			return "invalid channel state";
		case E_CHANNEL_THREAD_FAILED:
			return "create thread faield";
		case E_CHANNEL_NOT_SEQUENCE:
			return "channel index need sequence.";

		case E_INVALID_DEVICE_SIZE:
			return "invalid device size";
		case E_DEVICE_NOT_SEQUENCE:
			return "device index need sequence.";

		case E_SHMGET_PROBE_ERROR:
		case E_SHMGET_CREATE_ERROR:
		case E_SHMAT_ERROR:
		case E_SHM_VERSION_WRONG:
		case E_VERSION_MISMATCH:
			return "shm error";

		case E_DISABLE_HOTUPDATE:
		case E_NO_CONFIG_PATH:
		case E_CONFIG_NO_CHANGE:
		case E_OUT_RINGBUFFER:
			return "some warn";

		case PEC_ERROR:
			return "syntax error";
		case PEC_NONSUPPORT_SYNTAX:
			return "unsupport syntax";
		case PEC_ILLEGAL_KEY:
			return "unknown key";

		case PEC_ILLEGAL_VAR_NAME:
			return "name or key invalid";
		case PEC_NOT_CLOSURE:
			return "not closure line ";
		case PEC_ILLEGAL_ADDR_IP:
		case PEC_ILLEGAL_ADDR_PORT:
			return "udp addr error";
		case PEC_DEFINED_TARGET_TOO_LONG:
			return "var/macro name len must longger than/equal new name.";

		case PEC_UNDEFINED_DEVICE_KEY:
		case PEC_UNDEFINED_DEVICE_TYPE:
		case PEC_UNDEFINED_CHANNEL_KEY:
		case PEC_UNDEFINED_GLOBAL_KEY:
			return "undefined type";





		default:
			break;
		}
		return "unknown error.";
	}


	enum BlockType
	{
		BLOCK_NONE,
		BLOCK_KEY,
		BLOCK_PRE_SEP,
		BLOCK_PRE_VAL,
		BLOCK_VAL,
		BLOCK_CLEAN,
	};

	enum ReseveKey
	{
		RK_NULL,
		RK_SHM_KEY,
		RK_CHANNEL,
		RK_DEFINE, //the symbol name len must equal or great than new name;   like tag0 10, tag1 100;  will error by "tag0 100000" 
		RK_VARIABLE,
		RK_DEVICE,
		RK_SYNC,
		RK_DISABLE,
		RK_HOT_UPDATE,
		RK_LOGGER_NAME,
		PK_LOGGER_DESC,
		RK_PRIORITY,
		RK_CATEGORY,
		RK_CATEGORY_EXTEND,
		RK_CATEGORY_WLIST, //bitset list 
		RK_CATEGORY_BLIST,
		RK_CATEGORY_WMASK,
		RK_CATEGORY_BMASK,
		RK_IDENTIFY,
		RK_IDENTIFY_EXTEND,
		RK_IDENTIFY_WLIST, //bitset list 
		RK_IDENTIFY_BLIST,
		RK_IDENTIFY_WMASK,
		RK_IDENTIFY_BMASK,
		RK_IN_TYPE,
		RK_OUT_TYPE,
		RK_FILE,
		RK_PATH,
		RK_LIMIT_SIZE,
		RK_ROLLBACK,
		RK_ROLLDAILY,
		RK_ROLLHOURLY,
		RK_FILE_STUFF_UP,
		RK_UDP_ADDR,
	};

#if __GNUG__ && __GNUC__ >= 6
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif

	struct Line
	{
		int blank_;
		int chars_;
		int block_type_;
		char block_stack_;
		int key_;
		const char* key_begin_;
		const char* key_end_;
		const char* val_begin_;
		const char* val_end_;
	};

	struct LexState
	{
		int line_no_;
		const char* first_;
		const char* current_;
		const char* end_;
		Line line_;
		SHMLogger::Channels channels_;
		int channel_size_;
		long long shm_key_;
		bool hot_update_;
		char desc_[Logger::MAX_LOGGER_DESC_LEN];
		int desc_len_;
		char name_[Logger::MAX_LOGGER_NAME_LEN];
		int name_len_;
	};
	inline bool IsNumber(char ch)
	{
		return ch >= '0' && ch <= '9';
	}
	inline bool IsBlank(char ch)
	{
		//not std::isblank
		switch (ch)
		{
		case ' ':
		case '\t':
		case '\v':
		case '\f':
			return true;
		default:
			break;
		}
		return false;
	}
	inline bool IsSoftLineBound(char ch)
	{
		//not std::isblank
		switch (ch)
		{
		case '\0':
		case '\r':
		case '\n':
		case '#':
			return true;
		default:
			break;
		}
		return false;
	}
	inline bool IsLineBound(char ch)
	{
		//not std::isblank
		switch (ch)
		{
		case '\0':
		case '\r':
		case '\n':
			return true;
		default:
			break;
		}
		return false;
	}

	inline bool IsValidName(char ch)
	{
		if (ch >= 'a' && ch <= 'z')
		{
			return true;
		}
		if (ch >= 'A' && ch <= 'Z')
		{
			return true;
		}
		if (ch >= '0' && ch <= '9')
		{
			return true;
		}
		if (ch == '_')
		{
			return true;
		}
		return false;
	}

	inline bool IsValidNameFirst(char ch)
	{
		if (ch >= 'a' && ch <= 'z')
		{
			return true;
		}
		if (ch >= 'A' && ch <= 'Z')
		{
			return true;
		}
		if (ch == '_')
		{
			return true;
		}
		return false;
	}

	inline ReseveKey ParseReserve(const char* begin, const char* end)
	{
		if (end - begin < 2)
		{
			return RK_NULL;
		}
		switch (*begin)
		{
		case 'c':
			if (*(begin + 1) == 'h')
			{
				return RK_CHANNEL;
			}
			else if (*(begin + 1) == 'a')
			{
				if (end - begin > (int)sizeof("category_ex") - 1)
				{
					if (*(begin + 9) == 'e')  //category_extend
					{
						return RK_CATEGORY_EXTEND;
					}
					else if (*(begin + 9) == 'w')
					{
						if (*(begin + 10) == 'l')  //category_wlist  
						{
							return RK_CATEGORY_WLIST;
						}
						else if (*(begin + 10) == 'm')
						{
							return RK_CATEGORY_WMASK;
						}
					}
					else if (*(begin + 9) == 'b')
					{
						if (*(begin + 10) == 'l')  //category_blist; black list   
						{
							return RK_CATEGORY_BLIST;
						}
						else if (*(begin + 10) == 'm')
						{
							return RK_CATEGORY_BMASK;
						}
					}
				}
				else
				{
					return RK_CATEGORY;
				}
			}
			break;
		case 'd':
			if (end - begin < 3)
			{
				return RK_NULL;
			}
			if (*(begin + 2) == 'f')
			{
				return RK_DEFINE;
			}
			else if (*(begin + 1) == 'e')
			{
				return RK_DEVICE;
			}
			else if (*(begin + 1) == 'i')
			{
				return RK_DISABLE;
			}
			break;
		case  'f':
			return RK_FILE;
		case 'h':
			return RK_HOT_UPDATE;
		case 'i':
			if (*(begin + 1) == 'n')
			{
				return RK_IN_TYPE;
			}
			else if (end - begin > (int)sizeof("identify_ex") - 1)
			{
				if (*(begin + 9) == 'e')
				{
					return RK_IDENTIFY_EXTEND;
				}
				else if (*(begin + 9) == 'w')
				{
					if (*(begin + 10) == 'l')  //identify_wlist  
					{
						return RK_IDENTIFY_WLIST;
					}
					else if (*(begin + 10) == 'm')
					{
						return RK_IDENTIFY_WMASK;
					}
				}
				else if (*(begin + 9) == 'b')
				{
					if (*(begin + 10) == 'l')  //identify_blist;  black list  
					{
						return RK_IDENTIFY_BLIST;
					}
					else if (*(begin + 10) == 'm')
					{
						return RK_IDENTIFY_BMASK;
					}
				}
			}
			else
			{
				return RK_IDENTIFY;
			}
			break;
		case 'l':
			if (*(begin + 1) == 'i')
			{
				return RK_LIMIT_SIZE;
			}
			else if (end - begin > 8)
			{
				if (*(begin + 7) == 'n')
				{
					return RK_LOGGER_NAME;
				}
				if (*(begin + 7) == 'd')
				{
					return PK_LOGGER_DESC;
				}
			}
			break;
		case 'p':
			if (*(begin + 1) == 'r')
			{
				return RK_PRIORITY;
			}
			else if (*(begin + 1) == 'a')
			{
				return RK_PATH;
			}
			break;
		case 'r':
			if (end - begin > (int)sizeof("rollb"))
			{
				if (*(begin + 4) == 'b')
				{
					return RK_ROLLBACK;
				}
				else if (*(begin + 4) == 'd')
				{
					return RK_ROLLDAILY;
				}
				else if (*(begin + 4) == 'h')
				{
					return RK_ROLLHOURLY;
				}
			}
			break;
		case 'o':
			return RK_OUT_TYPE;
		case 's':
			if (*(begin + 1) == 'y')
			{
				return RK_SYNC;
			}
			else if (*(begin + 1) == 't')
			{
				return RK_FILE_STUFF_UP;
			}
			return RK_SHM_KEY;
		case 'u':
			return RK_UDP_ADDR;
		case 'v':
			return RK_VARIABLE;
		default:
			break;
		}
		return RK_NULL;
	}

	inline LogPriority ParsePriority(const char* begin, const char* end)
	{
		if (end <= begin)
		{
			return PRIORITY_TRACE;
		}
		switch (*begin)
		{
		case 't':case 'T':
		case 'n':case 'N':
			return PRIORITY_TRACE;
		case 'd':case 'D':
			return PRIORITY_DEBUG;
		case 'i':case 'I':
			return PRIORITY_INFO;
		case 'w':case 'W':
			return PRIORITY_WARN;
		case 'e':case 'E':
			return PRIORITY_ERROR;
		case 'a':case 'A':
			return PRIORITY_ALARM;
		case 'f':case 'F':
			return PRIORITY_FATAL;
		}
		return PRIORITY_TRACE;
	}

	inline bool ParseBool(const char* begin, const char* end)
	{
		if (end <= begin)
		{
			return false;
		}
		if (*begin == '0' || *begin == 'f' || *begin == 'F')
		{
			return false;
		}
		return true;
	}

	inline long long ParseNumber(const char* begin, const char* end)
	{
		if (end <= begin)
		{
			return 0;
		}

		if (end - begin > 40)
		{
			return 0;
		}

		char buff[50];
		memcpy(buff, begin, end - begin);
		buff[end - begin] = '\0';
		return strtoll(buff, NULL, 0);
	}

	inline bool ParseString(const char* begin, const char* end, char * buffer, int buffer_len, int& write_len)
	{
		write_len = 0;
		if (end <= begin)
		{
			return false;
		}
		write_len = buffer_len - 1;
		if (end - begin < write_len)
		{
			write_len = (int)(end - begin);
		}
		memcpy(buffer, begin, write_len);
		buffer[write_len] = '\0';
		return true;
	}
	inline ChannelType ParseChannelType(const char* begin, const char* end)
	{
		if (end <= begin || *begin != 's')
		{
			return CHANNEL_ASYNC;
		}
		return CHANNEL_SYNC;
	}

	inline DeviceInType ParseInType(const char* begin, const char* end)
	{
		if (end <= begin)
		{
			return DEVICE_IN_NULL;
		}
		switch (*begin)
		{
		case 'u': case 'U':
			return DEVICE_IN_UDP;
		}
		return DEVICE_IN_NULL;
	}

	inline DeviceOutType ParseOutType(const char* begin, const char* end)
	{
		if (end <= begin)
		{
			return DEVICE_OUT_NULL;
		}
		switch (*begin)
		{
		case 'e': case 'E':
			return DEVICE_OUT_EMPTY;
		case 'f': case 'F':
			return DEVICE_OUT_FILE;
		case 'n': case 'N':
			return DEVICE_OUT_NULL;
		case 'u': case 'U':
			return DEVICE_OUT_UDP;
		case 's':case 'S':
			return DEVICE_OUT_SCREEN;
		case 'v':case 'V':
			return DEVICE_OUT_VIRTUAL;
		}
		return DEVICE_OUT_NULL;
	}

	inline std::pair<long long, const char*> ParseAddresIP(const char* begin, const char* end, bool parse_dn)
	{
		if (end <= begin)
		{
			return std::make_pair(0, end);
		}
		//only support ipv4 
		const char* ip_begin = begin;
		while (IsBlank(*ip_begin) && ip_begin != end)
		{
			ip_begin++;
		}
		const char* ip_end = ip_begin;
		bool is_dn = false;
		while (!(IsBlank(*ip_end) || *ip_end == ':') && ip_end != end)
		{
			if (!(*ip_end >= '0' && *ip_end <= '9') && *ip_end != '.')
			{
				is_dn = true;
			}
			ip_end++;
		}

		if (ip_end - ip_begin <= 0)
		{
			return std::make_pair(0, end);
		}
		std::string dn(ip_begin, ip_end - ip_begin);

		if (is_dn) //syn to get    
		{
			if (false)
			{
				struct addrinfo* res = nullptr;
				struct addrinfo hints;
				memset(&hints, 0, sizeof(hints));
				hints.ai_family = AF_UNSPEC;
				hints.ai_socktype = SOCK_STREAM;
				hints.ai_flags = AI_PASSIVE;

				if (getaddrinfo(dn.c_str(), "", &hints, &res) == 0)
				{
					char buf[100] = { 0 };
					if (res->ai_family == AF_INET)
					{
						inet_ntop(res->ai_family, &(((sockaddr_in*)res->ai_addr)->sin_addr), buf, 100);
					}
					else if (res->ai_family == AF_INET6)
					{
						inet_ntop(res->ai_family, &(((sockaddr_in6*)res->ai_addr)->sin6_addr), buf, 100);
					}
					return std::make_pair((long long)inet_addr(buf), ip_end);
				}
			}
			if (parse_dn)
			{
				struct hostent* rhost = gethostbyname(dn.c_str());
				if (rhost == nullptr)
				{
					return std::make_pair(0, end);
				}
				if (rhost->h_addrtype == AF_INET)
				{
					int i = 0;
					struct in_addr addr;
					while (rhost->h_addr_list[i] != 0)
					{
						addr.s_addr = *(u_long*)rhost->h_addr_list[i++];
						return std::make_pair((long long)inet_addr(inet_ntoa(addr)), ip_end);
						//printf("%s", inet_ntoa(addr));
					}
				}

			}
			return std::make_pair(0, end);
		}


		return std::make_pair((long long)inet_addr(dn.c_str()), ip_end);
	}


	inline void ParseAddres(const char* begin, const char* end, bool parse_dn, long long & ip, long long& port)
	{
		ip = 0;
		port = 0;
		if (end <= begin)
		{
			return;
		}
		auto result_ip = ParseAddresIP(begin, end, parse_dn);
		const char* port_begin = result_ip.second;
		while (port_begin != end && (*port_begin < '1' || *port_begin > '9'))
		{
			port_begin++;
		}
		if (port_begin >= end)
		{
			return;
		}
		if (end - port_begin >= 40)
		{
			return;
		}
		char buff[50];
		memcpy(buff, port_begin, end - port_begin);
		buff[end - port_begin] = '\0';
		port = htons((unsigned short)atoi(buff));
		ip = result_ip.first;
		return;
	}

	inline unsigned long long ParseBitArray(const char* begin, const char* end)
	{
		unsigned long long bitmap = 0;
		if (end <= begin)
		{
			return bitmap;
		}
		const char* offset = begin;
		while (offset < end)
		{
			while (offset < end && !IsNumber(*offset))
			{
				offset++;
			}
			if (offset == end)
			{
				break;
			}

			int bit_offset_first = atoi(offset);
			while (offset < end && (IsNumber(*offset) || IsBlank(*offset)))
			{
				offset++;
			}

			if (offset == end || *offset != '-') //
			{
				bitmap |= (1ULL << bit_offset_first);
				continue;
			}

			while (offset < end && !IsNumber(*offset) && *offset != ',')
			{
				offset++;
			}

			if (offset == end || !IsNumber(*offset))
			{
				bitmap |= (1ULL << bit_offset_first);
				continue;
			}

			int bit_offset_last = atoi(offset);
			for (int i = (std::min)(bit_offset_first, bit_offset_last); i <= (std::max)(bit_offset_first, bit_offset_last); i++)
			{
				bitmap |= (1ULL << i);
			}
		}
		return bitmap;
	}
	inline int PredefinedInplace(std::string& text, std::string::size_type text_offset, std::string key, std::string val, bool pre_bound, bool suffix_bound)
	{
		if (val.length() > key.length())
		{
			return PEC_DEFINED_TARGET_TOO_LONG;
		}

		//fixed len
		while (val.length() < key.length())
		{
			val.push_back(' ');
		}

		//replace 
		while (true)
		{
			text_offset = text.find(key, text_offset);
			if (text_offset == std::string::npos)
			{
				//finish 
				break;
			}
			bool bound_ok = true;
			while (bound_ok && pre_bound)
			{
				if (text_offset == 0)
				{
					break;
				}
				char ch = text[text_offset - 1];
				if (IsValidName(ch))
				{
					bound_ok = false;
					text_offset++;
					break;
				}
				break;
			}

			while (bound_ok && suffix_bound)
			{
				if (text_offset + val.length() == text.length())
				{
					break;
				}
				char ch = text[text_offset + val.length()];
				if (IsValidName(ch))
				{
					bound_ok = false;
					text_offset++;
					break;
				}
				break;
			}

			if (bound_ok)
			{
				memcpy(&text[text_offset], val.c_str(), val.length());
			}
		};
		return 0;
	}
	inline int PredefinedMacro(LexState& ls, std::string& text)
	{
		std::string line(ls.line_.val_begin_, ls.line_.val_end_ - ls.line_.val_begin_);
		std::string symbol;
		std::string::size_type sep = line.find(' ');
		if (sep == std::string::npos)
		{
			return PEC_NOT_CLOSURE;
		}
		symbol = line.substr(0, sep);
		if (symbol.empty())
		{
			return PEC_NOT_CLOSURE;
		}

		while (sep < line.length())
		{
			if (IsBlank(line[sep]))
			{
				sep++;
				continue;
			}
			break;
		}
		std::string content = line.substr(sep);

		int ret = PredefinedInplace(text, ls.line_.val_end_ - text.c_str(), symbol, content, true, true);
		if (ret != PEC_NONE)
		{
			return ret;
		}
		return PEC_NONE;
	}

	inline int PredefinedVar(LexState& ls, std::string& text)
	{
		std::string line(ls.line_.val_begin_, ls.line_.val_end_ - ls.line_.val_begin_);
		std::string::size_type offset = 0;
		std::string var;
		std::string val;
		while (offset < line.length())
		{
			if (line.at(offset) == ' ' || line.at(offset) == '\t')
			{
				offset++;
				continue;
			}

			std::string::size_type dot = line.find(',', offset);
			if (dot == std::string::npos)
			{
				dot = line.length();
			}

			std::string::size_type sep = line.find('=', offset);
			if (sep >= dot)
			{
				offset = dot + 1;
				return PEC_ILLEGAL_VAR_VALUE;
			}
			var = line.substr(offset, sep - offset);
			while (!var.empty() && IsBlank(var.back()))
			{
				var.pop_back();
			}

			if (var.empty())
			{
				//has dot but wrong
				offset = dot + 1;
				//continue;
				return PEC_DEFINED_TARGET_TOO_LONG;
			}

			if (!IsValidNameFirst(var[0]))
			{
				//has dot but wrong
				offset = dot + 1;
				//continue;
				return PEC_ILLEGAL_VAR_NAME;
			}

			sep++;
			while (sep < dot &&  IsBlank(line.at(offset)))
			{
				sep++;
			}
			val = line.substr(sep, dot - sep);
			while (!val.empty() && IsBlank(val.back()))
			{
				val.pop_back();
			}



			std::string::size_type text_offset = ls.line_.val_end_ - text.c_str();

			int ret = PredefinedInplace(text, text_offset, std::string("${") + var + "}", val, false, false);
			if (ret != 0)
			{
				offset = dot + 1;
				return ret;
			}
			ret = PredefinedInplace(text, text_offset, std::string("$") + var, val, false, true);
			if (ret != 0)
			{
				offset = dot + 1;
				return ret;
			}

			offset = dot + 1;
		}

		return PEC_NONE;
	}

	inline void InitState(LexState& state)
	{
		//static_assert(std::is_trivial<LexState>::value, "");
		memset(&state, 0, sizeof(state));
	}

	inline int Lex(LexState& ls)
	{
		memset(&ls.line_, 0, sizeof(ls.line_));
		if (ls.current_ >= ls.end_)
		{
			if (ls.line_no_ == 0)
			{
				ls.line_no_ = 1;
			}
			return PEC_NONE;
		}
		while (true)
		{
			char ch = *ls.current_++;
			ls.line_.chars_++;

			bool is_blank = IsBlank(ch);
			bool is_end_char = IsSoftLineBound(ch);

			if (ls.line_.block_type_ == BLOCK_CLEAN && !IsLineBound(ch))
			{
				continue;
			}

			if (is_end_char)
			{
				if (ls.line_.block_type_ == BLOCK_VAL)
				{
					if (ls.line_.block_stack_ != '\0')
					{
						return PEC_NOT_CLOSURE;
					}

					ls.line_.block_type_ = BLOCK_CLEAN;
					ls.line_.val_end_ = ls.current_ - 1;
				}

				//no value 
				if (ls.line_.block_type_ == BLOCK_PRE_VAL)
				{
					ls.line_.block_type_ = BLOCK_CLEAN;
					ls.line_.val_begin_ = ls.current_ - 1;
					ls.line_.val_end_ = ls.current_ - 1;
				}

				if (ls.line_.block_type_ != BLOCK_NONE && ls.line_.block_type_ != BLOCK_CLEAN)
				{
					return PEC_NOT_CLOSURE;
				}

				while (ls.line_.val_end_ > ls.line_.val_begin_)
				{
					char suffix = *(ls.line_.val_end_ - 1);
					if (IsBlank(suffix))
					{
						ls.line_.val_end_--;
						continue;
					}
					break;
				}


				if (ch == '\r' || ch == '\n')
				{
					if ((*ls.current_ == '\r' || *ls.current_ == '\n') && *ls.current_ != ch)
					{
						ls.current_++;//jump win rt  
					}
					ls.line_no_++;
					return PEC_NONE;
				}

				if (ch == '\0')
				{
					ls.current_--; //safe; set current referer to '\0'   
					ls.line_no_++; //last line compensate that the line no aways ref valid lex line no.  
					return PEC_NONE;
				}

				if (ch == '#')
				{
					ls.line_.block_type_ = BLOCK_CLEAN;
					continue;
				}
			}


			if (ls.line_.block_type_ == BLOCK_NONE)
			{
				if (is_blank)
				{
					if (ls.line_.blank_ == ls.line_.chars_ - 1)
					{
						ls.line_.blank_++;
					}
					continue;
				}
				//ignore array char 
				if (ch == '-')
				{
					continue;
				}

				//first char 
				if (ch < 'a' || ch > 'z')
				{
					return PEC_ILLEGAL_KEY;
				}
				ls.line_.block_type_ = BLOCK_KEY;
				ls.line_.key_begin_ = ls.current_ - 1;
			}

			//key must in [a-z_0-9]
			if (ls.line_.block_type_ == BLOCK_KEY)
			{
				if ((ch >= 'a' && ch <= 'z') || ch == '_' || (ch >= '0' && ch <= '9'))
				{
					continue;
				}
				ls.line_.block_type_ = BLOCK_PRE_SEP;
				ls.line_.key_end_ = ls.current_ - 1;
				ls.line_.key_ = ParseReserve(ls.line_.key_begin_, ls.line_.key_end_);
				if (ls.line_.key_ == RK_NULL)
				{
					return PEC_ILLEGAL_KEY;
				}

			}


			if (ls.line_.block_type_ == BLOCK_PRE_SEP)
			{
				if (ch == ':')
				{
					ls.line_.block_type_ = BLOCK_PRE_VAL;
					continue;
				}
				//not support yaml '-' array 
				if (ch != ' ' && ch != '\t')
				{
					return PEC_NONSUPPORT_SYNTAX;
				}
				continue;
			}

			if (ls.line_.block_type_ == BLOCK_PRE_VAL)
			{
				if (IsBlank(ch))
				{
					continue;
				}
				if (ch == '\"')
				{
					ls.line_.block_stack_ = '\"';
					ls.line_.block_type_ = BLOCK_VAL;
					ls.line_.val_begin_ = ls.current_;
					continue;
				}
				if (ch == '[')
				{
					ls.line_.block_stack_ = ']';
					ls.line_.block_type_ = BLOCK_VAL;
					ls.line_.val_begin_ = ls.current_;
					continue;
				}
				if (ch == '{')
				{
					ls.line_.block_stack_ = '}';
					ls.line_.block_type_ = BLOCK_VAL;
					ls.line_.val_begin_ = ls.current_;
					continue;
				}
				ls.line_.block_stack_ = '\0';
				ls.line_.block_type_ = BLOCK_VAL;
				ls.line_.val_begin_ = ls.current_ - 1;
			}


			if (ls.line_.block_type_ == BLOCK_VAL)
			{
				if (ls.line_.val_begin_ == ls.current_ - 1)
				{
					//trim blank begin "{[  
					if (is_blank)
					{
						ls.line_.val_begin_ = ls.current_;
						continue;
					}
				}

				if (ls.line_.block_stack_ != '\0' && ch == ls.line_.block_stack_)
				{
					ls.line_.block_type_ = BLOCK_CLEAN;
					ls.line_.val_end_ = ls.current_ - 1;
					continue;
				}



				continue;
			}

		}
		return PEC_ERROR;
	}

	inline int ParseDevice(LexState& ls, Device& device, int indent)
	{
		do
		{
			const char* current = ls.current_;
			int ret = Lex(ls);
			if (ret != PEC_NONE)
			{
				ls.current_ = current;
				return ret;
			}

			if (ls.line_.key_end_ - ls.line_.key_begin_ == 0)
			{
				if (ls.current_ >= ls.end_)
				{
					//eof 
					return ret;
				}
				//blank line 
				continue;
			}

			if (ls.line_.blank_ <= indent)
			{
				ls.current_ = current;
				return 0;
			}

			switch (ls.line_.key_)
			{
			case RK_IN_TYPE:
				device.in_type_ = ParseInType(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_OUT_TYPE:
				device.out_type_ = ParseOutType(ls.line_.val_begin_, ls.line_.val_end_);
				if (device.out_type_ == DEVICE_OUT_NULL)
				{
					return PEC_UNDEFINED_DEVICE_TYPE;
				}
				break;
			case RK_DISABLE:
				device.config_fields_[DEVICE_CFG_ABLE] = !ParseBool(ls.line_.val_begin_, ls.line_.val_end_); //"disable"
				break;
			case RK_PRIORITY:
				device.config_fields_[DEVICE_CFG_PRIORITY] = ParsePriority(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_CATEGORY:
				device.config_fields_[DEVICE_CFG_CATEGORY] = atoll(ls.line_.val_begin_);
				break;
			case RK_CATEGORY_EXTEND:
				device.config_fields_[DEVICE_CFG_CATEGORY_EXTEND] = atoll(ls.line_.val_begin_);
				break;
			case RK_CATEGORY_WLIST:
				device.config_fields_[DEVICE_CFG_CATEGORY_MASK] = ParseBitArray(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_CATEGORY_BLIST:
				device.config_fields_[DEVICE_CFG_CATEGORY_MASK] = ~ParseBitArray(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_CATEGORY_WMASK:
				device.config_fields_[DEVICE_CFG_CATEGORY_MASK] = atoll(ls.line_.val_begin_);
				break;
			case RK_CATEGORY_BMASK:
				device.config_fields_[DEVICE_CFG_CATEGORY_MASK] = ~atoll(ls.line_.val_begin_);
				break;
			case RK_IDENTIFY:
				device.config_fields_[DEVICE_CFG_IDENTIFY] = atoll(ls.line_.val_begin_);
				break;
			case RK_IDENTIFY_EXTEND:
				device.config_fields_[DEVICE_CFG_IDENTIFY_EXTEND] = atoll(ls.line_.val_begin_);
				break;
			case RK_IDENTIFY_WLIST:
				device.config_fields_[DEVICE_CFG_IDENTIFY_MASK] = ParseBitArray(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_IDENTIFY_BLIST:
				device.config_fields_[DEVICE_CFG_IDENTIFY_MASK] = ~ParseBitArray(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_IDENTIFY_WMASK:
				device.config_fields_[DEVICE_CFG_IDENTIFY_MASK] = atoll(ls.line_.val_begin_);
				break;
			case RK_IDENTIFY_BMASK:
				device.config_fields_[DEVICE_CFG_IDENTIFY_MASK] = ~atoll(ls.line_.val_begin_);
				break;
			case RK_LIMIT_SIZE:
				device.config_fields_[DEVICE_CFG_FILE_LIMIT_SIZE] = atoll(ls.line_.val_begin_) * 1000 * 1000;
				break;
			case RK_ROLLBACK:
				device.config_fields_[DEVICE_CFG_FILE_ROLLBACK] = atoll(ls.line_.val_begin_);
				break;
			case RK_ROLLDAILY:
				device.config_fields_[DEVICE_CFG_FILE_ROLLDAILY] = ParseBool(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_ROLLHOURLY:
				device.config_fields_[DEVICE_CFG_FILE_ROLLHOURLY] = ParseBool(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_FILE_STUFF_UP:
				device.config_fields_[DEVICE_CFG_FILE_STUFF_UP] = ParseBool(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_PATH:
				if (ls.line_.val_end_ - ls.line_.val_begin_ < Device::MAX_PATH_LEN - 1
					&& ls.line_.val_end_ - ls.line_.val_begin_ >= 1)
				{
					memcpy(device.out_path_, ls.line_.val_begin_, ls.line_.val_end_ - ls.line_.val_begin_);
					device.out_path_[ls.line_.val_end_ - ls.line_.val_begin_] = '\0';
				}
				break;
			case RK_FILE:
				if (ls.line_.val_end_ - ls.line_.val_begin_ < Device::MAX_LOGGER_NAME_LEN - 1
					&& ls.line_.val_end_ - ls.line_.val_begin_ >= 1)
				{
					memcpy(device.out_file_, ls.line_.val_begin_, ls.line_.val_end_ - ls.line_.val_begin_);
					device.out_file_[ls.line_.val_end_ - ls.line_.val_begin_] = '\0';
				}
				break;
			case RK_UDP_ADDR:
				if (true)
				{
					long long ip = 0;
					long long port = 0;
					bool parse_dn = device.config_fields_[DEVICE_CFG_ABLE] != 0;
					ParseAddres(ls.line_.val_begin_, ls.line_.val_end_, parse_dn, ip, port);
					device.config_fields_[DEVICE_CFG_UDP_IP] = ip;
					device.config_fields_[DEVICE_CFG_UDP_PORT] = port;
				}
				if (device.config_fields_[DEVICE_CFG_ABLE])
				{
					if (device.in_type_ == DEVICE_IN_NULL && device.config_fields_[DEVICE_CFG_UDP_IP] == 0)
					{
						return PEC_ILLEGAL_ADDR_IP;
					}

					if (device.config_fields_[DEVICE_CFG_UDP_PORT] == 0)
					{
						return PEC_ILLEGAL_ADDR_PORT;
					}
				}


				break;
			default:
				return PEC_UNDEFINED_DEVICE_KEY;
			}
		} while (ls.current_ < ls.end_);
		return 0;
	}
	inline int ParseChannel(LexState& ls, Channel& channel, int indent)
	{
		do
		{
			const char* current = ls.current_;
			int ret = Lex(ls);
			if (ret != PEC_NONE)
			{
				ls.current_ = current;
				return ret;
			}
			if (ls.line_.key_end_ - ls.line_.key_begin_ == 0)
			{
				if (ls.current_ >= ls.end_)
				{
					return ret;
				}
				continue;
			}

			if (ls.line_.blank_ <= indent)
			{
				ls.current_ = current;
				return 0;
			}

			switch (ls.line_.key_)
			{
			case RK_SYNC:
				channel.channel_type_ = ParseChannelType(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_PRIORITY:
				channel.config_fields_[CHANNEL_CFG_PRIORITY] = ParsePriority(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_CATEGORY:
				channel.config_fields_[CHANNEL_CFG_CATEGORY] = atoi(ls.line_.val_begin_);
				break;
			case RK_CATEGORY_EXTEND:
				channel.config_fields_[CHANNEL_CFG_CATEGORY_EXTEND] = atoi(ls.line_.val_begin_);
				break;
			case RK_CATEGORY_WLIST:
				channel.config_fields_[CHANNEL_CFG_CATEGORY_MASK] = ParseBitArray(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_CATEGORY_BLIST:
				channel.config_fields_[CHANNEL_CFG_CATEGORY_MASK] = ~ParseBitArray(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_CATEGORY_WMASK:
				channel.config_fields_[CHANNEL_CFG_CATEGORY_MASK] = atoll(ls.line_.val_begin_);
				break;
			case RK_CATEGORY_BMASK:
				channel.config_fields_[CHANNEL_CFG_CATEGORY_MASK] = ~atoll(ls.line_.val_begin_);
				break;
			case RK_IDENTIFY:
				channel.config_fields_[CHANNEL_CFG_IDENTIFY] = atoi(ls.line_.val_begin_);
				break;
			case RK_IDENTIFY_EXTEND:
				channel.config_fields_[CHANNEL_CFG_IDENTIFY_EXTEND] = atoi(ls.line_.val_begin_);
				break;
			case RK_IDENTIFY_WLIST:
				channel.config_fields_[CHANNEL_CFG_IDENTIFY_MASK] = ParseBitArray(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_IDENTIFY_BLIST:
				channel.config_fields_[CHANNEL_CFG_IDENTIFY_MASK] = ~ParseBitArray(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_IDENTIFY_WMASK:
				channel.config_fields_[CHANNEL_CFG_IDENTIFY_MASK] = atoll(ls.line_.val_begin_);
				break;
			case RK_IDENTIFY_BMASK:
				channel.config_fields_[CHANNEL_CFG_IDENTIFY_MASK] = ~atoll(ls.line_.val_begin_);
				break;

			case RK_DEVICE:
			{
				int device_id = atoi(ls.line_.val_begin_);
				if (channel.device_size_ >= Channel::MAX_DEVICE_SIZE)
				{
					return E_INVALID_DEVICE_SIZE;
				}
				if (device_id != channel.device_size_)
				{
					return E_DEVICE_NOT_SEQUENCE;
				}
				Device& device = channel.devices_[channel.device_size_++];
				memset(&device, 0, sizeof(device));
				device.device_id_ = device_id;
				ret = ParseDevice(ls, device, ls.line_.blank_);
				if (device.out_type_ == DEVICE_OUT_VIRTUAL)
				{
					channel.virtual_device_id_ = device.device_id_;
				}
				if (ret != PEC_NONE)
				{
					return ret;
				}
			}
			break;
			default:
				return PEC_UNDEFINED_CHANNEL_KEY;
			}

		} while (ls.current_ < ls.end_);
		return 0;
	}

	inline int ParseLogger(LexState& ls, std::string& text)
	{
		//UTF8 BOM 
		const char* first = &text[0];
		if (text.size() >= 3)
		{
			if ((unsigned char)text[0] == 0xEF && (unsigned char)text[1] == 0xBB && (unsigned char)text[2] == 0xBF)
			{
				first += 3;
			}
		}
		ls.first_ = first;
		ls.end_ = first + text.length();
		memset(&ls.channels_, 0, sizeof(ls.channels_));
		ls.channel_size_ = 0;
		ls.hot_update_ = false;
		ls.current_ = ls.first_;
		ls.line_no_ = 0;
		ls.desc_len_ = 0;
		ls.name_len_ = 0;
		do
		{
			const char* current = ls.current_;
			int ret = Lex(ls);
			if (ret != PEC_NONE)
			{
				ls.current_ = current;
				return ret;
			}
			if (ls.line_.key_end_ - ls.line_.key_begin_ == 0)
			{
				if (ls.current_ >= ls.end_)
				{
					return ret;
				}
				continue;
			}

			switch (ls.line_.key_)
			{
			case RK_HOT_UPDATE:
				ls.hot_update_ = ParseBool(ls.line_.val_begin_, ls.line_.val_end_);//"disable"
				break;
			case RK_DEFINE:
				//do nothing  
				ret = PredefinedMacro(ls, text);
				if (ret != PEC_NONE)
				{
					return ret;
				}
				break;
			case RK_VARIABLE:
				//do nothing  
				ret = PredefinedVar(ls, text);
				if (ret != PEC_NONE)
				{
					return ret;
				}
				break;
			case RK_LOGGER_NAME:
				ParseString(ls.line_.val_begin_, ls.line_.val_end_, ls.name_, Logger::MAX_LOGGER_NAME_LEN, ls.name_len_);
				break;
			case PK_LOGGER_DESC:
				ParseString(ls.line_.val_begin_, ls.line_.val_end_, ls.desc_, Logger::MAX_LOGGER_DESC_LEN, ls.desc_len_);
				break;
			case RK_SHM_KEY:
				ls.shm_key_ = ParseNumber(ls.line_.val_begin_, ls.line_.val_end_);
				break;
			case RK_CHANNEL:
			{
				int channel_id = atoi(ls.line_.val_begin_);
				if (ls.channel_size_ >= Logger::MAX_CHANNEL_SIZE)
				{
					return E_INVALID_CHANNEL_SIZE;
				}
				if (ls.channel_size_ != channel_id)
				{
					return E_CHANNEL_NOT_SEQUENCE;
				}
				Channel& channel = ls.channels_[ls.channel_size_++];
				memset(&channel, 0, sizeof(channel));
				channel.channel_id_ = channel_id;
				ret = ParseChannel(ls, channel, ls.line_.blank_);
				if (ret != 0)
				{
					return ret;
				}
			}
			break;
			default:
				return PEC_UNDEFINED_GLOBAL_KEY;
			}
		} while (ls.current_ < ls.end_);
		if (ls.channel_size_ == 0)
		{
			return E_INVALID_CHANNEL_SIZE;
		}
		return PEC_NONE;
	}

#if __GNUG__ && __GNUC__ >= 6
#pragma GCC diagnostic pop
#endif

}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_FMT_H_
#define _FN_LOG_FMT_H_



namespace FNLog
{


#ifndef WIN32
	struct PriorityRender
	{
		const char* const priority_name_;
		int priority_len_;
		const char* const scolor_;
	};
	static const PriorityRender PRIORITY_RENDER[] =
	{
		{ "[TRACE]", sizeof("[TRACE]") - 1, "\e[0m", },
		{ "[DEBUG]", sizeof("[DEBUG]") - 1, "\e[0m", },
		{ "[INFO ]", sizeof("[INFO ]") - 1, "\e[34m\e[1m",/*hight blue*/ },
		{ "[WARN ]", sizeof("[WARN ]") - 1, "\e[33m", /*yellow*/ },
		{ "[ERROR]", sizeof("[ERROR]") - 1, "\e[31m", /*red*/ },
		{ "[ALARM]", sizeof("[ALARM]") - 1, "\e[32m", /*green*/ },
		{ "[FATAL]", sizeof("[FATAL]") - 1, "\e[35m", },
	};
#else
	struct PriorityRender
	{
		const char* const priority_name_;
		int priority_len_;
		const WORD color_;
	};
	static const PriorityRender PRIORITY_RENDER[] =
	{
		{ "[TRACE]", sizeof("[TRACE]") - 1,  FOREGROUND_INTENSITY, },
		{ "[DEBUG]", sizeof("[DEBUG]") - 1,  FOREGROUND_INTENSITY, },
		{ "[INFO ]", sizeof("[INFO ]") - 1,  FOREGROUND_BLUE | FOREGROUND_GREEN, },
		{ "[WARN ]", sizeof("[WARN ]") - 1,  FOREGROUND_GREEN | FOREGROUND_RED, },
		{ "[ERROR]", sizeof("[ERROR]") - 1,  FOREGROUND_RED, },
		{ "[ALARM]", sizeof("[ALARM]") - 1,  FOREGROUND_GREEN, },
		{ "[FATAL]", sizeof("[FATAL]") - 1,  FOREGROUND_RED | FOREGROUND_BLUE, },
	};
#endif



	static_assert(PRIORITY_TRACE == 0, "");
	static_assert(sizeof(PRIORITY_RENDER) / sizeof(PriorityRender) == PRIORITY_MAX, "");


	template<int WIDE>
	int write_bin_unsafe(char* dst, unsigned long long number);
	template<int WIDE>
	int write_dec_unsafe(char* dst, unsigned long long number);
	template<int WIDE>
	int write_hex_unsafe(char* dst, unsigned long long number);

	template<int WIDE>
	int write_bin_unsafe(char* dst, long long number)
	{
		return write_bin_unsafe<WIDE>(dst, (unsigned long long) number);
	}
	template<int WIDE>
	int write_hex_unsafe(char* dst, long long number)
	{
		return write_hex_unsafe<WIDE>(dst, (unsigned long long) number);
	}
	template<int WIDE>
	int write_dec_unsafe(char* dst, long long number)
	{
		if (number < 0)
		{
			*dst = '-';
			number = -number;
			return 1 + write_dec_unsafe<WIDE - 1>(dst + 1, (unsigned long long) number);
		}
		return write_dec_unsafe<WIDE>(dst, (unsigned long long) number);
	}


	template<int WIDE>
	int write_dec_unsafe(char* dst, unsigned long long number)
	{
		static const char* dec_lut =
			"00010203040506070809"
			"10111213141516171819"
			"20212223242526272829"
			"30313233343536373839"
			"40414243444546474849"
			"50515253545556575859"
			"60616263646566676869"
			"70717273747576777879"
			"80818283848586878889"
			"90919293949596979899";

		static const int buf_len = 30;
		char buf[buf_len];
		int write_index = buf_len;
		unsigned long long m1 = 0;
		unsigned long long m2 = 0;
		do
		{
			m1 = number / 100;
			m2 = number % 100;
			m2 += m2;
			number = m1;
			*(buf + write_index - 1) = dec_lut[m2 + 1];
			*(buf + write_index - 2) = dec_lut[m2];
			write_index -= 2;
		} while (number);
		if (buf[write_index] == '0')
		{
			write_index++;
		}
		while (buf_len - write_index < WIDE)
		{
			write_index--;
			buf[write_index] = '0';
		}
		memcpy(dst, buf + write_index, buf_len - write_index);
		return buf_len - write_index;
	}


	template<int WIDE>
	int write_hex_unsafe(char* dst, unsigned long long number)
	{
		static const char* lut =
			"0123456789ABCDEFGHI";
		static const char* hex_lut =
			"000102030405060708090A0B0C0D0E0F"
			"101112131415161718191A1B1C1D1E1F"
			"202122232425262728292A2B2C2D2E2F"
			"303132333435363738393A3B3C3D3E3F"
			"404142434445464748494A4B4C4D4E4F"
			"505152535455565758595A5B5C5D5E5F"
			"606162636465666768696A6B6C6D6E6F"
			"707172737475767778797A7B7C7D7E7F"
			"808182838485868788898A8B8C8D8E8F"
			"909192939495969798999A9B9C9D9E9F"
			"A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
			"B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
			"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
			"D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
			"E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
			"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF";



		int real_wide = 0;
#ifndef WIN32
		real_wide = sizeof(number) * 8 - __builtin_clzll(number);
#else
		unsigned long win_index = 0;
		if (_BitScanReverse(&win_index, (unsigned long)(number >> 32)))
		{
			real_wide = win_index + 1 + 32;
		}
		else if (_BitScanReverse(&win_index, (unsigned long)(number & 0xffffffff)))
		{
			real_wide = win_index + 1;
		}
#endif 
		switch (real_wide)
		{
		case  1:case  2:case  3:case  4:real_wide = 1; break;
		case  5:case  6:case  7:case  8:real_wide = 2; break;
		case 9: case 10:case 11:case 12:real_wide = 3; break;
		case 13:case 14:case 15:case 16:real_wide = 4; break;
		case 17:case 18:case 19:case 20:real_wide = 5; break;
		case 21:case 22:case 23:case 24:real_wide = 6; break;
		case 25:case 26:case 27:case 28:real_wide = 7; break;
		case 29:case 30:case 31:case 32:real_wide = 8; break;
		case 33:case 34:case 35:case 36:real_wide = 9; break;
		case 37:case 38:case 39:case 40:real_wide = 10; break;
		case 41:case 42:case 43:case 44:real_wide = 11; break;
		case 45:case 46:case 47:case 48:real_wide = 12; break;
		case 49:case 50:case 51:case 52:real_wide = 13; break;
		case 53:case 54:case 55:case 56:real_wide = 14; break;
		case 57:case 58:case 59:case 60:real_wide = 15; break;
		case 61:case 62:case 63:case 64:real_wide = 16; break;
		}
		if (real_wide < WIDE)
		{
			real_wide = WIDE;
		}
		unsigned long long cur_wide = real_wide;
		while (number && cur_wide >= 2)
		{
			const unsigned long long m2 = (unsigned long long)((number % 256) * 2);
			number /= 256;
			*(dst + cur_wide - 1) = hex_lut[m2 + 1];
			*(dst + cur_wide - 2) = hex_lut[m2];
			cur_wide -= 2;
		}
		if (number)
		{
			*dst = lut[number % 16];
			cur_wide--;
		}
		while (cur_wide-- != 0)
		{
			*(dst + cur_wide) = '0';
		}
		return real_wide;
	}


	template<int WIDE>
	int write_bin_unsafe(char* dst, unsigned long long number)
	{
		static const char* lut =
			"0123456789abcdefghijk";

		int real_wide = 0;
#ifndef WIN32
		real_wide = sizeof(number) * 8 - __builtin_clzll(number);
#else
		unsigned long win_index = 0;
		_BitScanReverse64(&win_index, number);
		real_wide = (int)win_index + 1;
#endif 
		if (real_wide < WIDE)
		{
			real_wide = WIDE;
		}
		unsigned long long cur_wide = real_wide;
		do
		{
			const unsigned long long m2 = number & 1;
			number >>= 1;
			*(dst + cur_wide - 1) = lut[m2];
			cur_wide--;
		} while (number);
		return real_wide;
	}

	inline int write_double_unsafe(char* dst, double number)
	{
		int fp_class = std::fpclassify(number);
		switch (fp_class)
		{
		case FP_SUBNORMAL:
		case FP_ZERO:
			*dst = '0';
			return 1;
		case FP_INFINITE:
			memcpy(dst, "inf", 3);
			return 3;
		case FP_NAN:
			memcpy(dst, "nan", 3);
			return 3;
		case FP_NORMAL:
			break;
		default:
			return 0;
		}


		double fabst = std::fabs(number);
		if (fabst < 0.0001 || fabst > 0xFFFFFFFFFFFFFFFULL)
		{
			if (fabst < 0.0001 && fabst > 0.0000001)
			{
				sprintf(dst, "%.08lf", fabst);
			}
			else
			{
				char* buf = gcvt(number, 16, dst);
				(void)buf;
			}
			return (int)strlen(dst);
		}
		bool is_neg = std::signbit(number);
		int neg_offset = 0;
		if (is_neg)
		{
			*dst = '-';
			neg_offset = 1;
		}

		double intpart = 0;
		unsigned long long fractpart = (unsigned long long)(modf(fabst, &intpart) * 10000);
		int base_offset = write_dec_unsafe<0>(dst + neg_offset, (unsigned long long)intpart);
		if (fractpart > 0)
		{
			*(dst + neg_offset + base_offset) = '.';
			int fractpat_offset = 1 + write_dec_unsafe<4>(dst + neg_offset + base_offset + 1, (unsigned long long)fractpart);
			for (int i = neg_offset + base_offset + fractpat_offset - 1; i > neg_offset + base_offset + 2; i--)
			{
				if (*(dst + i) == '0')
				{
					fractpat_offset--;
					continue;
				}
				break;
			}
			return neg_offset + base_offset + fractpat_offset;
		}
		return neg_offset + base_offset;
	}

	inline int write_float_unsafe(char* dst, float number)
	{
		return write_double_unsafe(dst, number);
	}

	inline int write_date_unsafe(char* dst, long long timestamp, unsigned int precise)
	{
		static thread_local tm cache_date = { 0 };
		static thread_local long long cache_timestamp = 0;
		static const char date_fmt[] = "[20190412 13:05:35.417]";
		long long day_second = timestamp - cache_timestamp;
		if (day_second < 0 || day_second >= 24 * 60 * 60)
		{
			cache_date = FileHandler::time_to_tm((time_t)timestamp);
			struct tm daytm = cache_date;
			daytm.tm_hour = 0;
			daytm.tm_min = 0;
			daytm.tm_sec = 0;
			cache_timestamp = mktime(&daytm);
			day_second = timestamp - cache_timestamp;
		}
		int write_bytes = 0;

		*(dst + write_bytes++) = '[';

		write_bytes += write_dec_unsafe<4>(dst + write_bytes, (unsigned long long)cache_date.tm_year + 1900);
		write_bytes += write_dec_unsafe<2>(dst + write_bytes, (unsigned long long)cache_date.tm_mon + 1);
		write_bytes += write_dec_unsafe<2>(dst + write_bytes, (unsigned long long)cache_date.tm_mday);

		*(dst + write_bytes++) = ' ';

		write_bytes += write_dec_unsafe<2>(dst + write_bytes, (unsigned long long)day_second / 3600);
		*(dst + write_bytes++) = ':';
		day_second %= 3600;
		write_bytes += write_dec_unsafe<2>(dst + write_bytes, (unsigned long long)day_second / 60);
		*(dst + write_bytes++) = ':';
		day_second %= 60;
		write_bytes += write_dec_unsafe<2>(dst + write_bytes, (unsigned long long)day_second);

		*(dst + write_bytes++) = '.';
		if (precise >= 1000)
		{
			precise = 999;
		}
		write_bytes += write_dec_unsafe<3>(dst + write_bytes, (unsigned long long)precise);

		*(dst + write_bytes++) = ']';

		if (write_bytes != sizeof(date_fmt) - 1)
		{
			return 0;
		}

		return write_bytes;
	}

	inline int write_log_priority_unsafe(char* dst, int priority)
	{
		priority = priority % PRIORITY_MAX;
		memcpy(dst, PRIORITY_RENDER[priority].priority_name_, PRIORITY_RENDER[priority].priority_len_);
		return PRIORITY_RENDER[priority].priority_len_;
	}

	inline int write_log_thread_unsafe(char* dst, unsigned int thread_id)
	{
		int write_bytes = 0;
		*(dst + write_bytes) = '[';
		write_bytes++;
		write_bytes += write_dec_unsafe<0>(dst + write_bytes, (unsigned long long) thread_id);
		*(dst + write_bytes) = ']';
		write_bytes++;
		return write_bytes;
	}

	inline int write_pointer_unsafe(char* dst, const void* ptr)
	{
		if (ptr == nullptr)
		{
			memcpy(dst, "null", 4);
			return 4;
		}
		int write_bytes = 2;
		memcpy(dst, "0x", 2);
		write_bytes += write_hex_unsafe<0>(dst + write_bytes, (unsigned long long) ptr);
		return write_bytes;
	}

}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/

#pragma once
#ifndef _FN_LOG_LOAD_H_
#define _FN_LOG_LOAD_H_

#include <sstream>

namespace FNLog
{
#if __GNUG__ && __GNUC__ >= 6
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif

	inline void ResetSHMLogger(SHMLogger& shm)
	{
		shm.shm_id_ = 0;
		shm.shm_size_ = 0;
		shm.channel_size_ = 0;
		memset(&shm.channels_, 0, sizeof(SHMLogger::Channels));
		for (int i = 0; i < SHMLogger::MAX_CHANNEL_SIZE; i++)
		{
			shm.ring_buffers_[i].write_idx_ = 0;
			shm.ring_buffers_[i].hold_idx_ = 0;
			shm.ring_buffers_[i].read_idx_ = 0;
			shm.ring_buffers_[i].proc_idx_ = 0;
		}
	}

	inline int LoadSharedMemory(Logger& logger)
	{
#if !defined(WIN32)
		if (logger.shm_key_ <= 0)
		{
			logger.shm_ = new SHMLogger();
			ResetSHMLogger(*logger.shm_);
			return 0;
		}
		SHMLogger* shm = nullptr;
		int idx = shmget(logger.shm_key_, 0, 0);
		if (idx < 0 && errno != ENOENT)
		{
			printf("shmget error. key:<0x%llx>, errno:<%d>. can use 'ipcs -m', 'ipcrm -m' to view and clear.\n",
				logger.shm_key_, errno);
			return E_SHMGET_PROBE_ERROR;
		}

		if (idx < 0)
		{
			idx = shmget(logger.shm_key_, sizeof(SHMLogger), IPC_CREAT | IPC_EXCL | 0600);
			if (idx < 0)
			{
				printf("new shm. shmget error. key:<0x%llx>, errno:<%d>.\n", logger.shm_key_, errno);
				return E_SHMGET_CREATE_ERROR;
			}
			void* addr = shmat(idx, nullptr, 0);
			if (addr == nullptr || addr == (void*)-1)
			{
				printf("new shm. shmat error. key:<0x%llx>, idx:<%d>, errno:<%d>.\n", logger.shm_key_, idx, errno);
				return E_SHMAT_ERROR;
			}
			shm = (SHMLogger*)addr;
			ResetSHMLogger(*shm);
			shm->shm_size_ = sizeof(SHMLogger);
			shm->shm_id_ = idx;
		}
		else
		{
			void* addr = shmat(idx, nullptr, 0);
			if (addr == nullptr || addr == (void*)-1)
			{
				printf("shmat error. key:<%llx>, idx:<%d>, errno:<%d>.\n", logger.shm_key_, idx, errno);
				return E_SHMAT_ERROR;
			}
			shm = (SHMLogger*)addr;
		}

		if (shm->shm_size_ != sizeof(SHMLogger) || shm->shm_id_ != idx)
		{
			printf("shm version error. key:<0x%llx>, old id:<%d>, new id:<%d>, old size:<%d> new size:<%d>. "
				"can use 'ipcs -m', 'ipcrm -m' to view and clear.\n",
				logger.shm_key_, shm->shm_id_, idx, shm->shm_size_, (int)sizeof(SHMLogger));
			return E_SHM_VERSION_WRONG;
		}
		for (int i = 0; i < shm->channel_size_; i++)
		{
			if (i >= SHMLogger::MAX_CHANNEL_SIZE)
			{
				return E_SHM_VERSION_WRONG;
			}

			if (shm->ring_buffers_[i].write_idx_ >= RingBuffer::BUFFER_LEN
				|| shm->ring_buffers_[i].write_idx_ < 0)
			{
				return E_SHM_VERSION_WRONG;
			}

			while (shm->ring_buffers_[i].write_idx_.load() != shm->ring_buffers_[i].hold_idx_.load())
			{
				auto& log = shm->ring_buffers_[i].buffer_[shm->ring_buffers_[i].write_idx_];
				log.data_mark_ = 2;
				log.priority_ = PRIORITY_FATAL;
				std::string core_desc = "!!!core recover!!!";
				log.content_len_ = FN_MIN(log.content_len_, LogData::LOG_SIZE - (int)core_desc.length() - 2);
				memcpy(&log.content_[log.content_len_], core_desc.c_str(), core_desc.length());

				log.content_len_ += core_desc.length();
				log.content_[log.content_len_++] = '\n';
				log.content_[log.content_len_] = '\0';

				shm->ring_buffers_[i].write_idx_ = (shm->ring_buffers_[i].write_idx_ + 1) % RingBuffer::BUFFER_LEN;
			}
			shm->ring_buffers_[i].hold_idx_ = shm->ring_buffers_[i].write_idx_.load();

			if (shm->ring_buffers_[i].read_idx_ >= RingBuffer::BUFFER_LEN
				|| shm->ring_buffers_[i].read_idx_ < 0)
			{
				return -10;
			}
			shm->ring_buffers_[i].proc_idx_ = shm->ring_buffers_[i].read_idx_.load();
			if (shm->ring_buffers_[i].read_idx_ != 0 || shm->ring_buffers_[i].write_idx_ != 0)
			{
				printf("attach shm key:<0x%llx> channel:<%d>, write:<%d>, read:<%d> \n", logger.shm_key_,
					i, shm->ring_buffers_[i].write_idx_.load(), (int)shm->ring_buffers_[i].read_idx_.load());
			}
		}
		logger.shm_ = shm;
#else
		logger.shm_ = new SHMLogger();
		ResetSHMLogger(*logger.shm_);
#endif
		return 0;
	}
	inline void UnloadSharedMemory(Logger& logger)
	{
#if !defined(WIN32)
		if (logger.shm_ && logger.shm_key_ > 0)
		{
			int idx = logger.shm_->shm_id_;
			shmdt(logger.shm_);
			shmctl(idx, IPC_RMID, nullptr);
			logger.shm_ = nullptr;
		}
#endif
		if (logger.shm_)
		{
			delete logger.shm_;
			logger.shm_ = nullptr;
		}
	}


	inline int InitFromYMAL(Logger& logger, std::string text, const std::string& path)
	{
		if (logger.logger_state_ != LOGGER_STATE_UNINIT)
		{
			printf("InitFromYMAL:<%s> text error\n", path.c_str());
			return E_LOGGER_IN_USE;
		}

		std::unique_ptr<LexState> ls(new LexState);
		int ret = ParseLogger(*ls, text);
		if (ret != PEC_NONE)
		{
			std::stringstream os;
			os << "ParseLogger has error:<" << ret << " " << DebugErrno(ret).c_str() << "> in line:[" << ls->line_no_ << "] ";
			if (ls->current_ != nullptr)
			{
				os << " before:";
				int limit = 0;
				while (limit < 30 && ls->current_ + limit < ls->end_ && ls->current_[limit] != '\0')
				{
					limit++;
				}
				os.write(ls->current_, limit);
			}
			printf("%s\n", os.str().c_str());
			return ret;
		}

		Logger::StateLockGuard state_guard(logger.state_lock_);
		if (logger.logger_state_ != LOGGER_STATE_UNINIT)
		{
			printf("InitFromYMAL:<%s> text error\n", path.c_str());
			return E_LOGGER_IN_USE;
		}


		if (ls->name_len_ > 0)
		{
			memcpy(logger.name_, ls->name_, ls->name_len_ + 1);
			logger.name_len_ = ls->name_len_;
		}
		if (ls->desc_len_ > 0)
		{
			memcpy(logger.desc_, ls->desc_, ls->desc_len_ + 1);
			logger.desc_len_ = ls->desc_len_;
		}
		logger.yaml_path_ = path;
		logger.hot_update_ = ls->hot_update_;
		logger.shm_key_ = ls->shm_key_;
		if (logger.shm_ == NULL)
		{
			ret = LoadSharedMemory(logger);
			if (ret != 0 || logger.shm_ == NULL)
			{
				printf("InitFromYMAL has error:%d,  yaml:%s\n", ret, text.c_str());
				return ret;
			}
		}
		logger.shm_->channel_size_ = ls->channel_size_;
		for (int i = 0; i < ls->channel_size_; i++)
		{
			memcpy(&ls->channels_[i].channel_log_fields_, &logger.shm_->channels_[i].channel_log_fields_,
				sizeof(ls->channels_[i].channel_log_fields_));

			memcpy(&ls->channels_[i].device_log_fields_, &logger.shm_->channels_[i].device_log_fields_,
				sizeof(ls->channels_[i].device_log_fields_));
		}
		memcpy(&logger.shm_->channels_, &ls->channels_, sizeof(logger.shm_->channels_));

		if (logger.shm_->channel_size_ > Logger::MAX_CHANNEL_SIZE || logger.shm_->channel_size_ <= 0)
		{
			printf("InitFromYMAL channel size:%d out channel max%d. \n", logger.shm_->channel_size_, Logger::MAX_CHANNEL_SIZE);
			return E_INVALID_CHANNEL_SIZE;
		}
		return 0;
	}

	inline int InitFromYMALFile(Logger& logger, const std::string& path)
	{
		std::unique_ptr<LexState> ls(new LexState);
		FileHandler config;
		static_assert(std::is_same<decltype(logger.shm_->channels_), decltype(ls->channels_)>::value, "");
		//static_assert(std::is_trivial<decltype(logger.shm_->channels_)>::value, "");

		struct stat file_stat;
		config.open(path.c_str(), "rb", file_stat);
		if (!config.is_open())
		{
			printf("InitFromYMALFile:<%s> open file error\n", path.c_str());
			return E_INVALID_CONFIG_PATH;
		}
		std::string text = config.read_content();
		config.close();
		int ret = InitFromYMAL(logger, text, path);
		if (ret != 0)
		{
			printf("InitFromYMALFile:<%s> has parse/init error:%d %s\n", path.c_str(), ret, DebugErrno(ret).c_str());
			return ret;
		}

		for (int i = 0; i < logger.shm_->channel_size_; i++)
		{
			logger.shm_->channels_[i].yaml_mtime_ = file_stat.st_mtime;
		}
		return 0;
	}

	inline int HotUpdateLogger(Logger& logger, int channel_id)
	{
		if (logger.shm_->channel_size_ <= channel_id)
		{
			return E_INVALID_CHANNEL_SIZE;
		}
		if (!logger.hot_update_)
		{
			return E_DISABLE_HOTUPDATE;
		}
		if (logger.yaml_path_.empty())
		{
			return E_NO_CONFIG_PATH;
		}

		Channel& dst_chl = logger.shm_->channels_[channel_id];
		time_t now = time(nullptr);
		if (now - dst_chl.last_hot_check_ < Logger::HOTUPDATE_INTERVEL)
		{
			return 0;
		}
		dst_chl.last_hot_check_ = now;

		FileHandler config;
		struct stat file_stat;
		config.open(logger.yaml_path_.c_str(), "rb", file_stat);
		if (!config.is_open())
		{
			return E_INVALID_CONFIG_PATH;
		}
		if (file_stat.st_mtime == dst_chl.yaml_mtime_)
		{
			return E_CONFIG_NO_CHANGE;
		}
		if (logger.logger_state_ != LOGGER_STATE_RUNNING)
		{
			return E_LOGGER_NOT_RUNNING;
		}

		std::string text = config.read_content();
		config.close();
		std::unique_ptr<LexState> ls(new LexState);
		int ret = ParseLogger(*ls, text);
		if (ret != PEC_NONE)
		{
			return ret;
		}


		Logger::StateLockGuard state_guard(logger.state_lock_);
		if (logger.logger_state_ != LOGGER_STATE_RUNNING)
		{
			return E_LOGGER_NOT_RUNNING;
		}
		if (!logger.hot_update_)
		{
			return E_DISABLE_HOTUPDATE;
		}
		dst_chl.yaml_mtime_ = file_stat.st_mtime;
		logger.hot_update_ = ls->hot_update_;

		static_assert(std::is_same<decltype(logger.shm_->channels_), decltype(ls->channels_)>::value, "");
		//static_assert(std::is_trivial<decltype(logger.shm_->channels_)>::value, "");

		static_assert(std::is_same<decltype(logger.shm_->channels_[channel_id].config_fields_), decltype(ls->channels_[channel_id].config_fields_)>::value, "");



		Channel& src_chl = ls->channels_[channel_id];
		if (dst_chl.channel_id_ != src_chl.channel_id_ || src_chl.channel_id_ != channel_id)
		{
			return E_VERSION_MISMATCH;
		}
		for (int field_id = 0; field_id < CHANNEL_CFG_MAX_ID; field_id++)
		{
			//this is multi-thread safe op. 
			dst_chl.config_fields_[field_id] = AtomicLoadC(src_chl, field_id);
		}

		//single thread op.
		for (int device_id = 0; device_id < src_chl.device_size_; device_id++)
		{
			Device& src_dvc = src_chl.devices_[device_id];
			if (src_dvc.device_id_ != device_id)
			{
				return E_VERSION_MISMATCH;
			}
			if (device_id < dst_chl.device_size_)
			{
				Device& dst_dvc = dst_chl.devices_[device_id];
				if (dst_dvc.device_id_ != device_id)
				{
					return E_VERSION_MISMATCH;
				}
				memcpy(&dst_dvc.config_fields_, &src_dvc.config_fields_, sizeof(dst_dvc.config_fields_));
				continue;
			}
			if (dst_chl.device_size_ != device_id)
			{
				return E_VERSION_MISMATCH;
			}
			memcpy(&dst_chl.devices_[dst_chl.device_size_++], &src_dvc, sizeof(src_dvc));

		}

		return 0;
	}

#if __GNUG__ && __GNUC__ >= 6
#pragma GCC diagnostic pop
#endif
}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_OUT_EMPTY_DEVICE_H_
#define _FN_LOG_OUT_EMPTY_DEVICE_H_


namespace FNLog
{

	inline void EnterProcOutEmptyDevice(Logger& logger, int channel_id, int device_id, LogData& log)
	{
		Channel& channel = logger.shm_->channels_[channel_id];
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_LINE, 1);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_BYTE, log.content_len_);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_PRIORITY + log.priority_, log.content_len_);
	}

}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/

#pragma once
#ifndef _FN_LOG_OUT_FILE_DEVICE_H_
#define _FN_LOG_OUT_FILE_DEVICE_H_


namespace FNLog
{

	//support
	inline std::string PreFmtName(const std::string& fmt_name)
	{
		if (fmt_name.empty())
		{
			return fmt_name;
		}
		std::string name = fmt_name;
		size_t pos = 0;
		do
		{
			bool has_error = false;
			pos = name.find('%', pos);
			if (pos == std::string::npos)
			{
				break;
			}
			if (name.length() - pos < 2)//min(escape) 
			{
				break;
			}

			switch (name[pos + 1])
			{
			case 'F':
				if (true)
				{
					name.replace(pos, 2, "$YEAR-$MON-$DAY");
					break;
				}
				has_error = true;
				break;
			default:
				has_error = true;
				break;
			}
			if (has_error)
			{
				pos++;
			}
		} while (true);
		return name;
	}
	// 
	inline std::string FmtName(const std::string& fmt_name, int channel_id, int device_id, const struct tm& t)
	{
		(void)device_id;
		(void)channel_id;
		if (fmt_name.empty())
		{
			return fmt_name;
		}

		std::string name = PreFmtName(fmt_name);

		size_t pos = 0;
		do
		{
			bool has_error = false;
			pos = name.find('$', pos);
			if (pos == std::string::npos)
			{
				break;
			}
			if (name.length() - pos <2)//min(escape) 
			{
				break;
			}

			switch (name[pos + 1])
			{
			case 'P':
				if (name.substr(pos + 2, 4) == "NAME")
				{
					name.replace(pos, 6, FileHandler::process_name());
					break;
				}
				if (name.substr(pos + 2, 2) == "ID")
				{
					name.replace(pos, 4, FileHandler::process_id());
					break;
				}
				has_error = true;
				break;
			case 'Y':
				if (name.substr(pos + 2, 3) == "EAR")
				{
					char buff[30] = { 0 };
					sprintf(buff, "%04d", t.tm_year + 1900);
					name.replace(pos, 5, buff);
					break;
				}
				has_error = true;
				break;
			case 'M':
				if (name.substr(pos + 2, 2) == "ON")
				{
					char buff[30] = { 0 };
					sprintf(buff, "%02d", t.tm_mon + 1);
					name.replace(pos, 4, buff);
					break;
				}
				if (name.substr(pos + 2, 2) == "IN")
				{
					char buff[30] = { 0 };
					sprintf(buff, "%02d", t.tm_min);
					name.replace(pos, 4, buff);
					break;
				}
				has_error = true;
				break;
			case 'D':
				if (name.substr(pos + 2, 2) == "AY")
				{
					char buff[30] = { 0 };
					sprintf(buff, "%02d", t.tm_mday);
					name.replace(pos, 4, buff);
					break;
				}
				has_error = true;
				break;
			case 'H':
				if (name.substr(pos + 2, 3) == "OUR")
				{
					char buff[30] = { 0 };
					sprintf(buff, "%02d", t.tm_hour);
					name.replace(pos, 5, buff);
					break;
				}
				has_error = true;
				break;
			case 'S':
				if (name.substr(pos + 2, 2) == "EC")
				{
					char buff[30] = { 0 };
					sprintf(buff, "%02d", t.tm_sec);
					name.replace(pos, 4, buff);
					break;
				}
				has_error = true;
				break;
			default:
				has_error = true;
				break;
			}

			if (has_error)
			{
				pos++;
			}
		} while (true);
		return name;
	}


	//[$PNAME $PID $YEAR $MON $DAY $HOUR $MIN $SEC]
	inline std::string MakeFileName(const std::string& fmt_name, int channel_id, int device_id, const struct tm& t)
	{
		std::string name = fmt_name;
		if (name.empty())
		{
			name = "$PNAME_$YEAR$MON$DAY_$PID.";
			name += std::to_string(channel_id);
			name += std::to_string(device_id);
		}
		name += ".log";
		return FmtName(name, channel_id, device_id, t);
	}
	//[$PNAME $PID $YEAR $MON $DAY $HOUR $MIN $SEC]
	inline std::string MakePathName(const std::string& fmt_name, int channel_id, int device_id, const struct tm& t)
	{
		return FmtName(fmt_name, channel_id, device_id, t);
	}


	inline void OpenFileDevice(Logger & logger, Channel & channel, Device & device, FileHandler & writer, LogData & log)
	{
		(void)logger;

		bool close_file = false;
		bool limit_out = false;



		do
		{
			//rollback only limit size && rollback > 0 
			if (AtomicLoadC(device, DEVICE_CFG_FILE_LIMIT_SIZE) > 0 && AtomicLoadC(device, DEVICE_CFG_FILE_ROLLBACK) > 0
				&& AtomicLoadDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_SIZE) + log.content_len_ > AtomicLoadC(device, DEVICE_CFG_FILE_LIMIT_SIZE))
			{
				close_file = true;
				limit_out = true;
				break;
			}

			//daily rolling
			if (AtomicLoadC(device, DEVICE_CFG_FILE_ROLLDAILY))
			{
				if (log.timestamp_ < AtomicLoadDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_CREATE_DAY)
					|| log.timestamp_ >= AtomicLoadDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_CREATE_DAY) + 24 * 3600)
				{
					close_file = true;
				}
				break;
			}

			//hourly rolling 
			if (AtomicLoadC(device, DEVICE_CFG_FILE_ROLLHOURLY))
			{
				if (log.timestamp_ < AtomicLoadDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_CREATE_HOUR)
					|| log.timestamp_ >= AtomicLoadDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_CREATE_HOUR) + 3600)
				{
					close_file = true;
				}
				break;
			}

		} while (false);


		if (close_file)
		{
			AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_SIZE, 0);
			if (writer.is_open())
			{
				writer.close();
			}
		}

		if (writer.is_open())
		{
			return;
		}

		long long create_day = 0;
		long long create_hour = 0;
		tm t = FileHandler::time_to_tm(log.timestamp_);
		if (true) //process day time   
		{
			tm day = t;
			day.tm_min = 0;
			day.tm_sec = 0;
			create_hour = mktime(&day);
			day.tm_hour = 0;
			create_day = mktime(&day);
		}

		std::string name = MakeFileName(device.out_file_, channel.channel_id_, device.device_id_, t);
		std::string path = MakePathName(device.out_path_, channel.channel_id_, device.device_id_, t);
		if (!path.empty())
		{
			std::for_each(path.begin(), path.end(), [](char& ch) {if (ch == '\\') { ch = '/'; } });
			if (path.back() != '/') { path.push_back('/'); }

			if (!FileHandler::is_dir(path))
			{
				FileHandler::create_dir(path);
			}
		}

		path += name;

		if (path.length() >= Device::MAX_PATH_LEN + Device::MAX_LOGGER_NAME_LEN)
		{
			AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_LAST_TRY_CREATE_ERROR, 1);
			AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_LAST_TRY_CREATE_TIMESTAMP, log.timestamp_);
			return;
		}

		if (AtomicLoadC(device, DEVICE_CFG_FILE_ROLLBACK) > 0 || AtomicLoadC(device, DEVICE_CFG_FILE_LIMIT_SIZE) > 0)
		{
			bool stuff_up = (bool)AtomicLoadC(device, DEVICE_CFG_FILE_STUFF_UP);
			if (!stuff_up || limit_out)
			{
				//when no rollback but has limit size. need try rollback once.
				long long limit_roll = device.config_fields_[DEVICE_CFG_FILE_ROLLBACK];
				limit_roll = limit_roll > 0 ? limit_roll : 1;
				FileHandler::rollback(path, 1, (int)limit_roll);
			}
		}

		struct stat file_stat;
		long writed_byte = writer.open(path.c_str(), "ab", file_stat);
		if (!writer.is_open())
		{
			AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_LAST_TRY_CREATE_ERROR, 2);
			AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_LAST_TRY_CREATE_TIMESTAMP, log.timestamp_);
			return;
		}

		AtomicIncDeviceLog(channel, device.device_id_, DEVICE_LOG_LAST_TRY_CREATE_CNT, 1);

		AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_LAST_TRY_CREATE_ERROR, 0);
		AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_LAST_TRY_CREATE_TIMESTAMP, 0);
		AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_CREATE_TIMESTAMP, log.timestamp_);
		AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_CREATE_DAY, create_day);
		AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_CREATE_HOUR, create_hour);
		AtomicStoreDeviceLog(channel, device.device_id_, DEVICE_LOG_CUR_FILE_SIZE, writed_byte);
	}



	inline void EnterProcOutFileDevice(Logger& logger, int channel_id, int device_id, LogData& log)
	{
		Channel& channel = logger.shm_->channels_[channel_id];
		Device& device = channel.devices_[device_id];
		FileHandler& writer = logger.file_handles_[channel_id * Channel::MAX_DEVICE_SIZE + device_id];

		if (!writer.is_open() && AtomicLoadDeviceLog(channel, device_id, DEVICE_LOG_LAST_TRY_CREATE_TIMESTAMP) + 5 > log.timestamp_)
		{
			return;
		}
		OpenFileDevice(logger, channel, device, writer, log);
		if (!writer.is_open())
		{
			return;
		}
		writer.write(log.content_, log.content_len_);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_LINE, 1);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_BYTE, log.content_len_);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_CUR_FILE_SIZE, log.content_len_);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_PRIORITY + log.priority_, log.content_len_);
	}


}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_OUT_UDP_DEVICE_H_
#define _FN_LOG_OUT_UDP_DEVICE_H_


namespace FNLog
{

	inline void EnterProcOutUDPDevice(Logger& logger, int channel_id, int device_id, LogData& log)
	{
		auto& udp = logger.udp_handles_[channel_id * Channel::MAX_DEVICE_SIZE + device_id];
		Channel& channel = logger.shm_->channels_[channel_id];
		Device& device = logger.shm_->channels_[channel_id].devices_[device_id];

		if (!udp.is_open())
		{
			udp.open();
		}
		if (!udp.is_open())
		{
			AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_LOSE_LINE, 1);
			return;
		}

		long long ip = AtomicLoadC(device, DEVICE_CFG_UDP_IP);
		long long port = AtomicLoadC(device, DEVICE_CFG_UDP_PORT);
		int ret = udp.write((unsigned long)ip, (unsigned short)port, log.content_, log.content_len_);
		if (ret <= 0)
		{
			AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_LOSE_LINE, 1);
		}
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_LINE, 1);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_BYTE, log.content_len_);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_PRIORITY + log.priority_, log.content_len_);
	}
}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_OUT_SCREEN_DEVICE_H_
#define _FN_LOG_OUT_SCREEN_DEVICE_H_


namespace FNLog
{

	inline void EnterProcOutScreenDevice(Logger& logger, int channel_id, int device_id, LogData& log)
	{
		Logger::ScreenLockGuard l(logger.screen_lock_);
		Channel& channel = logger.shm_->channels_[channel_id];

		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_LINE, 1);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_BYTE, log.content_len_);
		AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_PRIORITY + log.priority_, log.content_len_);

		int priority = log.priority_;
		if (log.priority_ < PRIORITY_INFO)
		{
			printf("%s", log.content_);
			return;
		}
		if (priority >= PRIORITY_MAX)
		{
			priority = PRIORITY_ALARM;
		}
#ifndef WIN32
		printf("%s%s\e[0m", PRIORITY_RENDER[priority].scolor_, log.content_);
#else

		HANDLE sc_handle = ::GetStdHandle(STD_OUTPUT_HANDLE);
		if (sc_handle == INVALID_HANDLE_VALUE)
		{
			printf("%s", log.content_);
			return;
		}
		CONSOLE_SCREEN_BUFFER_INFO old_info;
		if (!GetConsoleScreenBufferInfo(sc_handle, &old_info))
		{
			printf("%s", log.content_);
			return;
		}
		else
		{
			SetConsoleTextAttribute(sc_handle, (old_info.wAttributes& ~7u) | PRIORITY_RENDER[priority].color_);
			printf("%s", log.content_);
			SetConsoleTextAttribute(sc_handle, old_info.wAttributes);
		}
#endif
	}



}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_OUT_VIRTUAL_DEVICE_H_
#define _FN_LOG_OUT_VIRTUAL_DEVICE_H_


namespace FNLog
{
	using VirtualDevicePtr = void(*)(const LogData& log);

	inline VirtualDevicePtr& RefVirtualDevice()
	{
		static VirtualDevicePtr g_virtual_device_ptr = NULL;
		return g_virtual_device_ptr;
	}
	inline void SetVirtualDevice(VirtualDevicePtr vdp)
	{
		RefVirtualDevice() = vdp;
	}

	//the virtual device like log hook;  this virtual device call at the log create time(thread) not at log write thread .    
	//can used translate log  
	inline void EnterProcOutVirtualDevice(Logger& logger, int channel_id, int device_id, LogData& log)
	{
		if (RefVirtualDevice())
		{
			Channel& channel = logger.shm_->channels_[channel_id];
			Device::ConfigFields& fields = channel.devices_[device_id].config_fields_;
			long long field_begin_category = fields[FNLog::DEVICE_CFG_CATEGORY];
			long long field_category_count = fields[FNLog::DEVICE_CFG_CATEGORY_EXTEND];
			unsigned long long field_category_mask = (unsigned long long)fields[FNLog::DEVICE_CFG_CATEGORY_MASK];
			long long field_begin_identify = fields[FNLog::DEVICE_CFG_IDENTIFY];
			long long field_identify_count = fields[FNLog::DEVICE_CFG_IDENTIFY_EXTEND];
			unsigned long long field_identify_mask = (unsigned long long)fields[FNLog::DEVICE_CFG_IDENTIFY_MASK];

			if (field_category_count > 0 && (log.category_ < field_begin_category || log.category_ >= field_begin_category + field_category_count))
			{
				return;
			}

			if (field_identify_count > 0 && (log.identify_ < field_begin_identify || log.identify_ >= field_begin_identify + field_identify_count))
			{
				return;
			}
			if (field_category_mask && (field_category_mask & ((1ULL) << (unsigned int)log.category_)) == 0)
			{
				return;
			}
			if (field_identify_mask && (field_identify_mask & ((1ULL) << (unsigned int)log.identify_)) == 0)
			{
				return;
			}

			int content_len_ = FN_MIN(log.content_len_, LogData::LOG_SIZE - 1);
			log.content_[content_len_] = '\0'; //virtual device hook maybe direct used content like c-string 

			AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_LINE, 1);
			AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_TOTAL_WRITE_BYTE, log.content_len_);
			AtomicIncDeviceLog(channel, device_id, DEVICE_LOG_PRIORITY + log.priority_, log.content_len_);
			(*RefVirtualDevice())(log);
		}
	}
}


#endif
/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/



#pragma once
#ifndef _FN_LOG_CHANNEL_H_
#define _FN_LOG_CHANNEL_H_


namespace FNLog
{
	inline void EnterProcDevice(Logger& logger, int channel_id, int device_id, LogData & log)
	{
		Channel& channel = logger.shm_->channels_[channel_id];
		Device& device = channel.devices_[device_id];
		//async promise only single thread proc. needn't lock.
		Logger::ReadGuard rg(logger.read_locks_[channel_id], channel.channel_type_ == CHANNEL_ASYNC);
		switch (device.out_type_)
		{
		case DEVICE_OUT_SCREEN:
			EnterProcOutScreenDevice(logger, channel_id, device_id, log);
			break;
		case DEVICE_OUT_FILE:
			EnterProcOutFileDevice(logger, channel_id, device_id, log);
			break;
		case DEVICE_OUT_UDP:
			EnterProcOutUDPDevice(logger, channel_id, device_id, log);
			break;
		case DEVICE_OUT_VIRTUAL:
			//EnterProcOutVirtualDevice(logger, channel_id, device_id, log);
			break;
		case DEVICE_OUT_EMPTY:
			EnterProcOutEmptyDevice(logger, channel_id, device_id, log);
			break;
			break;
		default:
			break;
		}
	}


	inline void DispatchLog(Logger & logger, Channel& channel, LogData& log)
	{
		for (int device_id = 0; device_id < channel.device_size_; device_id++)
		{
			Device& device = channel.devices_[device_id];
			if (!AtomicLoadC(device, DEVICE_CFG_ABLE))
			{
				continue;
			}
			if (device.in_type_ != DEVICE_IN_NULL)
			{
				continue;
			}
			if (log.priority_ < AtomicLoadC(device, DEVICE_CFG_PRIORITY))
			{
				continue;
			}
			long long begin_category = AtomicLoadC(device, DEVICE_CFG_CATEGORY);
			long long category_count = AtomicLoadC(device, DEVICE_CFG_CATEGORY_EXTEND);
			unsigned long long category_mask = (unsigned long long)AtomicLoadC(device, DEVICE_CFG_CATEGORY_MASK);
			long long begin_identify = AtomicLoadC(device, DEVICE_CFG_IDENTIFY);
			long long identify_count = AtomicLoadC(device, DEVICE_CFG_IDENTIFY_EXTEND);
			unsigned long long identify_mask = (unsigned long long)AtomicLoadC(device, DEVICE_CFG_IDENTIFY_MASK);

			if (category_count > 0 && (log.category_ < begin_category || log.category_ >= begin_category + category_count))
			{
				continue;
			}
			if (identify_count > 0 && (log.identify_ < begin_identify || log.identify_ >= begin_identify + identify_count))
			{
				continue;
			}
			if (category_mask && (category_mask & ((1ULL) << (unsigned int)log.category_)) == 0)
			{
				continue;
			}
			if (identify_mask && (identify_mask & ((1ULL) << (unsigned int)log.identify_)) == 0)
			{
				continue;
			}
			EnterProcDevice(logger, channel.channel_id_, device_id, log);
		}
	}

	inline void InitLogData(Logger& logger, LogData& log, int channel_id, int priority, int category, unsigned long long identify, unsigned int prefix);

	inline void EnterProcChannel(Logger& logger, int channel_id)
	{
		Channel& channel = logger.shm_->channels_[channel_id];
		RingBuffer& ring_buffer = logger.shm_->ring_buffers_[channel_id];
		do
		{
			int flush_count = 0;
			bool empty_tick = true;
			do
			{
				int old_idx = ring_buffer.proc_idx_.load(std::memory_order_acquire);
				int next_idx = (old_idx + 1) % RingBuffer::BUFFER_LEN;
				if (old_idx == ring_buffer.write_idx_.load(std::memory_order_acquire))
				{
					//empty branch    
					break;
				}

				//set proc index  
				if (!ring_buffer.proc_idx_.compare_exchange_strong(old_idx, next_idx))
				{
					//only one thread get log. this branch will not hit.   
					break;
				}
				auto& cur_log = ring_buffer.buffer_[old_idx];
				DispatchLog(logger, channel, cur_log);
				cur_log.data_mark_ = 0;
				AtomicIncChannelLog(channel, CHANNEL_LOG_PROCESSED, 1);
				AtomicIncChannelLog(channel, CHANNEL_LOG_PROCESSED_BYTES, cur_log.content_len_);
				flush_count++;
				empty_tick = false;
				int write_id = ring_buffer.write_idx_.load(std::memory_order_acquire);
				int proc_que_size = 0;
				if (old_idx <= write_id)
				{
					proc_que_size = write_id - old_idx;
				}
				else
				{
					proc_que_size = write_id + RingBuffer::BUFFER_LEN - old_idx;
				}

				if (proc_que_size > AtomicLoadChannelLog(channel, CHANNEL_LOG_MAX_PROC_QUE_SIZE))
				{
					AtomicStoreChannelLog(channel, CHANNEL_LOG_MAX_PROC_QUE_SIZE, proc_que_size);
				}

				if (cur_log.timestamp_ > 0)
				{
					long long now = (long long)time(NULL);
					long long diff = now - 1 - cur_log.timestamp_;
					if (diff > AtomicLoadChannelLog(channel, CHANNEL_LOG_MAX_DELAY_TIME_S))
					{
						AtomicStoreChannelLog(channel, CHANNEL_LOG_MAX_DELAY_TIME_S, diff);
					}
				}
				do
				{
					//set read index to proc index  
					old_idx = ring_buffer.read_idx_.load(std::memory_order_acquire);
					next_idx = (old_idx + 1) % RingBuffer::BUFFER_LEN;
					if (old_idx == ring_buffer.proc_idx_.load(std::memory_order_acquire))
					{
						break;
					}
					if (ring_buffer.buffer_[old_idx].data_mark_.load(std::memory_order_acquire) != MARK_INVALID)
					{
						break;
					}
					ring_buffer.read_idx_.compare_exchange_strong(old_idx, next_idx);
				} while (true);

				//if want the high log security can reduce this threshold or enable shared memory queue.  
				if (flush_count > FN_LOG_FORCE_FLUSH_QUE)
				{
					//break;
					flush_count = 0;
					for (int i = 0; i < channel.device_size_; i++)
					{
						if (channel.devices_[i].out_type_ == DEVICE_OUT_FILE)
						{
							logger.file_handles_[channel_id * Channel::MAX_DEVICE_SIZE + i].flush();
						}
					}
				}
			} while (true);


			if (channel.channel_state_ == CHANNEL_STATE_NULL)
			{
				channel.channel_state_ = CHANNEL_STATE_RUNNING;
			}

			if (flush_count != 0)
			{
				for (int i = 0; i < channel.device_size_; i++)
				{
					if (channel.devices_[i].out_type_ == DEVICE_OUT_FILE)
					{
						logger.file_handles_[channel_id * Channel::MAX_DEVICE_SIZE + i].flush();
					}
				}
			}

			for (int i = 0; i < channel.device_size_; i++)
			{
				Device& device = channel.devices_[i];
				if (device.in_type_ != DEVICE_IN_UDP)
				{
					continue;
				}

				UDPHandler& udp = logger.udp_handles_[channel_id * Channel::MAX_DEVICE_SIZE + i];
				if (!device.config_fields_[DEVICE_CFG_ABLE])
				{
					if (udp.is_open())
					{
						udp.close();
					}
					continue;
				}

				if (!udp.is_open())
				{
					udp.open();
					if (udp.is_open())
					{
						int ret = udp.bind((unsigned int)AtomicLoadC(device, DEVICE_CFG_UDP_IP), (unsigned short)AtomicLoadC(device, DEVICE_CFG_UDP_PORT));
						if (ret != 0)
						{
							udp.close();
						}
					}
				}

				if (!udp.is_open())
				{
					continue;
				}

				for (int i = 0; i < 1000; i++)
				{
					InitLogData(logger, ring_buffer.udp_buffer_, channel.channel_id_, PRIORITY_INFO, 0, 0, LOG_PREFIX_NULL);
					ring_buffer.udp_buffer_.content_len_ = udp.read(ring_buffer.udp_buffer_.content_, LogData::LOG_SIZE);
					if (ring_buffer.udp_buffer_.content_len_ == LogData::LOG_SIZE)
					{
						ring_buffer.udp_buffer_.content_[ring_buffer.udp_buffer_.content_len_ - 2] = '\n';
						ring_buffer.udp_buffer_.content_[ring_buffer.udp_buffer_.content_len_ - 1] = '\0';
					}
					if (ring_buffer.udp_buffer_.content_len_ == 0)
					{
						break;
					}
					empty_tick = false;
					EnterProcDevice(logger, channel.channel_id_, device.device_id_, ring_buffer.udp_buffer_);
				}
			}

			HotUpdateLogger(logger, channel.channel_id_);
			if (channel.channel_type_ == CHANNEL_ASYNC && !empty_tick)
			{
				std::this_thread::sleep_for(std::chrono::milliseconds(FN_LOG_MAX_ASYNC_SLEEP_MS));
			}

		} while (channel.channel_type_ == CHANNEL_ASYNC
			&& (channel.channel_state_ == CHANNEL_STATE_RUNNING || ring_buffer.write_idx_ != ring_buffer.read_idx_));

		if (channel.channel_type_ == CHANNEL_ASYNC)
		{
			channel.channel_state_ = CHANNEL_STATE_FINISH;
		}
	}



	inline void InitLogData(Logger& logger, LogData& log, int channel_id, int priority, int category, unsigned long long identify, unsigned int prefix)
	{
		(void)logger;
		(void)prefix;
		log.channel_id_ = channel_id;
		log.priority_ = priority;
		log.category_ = category;
		log.identify_ = identify;
		log.code_line_ = 0;
		log.code_func_len_ = 0;
		log.code_file_len_ = 0;
		log.code_file_ = "";
		log.code_func_ = "";
		log.prefix_len_ = 0;
		log.content_len_ = 0;
		log.content_[log.content_len_] = '\0';

#ifdef WIN32
		FILETIME ft;
		GetSystemTimeAsFileTime(&ft);
		unsigned long long now = ft.dwHighDateTime;
		now <<= 32;
		now |= ft.dwLowDateTime;
		now /= 10;
		now -= 11644473600000000ULL;
		now /= 1000;
		log.timestamp_ = now / 1000;
		log.precise_ = (unsigned int)(now % 1000);
#else
		struct timeval tm;
		gettimeofday(&tm, nullptr);
		log.timestamp_ = tm.tv_sec;
		log.precise_ = tm.tv_usec / 1000;
#endif
		log.thread_ = 0;

#ifdef WIN32
		static thread_local unsigned int therad_id = GetCurrentThreadId();
		log.thread_ = therad_id;
#elif defined(__APPLE__)
		unsigned long long tid = 0;
		pthread_threadid_np(nullptr, &tid);
		log.thread_ = (unsigned int)tid;
#else
		static thread_local unsigned int therad_id = (unsigned int)syscall(SYS_gettid);
		log.thread_ = therad_id;
#endif
		log.content_[log.content_len_] = '\0';
		log.prefix_len_ = log.content_len_;
		return;
	}
#ifdef __GNUG__
#pragma GCC push_options
#pragma GCC optimize ("O2")
#endif
	inline bool BlockInput(Logger& logger, int channel_id, int priority, int category, long long identify)
	{
		if (logger.shm_ == NULL || channel_id >= logger.shm_->channel_size_ || channel_id < 0)
		{
			return true;
		}
		if (logger.logger_state_ != LOGGER_STATE_RUNNING)
		{
			return true;
		}
		Channel& channel = logger.shm_->channels_[channel_id];
		if (channel.channel_state_ != CHANNEL_STATE_RUNNING)
		{
			return true;
		}
		if (priority < AtomicLoadC(channel, CHANNEL_CFG_PRIORITY))
		{
			return true;
		}
		if (priority >= PRIORITY_MAX)
		{
			static_assert(PRIORITY_MAX == PRIORITY_FATAL + 1, "safety priority to record channel log CHANNEL_LOG_PRIORITY");
			priority = PRIORITY_FATAL;
		}

		long long begin_category = AtomicLoadC(channel, CHANNEL_CFG_CATEGORY);
		long long category_count = AtomicLoadC(channel, CHANNEL_CFG_CATEGORY_EXTEND);
		unsigned long long category_mask = (unsigned long long)AtomicLoadC(channel, CHANNEL_CFG_CATEGORY_MASK);
		long long begin_identify = AtomicLoadC(channel, CHANNEL_CFG_IDENTIFY);
		long long identify_count = AtomicLoadC(channel, CHANNEL_CFG_IDENTIFY_EXTEND);
		unsigned long long identify_mask = (unsigned long long)AtomicLoadC(channel, CHANNEL_CFG_IDENTIFY_MASK);

		if (category_count > 0 && (category < begin_category || category >= begin_category + category_count))
		{
			return true;
		}
		if (identify_count > 0 && (identify < begin_identify || identify >= begin_identify + identify_count))
		{
			return true;
		}
		if (category_mask && (category_mask & ((1ULL) << (unsigned int)category)) == 0)
		{
			return true;
		}
		if (identify_mask && (identify_mask & ((1ULL) << (unsigned int)identify)) == 0)
		{
			return true;
		}

		bool need_write = false;

		for (int i = 0; i < channel.device_size_; i++)
		{
			Device::ConfigFields& fields = channel.devices_[i].config_fields_;
			long long field_able = fields[FNLog::DEVICE_CFG_ABLE];
			long long field_priority = fields[FNLog::DEVICE_CFG_PRIORITY];
			if (!field_able || priority < field_priority)
			{
				continue;
			}
			if (channel.devices_[i].in_type_ != DEVICE_IN_NULL)
			{
				continue;
			}
			long long field_begin_category = fields[FNLog::DEVICE_CFG_CATEGORY];
			long long field_category_count = fields[FNLog::DEVICE_CFG_CATEGORY_EXTEND];
			unsigned long long field_category_mask = (unsigned long long)fields[FNLog::DEVICE_CFG_CATEGORY_MASK];
			long long field_begin_identify = fields[FNLog::DEVICE_CFG_IDENTIFY];
			long long field_identify_count = fields[FNLog::DEVICE_CFG_IDENTIFY_EXTEND];
			unsigned long long field_identify_mask = (unsigned long long)fields[FNLog::DEVICE_CFG_IDENTIFY_MASK];


			if (field_category_count > 0 && (category < field_begin_category || category >= field_begin_category + field_category_count))
			{
				continue;
			}
			if (field_identify_count > 0 && (identify < field_begin_identify || identify >= field_begin_identify + field_identify_count))
			{
				continue;
			}
			if (field_category_mask && (field_category_mask & ((1ULL) << (unsigned int)category)) == 0)
			{
				continue;
			}
			if (field_identify_mask && (field_identify_mask & ((1ULL) << (unsigned int)identify)) == 0)
			{
				continue;
			}
			need_write = true;
			break;
		}
		if (!need_write)
		{
			return true;
		}

		return false;
	}
#ifdef __GNUG__
#pragma GCC pop_options
#endif
	inline int HoldChannel(Logger& logger, int channel_id, int priority, int category, long long identify)
	{
		if (BlockInput(logger, channel_id, priority, category, identify))
		{
			return -1;
		}
		Channel& channel = logger.shm_->channels_[channel_id];
		RingBuffer& ring_buffer = logger.shm_->ring_buffers_[channel_id];

		int state = 0;
		do
		{
			if (state > 0)
			{
				AtomicIncChannelLog(channel, CHANNEL_LOG_WAIT_COUNT, 1);
				std::this_thread::sleep_for(std::chrono::milliseconds(FN_LOG_MAX_ASYNC_SLEEP_MS));
			}
			state++;

			for (int i = 0; i < FN_MAX(RingBuffer::BUFFER_LEN, 10); i++)
			{
				if (channel.channel_state_ != CHANNEL_STATE_RUNNING)
				{
					break;
				}
				int old_idx = ring_buffer.hold_idx_.load(std::memory_order_acquire);
				int hold_idx = (old_idx + 1) % RingBuffer::BUFFER_LEN;
				if (hold_idx == ring_buffer.read_idx_.load(std::memory_order_acquire))
				{
					break;
				}
				if (ring_buffer.hold_idx_.compare_exchange_strong(old_idx, hold_idx))
				{
					AtomicIncChannelLog(channel, CHANNEL_LOG_HOLD, 1);
					ring_buffer.buffer_[old_idx].data_mark_.store(MARK_HOLD, std::memory_order_release);
					return old_idx;
				}
				continue;
			}
			if (channel.channel_state_ != CHANNEL_STATE_RUNNING)
			{
				break;
			}
		} while (true);
		return -11;
	}

	inline int PushChannel(Logger& logger, int channel_id, int hold_idx)
	{
		if (channel_id >= logger.shm_->channel_size_ || channel_id < 0)
		{
			return E_INVALID_CHANNEL_SIZE;
		}
		if (hold_idx >= RingBuffer::BUFFER_LEN || hold_idx < 0)
		{
			return E_OUT_RINGBUFFER;
		}
		Channel& channel = logger.shm_->channels_[channel_id];
		RingBuffer& ring_buffer = logger.shm_->ring_buffers_[channel_id];
		if (channel.channel_state_ != CHANNEL_STATE_RUNNING)
		{
			return E_LOGGER_NOT_RUNNING;
		}

		LogData& log = ring_buffer.buffer_[hold_idx];
		log.content_len_ = FN_MIN(log.content_len_, LogData::LOG_SIZE - 2);
		log.content_[log.content_len_++] = '\n';
		log.content_[log.content_len_] = '\0';

		log.data_mark_ = 2;
		AtomicIncChannelLog(channel, CHANNEL_LOG_PRIORITY + log.priority_, log.content_len_);

		do
		{
			int old_idx = ring_buffer.write_idx_.load(std::memory_order_acquire);
			int next_idx = (old_idx + 1) % RingBuffer::BUFFER_LEN;
			if (old_idx == ring_buffer.hold_idx_.load(std::memory_order_acquire))
			{
				break;
			}
			if (ring_buffer.buffer_[old_idx].data_mark_.load(std::memory_order_acquire) != 2)
			{
				break;
			}
			if (ring_buffer.write_idx_.compare_exchange_strong(old_idx, next_idx))
			{
				AtomicIncChannelLog(channel, CHANNEL_LOG_PUSH, 1);
			}
		} while (channel.channel_state_ == CHANNEL_STATE_RUNNING);

		if (channel.channel_type_ == CHANNEL_SYNC && channel.channel_state_ == CHANNEL_STATE_RUNNING)
		{
			EnterProcChannel(logger, channel_id); //no affect channel.single_thread_write_
		}
		return 0;
	}

	//combine virtual device  can transmit log to other channel 
	inline int TransmitChannel(Logger& logger, int channel_id, int category, long long identify, const LogData& log)
	{
		if (log.channel_id_ == channel_id)
		{
			return 0;
		}
		int hold_idx = FNLog::HoldChannel(logger, channel_id, log.priority_, category, identify);
		if (hold_idx < 0)
		{
			if (hold_idx == -1)
			{
				return 0;
			}
			return 0;
		}
		LogData& trans_log = logger.shm_->ring_buffers_[channel_id].buffer_[hold_idx];
		trans_log.channel_id_ = channel_id;
		trans_log.priority_ = log.priority_;
		trans_log.category_ = category;
		trans_log.identify_ = identify;
		trans_log.code_line_ = log.code_line_;
		trans_log.code_func_len_ = log.code_func_len_;
		trans_log.code_file_len_ = log.code_file_len_;
		trans_log.code_func_ = log.code_func_;
		trans_log.code_file_ = log.code_file_;
		trans_log.timestamp_ = log.timestamp_;
		trans_log.precise_ = log.precise_;
		trans_log.thread_ = log.thread_;
		trans_log.prefix_len_ = log.prefix_len_;
		trans_log.content_len_ = log.content_len_;
		memcpy(trans_log.content_, log.content_, log.content_len_);
		return PushChannel(logger, channel_id, hold_idx);
	}
}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_CORE_H_
#define _FN_LOG_CORE_H_


namespace FNLog
{

#if __GNUG__ && __GNUC__ >= 6
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wclass-memaccess"
#endif

	inline int PushLog(Logger& logger, int channel_id, int hold_idx, bool state_safly_env = false)
	{
		(void)state_safly_env;
		return PushChannel(logger, channel_id, hold_idx);
	}


	inline int StartChannels(Logger& logger)
	{
		for (int channel_id = 0; channel_id < logger.shm_->channel_size_; channel_id++)
		{
			static_assert(LogData::LOG_SIZE > Device::MAX_PATH_SYS_LEN * 2 + 100, "");
			Channel& channel = logger.shm_->channels_[channel_id];
			std::thread& thd = logger.async_threads[channel_id];
			switch (channel.channel_type_)
			{
			case CHANNEL_SYNC:
				channel.channel_state_ = CHANNEL_STATE_RUNNING;
				break;
			case CHANNEL_ASYNC:
			{
				thd = std::thread(EnterProcChannel, std::ref(logger), channel_id);
				if (!thd.joinable())
				{
					printf("StartChannels %s", "start async log thread has error.\n");
					return E_CHANNEL_THREAD_FAILED;
				}
				int state = 0;
				while (channel.channel_state_ == CHANNEL_STATE_NULL && state < 100)
				{
					state++;
					std::this_thread::sleep_for(std::chrono::milliseconds(10));
				}
				if (channel.channel_state_ == CHANNEL_STATE_NULL)
				{
					printf("StartChannels %s", "start async log thread timeout.\n");
					return E_CHANNEL_THREAD_FAILED;
				}
				if (channel.channel_state_ != CHANNEL_STATE_RUNNING)
				{
					printf("StartChannels %s", "start async log thread has inner error.\n");
					return E_INVALID_CHANNEL_STATE;
				}
			}
			break;
			default:
				printf("%s", "unknown channel type");
				return E_INVALID_CHANNEL_SYNC;
			}
		}
		return 0;
	}

	inline int StopChannels(Logger& logger)
	{
		for (int channel_id = 0; channel_id < logger.shm_->channel_size_; channel_id++)
		{
			static_assert(LogData::LOG_SIZE > Device::MAX_PATH_SYS_LEN * 2 + 100, "");
			Channel& channel = logger.shm_->channels_[channel_id];
			std::thread& thd = logger.async_threads[channel_id];
			switch (channel.channel_type_)
			{
			case CHANNEL_SYNC:
				channel.channel_state_ = CHANNEL_STATE_NULL;
				break;
			case CHANNEL_ASYNC:
			{
				if (thd.joinable())
				{
					if (channel.channel_state_ == CHANNEL_STATE_RUNNING)
					{
						channel.channel_state_ = CHANNEL_STATE_WAITING_FINISH;
					}
					thd.join();
				}
				channel.channel_state_ = CHANNEL_STATE_NULL;
			}
			break;
			default:
				printf("StopChannels %s", "unknown channel type");
				return E_INVALID_CHANNEL_SYNC;
			}
		}
		return 0;
	}

	inline int StartLogger(Logger& logger)
	{
		if (logger.logger_state_ != LOGGER_STATE_UNINIT)
		{
			printf("StartLogger error. state:<%u> not uninit:<%u>.\n", logger.logger_state_, LOGGER_STATE_UNINIT);
			return E_LOGGER_IN_USE;
		}
		if (logger.shm_ == NULL)
		{
			printf("StartLogger error. logger not init %p.\n", logger.shm_);
			return E_LOGGER_NOT_INIT;
		}
		if (logger.shm_->channel_size_ > Logger::MAX_CHANNEL_SIZE || logger.shm_->channel_size_ <= 0)
		{
			printf("StartLogger error. logger invalid channel size: %d.\n", logger.shm_->channel_size_);
			return E_INVALID_CHANNEL_SIZE;
		}
		Logger::StateLockGuard state_guard(logger.state_lock_);
		if (logger.logger_state_ != LOGGER_STATE_UNINIT)
		{
			printf("StartLogger error. state:<%u> not uninit:<%u>.\n", logger.logger_state_, LOGGER_STATE_UNINIT);
			return E_LOGGER_IN_USE;
		}
		if (logger.shm_->channel_size_ > Logger::MAX_CHANNEL_SIZE || logger.shm_->channel_size_ <= 0)
		{
			printf("StartLogger error. channel size:<%d> invalid.\n", logger.shm_->channel_size_);
			return E_INVALID_CHANNEL_SIZE;
		}
		logger.logger_state_ = LOGGER_STATE_INITING;
		int ret = StartChannels(logger);
		if (ret != 0)
		{
			StopChannels(logger);
			logger.logger_state_ = LOGGER_STATE_UNINIT;
			printf("StartLogger error. StartChannels failed. channel size:<%d>. \n", logger.shm_->channel_size_);
			return ret;
		}
		logger.logger_state_ = LOGGER_STATE_RUNNING;
		return 0;
	}

	inline int CleanChannels(Logger& logger)
	{
		for (int channel_id = 0; channel_id < logger.shm_->channel_size_; channel_id++)
		{
			RingBuffer& ring_buffer = logger.shm_->ring_buffers_[channel_id];

			while (ring_buffer.read_idx_ != ring_buffer.write_idx_)
			{
				ring_buffer.buffer_[ring_buffer.read_idx_].data_mark_ = 0;
				ring_buffer.read_idx_ = (ring_buffer.read_idx_ + 1) % RingBuffer::BUFFER_LEN;
			}
			ring_buffer.read_idx_ = 0;
			ring_buffer.proc_idx_ = 0;
			ring_buffer.write_idx_ = 0;
			ring_buffer.hold_idx_ = 0;
		}
		return 0;
	}


	inline int StopLogger(Logger& logger)
	{
		if (logger.shm_->channel_size_ > Logger::MAX_CHANNEL_SIZE || logger.shm_->channel_size_ <= 0)
		{
			printf("StopLogger error. channel size:<%d> invalid.\n", logger.shm_->channel_size_);
			return E_INVALID_CHANNEL_SIZE;
		}
		if (logger.logger_state_ != LOGGER_STATE_RUNNING)
		{
			printf("StopLogger logger error. state:<%u> not running:<%u>.\n", logger.logger_state_, LOGGER_STATE_RUNNING);
			return E_LOGGER_NOT_RUNNING;
		}
		Logger::StateLockGuard state_guard(logger.state_lock_);

		if (logger.logger_state_ != LOGGER_STATE_RUNNING)
		{
			printf("StopLogger logger error. state:<%u> not running:<%u>.\n", logger.logger_state_, LOGGER_STATE_RUNNING);
			return E_LOGGER_NOT_RUNNING;
		}
		logger.logger_state_ = LOGGER_STATE_CLOSING;
		StopChannels(logger);
		CleanChannels(logger);

		for (auto& writer : logger.file_handles_)
		{
			if (writer.is_open())
			{
				writer.close();
			}
		}
		for (auto& writer : logger.udp_handles_)
		{
			if (writer.is_open())
			{
				writer.close();
			}
		}
		logger.logger_state_ = LOGGER_STATE_UNINIT;
		return 0;
	}


	inline int ParseAndStartLogger(Logger& logger, const std::string& config_content)
	{
		if (logger.logger_state_ != LOGGER_STATE_UNINIT)
		{
			printf("ParseAndStartLogger error. state:<%u> not uninit:<%u> by fast try.\n", logger.logger_state_, LOGGER_STATE_UNINIT);
			return E_LOGGER_IN_USE;
		}
		Logger::StateLockGuard state_guard(logger.state_lock_);
		if (logger.logger_state_ != LOGGER_STATE_UNINIT)
		{
			printf("ParseAndStartLogger error. state:<%u> not uninit:<%u> in locked check.\n", logger.logger_state_, LOGGER_STATE_UNINIT);
			return E_LOGGER_IN_USE;
		}
		int ret = InitFromYMAL(logger, config_content, "");
		if (ret != 0)
		{
			printf("ParseAndStartLogger error. ret:<%d>:%s.\n", ret, DebugErrno(ret).c_str());
			return ret;
		}
		ret = StartLogger(logger);
		if (ret != 0)
		{
			printf("ParseAndStartLogger error. ret:<%d>.\n", ret);
			return ret;
		}
		return 0;
	}

	inline int LoadAndStartLogger(Logger& logger, const std::string& confg_path)
	{
		if (logger.logger_state_ != LOGGER_STATE_UNINIT)
		{
			printf("LoadAndStartLogger error. state:<%u> not uninit:<%u>.\n", logger.logger_state_, LOGGER_STATE_UNINIT);
			return E_LOGGER_IN_USE;
		}
		Logger::StateLockGuard state_guard(logger.state_lock_);
		int ret = InitFromYMALFile(logger, confg_path);
		if (ret != 0)
		{
			printf("LoadAndStartLogger error. ret:<%d %s>.\n", ret, DebugErrno(ret).c_str());
			return ret;
		}
		ret = StartLogger(logger);
		if (ret != 0)
		{
			printf("LoadAndStartLogger error. ret:<%d>.\n", ret);
			return ret;
		}
		return 0;
	}


	inline long long GetChannelLog(Logger& logger, int channel_id, ChannelLogEnum field)
	{
		if (logger.shm_->channel_size_ <= channel_id || channel_id < 0)
		{
			return 0;
		}
		Channel& channel = logger.shm_->channels_[channel_id];
		if (field >= CHANNEL_LOG_MAX_ID)
		{
			return 0;
		}
		return AtomicLoadChannelLog(channel, field);
	}

	inline void SetChannelConfig(Logger& logger, int channel_id, ChannelConfigEnum field, long long val)
	{
		if (logger.shm_->channel_size_ <= channel_id || channel_id < 0)
		{
			return;
		}
		Channel& channel = logger.shm_->channels_[channel_id];
		if (field >= CHANNEL_CFG_MAX_ID)
		{
			return;
		}
		channel.config_fields_[field] = val;
	}

	inline long long GetDeviceLog(Logger& logger, int channel_id, int device_id, DeviceLogEnum field)
	{
		if (logger.shm_->channel_size_ <= channel_id || channel_id < 0)
		{
			return 0;
		}
		Channel& channel = logger.shm_->channels_[channel_id];
		if (field >= DEVICE_LOG_MAX_ID)
		{
			return 0;
		}
		if (channel.device_size_ <= device_id || device_id < 0)
		{
			return 0;
		}
		return AtomicLoadDeviceLog(channel, device_id, field);
	}

	inline void SetDeviceConfig(Logger& logger, int channel_id, int device_id, DeviceConfigEnum field, long long val)
	{
		if (logger.shm_->channel_size_ <= channel_id || channel_id < 0)
		{
			return;
		}
		if (field >= DEVICE_CFG_MAX_ID)
		{
			return;
		}
		Channel& channel = logger.shm_->channels_[channel_id];
		if (channel.device_size_ <= device_id || device_id < 0)
		{
			return;
		}
		channel.devices_[device_id].config_fields_[field] = val;
	}

	inline long long GetDeviceConfig(Logger& logger, int channel_id, int device_id, DeviceConfigEnum field)
	{
		if (logger.shm_->channel_size_ <= channel_id || channel_id < 0)
		{
			return 0;
		}
		if (field >= DEVICE_CFG_MAX_ID)
		{
			return 0;
		}
		Channel& channel = logger.shm_->channels_[channel_id];
		if (channel.device_size_ <= device_id || device_id < 0)
		{
			return 0;
		}
		return  AtomicLoadC(channel.devices_[device_id], field);
	}

	inline void BatchSetChannelConfig(Logger& logger, ChannelConfigEnum cce, long long v)
	{
		for (int i = 0; i < logger.shm_->channel_size_; i++)
		{
			auto& channel = logger.shm_->channels_[i];
			channel.config_fields_[cce] = v;
		}
	}

	inline void BatchSetDeviceConfig(Logger& logger, DeviceOutType out_type, DeviceConfigEnum dce, long long v)
	{
		for (int i = 0; i < logger.shm_->channel_size_; i++)
		{
			auto& channel = logger.shm_->channels_[i];
			for (int j = 0; j < channel.device_size_; j++)
			{
				auto& device = channel.devices_[j];
				if (device.out_type_ == (unsigned int)out_type || out_type == DEVICE_OUT_NULL)
				{
					device.config_fields_[dce] = v;
				}
			}
		}
	}



	inline void InitLogger(Logger& logger)
	{
		logger.hot_update_ = false;
		logger.logger_state_ = LOGGER_STATE_UNINIT;
		memset(logger.desc_, 0, Logger::MAX_LOGGER_DESC_LEN);
		logger.desc_len_ = 0;
		memset(logger.name_, 0, Logger::MAX_LOGGER_NAME_LEN);
		logger.name_len_ = 0;
		std::string name = FileHandler::process_name();
		name = name.substr(0, Logger::MAX_LOGGER_NAME_LEN - 1);
		memcpy(logger.name_, name.c_str(), name.length() + 1);
		logger.name_len_ = (int)name.length();
		logger.shm_key_ = 0;
		logger.shm_ = NULL;


#if ((defined WIN32) && !KEEP_INPUT_QUICK_EDIT)
		HANDLE input_handle = ::GetStdHandle(STD_INPUT_HANDLE);
		if (input_handle != INVALID_HANDLE_VALUE)
		{
			DWORD mode = 0;
			if (GetConsoleMode(input_handle, &mode))
			{
				mode &= ~ENABLE_QUICK_EDIT_MODE;
				mode &= ~ENABLE_INSERT_MODE;
				mode &= ~ENABLE_MOUSE_INPUT;
				SetConsoleMode(input_handle, mode);
			}
		}
#endif
	}

	inline Logger::Logger()
	{
		InitLogger(*this);
	}

	inline Logger::~Logger()
	{
		while (logger_state_ != LOGGER_STATE_UNINIT)
		{
			int ret = StopLogger(*this);
			if (ret != 0)
			{
				std::this_thread::sleep_for(std::chrono::milliseconds(20));
			}
		};
		UnloadSharedMemory(*this);
	}

#if __GNUG__ && __GNUC__ >= 6
#pragma GCC diagnostic pop
#endif

}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_STREAM_H_
#define _FN_LOG_STREAM_H_


namespace FNLog
{
	template<int BLANK_SIZE>
	struct LogBlankAlign
	{
		//static const int blank_size = BLANK_SIZE;
	};


	struct LogPercent
	{
		LogPercent(float v) :v_(v) {}
		LogPercent(double v) :v_(static_cast<float>(v)) {}
		float v_;
	};

	struct LogTimestamp
	{
		LogTimestamp()
		{
#ifdef WIN32
			FILETIME ft;
			GetSystemTimeAsFileTime(&ft);
			unsigned long long now = ft.dwHighDateTime;
			now <<= 32;
			now |= ft.dwLowDateTime;
			now /= 10;
			now -= 11644473600000000ULL;
			now /= 1000;
			ts_ = now / 1000;
			precise_ = (unsigned int)(now % 1000);
#else
			struct timeval tm;
			gettimeofday(&tm, nullptr);
			ts_ = tm.tv_sec;
			precise_ = tm.tv_usec / 1000;
#endif
		}
		LogTimestamp(long long ts, int precise)
		{
			ts_ = ts;
			precise_ = precise;
		}
		LogTimestamp(long long ts_ms)
		{
			ts_ = ts_ms / 1000;
			precise_ = ts_ms % 1000;
		}
		long long ts_;
		int precise_;
	};

	class LogStream
	{
	public:
		static const int MAX_CONTAINER_DEPTH = 5;
	public:
		LogStream(const LogStream& other) = delete;
		LogStream(LogStream&& other) noexcept
		{
			logger_ = other.logger_;
			log_data_ = other.log_data_;
			hold_idx_ = other.hold_idx_;
			other.logger_ = nullptr;
			other.log_data_ = nullptr;
			other.hold_idx_ = -1;
		}

		explicit LogStream(Logger& logger, int channel_id, int priority, int category, long long identify,
			const char * const file_name, int file_name_len, int line,
			const char * const func_name, int func_name_len, unsigned int prefix)
		{
			logger_ = nullptr;
			log_data_ = nullptr;
			int hold_idx = HoldChannel(logger, channel_id, priority, category, identify);
			if (hold_idx < 0)
			{
				return;
			}

			try
			{
				InitLogData(logger, logger.shm_->ring_buffers_[channel_id].buffer_[hold_idx], channel_id, priority, category, identify, prefix);
			}
			catch (const std::exception&)
			{
				printf("%s", "alloc log error. no more memory.");
				return;
			}
			logger_ = &logger;
			log_data_ = &logger.shm_->ring_buffers_[channel_id].buffer_[hold_idx];
			hold_idx_ = hold_idx;
			log_data_->code_line_ = line;
			log_data_->code_func_ = func_name;
			log_data_->code_func_len_ = func_name_len;
			log_data_->code_file_ = file_name;
			log_data_->code_file_len_ = file_name_len;
			if (prefix == LOG_PREFIX_NULL)
			{
				return;
			}
			if (prefix & LOG_PREFIX_TIMESTAMP)
			{
				log_data_->content_len_ += write_date_unsafe(log_data_->content_ + log_data_->content_len_, log_data_->timestamp_, log_data_->precise_);
			}
			if (prefix & LOG_PREFIX_PRIORITY)
			{
				log_data_->content_len_ += write_log_priority_unsafe(log_data_->content_ + log_data_->content_len_, log_data_->priority_);
			}
			if (prefix & LOG_PREFIX_THREAD)
			{
				log_data_->content_len_ += write_log_thread_unsafe(log_data_->content_ + log_data_->content_len_, log_data_->thread_);
			}
			if (prefix & LOG_PREFIX_NAME)
			{
				write_char_unsafe('[');
				write_buffer_unsafe(logger.name_, logger.name_len_);
				write_char_unsafe(']');
			}
			if (prefix & LOG_PREFIX_DESC)
			{
				write_char_unsafe('[');
				write_buffer_unsafe(logger.desc_, logger.desc_len_);
				write_char_unsafe(']');
			}
			if (prefix & LOG_PREFIX_FILE)
			{
				write_char_unsafe('[');
				if (file_name && file_name_len > 0)
				{
					int jump_bytes = short_path(file_name, file_name_len);
					write_buffer_unsafe(file_name + jump_bytes, file_name_len - jump_bytes);
				}
				else
				{
					write_buffer_unsafe("nofile", 6);
				}
				write_char_unsafe(':');
				*this << (unsigned long long)line;
				write_char_unsafe(']');
			}
			if (prefix & LOG_PREFIX_FUNCTION)
			{
				write_char_unsafe('(');
				if (func_name && func_name_len > 0)
				{
					write_buffer_unsafe(func_name, func_name_len);
				}
				else
				{
					write_buffer_unsafe("null", 4);
				}
				write_char_unsafe(')');
			}
			write_char_unsafe(' ');
			log_data_->prefix_len_ = log_data_->content_len_;
		}

		~LogStream()
		{
			if (log_data_)
			{
				if (RefVirtualDevice() != NULL)
				{
					Channel& channel = logger_->shm_->channels_[log_data_->channel_id_];
					if (channel.virtual_device_id_ >= 0)
					{
						Device& device = channel.devices_[channel.virtual_device_id_];
						if (log_data_->priority_ >= device.config_fields_[DEVICE_CFG_PRIORITY])
						{
							//more block check in the proc 
							EnterProcOutVirtualDevice(*logger_, log_data_->channel_id_, channel.virtual_device_id_, *log_data_);
							//(*RefVirtualDevice())(*log_data_);
						}

					}
				}
				PushLog(*logger_, log_data_->channel_id_, hold_idx_);
				hold_idx_ = -1;
				log_data_ = nullptr;
				logger_ = nullptr;
			}
		}
		//trans LogStream (temporary values) to  LogStream& (left values) 
		//all user's LogStream operator  only care about LogStream& without temporary.   
		LogStream& self() { return *this; }
		LogStream& set_category(int category) { if (log_data_) log_data_->category_ = category;  return *this; }
		LogStream& write_char_unsafe(char ch)
		{
			log_data_->content_[log_data_->content_len_] = ch;
			log_data_->content_len_++;
			return *this;
		}
		LogStream& write_buffer_unsafe(const char* src, int src_len)
		{
			memcpy(log_data_->content_ + log_data_->content_len_, src, src_len);
			log_data_->content_len_ += src_len;
			return *this;
		}

		LogStream& write_buffer(const char* src, int src_len)
		{
			if (log_data_ && src && src_len > 0 && log_data_->content_len_ < LogData::LOG_SIZE)
			{
				src_len = FN_MIN(src_len, LogData::LOG_SIZE - log_data_->content_len_);
				memcpy(log_data_->content_ + log_data_->content_len_, src, src_len);
				log_data_->content_len_ += src_len;
			}
			return *this;
		}


		template<size_t Wide>
		LogStream& write_longlong(long long n)
		{
			if (log_data_ && log_data_->content_len_ + 30 <= LogData::LOG_SIZE)
			{
				log_data_->content_len_ += write_dec_unsafe<Wide>(log_data_->content_ + log_data_->content_len_, n);
			}
			return *this;
		}
		template<size_t Wide>
		LogStream& write_ulonglong(unsigned long long n)
		{
			if (log_data_ && log_data_->content_len_ + 30 <= LogData::LOG_SIZE)
			{
				log_data_->content_len_ += write_dec_unsafe<Wide>(log_data_->content_ + log_data_->content_len_, n);
			}
			return *this;
		}

		template<size_t Wide, class N>
		LogStream& write_number(N n)
		{
			if (std::is_signed<N>::value)
			{
				return write_longlong<Wide>((long long)n);
			}
			return write_ulonglong<Wide>((unsigned long long)n);
		}

		LogStream& write_pointer(const void* ptr)
		{
			if (log_data_ && log_data_->content_len_ + 30 <= LogData::LOG_SIZE)
			{
				log_data_->content_len_ += FNLog::write_pointer_unsafe(log_data_->content_ + log_data_->content_len_, ptr);
			}
			return *this;
		}

		LogStream& write_binary(const char* dst, int len)
		{
			if (!log_data_)
			{
				return *this;
			}
			write_buffer("\r\n\t[", sizeof("\r\n\t[") - 1);
			for (int i = 0; i < (len / 32) + 1; i++)
			{
				write_buffer("\r\n\t[", sizeof("\r\n\t[") - 1);
				*this << (void*)(dst + (size_t)i * 32);
				write_buffer(": ", sizeof(": ") - 1);
				for (int j = i * 32; j < (i + 1) * 32 && j < len; j++)
				{
					if (isprint((unsigned char)dst[j]))
					{
						write_buffer(" ", sizeof(" ") - 1);
						write_buffer(dst + j, 1);
						write_buffer(" ", sizeof(" ") - 1);
					}
					else
					{
						write_buffer(" . ", sizeof(" . ") - 1);
					}
				}
				write_buffer("\r\n\t[", sizeof("\r\n\t[") - 1);
				if (log_data_->content_len_ + sizeof(void*) <= LogData::LOG_SIZE)
				{
					write_pointer(dst + (size_t)i * 32);
				}
				write_buffer(": ", sizeof(": ") - 1);
				for (int j = i * 32; j < (i + 1) * 32 && j < len; j++)
				{
					if (log_data_->content_len_ + 30 >= LogData::LOG_SIZE)
					{
						break;
					}
					log_data_->content_len_ += FNLog::write_hex_unsafe<2>(log_data_->content_ + log_data_->content_len_,
						(unsigned long long)(unsigned char)dst[j]);
					write_buffer(" ", sizeof(" ") - 1);
				}
			}
			write_buffer("\r\n\t]\r\n\t", sizeof("\r\n\t]\r\n\t") - 1);
			return *this;
		}

		LogStream& operator <<(const char* cstr)
		{
			if (log_data_)
			{
				if (cstr)
				{
					write_buffer(cstr, (int)strlen(cstr));
				}
				else
				{
					write_buffer("null", 4);
				}
			}
			return *this;
		}
		LogStream& operator <<(const void* ptr)
		{
			write_pointer(ptr);
			return *this;
		}

		LogStream& operator <<(std::nullptr_t)
		{
			return write_pointer(nullptr);
		}

		LogStream& operator << (char ch) { return write_buffer(&ch, 1); }
		LogStream & operator << (unsigned char ch) { return (*this << (unsigned long long)ch); }

		LogStream& operator << (bool val) { return (val ? write_buffer("true", 4) : write_buffer("false", 5)); }

		LogStream & operator << (short val) { return (*this << (long long)val); }
		LogStream & operator << (unsigned short val) { return (*this << (unsigned long long)val); }
		LogStream & operator << (int val) { return (*this << (long long)val); }
		LogStream & operator << (unsigned int val) { return (*this << (unsigned long long)val); }
		LogStream & operator << (long val) { return (*this << (long long)val); }
		LogStream & operator << (unsigned long val) { return (*this << (unsigned long long)val); }

		LogStream& operator << (long long integer) { return write_longlong<0>(integer); }

		LogStream& operator << (unsigned long long integer) { return write_ulonglong<0>(integer); }

		LogStream& operator << (float f)
		{
			if (log_data_ && log_data_->content_len_ + 30 <= LogData::LOG_SIZE)
			{
				log_data_->content_len_ += write_float_unsafe(log_data_->content_ + log_data_->content_len_, f);
			}
			return *this;
		}
		LogStream& operator << (double df)
		{
			if (log_data_ && log_data_->content_len_ + 30 <= LogData::LOG_SIZE)
			{
				log_data_->content_len_ += write_double_unsafe(log_data_->content_ + log_data_->content_len_, df);
			}
			return *this;
		}


		template<class _Ty1, class _Ty2>
		inline LogStream & operator <<(const std::pair<_Ty1, _Ty2> & val) { return *this << '<' << val.first << ':' << val.second << '>'; }

		template<class Container>
		LogStream& write_container(const Container& container, const char* name, int len)
		{
			write_buffer(name, len);
			write_buffer("(", 1);
			*this << container.size();
			write_buffer(")[", 2);
			int input_count = 0;
			for (auto iter = container.begin(); iter != container.end(); iter++)
			{
				if (input_count >= MAX_CONTAINER_DEPTH)
				{
					*this << "..., ";
					break;
				}
				if (input_count > 0)
				{
					*this << ", ";
				}
				*this << *iter;
				input_count++;
			}
			return *this << "]";
		}


		template<class _Elem, class _Alloc>
		LogStream & operator <<(const std::vector<_Elem, _Alloc> & val) { return write_container(val, "vector:", sizeof("vector:") - 1); }
		template<class _Elem, class _Alloc>
		LogStream & operator <<(const std::list<_Elem, _Alloc> & val) { return write_container(val, "list:", sizeof("list:") - 1); }
		template<class _Elem, class _Alloc>
		LogStream & operator <<(const std::deque<_Elem, _Alloc> & val) { return write_container(val, "deque:", sizeof("deque:") - 1); }
		template<class _Key, class _Tp, class _Compare, class _Allocator>
		LogStream & operator <<(const std::map<_Key, _Tp, _Compare, _Allocator> & val) { return write_container(val, "map:", sizeof("map:") - 1); }
		template<class _Key, class _Compare, class _Allocator>
		LogStream & operator <<(const std::set<_Key, _Compare, _Allocator> & val) { return write_container(val, "set:", sizeof("set:") - 1); }
		template<class _Key, class _Tp, class _Hash, class _Compare, class _Allocator>
		LogStream& operator <<(const std::unordered_map<_Key, _Tp, _Hash, _Compare, _Allocator>& val)
		{
			return write_container(val, "unordered_map:", sizeof("unordered_map:") - 1);
		}
		template<class _Key, class _Hash, class _Compare, class _Allocator>
		LogStream& operator <<(const std::unordered_set<_Key, _Hash, _Compare, _Allocator> & val)
		{
			return write_container(val, "unordered_set:", sizeof("unordered_set:") - 1);
		}
		template<class _Traits, class _Allocator>
		LogStream & operator <<(const std::basic_string<char, _Traits, _Allocator> & str) { return write_buffer(str.c_str(), (int)str.length()); }
		template<int BLANK_SIZE>
		LogStream & operator <<(const LogBlankAlign<BLANK_SIZE>& blanks)
		{
			if (log_data_ && log_data_->content_len_ + BLANK_SIZE < LogData::LOG_SIZE)
			{
				for (int i = log_data_->content_len_; i < BLANK_SIZE; i++)
				{
					write_char_unsafe(' ');
				}
			}
			return *this;
		}
		LogStream & operator <<(const LogPercent& blanks)
		{
			if (log_data_ && log_data_->content_len_ + 40 < LogData::LOG_SIZE)
			{
				if (blanks.v_ < 0.000001)
				{
					write_buffer("00.00%", (int)strlen("00.00%"));
				}
				else
				{
					log_data_->content_len_ += write_double_unsafe(log_data_->content_ + log_data_->content_len_, blanks.v_*100.0);
					write_char_unsafe('%');
				}
			}
			return *this;
		}

		LogStream & operator <<(const LogTimestamp& date)
		{
			if (log_data_ && log_data_->content_len_ + 40 < LogData::LOG_SIZE)
			{
				int write_bytes = write_date_unsafe(log_data_->content_ + log_data_->content_len_, date.ts_, date.precise_);
				log_data_->content_len_ += write_bytes;
			}
			return *this;
		}



	public:
		LogData * log_data_ = nullptr;
		Logger* logger_ = nullptr;
		int hold_idx_ = -1;//ring buffer  
	};



}


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_MACRO_H_
#define _FN_LOG_MACRO_H_


namespace FNLog
{

	inline Logger& GetDefaultLogger()
	{
		static Logger logger;
		return logger;
	}

	inline int LoadAndStartDefaultLogger(const std::string& config_path)
	{
		int ret = LoadAndStartLogger(GetDefaultLogger(), config_path);
		if (ret != 0)
		{
			printf("load auto start default logger error. ret:<%d>.\n", ret);
			return ret;
		}
		return 0;
	}

	inline int FastStartDefaultLogger(const std::string& config_text)
	{
		int ret = ParseAndStartLogger(GetDefaultLogger(), config_text);
		if (ret != 0)
		{
			printf("fast start default logger error. ret:<%d>.\n", ret);
			return ret;
		}
		return 0;
	}


	inline int FastStartDefaultLogger()
	{
		static const std::string default_config_text =
			R"----(
 # default channel 0
   # write full log to pname.log 
   # write error log to pname_error.log 
   # view  info log to screen 
 # sync channel 1 
   # write full log to pname.log
   # write info log to pname_info.log
   # view  info log to screen 

			 - channel: 0
    sync: async
    -device: 0
        disable: false
        out_type: file
        file: "$PNAME"
        rollback: 4
        limit_size: 100 m #only support M byte
    -device: 1
        disable: false
        out_type: file
        priority: error
        file: "$PNAME_error"
        rollback: 4
        limit_size: 100 m #only support M byte
    -device:2
        disable: false
        out_type: screen
        priority: info
 - channel: 1
    sync: sync
    -device: 0
        disable: false
        out_type: file
        file: "$PNAME_sync"
        rollback: 4
        limit_size: 100 m #only support M byte
    -device: 1
        disable: false
        out_type: file
        priority: info
        file: "$PNAME_sync_info"
        rollback: 4
        limit_size: 100 m #only support M byte
    -device:2
        disable: false
        out_type: screen
        priority: info 
)----";
		return FastStartDefaultLogger(default_config_text);
	}

	inline int FastStartDebugLogger()
	{
		int ret = FastStartDefaultLogger();
		if (ret == 0)
		{
			BatchSetDeviceConfig(GetDefaultLogger(), DEVICE_OUT_FILE, DEVICE_CFG_PRIORITY, PRIORITY_TRACE);
			SetDeviceConfig(GetDefaultLogger(), 0, 1, DEVICE_CFG_PRIORITY, PRIORITY_ERROR); //error file is still error file    
			BatchSetDeviceConfig(GetDefaultLogger(), DEVICE_OUT_SCREEN, DEVICE_CFG_PRIORITY, PRIORITY_DEBUG);
		}
		return ret;
	}
}

//--------------------BASE STREAM MACRO ---------------------------

//temporary LogStream  
#define LOG_STREAM_ORIGIN(logger, channel, priority, category, identify, prefix) \
FNLog::LogStream(logger, channel, priority, category, identify,\
__FILE__, sizeof(__FILE__) - 1, \
__LINE__, __FUNCTION__, sizeof(__FUNCTION__) -1, prefix)

//
#define LOG_STREAM_ORIGIN_REF(logger, channel, priority, category, identify, prefix) \
    LOG_STREAM_ORIGIN(logger, channel, priority, category, identify, prefix).self()

#define LOG_STREAM_DEFAULT_LOGGER(channel, priority, category, identify, prefix) \
    LOG_STREAM_ORIGIN_REF(FNLog::GetDefaultLogger(), channel, priority, category, identify, prefix)

#define LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel, priority, category, identify) \
    LOG_STREAM_DEFAULT_LOGGER(channel, priority, category, identify, FNLog::LOG_PREFIX_DEFAULT)


//--------------------CPP STREAM STYLE FORMAT ---------------------------
#define LogTraceStream(channel_id, category, identify) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_TRACE, category, identify)
#define LogDebugStream(channel_id, category, identify) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_DEBUG, category, identify)
#define LogInfoStream(channel_id,  category, identify) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_INFO,  category, identify)
#define LogWarnStream(channel_id,  category, identify) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_WARN,  category, identify)
#define LogErrorStream(channel_id, category, identify) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_ERROR, category, identify)
#define LogAlarmStream(channel_id, category, identify) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_ALARM, category, identify)
#define LogFatalStream(channel_id, category, identify) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_FATAL, category, identify)

#define LogTrace() LogTraceStream(0, 0, 0)
#define LogDebug() LogDebugStream(0, 0, 0)
#define LogInfo()  LogInfoStream(0, 0, 0)
#define LogWarn()  LogWarnStream(0, 0, 0)
#define LogError() LogErrorStream(0, 0, 0)
#define LogAlarm() LogAlarmStream(0, 0, 0)
#define LogFatal() LogFatalStream(0, 0, 0)


//--------------------CPP TEMPLATE STYLE FORMAT ---------------------------
inline FNLog::LogStream& LogTemplatePack(FNLog::LogStream& ls)
{
	return ls;
}
template<typename ... Args>
FNLog::LogStream& LogTemplatePack(FNLog::LogStream& ls, Args&& ... args)
{
	char buff[] = { (ls << args, '\0') ... };
	(void)buff;
	return ls;
}

#define LogTracePack(channel_id, category, identify, ...)  LogTemplatePack(LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_TRACE, category, identify), ##__VA_ARGS__)
#define LogDebugPack(channel_id, category, identify, ...)  LogTemplatePack(LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_DEBUG, category, identify), ##__VA_ARGS__)
#define LogInfoPack(channel_id,  category, identify, ...)  LogTemplatePack(LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_INFO,  category, identify), ##__VA_ARGS__)
#define LogWarnPack(channel_id,  category, identify, ...)  LogTemplatePack(LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_WARN,  category, identify), ##__VA_ARGS__)
#define LogErrorPack(channel_id, category, identify, ...)  LogTemplatePack(LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_ERROR, category, identify), ##__VA_ARGS__)
#define LogAlarmPack(channel_id, category, identify, ...)  LogTemplatePack(LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_ALARM, category, identify), ##__VA_ARGS__)
#define LogFatalPack(channel_id, category, identify, ...)  LogTemplatePack(LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_FATAL, category, identify), ##__VA_ARGS__)

#define PackTrace(...) LogTracePack(0, 0, 0, ##__VA_ARGS__)
#define PackDebug(...) LogDebugPack(0, 0, 0, ##__VA_ARGS__)
#define PackInfo( ...) LogInfoPack( 0, 0, 0, ##__VA_ARGS__)
#define PackWarn( ...) LogWarnPack( 0, 0, 0, ##__VA_ARGS__)
#define PackError(...) LogErrorPack(0, 0, 0, ##__VA_ARGS__)
#define PackAlarm(...) LogAlarmPack(0, 0, 0, ##__VA_ARGS__)
#define PackFatal(...) LogFatalPack(0, 0, 0, ##__VA_ARGS__)


//--------------------CPP MACRO STREAM STYLE FORMAT ---------------------------

#define LOG_TRACE(channel_id, category, identify, log) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_TRACE, category, identify) << log
#define LOG_DEBUG(channel_id, category, identify, log) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_DEBUG, category, identify) << log
#define LOG_INFO(channel_id,  category, identify, log) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_INFO,  category, identify) << log
#define LOG_WARN(channel_id,  category, identify, log) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_WARN,  category, identify) << log
#define LOG_ERROR(channel_id, category, identify, log) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_ERROR, category, identify) << log
#define LOG_ALARM(channel_id, category, identify, log) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_ALARM, category, identify) << log
#define LOG_FATAL(channel_id, category, identify, log) LOG_STREAM_DEFAULT_LOGGER_WITH_PREFIX(channel_id, FNLog::PRIORITY_FATAL, category, identify) << log

#define LOGT(log) LOG_TRACE(0, 0, 0, log)
#define LOGD(log) LOG_DEBUG(0, 0, 0, log)
#define LOGI(log) LOG_INFO(0,  0, 0, log)
#define LOGW(log) LOG_WARN(0,  0, 0, log)
#define LOGE(log) LOG_ERROR(0, 0, 0, log)
#define LOGA(log) LOG_ALARM(0, 0, 0, log)
#define LOGF(log) LOG_FATAL(0, 0, 0, log)


//--------------------C STYLE FORMAT ---------------------------

// function format warn:   void(int x1, int x2, const char *args, ...) __attribute__((format(printf, 3, 4)));    
#define LOG_FORMAT(channel_id, priority, category, identify, prefix, logformat, ...) \
do{ \
    if (FNLog::BlockInput(FNLog::GetDefaultLogger(), channel_id, priority, category, identify))  \
    { \
        break;   \
    } \
    FNLog::LogStream __log_stream(LOG_STREAM_ORIGIN(FNLog::GetDefaultLogger(), channel_id, priority, category, identify, prefix));\
    if (__log_stream.log_data_)\
    {\
        int __log_len = snprintf(__log_stream.log_data_ ->content_ + __log_stream.log_data_ ->content_len_, FNLog::LogData::LOG_SIZE - __log_stream.log_data_->content_len_, logformat, ##__VA_ARGS__); \
        if (__log_len < 0) __log_len = 0; \
        if (__log_len >= FNLog::LogData::LOG_SIZE - __log_stream.log_data_->content_len_) __log_len = FNLog::LogData::LOG_SIZE - __log_stream.log_data_->content_len_ -1; \
        __log_stream.log_data_ ->content_len_ += __log_len; \
    }\
} while (0)


#define LOGFMT_TRACE(channel_id, category, identify, fmt, ...)  LOG_FORMAT(channel_id, FNLog::PRIORITY_TRACE, category, identify, FNLog::LOG_PREFIX_DEFAULT, fmt, ##__VA_ARGS__)
#define LOGFMT_DEBUG(channel_id, category, identify, fmt, ...)  LOG_FORMAT(channel_id, FNLog::PRIORITY_DEBUG, category, identify, FNLog::LOG_PREFIX_DEFAULT, fmt, ##__VA_ARGS__)
#define LOGFMT_INFO( channel_id, category, identify, fmt, ...)  LOG_FORMAT(channel_id, FNLog::PRIORITY_INFO,  category, identify, FNLog::LOG_PREFIX_DEFAULT, fmt, ##__VA_ARGS__)
#define LOGFMT_WARN( channel_id, category, identify, fmt, ...)  LOG_FORMAT(channel_id, FNLog::PRIORITY_WARN,  category, identify, FNLog::LOG_PREFIX_DEFAULT, fmt, ##__VA_ARGS__)
#define LOGFMT_ERROR(channel_id, category, identify, fmt, ...)  LOG_FORMAT(channel_id, FNLog::PRIORITY_ERROR, category, identify, FNLog::LOG_PREFIX_DEFAULT, fmt, ##__VA_ARGS__)
#define LOGFMT_ALARM(channel_id, category, identify, fmt, ...)  LOG_FORMAT(channel_id, FNLog::PRIORITY_ALARM, category, identify, FNLog::LOG_PREFIX_DEFAULT, fmt, ##__VA_ARGS__)
#define LOGFMT_FATAL(channel_id, category, identify, fmt, ...)  LOG_FORMAT(channel_id, FNLog::PRIORITY_FATAL, category, identify, FNLog::LOG_PREFIX_DEFAULT, fmt, ##__VA_ARGS__)
#define LOGFMTT(fmt, ...) LOGFMT_TRACE(0, 0, 0, fmt,  ##__VA_ARGS__)
#define LOGFMTD(fmt, ...) LOGFMT_DEBUG(0, 0, 0, fmt,  ##__VA_ARGS__)
#define LOGFMTI(fmt, ...) LOGFMT_INFO( 0, 0, 0, fmt,  ##__VA_ARGS__)
#define LOGFMTW(fmt, ...) LOGFMT_WARN( 0, 0, 0, fmt,  ##__VA_ARGS__)
#define LOGFMTE(fmt, ...) LOGFMT_ERROR(0, 0, 0, fmt,  ##__VA_ARGS__)
#define LOGFMTA(fmt, ...) LOGFMT_ALARM(0, 0, 0, fmt,  ##__VA_ARGS__)
#define LOGFMTF(fmt, ...) LOGFMT_FATAL(0, 0, 0, fmt,  ##__VA_ARGS__)


#endif

/*
* Copyright (C) 2019 YaweiZhang <yawei.zhang@foxmail.com>.
* All rights reserved
* This file is part of the fn-log, used MIT License.
*/


#pragma once
#ifndef _FN_LOG_LOG_H_
#define _FN_LOG_LOG_H_


namespace FNLog
{

	//inline void EnableAllChannel(Logger& logger, bool enable);
	inline void EnableAllFileDevice(Logger& logger, bool enable) { BatchSetDeviceConfig(logger, DEVICE_OUT_FILE, DEVICE_CFG_ABLE, enable); }
	inline void EnableAllScreenDevice(Logger& logger, bool enable) { BatchSetDeviceConfig(logger, DEVICE_OUT_SCREEN, DEVICE_CFG_ABLE, enable); }
	inline void EnableAllUDPDevice(Logger& logger, bool enable) { BatchSetDeviceConfig(logger, DEVICE_OUT_UDP, DEVICE_CFG_ABLE, enable); }

	inline void SetAllChannelPriority(Logger& logger, LogPriority priority) { BatchSetChannelConfig(logger, CHANNEL_CFG_PRIORITY, priority); }
	inline void SetAllFilePriority(Logger& logger, LogPriority priority) { BatchSetDeviceConfig(logger, DEVICE_OUT_FILE, DEVICE_CFG_PRIORITY, priority); }
	inline void SetAllScreenPriority(Logger& logger, LogPriority priority) { BatchSetDeviceConfig(logger, DEVICE_OUT_SCREEN, DEVICE_CFG_PRIORITY, priority); }
	inline void SetAllUDPPriority(Logger& logger, LogPriority priority) { BatchSetDeviceConfig(logger, DEVICE_OUT_UDP, DEVICE_CFG_PRIORITY, priority); }

#define BatchSetChannelCategoryMacro(begin, count) \
    BatchSetChannelConfig(logger, CHANNEL_CFG_CATEGORY, begin);\
    BatchSetChannelConfig(logger, CHANNEL_CFG_CATEGORY_EXTEND, count);
#define BatchSetDeviceCategoryMacro(out_type, begin, count) \
    BatchSetDeviceConfig(logger, out_type, DEVICE_CFG_CATEGORY, begin); \
    BatchSetDeviceConfig(logger, out_type, DEVICE_CFG_CATEGORY_EXTEND, count);


	inline void SetAllChannelCategory(Logger& logger, int begin, int count) { BatchSetChannelCategoryMacro(begin, count); }
	inline void SetAllFilePriority(Logger& logger, int begin, int count) { BatchSetDeviceCategoryMacro(DEVICE_OUT_FILE, begin, count); }
	inline void SetAllScreenCategory(Logger& logger, int begin, int count) { BatchSetDeviceCategoryMacro(DEVICE_OUT_SCREEN, begin, count); }
	inline void SetAllUDPCategory(Logger& logger, int begin, int count) { BatchSetDeviceCategoryMacro(DEVICE_OUT_UDP, begin, count); }

	inline void SetAllFileLimitSize(Logger& logger, int limit) { BatchSetDeviceConfig(logger, DEVICE_OUT_FILE, DEVICE_CFG_FILE_LIMIT_SIZE, limit); }
	inline void SetAllFileRollbackCount(Logger& logger, int rb_count) { BatchSetDeviceConfig(logger, DEVICE_OUT_FILE, DEVICE_CFG_FILE_ROLLBACK, rb_count); }

}

#endif
#ifdef __GNUG__
#pragma GCC pop_options
#endif
posted @ 2023-08-22 17:47  jiftle  阅读(41)  评论(0编辑  收藏  举报