/* * Copyright (c) 2017 rxi * Copyright (c) 2019 atmgnd * * 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. */ #include "log.h" #include #include #ifdef _MSC_VER #include #include #include #include #else #include #include #include #include #endif #include #include #include static struct { int level; FILE *fp; int size; #ifdef _MSC_VER CRITICAL_SECTION lock; #else pthread_mutex_t lock; #endif } L = { 0xFF }; static const char *level_names[] = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" }; #ifdef _MSC_VER FILE* fopen_unixlike(const char *file, const char *mode); #define fopen fopen_unixlike #endif static void lock(void) { #ifdef _MSC_VER EnterCriticalSection(&L.lock); #else pthread_mutex_lock(&L.lock); #endif } static void unlock(void) { #ifdef _MSC_VER LeaveCriticalSection(&L.lock); #else pthread_mutex_unlock(&L.lock); #endif } void logc_set_level(int level) { L.level = level; } static inline unsigned int thread_id() { #ifdef _MSC_VER return GetCurrentThreadId(); #else return (unsigned int)(syscall(__NR_gettid)); #endif } void logc_init(const char *path, int size) { L.fp = fopen(path, "ab+"); #ifdef _MSC_VER InitializeCriticalSection(&L.lock); #else pthread_mutexattr_t attr; int type; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutexattr_gettype(&attr, &type); pthread_mutex_init(&L.lock, &attr); pthread_mutexattr_destroy(&attr); #endif L.size = size; L.level = LOGC_TRACE; } void logc_cleanup() { if (L.fp) { fclose(L.fp); } if (L.level != 0xFF) { #ifdef _MSC_VER DeleteCriticalSection(&L.lock); #else pthread_mutex_destroy(&L.lock); #endif } } void logc_log(int level, const char *fmt, ...) { if (level < L.level) { return; } lock(); if (L.size > 0 && L.fp && ftell(L.fp) > L.size) { #ifdef _MSC_VER _chsize_s(_fileno(L.fp), 0); #else ftruncate(fileno(L.fp), 0); #endif } int ms; struct tm lt; #ifdef _MSC_VER struct timeb t; ftime(&t); localtime_s(<, &t.time); ms = t.millitm; #else struct timeval tv; gettimeofday(&tv, NULL); localtime_r(&tv.tv_sec, <); ms = (int)(tv.tv_usec / 1000); #endif FILE *real_fp; if (L.fp) { real_fp = L.fp; fseek(L.fp, 0, SEEK_END); // file truncated by user, stat.st_ino } else { real_fp = stderr; } va_list args; fprintf(real_fp, "%04d-%02d-%02d %02d:%02d:%02d.%03d" " %-5s [%d]: ", lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, lt.tm_hour, lt.tm_min, lt.tm_sec, ms, level_names[level], thread_id()); va_start(args, fmt); vfprintf(real_fp, fmt, args); va_end(args); fprintf(real_fp, "\n"); fflush(real_fp); unlock(); } #ifdef _MSC_VER /* from https://blog.httrack.com/blog/2013/10/05/creating-deletable-and-movable-files-on-windows/ */ static FILE* fopen_unixlike(const char *file, const char *mode) { DWORD dwDesiredAccess = 0; DWORD dwCreationDisposition = 0; const DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; const DWORD dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; size_t i; HANDLE handle; int fd; int _mode = 0; /* Infer flags. r: 1, w: 2, a: 4, +: 8 */ for (i = 0; mode[i] != '\0'; i++) { switch (mode[i]) { case 'r': _mode |= 1; break; case 'a': _mode |= 4; break; case 'w': _mode |= 2; break; case '+': _mode |= 8; break; } } switch (_mode) { case 12: dwDesiredAccess |= GENERIC_READ | GENERIC_WRITE | FILE_APPEND_DATA; dwCreationDisposition = OPEN_ALWAYS; break; } /* Create the file. */ handle = CreateFileA(file, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, dwFlagsAndAttributes, NULL); if (handle == INVALID_HANDLE_VALUE) { return NULL; } /* Associates a C run-time file descriptor with a file HANDLE. */ fd = _open_osfhandle((intptr_t)handle, _O_BINARY); if (fd == -1) { CloseHandle(handle); return NULL; } /* Associates a stream with a C run-time file descriptor. */ return _fdopen(fd, mode); } #endif