Tuukka Norri avatar Tuukka Norri committed e0ffab1

Added the ability to log to a file

Comments (0)

Files changed (3)

Sources/BXExport.h

 #endif
 
 
+#ifndef BX_ANALYZER_NORETURN
+#if __clang__
+#define BX_ANALYZER_NORETURN   __attribute__((analyzer_noreturn))
+#else
+#define BX_ANALYZER_NORETURN
+#endif
+#endif
+
+
+#ifndef BX_FORMAT_FUNCTION(X,Y)
+#ifdef NS_FORMAT_FUNCTION
+#define BX_FORMAT_FUNCTION(X,Y) NS_FORMAT_FUNCTION(X,Y)
+#else
+#define BX_FORMAT_FUNCTION(X,Y)
+#endif
+#endif
+
+
+#ifndef BX_DEPRECATED_IN_1_8
 #define BX_DEPRECATED_IN_1_8 DEPRECATED_ATTRIBUTE
+#endif

Sources/BXLogger.h

  * an assertion fails. The reason might be a bug in either BaseTen or in
  * user code.
  */
-BX_EXPORT void BXAssertionDebug ();
+BX_EXPORT void BXAssertionDebug () BX_ANALYZER_NORETURN;
 
 
 /**
 BX_EXPORT void BXDeprecationWarning ();
 
 
-BX_EXPORT void BXLog (const char* fileName, const char* functionName, void* functionAddress, int line, enum BXLogLevel level, id messageFmt, ...);
-BX_EXPORT void BXLog_v (const char* fileName, const char* functionName, void* functionAddress, int line, enum BXLogLevel level, id messageFmt, va_list args);
+BX_EXPORT void BXLogSetLogFile (NSBundle *bundle);
+BX_EXPORT void BXLog (const char *fileName, 
+					  const char *functionName, 
+					  void *functionAddress, 
+					  int line, 
+					  enum BXLogLevel level, 
+					  id messageFmt, 
+					  ...) BX_FORMAT_FUNCTION(6,7);
+BX_EXPORT void BXLog_v (const char *fileName, 
+						const char *functionName, 
+						void *functionAddress, 
+						int line, 
+						enum BXLogLevel level, 
+						id messageFmt, 
+						va_list args) BX_FORMAT_FUNCTION(6,0);

Sources/BXLogger.m

 #import <dlfcn.h>
 #import <unistd.h>
 
+
 enum BXLogLevel BXLogLevel = kBXLogLevelWarning;
 static BOOL stAbortOnAssertionFailure = NO;
+// If the log file will be larger than this amount of bytes then it'll be truncated
+static const unsigned long long kLogFileMaxSize = 1024 * 1024;
+// When the log file will be truncated, this amount of bytes will be left to the beginning of the file
+static const unsigned long long kLogFileTruncateSize = 1024 * 128; 
 
 
-void BXSetLogLevel (enum BXLogLevel level)
+static void TruncateLogFile (NSString *filePath)
 {
-	BXDeprecationLog ();
-	BXLogLevel = level;
+	NSFileManager *fm = [[NSFileManager alloc] init];
+	if ([fm fileExistsAtPath: filePath])
+	{
+		NSNumber *sizeAttr = nil;
+		NSError *error = nil;
+		if ([fm respondsToSelector: @selector (attributesOfItemAtPath:error:)])
+			sizeAttr = [[fm attributesOfItemAtPath: filePath error: &error] objectForKey: NSFileSize];
+		else
+			sizeAttr = [[fm fileAttributesAtPath: filePath traverseLink: NO] objectForKey: NSFileSize];
+		
+		if (sizeAttr)
+		{
+			unsigned long long fileSize = [sizeAttr unsignedLongLongValue];
+			if (kLogFileMaxSize < fileSize)
+			{
+				NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath: filePath];
+				[fileHandle seekToFileOffset: (fileSize - kLogFileTruncateSize)];
+				NSData *dataToLeave = [fileHandle readDataToEndOfFile];
+				
+				[fileHandle seekToFileOffset: 0];
+				[fileHandle writeData: dataToLeave];
+				[fileHandle truncateFileAtOffset: kLogFileTruncateSize];
+				[fileHandle synchronizeFile];
+				[fileHandle closeFile];
+			}
+		}
+		else if (error)
+		{
+			BXLogError (@"Couldn't get attributes of file at path '%@', error: '%@'.", filePath, error);
+		}
+		else
+		{
+			BXLogError (@"Couldn't get attributes of file at path '%@'.", filePath);
+		}
+	}	
+	[fm release];
 }
 
 
-void BXLogSetLevel (enum BXLogLevel level)
+static inline
+const char* LogLevel (enum BXLogLevel level)
 {
-	BXLogLevel = level;
-}
-
-
-void BXLogSetAbortsOnAssertionFailure (BOOL flag)
-{
-	stAbortOnAssertionFailure = flag;
-}
-
-
-static inline const char* LogLevel (enum BXLogLevel level)
-{
-	char* retval = NULL;
+	const char* retval = NULL;
 	switch (level)
 	{
 		case kBXLogLevelDebug:
 	return retval;
 }
 
-static inline const char* LastPathComponent (const char* path)
+
+static inline
+const char* LastPathComponent (const char* path)
 {
 	const char* retval = ((strrchr (path, '/') ?: path - 1) + 1);
 	return retval;
 }
 
-static char* CopyLibraryName (const void* addr)
+
+static char*
+CopyLibraryName (const void* addr)
 {
 	Dl_info info = {};
 	char* retval = NULL;
 	return retval;
 }
 
-static char* CopyExecutableName ()
+
+static char*
+CopyExecutableName ()
 {
 	uint32_t pathLength = 0;
 	_NSGetExecutablePath (NULL, &pathLength);
 	return retval;
 }
 
-void BXAssertionDebug ()
+
+void
+BXLogSetLogFile (NSBundle *bundle)
+{
+    FSRef fileRef = {};
+    OSErr err = FSFindFolder (kUserDomain, kLogsFolderType, (Boolean) YES, &fileRef);
+	if (noErr == err)
+	{
+		CFURLRef URL = CFURLCreateFromFSRef (kCFAllocatorSystemDefault, &fileRef);
+		CFStringRef logsFolder = CFURLCopyFileSystemPath (URL, kCFURLPOSIXPathStyle);
+		NSString *bundleName = [bundle objectForInfoDictionaryKey: (NSString *) kCFBundleNameKey];
+		NSString *logPath = [NSString stringWithFormat: @"%@/%@.%@", logsFolder, bundleName, @"log"];
+		
+		if (freopen ([logPath fileSystemRepresentation], "a", stderr))
+			TruncateLogFile (logPath);		
+		else
+		{
+			BXLogError (@"Couldn't redirect stderr stream to file at path '%@', errno: %d, error: '%s'.", 
+						logPath, errno, strerror (errno));
+		}
+		
+		if (logsFolder) 
+			CFRelease (logsFolder);
+		if (URL)
+			CFRelease (URL);
+	}
+	else
+	{
+		BXLogError (@"Unable to get logs folder in the user domain: %s.",
+					GetMacOSStatusCommentString (err));
+	}
+}
+
+
+void BXSetLogLevel (enum BXLogLevel level)
+{
+	BXDeprecationLog ();
+	BXLogLevel = level;
+}
+
+
+void BXLogSetLevel (enum BXLogLevel level)
+{
+	BXLogLevel = level;
+}
+
+
+void BXLogSetAbortsOnAssertionFailure (BOOL flag)
+{
+	stAbortOnAssertionFailure = flag;
+}
+
+
+void
+BXAssertionDebug ()
 {
 	if (stAbortOnAssertionFailure)
 		abort ();
 		BXLogError (@"Break on BXAssertionDebug to inspect.");
 }
 
+
 void
 BXDeprecationWarning ()
 {
 	BXLogError (@"Break on BXDeprecationWarning to inspect.");
 }
 
-void BXLog (const char* fileName, const char* functionName, void* functionAddress, int line, enum BXLogLevel level, id messageFmt, ...)
+
+void
+BXLog (const char* fileName, const char* functionName, void* functionAddress, int line, enum BXLogLevel level, id messageFmt, ...)
 {
 	va_list args;
     va_start (args, messageFmt);
 	va_end (args);
 }
 
-void BXLog_v (const char* fileName, const char* functionName, void* functionAddress, int line, enum BXLogLevel level, id messageFmt, va_list args)
+
+void
+BXLog_v (const char* fileName, const char* functionName, void* functionAddress, int line, enum BXLogLevel level, id messageFmt, va_list args)
 {
 	NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
 	char* executable = CopyExecutableName ();
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.