+2004-11-19 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * v4.5.95
+
+ * Defaults.plist: new defaults for NGLogging
+
+ * WOHttpAdaptor/WOHttpAdaptor.m: rewrote transaction logging to use
+ NGLogging. Configuration for transActionLogger is stored in
+ Defaults.plist.
+ NOTE: no profiling has been done, yet - thus the profiling
+ information needs to be updated (a TODO has been placed at the
+ appropriate place).
+
+ * DynamicElements/_WOTemporaryHyperlink.m: added correct cast to
+ circumvent gcc bug (false warning).
+
2004-11-19 Helge Hess <helge.hess@opengroupware.org>
* v4.5.94
{
- NGObjWeb_doc_ = "NSUserDefaults for NGObjWeb";
+ NGObjWeb_doc_ = "NSUserDefaults for NGObjWeb";
+ NGLogDefaultAppenderClass = "NGLogStdoutAppender";
+ NGLogDefaultLogLevel = "INFO";
+ NGLogDefaultLogEventFormatterClass = "NGLogEventDetailedFormatter";
WOAdaptor = "WOHttpAdaptor";
- WOAdaptorLogPath = "";
+ WOAdaptorLogPath = "";
WOAdditionalAdaptors = ( );
WOApplicationBaseURL = "/WebObjects";
WOApplicationSuffix = ".woa";
WOCoreOnApplicationException = NO;
WOCoreOnAwakeComponentInCtxDealloc = NO;
WOCoreOnHTTPAdaptorException = NO;
- WOCoreOnRecursiveSubcomponents = NO;
+ WOCoreOnRecursiveSubcomponents = NO;
WOCoreOnXmlRpcFault = NO;
WODebugActions = NO;
- WODebugComponentAwake = NO;
+ WODebugComponentAwake = NO;
WODebugComponentDefinition = NO;
- WODebugComponentLookup = NO;
+ WODebugComponentLookup = NO;
WODebugCursor = NO;
WODebugHttpTransaction = NO;
WODebugKeyPathAssociation = NO;
WOHttpAdaptor_LogStream = NO;
WOHttpAllowHost = "localhost";
WOHttpTransactionUseSimpleParser = NO;
+ WOHttpTransactionLoggerConfig = {
+ "LogLevel" = "INFO";
+ "Appenders" = (
+ {
+ "Class" = "NGLogStdoutAppender";
+ "Formatter" = {
+ "Class" = "NGLogEventFormatter";
+ };
+ },
+ );
+ };
WOIncludeCommentsInResponse = YES;
WOIsRedirectionEnabled = NO;
WOKeyPathAssociationsCacheSize = 200;
WOSimpleHTTPParserMaxUploadSizeInKB = 262144;
WOStatsStylesheetName = "WOStats.xsl";
WOUseRelativeURLs = YES;
- WOUseGlobalCookiePath = YES;
+ WOUseGlobalCookiePath = YES;
WOValueAssociationsCacheSize = 200;
WOWorkerThreadCount = 0;
WOxFileExtensions = ( wox, xtmpl, xhtml );
WOxElemBuilder_LogAssociationMapping = NO;
WOxElemBuilder_LogAssociationCreation = NO;
- WOxComponentElemBuilderDebugEnabled = NO;
+ WOxComponentElemBuilderDebugEnabled = NO;
WOxLogBuilderQueue = NO;
WOxBuilderClasses = (
WOxControlElemBuilder,
WOHyperlinkInfo *info;
Class linkClass = Nil;
- if ((info = [[WOHyperlinkInfo alloc] initWithConfig:(id)_config]) == nil) {
+ if ((info = [(WOHyperlinkInfo *)[WOHyperlinkInfo alloc]
+ initWithConfig:(id)_config]) == nil) {
return nil;
}
);
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
- DYLIB_CURRENT_VERSION = 4.5.91;
+ DYLIB_CURRENT_VERSION = 4.5.95;
FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
);
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
- DYLIB_CURRENT_VERSION = 4.5.91;
+ DYLIB_CURRENT_VERSION = 4.5.95;
FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
);
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
- DYLIB_CURRENT_VERSION = 4.5.91;
+ DYLIB_CURRENT_VERSION = 4.5.95;
FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
# version file
-SUBMINOR_VERSION:=94
+SUBMINOR_VERSION:=95
# v4.5.91 requires libNGExtensions v4.5.134
# v4.5.84 requires libNGExtensions v4.5.127
static NSMutableDictionary *pendingTransactions = nil; // THREAD
static BOOL useSimpleParser = YES;
static int doCore = -1;
-static NSString *adLogPath = nil;
-static NGLogger *debugLogger = nil;
-static NGLogger *perfLogger = nil;
+static NSString *adLogPath = nil;
+static NGLogger *debugLogger = nil;
+static NGLogger *perfLogger = nil;
+static NGLogger *transActionLogger = nil;
+ (int)version {
return 2;
if (didInit) return;
didInit = YES;
- lm = [NGLoggerManager defaultLoggerManager];
- perfLogger = [lm loggerForDefaultKey:@"WOProfileHttpAdaptor"];
- debugLogger = [lm loggerForDefaultKey:@"WODebugHttpTransaction"];
+ lm = [NGLoggerManager defaultLoggerManager];
+ perfLogger = [lm loggerForDefaultKey:@"WOProfileHttpAdaptor"];
+ debugLogger = [lm loggerForDefaultKey:@"WODebugHttpTransaction"];
+ transActionLogger = [lm loggerForClass:self];
ud = [NSUserDefaults standardUserDefaults];
useSimpleParser = [ud boolForKey:@"WOHttpTransactionUseSimpleParser"];
connection:(id<NGActiveSocket>)_connection
{
/*
+ NOTE: *obsoleted profiling information*
+ TODO: update profiling info! (left the old one for comparison)
Profiling: this method takes 0.95% of -run if the output is piped to
/dev/null on OSX, morphing caldate to string is 0.79% of that.
*/
- NSString *remoteHost = @"-";
- NSNumber *zippedLen;
- NSCalendarDate *now;
- NSDate *lstartDate;
- NSDictionary *startStats;
-
+ NSString *remoteHost;
+ NSNumber *zippedLen;
+ NSCalendarDate *now;
+ NSDate *lstartDate;
+ NSDictionary *startStats;
+ NSMutableString *buf;
+
lstartDate = [_request startDate];
startStats = [_request startStatistics];
zippedLen = [[_response userInfo] objectForKey:@"WOResponseZippedLength"];
// this is supposed to be in GMT ! TODO: explicitly set GMT
now = [NSCalendarDate calendarDate];
-
- /* print standard info */
- printf("%s - - [%02i/%s/%04i:%02i:%02i:%02i GMT] \"%s %s %s\" %i %i",
- remoteHost ? [remoteHost cString] : "-",
- [now dayOfMonth],
- monthAbbr([now monthOfYear]),
- [now yearOfCommonEra],
- [now hourOfDay], [now minuteOfHour], [now secondOfMinute],
- [[_request method] cString],
- [[_request uri] cString],
- [[_request httpVersion] cString],
- [_response status],
- [[_response content] length]);
-
- /* print duration */
+
+ buf = [[NSMutableString alloc] initWithCapacity:160]; /* 2 terminal lines */
+
+ /* append standard info */
+ [buf appendString:remoteHost];
+ [buf appendString:@" - - ["];
+ [buf appendFormat:@"%02i/%s/%04i:%02i:%02i:%02i",
+ [now dayOfMonth],
+ monthAbbr([now monthOfYear]),
+ [now yearOfCommonEra],
+ [now hourOfDay], [now minuteOfHour], [now secondOfMinute]];
+ [buf appendString:@" GMT] \""];
+ [buf appendString:[_request method]];
+ [buf appendString:@" "];
+ [buf appendString:[_request uri]];
+ [buf appendString:@" "];
+ [buf appendString:[_request httpVersion]];
+ [buf appendString:@"\" "];
+ [buf appendFormat:@"%i", [_response status]];
+ [buf appendFormat:@" %i", [[_response content] length]];
+
+ /* append duration */
if (lstartDate)
- printf(" %.3f", [now timeIntervalSinceDate:lstartDate]);
+ [buf appendFormat:@" %.3f", [now timeIntervalSinceDate:lstartDate]];
else
- printf(" -");
+ [buf appendString:@" -"];
- /* print zip level */
+ /* append zip level */
if (zippedLen) {
double p;
double unzippedLen;
unzippedLen =
[[[_response userInfo] objectForKey:@"WOResponseUnzippedLength"]
unsignedIntValue];
-
- printf(" %d", (unsigned int)unzippedLen);
+ [buf appendFormat:@" %d", (unsigned int)unzippedLen];
if ([zippedLen unsignedIntValue] == unzippedLen) {
- printf(" -");
+ [buf appendString:@" -"];
}
else {
p = unzippedLen / 100.0; // one percent
p = [zippedLen doubleValue] / p;
p = 100.0 - p;
- printf(" %-2d%%", (unsigned int)p);
+ [buf appendFormat:@" %-2d%%", (unsigned int)p];
}
}
else {
/* content was not zipped */
- printf(" - -");
+ [buf appendString:@" - -"];
}
- /* print statistics */
+ /* append statistics */
if (startStats) {
static NSProcessInfo *pi = nil;
diff *= 4; /* in KB */
if (diff == 0)
- printf(" 0");
+ [buf appendString:@" 0"];
else if (diff > 999)
- printf(" %iM", diff / 1024);
+ [buf appendFormat:@" %iM", diff / 1024];
else
- printf(" %iK", diff);
+ [buf appendFormat:@" %iK", diff];
}
else
- printf(" ?");
+ [buf appendString:@" ?"];
}
else
- printf(" -");
-
- /* flush log */
- puts("");
- fflush(stdout);
+ [buf appendString:@" -"];
+
+ [transActionLogger logLevel:NGLogLevelInfo message:buf];
+ [buf release];
}
/* NGHttpMessageParserDelegate */
);
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
- DYLIB_CURRENT_VERSION = 4.5.91;
+ DYLIB_CURRENT_VERSION = 4.5.95;
FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = NO;
+2004-11-19 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * NGLogging: updated - API considered stable now.
+ NOTE: "make distclean" is required this time. (v4.5.136)
+
2004-11-19 Helge Hess <helge.hess@opengroupware.org>
* v4.5.135
NGLogger.h \
NGLoggerManager.h \
NGLogEvent.h \
+ NGLogEventFormatter.h \
NGLogAppender.h \
- NGLogConsoleAppender.h \
+ NGLogFileHandleAppender.h \
NGLogSyslogAppender.h \
COMPILER_FLAGS = "-I..";
};
};
+ AD8B5D95074CF58C001AF5D3 = {
+ fileEncoding = 5;
+ indentWidth = 2;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = NGLogFileHandleAppender.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD8B5D97074CF58C001AF5D3 = {
+ fileRef = AD8B5D95074CF58C001AF5D3;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ AD8B5D98074CF614001AF5D3 = {
+ fileEncoding = 5;
+ indentWidth = 2;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = NGLogStdoutAppender.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD8B5D99074CF614001AF5D3 = {
+ fileRef = AD8B5D98074CF614001AF5D3;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ AD8B5DDC074CF7C8001AF5D3 = {
+ fileEncoding = 5;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = NGLogFileHandleAppender.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD8B5DDD074CF7C8001AF5D3 = {
+ fileRef = AD8B5DDC074CF7C8001AF5D3;
+ isa = PBXBuildFile;
+ settings = {
+ ATTRIBUTES = (
+ Public,
+ );
+ };
+ };
+ AD8B5F6C074D46D0001AF5D3 = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = NGLogEventFormatter.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD8B5F6D074D46D0001AF5D3 = {
+ fileRef = AD8B5F6C074D46D0001AF5D3;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ AD8B5FBE074D4D18001AF5D3 = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.h;
+ path = NGLogEventFormatter.h;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD8B5FBF074D4D18001AF5D3 = {
+ fileRef = AD8B5FBE074D4D18001AF5D3;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ AD8B5FFB074D57B2001AF5D3 = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = NGLogEventDetailedFormatter.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD8B5FFC074D57B2001AF5D3 = {
+ fileRef = AD8B5FFB074D57B2001AF5D3;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
+ AD8B6017074D59B4001AF5D3 = {
+ fileEncoding = 4;
+ isa = PBXFileReference;
+ lastKnownFileType = sourcecode.c.objc;
+ path = NGLogStderrAppender.m;
+ refType = 4;
+ sourceTree = "<group>";
+ };
+ AD8B6018074D59B4001AF5D3 = {
+ fileRef = AD8B6017074D59B4001AF5D3;
+ isa = PBXBuildFile;
+ settings = {
+ };
+ };
ADCD51360743BBBC0071C1A1 = {
children = (
AD595AB00745170400B2C064,
refType = 4;
sourceTree = "<group>";
};
- ADCD513B0743BBE10071C1A1 = {
- fileEncoding = 5;
- indentWidth = 2;
- isa = PBXFileReference;
- lastKnownFileType = sourcecode.c.h;
- path = NGLogConsoleAppender.h;
- refType = 4;
- sourceTree = "<group>";
- };
- ADCD513C0743BBE10071C1A1 = {
- fileEncoding = 5;
- indentWidth = 2;
- isa = PBXFileReference;
- lastKnownFileType = sourcecode.c.objc;
- path = NGLogConsoleAppender.m;
- refType = 4;
- sourceTree = "<group>";
- };
ADCD513D0743BBE10071C1A1 = {
fileEncoding = 5;
indentWidth = 2;
settings = {
};
};
- ADCD514A0743BBE10071C1A1 = {
- fileRef = ADCD513B0743BBE10071C1A1;
- isa = PBXBuildFile;
- settings = {
- ATTRIBUTES = (
- Public,
- );
- };
- };
- ADCD514B0743BBE10071C1A1 = {
- fileRef = ADCD513C0743BBE10071C1A1;
- isa = PBXBuildFile;
- settings = {
- };
- };
ADCD514C0743BBE10071C1A1 = {
fileRef = ADCD513D0743BBE10071C1A1;
isa = PBXBuildFile;
ADCD513F0743BBE10071C1A1,
ADCD52020743D8CA0071C1A1,
ADCD513D0743BBE10071C1A1,
+ AD8B5FBE074D4D18001AF5D3,
ADCD51390743BBE10071C1A1,
- ADCD513B0743BBE10071C1A1,
+ AD8B5DDC074CF7C8001AF5D3,
ADCD51420743BBE10071C1A1,
);
fileEncoding = 5;
ADCD51400743BBE10071C1A1,
ADCD52030743D8CA0071C1A1,
ADCD513E0743BBE10071C1A1,
+ AD8B5F6C074D46D0001AF5D3,
+ AD8B5FFB074D57B2001AF5D3,
ADCD513A0743BBE10071C1A1,
- ADCD513C0743BBE10071C1A1,
ADCD51430743BBE10071C1A1,
+ AD8B5D95074CF58C001AF5D3,
+ AD8B5D98074CF614001AF5D3,
+ AD8B6017074D59B4001AF5D3,
);
+ fileEncoding = 5;
+ indentWidth = 2;
isa = PBXGroup;
name = Classes;
refType = 4;
ADD45B6006FEF017004BBD65,
AD4BF6D60703147A006FB665,
ADCD51480743BBE10071C1A1,
- ADCD514A0743BBE10071C1A1,
ADCD514C0743BBE10071C1A1,
ADCD514E0743BBE10071C1A1,
ADCD51500743BBE10071C1A1,
ADCD51510743BBE10071C1A1,
ADCD51530743BBE10071C1A1,
ADCD52040743D8CA0071C1A1,
+ AD8B5DDD074CF7C8001AF5D3,
+ AD8B5FBF074D4D18001AF5D3,
);
isa = PBXHeadersBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
AD4BF6EC070314EE006FB665,
AD665E3A071F00AF00EC5911,
ADCD51490743BBE10071C1A1,
- ADCD514B0743BBE10071C1A1,
ADCD514D0743BBE10071C1A1,
ADCD514F0743BBE10071C1A1,
ADCD51520743BBE10071C1A1,
ADCD52050743D8CA0071C1A1,
+ AD8B5D97074CF58C001AF5D3,
+ AD8B5D99074CF614001AF5D3,
+ AD8B5F6D074D46D0001AF5D3,
+ AD8B5FFC074D57B2001AF5D3,
+ AD8B6018074D59B4001AF5D3,
);
isa = PBXSourcesBuildPhase;
runOnlyForDeploymentPostprocessing = 0;
);
buildSettings = {
DYLIB_COMPATIBILITY_VERSION = 1;
- DYLIB_CURRENT_VERSION = 4.5.134;
+ DYLIB_CURRENT_VERSION = 4.5.136;
FRAMEWORK_SEARCH_PATHS = "$(LOCAL_LIBRARY_DIR)/Frameworks";
FRAMEWORK_VERSION = A;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
ADD65D8E06DA3830007161CA,
ADD65D8F06DA3830007161CA,
);
+ fileEncoding = 5;
+ indentWidth = 2;
isa = PBXGroup;
name = Headers;
path = NGExtensions;
NGLogAppender
Abstract superclass for all log appenders.
+
+ NGLogAppender honours the following user default keys:
+
+ User Default key Function
+ ----------------------------------------------------------------------------
+ NGLogDefaultAppenderClass The appender class to use if no class
+ information was provided by the configuration.
+ The fallback is "NGLogStdoutAppender".
+
+
+ The following keys in the configuration dictionary will be recognized:
+
+ Key Function
+ ----------------------------------------------------------------------------
+ "Class" The class to use for instance creation. If no
+ class name is provided, the fallback path
+ described above will be taken.
+
+ "Formatter" Dictionary suitable as configuration
+ provided to NGLogEventFormatters's factory
+ method. Please see NGLogEventFormatteer.h for
+ further explanation.
*/
#import <Foundation/NSObject.h>
#include <NGExtensions/NGLogLevel.h>
-@class NGLogEvent;
+@class NSDictionary, NGLogEvent, NGLogEventFormatter;
@interface NGLogAppender : NSObject
{
+ NGLogEventFormatter *formatter;
}
+/* factory method */
++ (id)logAppenderFromConfig:(NSDictionary *)_config;
+
+/* designated initializer */
+- (id)initWithConfig:(NSDictionary *)_config;
+
/* subclass responsibility */
- (void)appendLogEvent:(NGLogEvent *)_event;
- (NSString *)formattedEvent:(NGLogEvent *)_event;
-- (NSString *)localizedNameOfLogLevel:(NGLogLevel)_level;
-
@end
#endif /* __NGExtensions_NGLogAppender_H_ */
#import <Foundation/NSDate.h>
#include <NGExtensions/NGLogLevel.h>
-@class NSString;
+@class NSString, NSCalendarDate;
@interface NGLogEvent : NSObject
{
- (NGLogLevel)level;
- (NSString *)message;
-- (NSDate *)date;
+- (NSCalendarDate *)date;
@end
--- /dev/null
+/*
+ Copyright (C) 2004 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ OGo 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+ */
+
+#ifndef __NGExtensions_NGLogEventFormatter_H_
+#define __NGExtensions_NGLogEventFormatter_H_
+
+/*
+ NGLogEventFormatter
+
+ Suits as factory and base class for all custom log event formatters.
+ Its purpose is to offer a lightweight interface to transform NGLogEvent
+ objects into string representations.
+
+
+ NGLogEventFormatter honours the following user default keys:
+
+ User Default key Function
+ ----------------------------------------------------------------------------
+ NGLogDefaultLogEventFormatterClass The formatter class to use if no class
+ information was provided by the
+ configuration.
+ The fallback is "NGLogEventFormatter".
+
+ The following keys in the configuration dictionary will be recognized:
+
+ Key Function
+ ----------------------------------------------------------------------------
+ "Class" The class to use for instance creation. If no
+ class name is provided, the fallback path
+ described above will be taken.
+*/
+
+#import <Foundation/NSObject.h>
+#include <NGExtensions/NGLogLevel.h>
+
+@class NSDictionary, NGLogEvent;
+
+@interface NGLogEventFormatter : NSObject
+{
+}
+
++ (id)logEventFormatterFromConfig:(NSDictionary *)_config;
+
+- (id)initWithConfig:(NSDictionary *)_config;
+
+/* formatting */
+- (NSString *)formattedEvent:(NGLogEvent *)_event;
+
+@end
+
+#endif /* __NGExtensions_NGLogEventFormatter_H_ */
--- /dev/null
+/*
+ Copyright (C) 2004 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ OGo 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#ifndef __NGExtensions_NGLogFileHandleAppender_H_
+#define __NGExtensions_NGLogFileHandleAppender_H_
+
+/*
+ NGLogFileHandleAppender
+
+ Suits as an abstract base class for all NSFileHandle based appenders.
+ */
+
+#include <NGExtensions/NGLogAppender.h>
+#import <Foundation/NSString.h> /* for NSStringEncoding */
+
+@class NSFileHandle, NSDictionary;
+
+@interface NGLogFileHandleAppender : NGLogAppender
+{
+ NSFileHandle *fh;
+ NSStringEncoding encoding;
+ BOOL flushImmediately;
+}
+
+- (BOOL)isFileHandleOpen;
+- (void)openFileHandleWithConfig:(NSDictionary *)_config;
+- (void)closeFileHandle;
+
+@end
+
+#endif /* __NGExtensions_NGLogFileHandleAppender_H_ */
only if this minimum log level is satisfied - otherwise it silently drops
these messages.
- Note: Except in rare circumstances, do not allocate loggers yourself. Always
- try to use the appropriate API of NGLoggerManager if possible.
+ NGLogger also offers a factory to instantiate loggers from configs stored
+ in NSUserDefaults.
+
+ NOTE: Except in rare circumstances, do not allocate loggers yourself!
+ Always try to use the appropriate API of NGLoggerManager if possible.
+
+
+ NGLogger honours the following user default keys:
+
+ User Default key Function
+ ----------------------------------------------------------------------------
+ NGLogDefaultLogLevel The log level to use as a fallback, if no
+ log level is provided during initialization.
+ The default is "INFO".
+
+
+ The following keys in the configuration dictionary will be recognized:
+
+ Key Function
+ ----------------------------------------------------------------------------
+ "LogLevel" The log level to use for this logger. If no
+ log level is provided, sets log level according
+ to fallback described above.
+
+ "Appenders" Array of dictionaries suitable as configuration
+ provided to NGLogAppender's factory method.
+ Please see NGLogAppender.h for further
+ explanation.
+
+ LoggerConfig example:
+
+ WOHttpTransactionLoggerConfig = {
+ "LogLevel" = "INFO";
+ "Appenders" = (
+ {
+ "Class" = "NGLogStdoutAppender";
+ "Formatter" = {
+ "Class" = "NGLogEventFormatter";
+ };
+ },
+ );
+ };
- Note: Currently the NGLogger implementation lacks an API for configuring
- appenders. Until this has been done appropriately, use the
- "NGLogDefaultAppenderClass" user default to select an appropriate
- default appender. If this default isn't set, "NGLogConsoleAppender"
- is used.
*/
#import <Foundation/NSObject.h>
#include <NGExtensions/NGLogLevel.h>
+@class NSMutableArray, NSString, NSDictionary, NGLogAppender;
+
@interface NGLogger : NSObject
{
- NGLogLevel logLevel;
- id _appender; // going away as soon as we have a config
+ NSMutableArray *appenders;
+ @public
+ NGLogLevel logLevel;
}
-+ (id)defaultLogger;
++ (id)loggerWithConfigFromUserDefaults:(NSString *)_defaultName;
+
+- (id)initWithConfig:(NSDictionary *)_config;
- (id)initWithLogLevel:(NGLogLevel)_level;
/* accessors */
- (void)setLogLevel:(NGLogLevel)_level;
- (NGLogLevel)logLevel;
+- (void)addAppender:(NGLogAppender *)_appender;
+- (void)removeAppender:(NGLogAppender *)_appender;
-
/* logging */
+- (void)logLevel:(NGLogLevel)_level message:(NSString *)_msg;
/* conditions */
same name/key. Also, NGLoggerManager offers conditional creation of loggers
based on user default keys (and special values associated with these keys).
- It's planned that NGLoggerManager will be provided with a configuration of
- some sort at a later stage, so configuration of log levels and appenders for
- loggers can reach a similar dynamism as is currently achieved in Log4J.
+ NGLoggerManager honours the following user default keys:
+
+ User Default key Function
+ ----------------------------------------------------------------------------
+ <Name>LoggerConfig contains the configuration of the logger
+ named <Name>. Depending on what method you used
+ to retrieve the logger, <Name> is either
+ the user default key, class name or another
+ arbitrary name. The config found for that key
+ is used to initialize the logger instance.
+
+ NGLogDebugAllEnabled if set to "YES" will always return a logger
+ when -loggerForDefaultKey: is called.
+
*/
#import <Foundation/NSObject.h>
#include <NGExtensions/NGLogger.h>
#include <NGExtensions/NGLoggerManager.h>
#include <NGExtensions/NGLogEvent.h>
+#include <NGExtensions/NGLogEventFormatter.h>
#include <NGExtensions/NGLogAppender.h>
-#include <NGExtensions/NGLogConsoleAppender.h>
+#include <NGExtensions/NGLogFileHandleAppender.h>
#include <NGExtensions/NGLogSyslogAppender.h>
#endif /* __NGExtensions_NGLogging_H_ */
+2004-11-19 Marcus Mueller <znek@mulle-kybernetik.com>
+
+ * *.h: added detailed documentation
+
+ * *.m: added -description where appropriate
+
+ * NGLogger.m: removed +defaultLogger from the API, it's used
+ internally though.
+
+ * NGLoggerManager.m: reinstated caching of loggers. Added optimization
+ to reuse a "default" logger when no config is available.
+
+ * NGLogEvent.[hm]: changed -date to return NSCalendarDate instead of
+ NSDate.
+
+ * NGLogEventFormatter.[hm]: new base class for implementing formatters.
+ Also offers a factory for creating log event formatter instances
+ from configurations.
+
+ * NGLogEventDetailedFormatter.m: offers rich logging, similar to what
+ NSLog() in libFoundation has to offer.
+
+ * NGLogConsoleAppender.m: removed, obsoleted by NGLogStdoutAppender.
+
+ * NGLogFileHandleAppender.[hm]: new base class for implementing file
+ handle based appenders.
+
+ * NGLogStdoutAppender.m, NGLogStderrAppender.m: appenders for logging
+ to stdout/stderr.
+
2004-11-19 Helge Hess <helge.hess@opengroupware.org>
* NGLoggerManager.m: use default logger if none is registered
NGLogger.m \
NGLoggerManager.m \
NGLogEvent.m \
+ NGLogEventFormatter.m \
+ NGLogEventDetailedFormatter.m \
NGLogAppender.m \
- NGLogConsoleAppender.m \
+ NGLogFileHandleAppender.m \
+ NGLogStdoutAppender.m \
+ NGLogStderrAppender.m \
# TODO: disable on Windows
NGLogging_OBJC_FILES += \
#include "NGLogAppender.h"
#include "NGLogLevel.h"
#include "NGLogEvent.h"
+#include "NGLogEventFormatter.h"
#include "common.h"
@implementation NGLogAppender
+static NSString *defaultAppenderClassName = nil;
+
++ (void)initialize {
+ static BOOL didInit = NO;
+ NSUserDefaults *ud;
+
+ if (didInit) return;
+
+ didInit = YES;
+ ud = [NSUserDefaults standardUserDefaults];
+ defaultAppenderClassName =
+ [[ud stringForKey:@"NGLogDefaultAppenderClass"] retain];
+ if (defaultAppenderClassName == nil)
+ defaultAppenderClassName = @"NGLogStdoutAppender";
+}
+
++ (id)logAppenderFromConfig:(NSDictionary *)_config {
+ NSString *className;
+ Class clazz;
+ id appender;
+
+ className = [_config objectForKey:@"Class"];
+ if (!className)
+ className = defaultAppenderClassName;
+ clazz = NSClassFromString(className);
+ if (clazz == Nil) {
+ NSLog(@"ERROR: can't instantiate appender class named '%@'",
+ className);
+ return nil;
+ }
+ appender = [[[clazz alloc] initWithConfig:_config] autorelease];
+ return appender;
+}
+
+- (id)initWithConfig:(NSDictionary *)_config {
+ self = [super init];
+ if (self) {
+ NSDictionary *formatterConfig;
+
+ formatterConfig = [_config objectForKey:@"Formatter"];
+ self->formatter =
+ [[NGLogEventFormatter logEventFormatterFromConfig:formatterConfig]
+ retain];
+ }
+ return self;
+}
+
- (void)appendLogEvent:(NGLogEvent *)_event {
#if LIB_FOUNDATION_LIBRARY
[self subclassResponsibility:_cmd];
#endif
}
-- (NSString *)formattedEvent:(NGLogEvent *)_event {
- NSMutableString *fe;
- NSString *lvl;
-
- lvl = [self localizedNameOfLogLevel:[_event level]];
- fe = [NSMutableString stringWithCapacity:128];
- if (lvl != nil) {
- [fe appendString:@"["];
- [fe appendString:lvl];
- [fe appendString:@"] "];
- }
- [fe appendString:[_event message]];
- return fe;
-}
+/* formatting */
-- (NSString *)localizedNameOfLogLevel:(NGLogLevel)_level {
- NSString *name;
-
- switch (_level) {
- case NGLogLevelWarn:
- name = @"WARN";
- break;
- case NGLogLevelError:
- name = @"ERROR";
- break;
- case NGLogLevelFatal:
- name = @"FATAL";
- break;
- default:
- name = nil;
- break;
- }
- return name;
+- (NSString *)formattedEvent:(NGLogEvent *)_event {
+ return [self->formatter formattedEvent:_event];
}
@end /* NGLogAppender */
+++ /dev/null
-/*
- Copyright (C) 2004 SKYRIX Software AG
-
- This file is part of OpenGroupware.org.
-
- OGo is free software; you can redistribute it and/or modify it under
- the terms of the GNU Lesser General Public License as published by the
- Free Software Foundation; either version 2, or (at your option) any
- later version.
-
- OGo 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 Lesser General Public
- License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with OGo; see the file COPYING. If not, write to the
- Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA.
-*/
-
-#include "NGLogConsoleAppender.h"
-#include "NGLogEvent.h"
-#include "common.h"
-#include <sys/types.h>
-#include <unistd.h>
-
-@implementation NGLogConsoleAppender
-
-static Class NSCalendarDateClass = Nil;
-static Class NSProcessInfoClass = Nil;
-static unsigned char *processName = NULL;
-
-static char *monthNames[14] = {
- "Dec",
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
- "Jan"
-};
-
-+ (void)initialize {
- if (NSCalendarDateClass == Nil)
- NSCalendarDateClass = [NSCalendarDate class];
- if (NSProcessInfoClass == Nil)
- NSProcessInfoClass = [NSProcessInfo class];
-
- if (processName == NULL) {
- /* process name can't change, right? */
- unsigned len;
- NSString *pn;
-
- pn = [[NSProcessInfoClass processInfo] processName];
- len = [pn cStringLength];
-
- processName = malloc(len + 4);
- [pn getCString:processName];
- }
-}
-
-static __inline__ unsigned char * levelPrefixForEvent(NGLogEvent *_event) {
- switch ([_event level]) {
- case NGLogLevelWarn: return "[WARN]";
- case NGLogLevelError: return "[ERROR]";
- case NGLogLevelFatal: return "[FATAL]";
- default: return "";
- }
-}
-
-- (void)appendLogEvent:(NGLogEvent *)_event {
- /* Note: pid can change after a fork() */
- NSCalendarDate *date;
-
- // TODO: get time using libc function, cheaper
- // TODO: set to GMT?
- date = [[NSCalendarDateClass alloc] init];
-
- // TODO: cString for message is expensive
- fprintf(stderr,
- "%s %02i %02i:%02i:%02i %s [%d]: %s\n",
- monthNames[[date monthOfYear]],
- [date dayOfMonth],
- [date hourOfDay], [date minuteOfHour], [date secondOfMinute],
- processName,
- getpid(),
- [[_event message] cString]);
-
- [date release];
-}
-
-@end /* NGLogConsoleAppender */
@implementation NGLogEvent
-static Class NSDateClass = Nil;
+static Class DateClass = Nil;
+ (void)initialize {
- NSDateClass = [NSDate class];
+ static BOOL didInit = NO;
+
+ if (didInit) return;
+ didInit = YES;
+
+ DateClass = [NSCalendarDate class];
}
- (id)initWithLevel:(NGLogLevel)_level message:(NSString *)_msg {
self = [super init];
- if(self) {
- self->date = [NSDateClass timeIntervalSinceReferenceDate];
- self->level = _level;
- self->msg = [_msg copy];
+ if (self) {
+ // TODO: get time using libc function, cheaper
+ self->date = [DateClass timeIntervalSinceReferenceDate];
+ self->level = _level;
+ self->msg = [_msg copy];
}
return self;
}
- (void)dealloc {
- [self->msg release];
+ [self->msg release];
[super dealloc];
}
return self->msg;
}
-- (NSDate *)date {
- return [NSDateClass dateWithTimeIntervalSinceReferenceDate:self->date];
+- (NSCalendarDate *)date {
+ // TODO: set to GMT?
+ return [DateClass dateWithTimeIntervalSinceReferenceDate:self->date];
+}
+
+/* description */
+
+- (NSString *)description {
+ NSString *lvl;
+
+ switch (self->level) {
+ case NGLogLevelOff: lvl = @"OFF"; break;
+ case NGLogLevelDebug: lvl = @"DEBUG"; break;
+ case NGLogLevelInfo: lvl = @"INFO"; break;
+ case NGLogLevelWarn: lvl = @"WARN"; break;
+ case NGLogLevelError: lvl = @"ERROR"; break;
+ case NGLogLevelFatal: lvl = @"FATAL"; break;
+ default: lvl = @"ALL"; break;
+ }
+ return [NSString stringWithFormat:@"<%@[0x%08X] date=%@ level=%@ msg:%@>",
+ NSStringFromClass([self class]), self,
+ [self date], lvl, self->msg];
}
@end /* NGLogEvent */
--- /dev/null
+/*
+ Copyright (C) 2000-2004 SKYRIX Software AG
+
+ This file is part of OGo
+
+ OGo is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ OGo 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+// $Id$
+
+
+#include "NGLogEventFormatter.h"
+#include "NGLogLevel.h"
+
+@class NSString;
+
+@interface NGLogEventDetailedFormatter : NGLogEventFormatter
+{
+}
+
+@end
+
+#include "NGLogEvent.h"
+#include "common.h"
+#include "NSProcessInfo+misc.h"
+
+@implementation NGLogEventDetailedFormatter
+
+static unsigned char *processName = NULL;
+static NSProcessInfo *processInfo = nil;
+
+static char *monthNames[14] = {
+ "Dec",
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ "Jan"
+};
+
++ (void)initialize {
+ static BOOL didInit = NO;
+ unsigned len;
+ NSString *pn;
+
+ if (didInit) return;
+
+ didInit = YES;
+ processInfo = [[NSProcessInfo processInfo] retain];
+ pn = [processInfo processName];
+ len = [pn cStringLength];
+ processName = malloc(len + 4);
+ [pn getCString:processName];
+}
+
+static __inline__ unsigned char * levelPrefixForEvent(NGLogEvent *_event) {
+ switch ([_event level]) {
+ case NGLogLevelWarn: return "[WARN] ";
+ case NGLogLevelError: return "[ERROR] ";
+ case NGLogLevelFatal: return "[FATAL] ";
+ default: return "";
+ }
+}
+
+- (NSString *)formattedEvent:(NGLogEvent *)_event {
+ NSMutableString *fe;
+ NSCalendarDate *date;
+
+ fe = [NSMutableString stringWithCapacity:160];
+ /* timestamp, process name, process id, level prefix */
+ date = [_event date];
+ [fe appendFormat:@"%s %02i %02i:%02i:%02i %s [%d]: %s",
+ monthNames[[date monthOfYear]],
+ [date dayOfMonth],
+ [date hourOfDay], [date minuteOfHour], [date secondOfMinute],
+ processName,
+ /* Note: pid can change after a fork() */
+#if NeXT_Foundation_LIBRARY || COCOA_Foundation_LIBRARY
+ [processInfo processIdentifier],
+#else
+ [[processInfo processId] intValue],
+#endif
+ levelPrefixForEvent(_event)];
+
+ /* message */
+ [fe appendString:[_event message]];
+ return fe;
+}
+
+@end /* NGLogEventDetailedFormatter */
--- /dev/null
+/*
+ Copyright (C) 2000-2004 SKYRIX Software AG
+
+ This file is part of OGo
+
+ OGo is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ OGo 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "NGLogEventFormatter.h"
+#include "NGLogEvent.h"
+#include "common.h"
+
+@implementation NGLogEventFormatter
+
+static NSString *defaultFormatterClassName = nil;
+
++ (void)initialize {
+ static BOOL didInit = NO;
+ NSUserDefaults *ud;
+
+ if (didInit) return;
+
+ didInit = YES;
+ ud = [NSUserDefaults standardUserDefaults];
+ defaultFormatterClassName =
+ [[ud stringForKey:@"NGLogDefaultLogEventFormatterClass"] retain];
+ if (defaultFormatterClassName == nil)
+ defaultFormatterClassName = @"NGLogEventFormatter";
+}
+
++ (id)logEventFormatterFromConfig:(NSDictionary *)_config {
+ NSString *className;
+ Class clazz;
+ id formatter;
+
+ className = [_config objectForKey:@"Class"];
+ if (!className)
+ className = defaultFormatterClassName;
+ clazz = NSClassFromString(className);
+ if (clazz == Nil) {
+ NSLog(@"ERROR: can't instantiate log event formatter class named '%@'",
+ className);
+ return nil;
+ }
+ formatter = [[[clazz alloc] initWithConfig:_config] autorelease];
+ return formatter;
+}
+
+- (id)initWithConfig:(NSDictionary *)_config {
+ self = [super init];
+ if (self) {
+ }
+ return self;
+}
+
+/* formatting */
+
+- (NSString *)formattedEvent:(NGLogEvent *)_event {
+ return [_event message];
+}
+
+@end /* NGLogEventFormatter */
--- /dev/null
+/*
+ Copyright (C) 2004 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ OGo 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "NGLogFileHandleAppender.h"
+#include "NGLogEvent.h"
+#include "common.h"
+
+@implementation NGLogFileHandleAppender
+
+static NSData *nextLineData = nil;
+
++ (void)initialize {
+ static BOOL didInit = NO;
+
+ if (didInit) return;
+
+ didInit = YES;
+ nextLineData = [[@"\n" dataUsingEncoding:NSASCIIStringEncoding] retain];
+}
+
+- (id)initWithConfig:(NSDictionary *)_config {
+ self = [super initWithConfig:_config];
+ if (self) {
+ self->flushImmediately = NO;
+ self->encoding = [NSString defaultCStringEncoding];
+ [self openFileHandleWithConfig:_config];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ if ([self isFileHandleOpen])
+ [self closeFileHandle];
+ [self->fh release];
+ [super dealloc];
+}
+
+- (BOOL)isFileHandleOpen {
+ return self->fh ? YES : NO;
+}
+
+- (void)openFileHandleWithConfig:(NSDictionary *)_config {
+}
+
+- (void)closeFileHandle {
+ [self->fh closeFile];
+}
+
+- (void)appendLogEvent:(NGLogEvent *)_event {
+ NSString *formatted;
+ NSData *bin;
+
+ formatted = [self formattedEvent:_event];
+ bin = [formatted dataUsingEncoding:self->encoding];
+ [self->fh writeData:bin];
+ [self->fh writeData:nextLineData];
+ if (self->flushImmediately)
+ [self->fh synchronizeFile];
+}
+
+@end /* NGLogFileHandleAppender */
--- /dev/null
+/*
+ Copyright (C) 2004 SKYRIX Software AG
+
+ This file is part of OpenGroupware.org.
+
+ OGo is free software; you can redistribute it and/or modify it under
+ the terms of the GNU Lesser General Public License as published by the
+ Free Software Foundation; either version 2, or (at your option) any
+ later version.
+
+ OGo 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 Lesser General Public
+ License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with OGo; see the file COPYING. If not, write to the
+ Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+ */
+
+#include "NGLogFileHandleAppender.h"
+
+@interface NGLogStderrAppender : NGLogFileHandleAppender
+{
+}
+
+@end
+
+#include "common.h"
+
+@implementation NGLogStderrAppender
+
+- (void)openFileHandleWithConfig:(NSDictionary *)_config {
+ self->fh = [[NSFileHandle fileHandleWithStandardError] retain];
+}
+
+@end /* NGLogStderrAppender */
02111-1307, USA.
*/
-#ifndef __NGExtensions_NGLogConsoleAppender_H_
-#define __NGExtensions_NGLogConsoleAppender_H_
+#include "NGLogFileHandleAppender.h"
-/*
- NGLogConsoleAppender
-
- A very simple console appender. The current implementation logs the
- formatted event via NSLog(). Please note that doing so discards the timestamp
- saved in the event, so the logged time isn't really exact, but will probably
- only differ marginally.
-*/
-
-#include <NGExtensions/NGLogAppender.h>
-
-@interface NGLogConsoleAppender : NGLogAppender
+@interface NGLogStdoutAppender : NGLogFileHandleAppender
{
}
@end
-#endif /* __NGExtensions_NGLogConsoleAppender_H_ */
+#include "common.h"
+
+@implementation NGLogStdoutAppender
+
+- (void)openFileHandleWithConfig:(NSDictionary *)_config {
+ self->fh = [[NSFileHandle fileHandleWithStandardOutput] retain];
+}
+
+@end /* NGLogStdoutAppender */
return sharedAppender;
}
-- (id)init {
- return [self initWithIdentifier:defaultSyslogIdentifier];
+- (id)initWithConfig:(NSDictionary *)_config {
+ NSString *identifier;
+
+ identifier = [_config objectForKey:@"SyslogIdentifier"];
+ if(!identifier)
+ identifier = defaultSyslogIdentifier;
+ return [self initWithIdentifier:identifier];
}
- (id)initWithIdentifier:(NSString *)_ident {
*/
#include "NGLogger.h"
-#include "common.h"
#include "NGLogEvent.h"
#include "NGLogAppender.h"
+#include "NSNull+misc.h"
+#include "common.h"
@interface NGLogger (PrivateAPI)
-- (void)logLevel:(NGLogLevel)_level message:(NSString *)_msg;
-- (void)logLevel:(NGLogLevel)_level withFormat:(NSString *)_fmt, ...;
++ (NGLogLevel)_logLevelForString:(NSString *)_level;
@end
@implementation NGLogger
-static Class NSStringClass = Nil;
+static Class NSStringClass = Nil;
+static NGLogger *defaultLogger = nil;
+static NGLogLevel defaultLogLevel = NGLogLevelInfo;
+ (void)initialize {
- static BOOL didInit = NO;
- if (didInit)
- return;
- didInit = YES;
- NSStringClass = [NSString class];
+ static BOOL didInit = NO;
+ NSUserDefaults *ud;
+ NSString *level;
+
+ if (didInit) return;
+
+ didInit = YES;
+ NSStringClass = [NSString class];
+ ud = [NSUserDefaults standardUserDefaults];
+ level = [ud stringForKey:@"NGLogDefaultLogLevel"];
+ defaultLogLevel = [self _logLevelForString:level];
+ defaultLogger = [[self alloc] init];
}
-+ (id)defaultLogger {
- static NGLogger *logger = nil; // THREAD
-
- if (logger == nil)
- logger = [[self alloc] init];
++ (id)loggerWithConfigFromUserDefaults:(NSString *)_defaultName {
+ NSUserDefaults *ud;
+ NSDictionary *config;
+ id logger;
+
+ ud = [NSUserDefaults standardUserDefaults];
+ config = [ud dictionaryForKey:_defaultName];
+ if(!config)
+ return defaultLogger;
+ logger = [[[NGLogger alloc] initWithConfig:config] autorelease];
return logger;
}
- (id)init {
- return [self initWithLogLevel:NGLogLevelAll];
+ return [self initWithConfig:nil];
}
-- (id)initWithLogLevel:(NGLogLevel)_level appender:(id)_appenderParam {
- if ((self = [super init]) != nil) {
- [self setLogLevel:_level];
- self->_appender = [_appenderParam retain]; // TODO: fix ivar name
+- (id)initWithConfig:(NSDictionary *)_config {
+ self = [super init];
+ if (self) {
+ NSArray *appenderConfigs;
+ NGLogAppender *appender;
+ NSString *levelString;
+ NGLogLevel level;
+ unsigned count;
+
+ self->appenders = [[NSMutableArray alloc] initWithCapacity:1];
+
+ levelString = [_config objectForKey:@"LogLevel"];
+ level = [NGLogger _logLevelForString:levelString];
+ [self setLogLevel:level];
+
+ appenderConfigs = [_config objectForKey:@"Appenders"];
+ count = [appenderConfigs count];
+ if(!count) {
+ /* create a default appender */
+ appender = [NGLogAppender logAppenderFromConfig:nil];
+ [self addAppender:appender];
+ }
+ else {
+ unsigned i;
+
+ for(i = 0; i < count; i++) {
+ NSDictionary *appenderConfig;
+
+ appenderConfig = [appenderConfigs objectAtIndex:i];
+ appender = [NGLogAppender logAppenderFromConfig:appenderConfig];
+ if(appender)
+ [self addAppender:appender];
+ }
+ }
}
return self;
}
- (id)initWithLogLevel:(NGLogLevel)_level {
- NSUserDefaults *ud;
- NSString *appenderClassName;
- id appender;
-
- // TODO: remove this as soon as we have a config
- ud = [NSUserDefaults standardUserDefaults];
- appenderClassName = [ud stringForKey:@"NGLogDefaultAppenderClass"];
- if (appenderClassName == nil)
- appenderClassName = @"NGLogConsoleAppender";
-
- appender = [[NSClassFromString(appenderClassName) alloc] init];
-
- self = [self initWithLogLevel:_level appender:appender];
- [appender release];
+ self = [self initWithConfig:nil];
+ if (self) {
+ [self setLogLevel:_level];
+ }
return self;
}
- (void)dealloc {
- [self->_appender release];
+ [self->appenders release];
[super dealloc];
}
return self->logLevel;
}
+- (void)addAppender:(NGLogAppender *)_appender {
+ [self->appenders addObject:_appender];
+}
+
+- (void)removeAppender:(NGLogAppender *)_appender {
+ [self->appenders removeObject:_appender];
+}
+
/* logging */
- (void)debugWithFormat:(NSString *)_fmt arguments:(va_list)_va {
- (void)logLevel:(NGLogLevel)_level message:(NSString *)_msg {
NGLogEvent *event;
+ unsigned i, count;
event = [[NGLogEvent alloc] initWithLevel:_level message:_msg];
- // iterate appenders
- // TODO: as soon as we have more appenders, we need to iterate on them
- [self->_appender appendLogEvent:event];
+ count = [self->appenders count];
+ for(i = 0; i < count; i++) {
+ NGLogAppender *appender;
+
+ appender = [self->appenders objectAtIndex:i];
+ [appender appendLogEvent:event];
+ }
[event release];
}
return self->logLevel >= NGLogLevelFatal;
}
+
+/* Private */
+
++ (NGLogLevel)_logLevelForString:(NSString *)_level {
+ if (![_level isNotNull]) {
+ _level = [_level uppercaseString];
+ if ([_level isEqualToString:@"DEBUG"])
+ return NGLogLevelDebug;
+ else if ([_level isEqualToString:@"INFO"])
+ return NGLogLevelInfo;
+ else if ([_level isEqualToString:@"WARN"])
+ return NGLogLevelWarn;
+ else if ([_level isEqualToString:@"ERROR"])
+ return NGLogLevelError;
+ else if ([_level isEqualToString:@"FATAL"])
+ return NGLogLevelFatal;
+ return NGLogLevelAll; /* better than nothing */
+ }
+ return NGLogLevelInfo;
+}
+
+/* description */
+
+- (NSString *)description {
+ NSString *lvl;
+
+ switch (self->logLevel) {
+ case NGLogLevelOff: lvl = @"OFF"; break;
+ case NGLogLevelDebug: lvl = @"DEBUG"; break;
+ case NGLogLevelInfo: lvl = @"INFO"; break;
+ case NGLogLevelWarn: lvl = @"WARN"; break;
+ case NGLogLevelError: lvl = @"ERROR"; break;
+ case NGLogLevelFatal: lvl = @"FATAL"; break;
+ default: lvl = @"ALL"; break;
+ }
+ return [NSString stringWithFormat:@"<%@[0x%08X] logLevel=%@ appenders:%@>",
+ NSStringFromClass([self class]), self,
+ lvl, self->appenders];
+}
+
@end /* NGLogger */
#include "NGLoggerManager.h"
#include "NGLogLevel.h"
#include "NGLogger.h"
+#include "NSNull+misc.h"
#include "common.h"
+@interface NGLoggerManager (PrivateAPI)
+- (NGLogger *)_getConfiguredLoggerNamed:(NSString *)_name;
+@end
+
@implementation NGLoggerManager
-static NGLoggerManager *sharedInstance;
-static NSNull *sharedNull;
+static NGLoggerManager *sharedInstance = nil;
+static NSNull *sharedNull = nil;
+static BOOL debugAll = NO;
+ (void)initialize {
- static BOOL didInit = NO;
-
- if (didInit)
- return;
+ static BOOL didInit = NO;
+ NSUserDefaults *ud;
+
+ if (didInit) return;
- didInit = YES;
- sharedInstance = [[self alloc] init];
- sharedNull = [[NSNull null] retain];
+ didInit = YES;
+ sharedInstance = [[self alloc] init];
+ sharedNull = [[NSNull null] retain];
+ ud = [NSUserDefaults standardUserDefaults];
+ debugAll = [ud boolForKey:@"NGLogDebugAllEnabled"];
}
+ (id)defaultLoggerManager {
NSUserDefaults *ud;
ud = [NSUserDefaults standardUserDefaults];
- if (![ud boolForKey:_defaultKey]) {
+ if (!debugAll && ![ud boolForKey:_defaultKey]) {
[self->loggerMap setObject:sharedNull forKey:_defaultKey];
logger = sharedNull;
}
else {
- logger = [[NGLogger alloc] initWithLogLevel:NGLogLevelDebug];
+ logger = [self _getConfiguredLoggerNamed:_defaultKey];
[self->loggerMap setObject:logger forKey:_defaultKey];
- [logger release];
}
}
return (logger != sharedNull) ? logger : nil;
}
- (NGLogger *)loggerForFacilityNamed:(NSString *)_name {
- id logger;
+ NGLogger *logger;
// TODO: expensive, use a faster map (at least NSMapTable)
if ((logger = [self->loggerMap objectForKey:_name]) != nil)
return logger;
-
- // TODO: is a registration really faster? I guess not?
- logger = [NGLogger defaultLogger]; /* fallback to shared logger */
+
+ logger = [self _getConfiguredLoggerNamed:_name];
[self->loggerMap setObject:logger forKey:_name];
return logger;
}
return [self loggerForFacilityNamed:name];
}
+/* Private */
+
+- (NGLogger *)_getConfiguredLoggerNamed:(NSString *)_name {
+ NSString *configKey;
+
+ configKey = [NSString stringWithFormat:@"%@LoggerConfig", _name];
+ return [NGLogger loggerWithConfigFromUserDefaults:configKey];
+}
+
+/* description */
+
+- (NSString *)description {
+ return [NSString stringWithFormat:@"<%@[0x%08X] debugAll=%@ #loggers=%d>",
+ NSStringFromClass([self class]), self,
+ debugAll ? @"YES" : @"NO", [self->loggerMap count]];
+}
+
@end /* NGLoggerManager */
# version
-SUBMINOR_VERSION:=135
+SUBMINOR_VERSION:=136
# v4.3.115 requires libFoundation v1.0.59
# v4.2.72 requires libEOControl v4.2.39