2012-06-10 16:50:54 +00:00
/*
* synergy - - mouse and keyboard sharing utility
2016-09-07 14:24:00 +00:00
* Copyright ( C ) 2012 - 2016 Symless Ltd .
2012-09-04 02:09:56 +00:00
* Copyright ( C ) 2002 Chris Schoeneman
2012-06-10 16:50:54 +00:00
*
* This package is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
2015-05-03 02:33:52 +00:00
* found in the file LICENSE that should have accompanied this file .
2012-06-10 16:50:54 +00:00
*
* This package is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2016-12-28 11:50:32 +00:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2012-06-10 16:50:54 +00:00
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
2016-12-28 11:50:32 +00:00
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2012-06-10 16:50:54 +00:00
*/
2014-02-28 12:36:45 +00:00
# include "arch/Arch.h"
# include "arch/XArch.h"
# include "base/Log.h"
# include "base/String.h"
# include "base/log_outputters.h"
# include "common/Version.h"
2022-12-08 09:16:44 +00:00
# include <cstdint>
2012-06-10 16:50:54 +00:00
# include <cstdio>
# include <cstring>
# include <iostream>
2022-12-08 09:16:44 +00:00
# include <ctime>
2012-06-10 16:50:54 +00:00
// names of priorities
2016-12-28 11:50:32 +00:00
static const char * g_priority [ ] = {
" FATAL " ,
" ERROR " ,
" WARNING " ,
" NOTE " ,
" INFO " ,
" DEBUG " ,
" DEBUG1 " ,
" DEBUG2 " ,
" DEBUG3 " ,
" DEBUG4 " ,
" DEBUG5 "
2014-03-14 16:04:21 +00:00
} ;
2012-06-10 16:50:54 +00:00
// number of priorities
static const int g_numPriority = ( int ) ( sizeof ( g_priority ) / sizeof ( g_priority [ 0 ] ) ) ;
2018-08-02 13:11:05 +00:00
// if NDEBUG (not debug) is not specified, i.e. you're building in debug,
// then set default log level to DEBUG, otherwise the max level is INFO.
//
// GOTCHA: if `-DCMAKE_BUILD_TYPE=Debug` isn't set when configuring cmake
// for visual studio, then NDEBUG will be set (even if your VS solution
// config is Debug).
2012-07-29 02:10:25 +00:00
# ifndef NDEBUG
2016-12-28 11:50:32 +00:00
static const int g_defaultMaxPriority = kDEBUG ;
2012-06-10 16:50:54 +00:00
# else
2016-12-28 11:50:32 +00:00
static const int g_defaultMaxPriority = kINFO ;
2012-06-10 16:50:54 +00:00
# endif
//
2014-11-11 13:51:47 +00:00
// Log
2012-06-10 16:50:54 +00:00
//
2016-12-28 11:50:32 +00:00
Log * Log : : s_log = NULL ;
2012-06-10 16:50:54 +00:00
2014-11-11 13:51:47 +00:00
Log : : Log ( )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
assert ( s_log = = NULL ) ;
2012-06-10 16:50:54 +00:00
2016-12-28 11:50:32 +00:00
// create mutex for multithread safe operation
m_mutex = ARCH - > newMutex ( ) ;
2012-07-10 01:51:51 +00:00
2016-12-28 11:50:32 +00:00
// other initalization
m_maxPriority = g_defaultMaxPriority ;
m_maxNewlineLength = 0 ;
insert ( new ConsoleLogOutputter ) ;
2012-07-10 01:51:51 +00:00
2016-12-28 11:50:32 +00:00
s_log = this ;
2012-06-10 16:50:54 +00:00
}
2015-01-14 17:24:45 +00:00
Log : : Log ( Log * src )
{
2016-12-28 11:50:32 +00:00
s_log = src ;
2015-01-14 17:24:45 +00:00
}
2014-11-11 13:51:47 +00:00
Log : : ~ Log ( )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
// clean up
for ( OutputterList : : iterator index = m_outputters . begin ( ) ;
index ! = m_outputters . end ( ) ; + + index ) {
delete * index ;
}
for ( OutputterList : : iterator index = m_alwaysOutputters . begin ( ) ;
index ! = m_alwaysOutputters . end ( ) ; + + index ) {
delete * index ;
}
ARCH - > closeMutex ( m_mutex ) ;
2012-06-10 16:50:54 +00:00
}
2014-11-11 13:51:47 +00:00
Log *
Log : : getInstance ( )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
assert ( s_log ! = NULL ) ;
return s_log ;
2012-06-10 16:50:54 +00:00
}
const char *
2014-11-11 13:51:47 +00:00
Log : : getFilterName ( ) const
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
return getFilterName ( getFilter ( ) ) ;
2012-06-10 16:50:54 +00:00
}
const char *
2014-11-11 13:51:47 +00:00
Log : : getFilterName ( int level ) const
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
if ( level < 0 ) {
return " Message " ;
}
return g_priority [ level ] ;
2012-06-10 16:50:54 +00:00
}
void
2014-11-11 13:51:47 +00:00
Log : : print ( const char * file , int line , const char * fmt , . . . )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
// check if fmt begins with a priority argument
ELevel priority = kINFO ;
2021-04-06 10:54:25 +00:00
if ( ( strnlen ( fmt , SIZE_MAX ) > 2 ) & & ( fmt [ 0 ] = = ' % ' & & fmt [ 1 ] = = ' z ' ) ) {
2016-12-28 11:50:32 +00:00
// 060 in octal is 0 (48 in decimal), so subtracting this converts ascii
// number it a true number. we could use atoi instead, but this is how
// it was done originally.
priority = ( ELevel ) ( fmt [ 2 ] - ' \060 ' ) ;
// move the pointer on past the debug priority char
fmt + = 3 ;
}
// done if below priority threshold
if ( priority > getFilter ( ) ) {
return ;
}
// compute prefix padding length
char stack [ 1024 ] ;
// compute suffix padding length
int sPad = m_maxNewlineLength ;
// print to buffer, leaving space for a newline at the end and prefix
// at the beginning.
char * buffer = stack ;
int len = ( int ) ( sizeof ( stack ) / sizeof ( stack [ 0 ] ) ) ;
while ( true ) {
// try printing into the buffer
va_list args ;
va_start ( args , fmt ) ;
int n = ARCH - > vsnprintf ( buffer , len - sPad , fmt , args ) ;
va_end ( args ) ;
// if the buffer wasn't big enough then make it bigger and try again
if ( n < 0 | | n > ( int ) len ) {
if ( buffer ! = stack ) {
delete [ ] buffer ;
}
len * = 2 ;
buffer = new char [ len ] ;
}
// if the buffer was big enough then continue
else {
break ;
}
}
// print the prefix to the buffer. leave space for priority label.
// do not prefix time and file for kPRINT (CLOG_PRINT)
if ( priority ! = kPRINT ) {
2021-05-26 09:39:40 +00:00
struct tm tm ;
2020-08-25 18:16:21 +00:00
static const int timestamp_size = 50 ;
char timestamp [ timestamp_size ] ;
2016-12-28 11:50:32 +00:00
time_t t ;
time ( & t ) ;
2021-05-26 09:39:40 +00:00
# if WINAPI_MSWINDOWS
localtime_s ( & tm , & t ) ;
# else
localtime_r ( & t , & tm ) ;
# endif
snprintf ( timestamp , timestamp_size , " %04i-%02i-%02iT%02i:%02i:%02i " , tm . tm_year + 1900 , tm . tm_mon + 1 , tm . tm_mday , tm . tm_hour , tm . tm_min , tm . tm_sec ) ;
2016-12-28 11:50:32 +00:00
// square brackets, spaces, comma and null terminator take about 10
int size = 10 ;
2021-04-06 10:54:25 +00:00
size + = static_cast < int > ( strlen ( timestamp ) ) ; // Compliant: we made sure that timestamp variable ended with null(terminating null character is automatically appended in snprintf)
size + = static_cast < int > ( strlen ( g_priority [ priority ] ) ) ; // Compliant: we made sure that g_priority[priority] variable ended with null(static const char* declaration)
size + = static_cast < int > ( strnlen ( buffer , len ) ) ;
2016-07-14 14:55:13 +00:00
# ifndef NDEBUG
2021-04-06 10:54:25 +00:00
size + = static_cast < int > ( strnlen ( file , SIZE_MAX ) ) ;
2016-12-28 11:50:32 +00:00
// assume there is no file contains over 100k lines of code
size + = 6 ;
2016-07-14 14:55:13 +00:00
# endif
2016-12-28 11:50:32 +00:00
char * message = new char [ size ] ;
2016-07-14 14:55:13 +00:00
# ifndef NDEBUG
2020-08-25 18:16:21 +00:00
snprintf ( message , size , " [%s] %s: %s \n \t %s,%d " , timestamp , g_priority [ priority ] , buffer , file , line ) ;
2012-06-10 16:50:54 +00:00
# else
2020-08-25 19:10:51 +00:00
snprintf ( message , size , " [%s] %s: %s " , timestamp , g_priority [ priority ] , buffer ) ;
2012-06-10 16:50:54 +00:00
# endif
2016-12-28 11:50:32 +00:00
output ( priority , message ) ;
delete [ ] message ;
} else {
output ( priority , buffer ) ;
}
2012-06-10 16:50:54 +00:00
2016-12-28 11:50:32 +00:00
// clean up
if ( buffer ! = stack ) {
delete [ ] buffer ;
}
2012-06-10 16:50:54 +00:00
}
void
2014-11-11 13:51:47 +00:00
Log : : insert ( ILogOutputter * outputter , bool alwaysAtHead )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
assert ( outputter ! = NULL ) ;
ArchMutexLock lock ( m_mutex ) ;
if ( alwaysAtHead ) {
m_alwaysOutputters . push_front ( outputter ) ;
}
else {
m_outputters . push_front ( outputter ) ;
}
outputter - > open ( kAppVersion ) ;
// Issue 41
// don't show log unless user requests it, as some users find this
// feature irritating (i.e. when they lose network connectivity).
// in windows the log window can be displayed by selecting "show log"
// from the synergy system tray icon.
// if this causes problems for other architectures, then a different
// work around should be attempted.
//outputter->show(false);
2012-06-10 16:50:54 +00:00
}
void
2014-11-11 13:51:47 +00:00
Log : : remove ( ILogOutputter * outputter )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
ArchMutexLock lock ( m_mutex ) ;
m_outputters . remove ( outputter ) ;
m_alwaysOutputters . remove ( outputter ) ;
2012-06-10 16:50:54 +00:00
}
void
2014-11-11 13:51:47 +00:00
Log : : pop_front ( bool alwaysAtHead )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
ArchMutexLock lock ( m_mutex ) ;
OutputterList * list = alwaysAtHead ? & m_alwaysOutputters : & m_outputters ;
if ( ! list - > empty ( ) ) {
delete list - > front ( ) ;
list - > pop_front ( ) ;
}
2012-06-10 16:50:54 +00:00
}
bool
2014-11-11 13:51:47 +00:00
Log : : setFilter ( const char * maxPriority )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
if ( maxPriority ! = NULL ) {
for ( int i = 0 ; i < g_numPriority ; + + i ) {
if ( strcmp ( maxPriority , g_priority [ i ] ) = = 0 ) {
setFilter ( i ) ;
return true ;
}
}
return false ;
}
return true ;
2012-06-10 16:50:54 +00:00
}
void
2014-11-11 13:51:47 +00:00
Log : : setFilter ( int maxPriority )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
ArchMutexLock lock ( m_mutex ) ;
m_maxPriority = maxPriority ;
2012-06-10 16:50:54 +00:00
}
int
2014-11-11 13:51:47 +00:00
Log : : getFilter ( ) const
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
ArchMutexLock lock ( m_mutex ) ;
return m_maxPriority ;
2012-06-10 16:50:54 +00:00
}
void
2014-11-11 13:51:47 +00:00
Log : : output ( ELevel priority , char * msg )
2012-06-10 16:50:54 +00:00
{
2016-12-28 11:50:32 +00:00
assert ( priority > = - 1 & & priority < g_numPriority ) ;
assert ( msg ! = NULL ) ;
if ( ! msg ) return ;
2012-06-10 16:50:54 +00:00
2016-12-28 11:50:32 +00:00
ArchMutexLock lock ( m_mutex ) ;
2012-06-10 16:50:54 +00:00
2016-12-28 11:50:32 +00:00
OutputterList : : const_iterator i ;
2012-06-10 16:50:54 +00:00
2016-12-28 11:50:32 +00:00
for ( i = m_alwaysOutputters . begin ( ) ; i ! = m_alwaysOutputters . end ( ) ; + + i ) {
2012-06-10 16:50:54 +00:00
2016-12-28 11:50:32 +00:00
// write to outputter
( * i ) - > write ( priority , msg ) ;
}
2012-06-10 16:50:54 +00:00
2016-12-28 11:50:32 +00:00
for ( i = m_outputters . begin ( ) ; i ! = m_outputters . end ( ) ; + + i ) {
2012-06-10 16:50:54 +00:00
2016-12-28 11:50:32 +00:00
// write to outputter and break out of loop if it returns false
if ( ! ( * i ) - > write ( priority , msg ) ) {
break ;
}
}
2012-06-10 16:50:54 +00:00
}