diff options
-rw-r--r-- | Source/SPLogger.h | 39 | ||||
-rw-r--r-- | Source/SPLogger.m | 143 | ||||
-rw-r--r-- | sequel-pro.xcodeproj/project.pbxproj | 14 |
3 files changed, 196 insertions, 0 deletions
diff --git a/Source/SPLogger.h b/Source/SPLogger.h new file mode 100644 index 00000000..168f70b7 --- /dev/null +++ b/Source/SPLogger.h @@ -0,0 +1,39 @@ +// +// $Id$ +// +// SPLogger.h +// sequel-pro +// +// Created by Rowan Beentje on 17/06/2009. +// Copyright 2009 Rowan Beentje. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import <Cocoa/Cocoa.h> + + +@interface SPLogger : NSObject { + BOOL initializedSuccessfully; + NSFileHandle *logFileHandle; +} + ++ (SPLogger *)logger; + +- (void) outputTimeString; +- (void) log:(NSString *)theString, ...; + +@end diff --git a/Source/SPLogger.m b/Source/SPLogger.m new file mode 100644 index 00000000..8c65f245 --- /dev/null +++ b/Source/SPLogger.m @@ -0,0 +1,143 @@ +// +// $Id$ +// +// SPLogger.m +// sequel-pro +// +// Created by Rowan Beentje on 17/06/2009. +// Copyright 2009 Rowan Beentje. All rights reserved. +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program 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 General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// More info at <http://code.google.com/p/sequel-pro/> + +#import "SPLogger.h" + +static SPLogger *logger = nil; + +/** + * This is a small class intended to aid in user issue debugging; by including + * the header file, and using [[SPLogger logger] log:@"String with format", ...] + * a file will be created on the user's desktop including timestamps and + * the log message. + * This allows use of fine-grained and detailed logging, without asking the user + * to copy text from a console log via NSLog. + * As each log line must by synched to disk as soon as it is received, for safety, + * this class can add a performance hit when lots of logging is used. + */ + +@implementation SPLogger + +/* + * Returns the shared logger object. + */ ++ (SPLogger *)logger +{ + @synchronized(self) { + if (logger == nil) { + [[self alloc] init]; + } + } + + return logger; +} + +#pragma mark - +#pragma mark Initialisation and teardown + ++ (id)allocWithZone:(NSZone *)zone +{ + @synchronized(self) { + if (logger == nil) { + logger = [super allocWithZone:zone]; + + return logger; + } + } + + return nil; +} + +- (id)init +{ + if ((self = [super init])) { + NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDesktopDirectory, NSUserDomainMask, YES); + NSString *logFilePath = [NSString stringWithFormat:@"%@/Sequel Pro Debug Log.txt", [paths objectAtIndex:0]]; + + initializedSuccessfully = YES; + + // Check if the debug file exists, and is writable + if ( [[NSFileManager defaultManager] fileExistsAtPath:logFilePath] ) { + if ( ![[NSFileManager defaultManager] isWritableFileAtPath:logFilePath] ) { + initializedSuccessfully = NO; + NSRunAlertPanel(@"Logging error", @"Log file exists but is not writeable; no debug log will be generated!", @"OK", nil, nil); + } + + // Otherwise try creating one + } else { + if ( ![[NSFileManager defaultManager] createFileAtPath:logFilePath contents:[NSData data] attributes:nil] ) { + initializedSuccessfully = NO; + NSRunAlertPanel(@"Logging error", @"Could not create log file for writing; no debug log will be generated!", @"OK", nil, nil); + } + } + + // Get a file handle to the file if possible + if (initializedSuccessfully) { + logFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath]; + if (!logFileHandle) { + initializedSuccessfully = NO; + NSRunAlertPanel(@"Logging error", @"Could not open log file for writing; no debug log will be generated!", @"OK", nil, nil); + } else { + [logFileHandle retain]; + [logFileHandle seekToEndOfFile]; + NSString *bundleName = [[NSFileManager defaultManager] displayNameAtPath:[[NSBundle mainBundle] bundlePath]]; + NSMutableString *logStart = [NSMutableString stringWithString:@"\n\n\n==========================================================================\n\n"]; + [logStart appendString:[NSString stringWithFormat:@"%@ (r%i)\n", bundleName, [[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] intValue]]]; + [logFileHandle writeData:[logStart dataUsingEncoding:NSUTF8StringEncoding]]; + } + } + } + + return self; +} + +#pragma mark - +#pragma mark Logging functions + +- (void) log:(NSString *)theString, ... +{ + if (!initializedSuccessfully) return; + + // Extract any supplied arguments and build the formatted log string + va_list arguments; + va_start(arguments, theString); + NSString *logString = [[NSString alloc] initWithFormat:theString arguments:arguments]; + va_end(arguments); + + // Write the log line, forcing an immediate write to disk to ensure logging + [logFileHandle writeData:[[NSString stringWithFormat:@"%@ %@\n", [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S" timeZone:nil locale:[[NSUserDefaults standardUserDefaults] dictionaryRepresentation]], logString] dataUsingEncoding:NSUTF8StringEncoding]]; + [logFileHandle synchronizeFile]; + + [logString release]; +} + +- (void) outputTimeString +{ + if (!initializedSuccessfully) return; + + [logFileHandle writeData:[[NSString stringWithFormat:@"Launched at %@\n\n", [[NSDate date] description]] dataUsingEncoding:NSUTF8StringEncoding]]; +} + +@end diff --git a/sequel-pro.xcodeproj/project.pbxproj b/sequel-pro.xcodeproj/project.pbxproj index c3ab1a7d..3ef0efb7 100644 --- a/sequel-pro.xcodeproj/project.pbxproj +++ b/sequel-pro.xcodeproj/project.pbxproj @@ -158,6 +158,7 @@ 58D2E22E101222870063EF1D /* link-arrow-clicked.png in Resources */ = {isa = PBXBuildFile; fileRef = 58D2E22B101222870063EF1D /* link-arrow-clicked.png */; }; 58D2E22F101222870063EF1D /* link-arrow-highlighted-clicked.png in Resources */ = {isa = PBXBuildFile; fileRef = 58D2E22C101222870063EF1D /* link-arrow-highlighted-clicked.png */; }; 58D2E230101222870063EF1D /* link-arrow.png in Resources */ = {isa = PBXBuildFile; fileRef = 58D2E22D101222870063EF1D /* link-arrow.png */; }; + 58DA8863103E15B5000B98DF /* SPLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 58DA8862103E15B5000B98DF /* SPLogger.m */; }; 58FEF16D0F23D66600518E8E /* SPSQLParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF16C0F23D66600518E8E /* SPSQLParser.m */; }; 58FEF57E0F3B4E9700518E8E /* SPTableData.m in Sources */ = {isa = PBXBuildFile; fileRef = 58FEF57D0F3B4E9700518E8E /* SPTableData.m */; }; 8D15AC340486D014006FF6A4 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A7FEA54F5311CA2CBB /* Cocoa.framework */; }; @@ -520,6 +521,8 @@ 58D2E22B101222870063EF1D /* link-arrow-clicked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "link-arrow-clicked.png"; sourceTree = "<group>"; }; 58D2E22C101222870063EF1D /* link-arrow-highlighted-clicked.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "link-arrow-highlighted-clicked.png"; sourceTree = "<group>"; }; 58D2E22D101222870063EF1D /* link-arrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "link-arrow.png"; sourceTree = "<group>"; }; + 58DA8861103E15B5000B98DF /* SPLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPLogger.h; sourceTree = "<group>"; }; + 58DA8862103E15B5000B98DF /* SPLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPLogger.m; sourceTree = "<group>"; }; 58FEF16B0F23D66600518E8E /* SPSQLParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPSQLParser.h; sourceTree = "<group>"; }; 58FEF16C0F23D66600518E8E /* SPSQLParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SPSQLParser.m; sourceTree = "<group>"; }; 58FEF57C0F3B4E9700518E8E /* SPTableData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPTableData.h; sourceTree = "<group>"; }; @@ -974,6 +977,7 @@ 17E641710EF01F5C001BC333 /* GUI */, 17E641720EF01F6B001BC333 /* SSHTunnel */, B57747D60F7A8990003B34F9 /* Category Additions */, + 58DA884E103E1597000B98DF /* Debugging & Support */, ); name = Other; sourceTree = "<group>"; @@ -1183,6 +1187,15 @@ path = UnitTests; sourceTree = "<group>"; }; + 58DA884E103E1597000B98DF /* Debugging & Support */ = { + isa = PBXGroup; + children = ( + 58DA8861103E15B5000B98DF /* SPLogger.h */, + 58DA8862103E15B5000B98DF /* SPLogger.m */, + ); + name = "Debugging & Support"; + sourceTree = "<group>"; + }; 58FEF15E0F23D60A00518E8E /* Parsing */ = { isa = PBXGroup; children = ( @@ -1627,6 +1640,7 @@ 584192A1101E57BB0089807F /* NSMutableArray-MultipleSort.m in Sources */, 589235321020C1230011DE00 /* SPHistoryController.m in Sources */, BCA6271C1031B9D40047E5D5 /* SPTooltip.m in Sources */, + 58DA8863103E15B5000B98DF /* SPLogger.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; |