/** Interface for NSLog for GNUStep Copyright (C) 1996, 1997 Free Software Foundation, Inc. Written by: Adam Fedor Date: November 1996 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. NSLog reference $Date$ $Revision$ */ #include "config.h" #include "GNUstepBase/preface.h" #include "Foundation/NSObjCRuntime.h" #include "Foundation/NSDate.h" #include "Foundation/NSCalendarDate.h" #include "Foundation/NSTimeZone.h" #include "Foundation/NSException.h" #include "Foundation/NSProcessInfo.h" #include "Foundation/NSLock.h" #include "Foundation/NSAutoreleasePool.h" #include "Foundation/NSData.h" #include "Foundation/NSThread.h" #ifdef HAVE_SYSLOG_H #include #endif #ifdef HAVE_UNISTD_H #include #endif #include "GSPrivate.h" extern NSThread *GSCurrentThread(); /** * A variable holding the file descriptor to which NSLogv() messages are * written by default. GNUstep initialises this to stderr.
* You may change this, but for thread safety should * use the lock provided by GSLogLock() to protect the change. */ int _NSLogDescriptor = 2; static NSRecursiveLock *myLock = nil; /** * Returns the lock used to protect the GNUstep NSLogv() implementation. * Use this to protect changes to * _NSLogDescriptor and * _NSLog_printf_handler */ NSRecursiveLock * GSLogLock() { if (myLock == nil) { [gnustep_global_lock lock]; if (myLock == nil) { myLock = [NSRecursiveLock new]; } [gnustep_global_lock unlock]; } return myLock; } static void _NSLog_standard_printf_handler (NSString* message) { NSData *d; const char *buf; unsigned len; static NSStringEncoding enc = 0; if (enc == 0) { enc = [NSString defaultCStringEncoding]; } d = [message dataUsingEncoding: enc allowLossyConversion: NO]; if (d == nil) { d = [message dataUsingEncoding: NSUTF8StringEncoding allowLossyConversion: NO]; } if (d == nil) // Should never happen. { buf = [message lossyCString]; len = strlen(buf); } else { buf = (const char*)[d bytes]; len = [d length]; } #ifdef HAVE_SYSLOG if (GSUserDefaultsFlag(GSLogSyslog) == YES || write(_NSLogDescriptor, buf, len) != (int)len) { int mask; /* We NULL-terminate the string in order to feed it to * syslog. */ char *null_terminated_buf = objc_malloc (sizeof (char) * (len + 1)); strncpy (null_terminated_buf, buf, len); null_terminated_buf[len] = '\0'; #ifdef LOG_ERR mask = LOG_ERR; #else # ifdef LOG_ERROR mask = LOG_ERROR; # else # error "Help, I can't find a logging level for syslog" # endif #endif #ifdef LOG_USER mask |= LOG_USER; #endif syslog(mask, "%s", null_terminated_buf); objc_free (null_terminated_buf); } #else write(_NSLogDescriptor, buf, len); #ifdef WIN32 { char *null_terminated_buf = objc_malloc (sizeof (char) * (len + 1)); strncpy (null_terminated_buf, buf, len); null_terminated_buf[len] = '\0'; OutputDebugString(null_terminated_buf); if (!IsDebuggerPresent()) { static HANDLE eventloghandle = 0; if (!eventloghandle) { eventloghandle = RegisterEventSource(NULL, [[[NSProcessInfo processInfo] processName] cString]); } if (eventloghandle) { ReportEvent(eventloghandle, // event log handle EVENTLOG_WARNING_TYPE, // event type 0, // category zero 0, // event identifier NULL, // no user security identifier 1, // one substitution string 0, // no data &null_terminated_buf, // pointer to string array NULL); // pointer to data } } objc_free (null_terminated_buf); } #endif // WIN32 #endif } /** * A pointer to a function used to actually write the log data. *

* GNUstep initialises this to a function implementing the standard * behavior for logging, but you may change this in your program * in order to implement any custom behavior you wish. You should * use the lock returned by GSLogLock() to protect any change you make. *

*

* Calls from NSLogv() to the function pointed to by this variable * are protected by a lock, and should therefore be thread safe. *

*

* This function should accept a single NSString argument and return void. *

* The default implementation in GNUstep performs as follows - * * * Converts the string to be logged to data in the default CString * encoding or, if that is not possible, to UTF8 data. * * * If the system supports writing to syslog and the user default to * say that logging should be done to syslog (GSLogSyslog) is set, * writes the data to the syslog. * * * Otherwise, writes the data to the file descriptor stored in the * variable * _NSLogDescriptor, * which is set by default to stderr.
* Your program may change this descriptor ... but you should protect * changes using the lock provided by GSLogLock().
* NB. If the write to the descriptor fails, and the system supports * writing to syslog, then the log is written to syslog as if the * appropriate user default had been set. *
*
*/ NSLog_printf_handler *_NSLog_printf_handler = _NSLog_standard_printf_handler; /** *

Provides the standard OpenStep logging facility. For details see * the lower level NSLogv() function (which this function uses). *

*

GNUstep provides powerful alternatives for logging ... see * NSDebugLog(), NSWarnLog() and GSPrintf() for example. We recommend * the use of NSDebugLog() and its relatives for debug purposes, and * GSPrintf() for general log messages, with NSLog() being reserved * for reporting possible/likely errors. GSPrintf() is declared in * GSObjCRuntime.h. *

*/ void NSLog (NSString* format, ...) { va_list ap; va_start (ap, format); NSLogv (format, ap); va_end (ap); } /** * The core logging function ... *

* The function generates a standard log entry by prepending * process ID and date/time information to your message, and * ensuring that a newline is present at the end of the message. *

*

* In GNUstep, the GSLogThread user default may be set to YES in * order to instruct this function to include the internal ID of * the mcurrent thread after the process ID. This can help you * to track the behavior of a multi-threaded program. *

*

* The resulting message is then passed to a handler function to * perform actual output. Locking is performed around the call to * the function actually writing the message out, to ensure that * logging is thread-safe. However, the actual creation of the * message written is only as safe as the [NSObject-description] methods * of the arguments you supply. *

*

* The function to write the data is pointed to by * _NSLog_printf_handler *

*/ void NSLogv (NSString* format, va_list args) { NSString *prefix; NSString *message; int pid; CREATE_AUTORELEASE_POOL(arp); if (_NSLog_printf_handler == NULL) _NSLog_printf_handler = *_NSLog_standard_printf_handler; #if defined(__MINGW__) pid = (int)GetCurrentProcessId(); #else pid = (int)getpid(); #endif #ifdef HAVE_SYSLOG if (GSUserDefaultsFlag(GSLogSyslog) == YES) { if (GSUserDefaultsFlag(GSLogThread) == YES) { prefix = [NSString stringWithFormat: @"[thread:%x] ", GSCurrentThread()]; } else { prefix = @""; } } else #endif { if (GSUserDefaultsFlag(GSLogThread) == YES) { prefix = [NSString stringWithFormat: @"%@ %@[%d,%x] ", [[NSCalendarDate calendarDate] descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S.%F"], [[NSProcessInfo processInfo] processName], pid, GSCurrentThread()]; } else { prefix = [NSString stringWithFormat: @"%@ %@[%d] ", [[NSCalendarDate calendarDate] descriptionWithCalendarFormat: @"%Y-%m-%d %H:%M:%S.%F"], [[NSProcessInfo processInfo] processName], pid]; } } /* Check if there is already a newline at the end of the format */ if (![format hasSuffix: @"\n"]) format = [format stringByAppendingString: @"\n"]; message = [NSString stringWithFormat: format arguments: args]; prefix = [prefix stringByAppendingString: message]; if (myLock == nil) { GSLogLock(); } [myLock lock]; _NSLog_printf_handler(prefix); [myLock unlock]; RELEASE(arp); }