Files
RestKit/Vendor/LibComponentLogging/LogFile/LCLLogFile.m

807 lines
26 KiB
Objective-C

//
//
// LCLLogFile.m
//
//
// Copyright (c) 2008-2010 Arne Harren <ah@0xc0.de>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "LCLLogFile.h"
#include <unistd.h>
#include <mach/mach_init.h>
#include <sys/time.h>
#include <sys/stat.h>
//
// Configuration checks.
//
#ifndef LCLLogFile
#error 'LCLLogFile' must be defined in LCLLogFileConfig.h
#endif
#ifndef _LCLLogFile_LogFilePath
#error '_LCLLogFile_LogFilePath' must be defined in LCLLogFileConfig.h
#endif
#ifndef _LCLLogFile_AppendToExistingLogFile
#error '_LCLLogFile_AppendToExistingLogFile' must be defined in LCLLogFileConfig.h
#endif
#ifndef _LCLLogFile_MaxLogFileSizeInBytes
#error '_LCLLogFile_MaxLogFileSizeInBytes' must be defined in LCLLogFileConfig.h
#endif
#ifndef _LCLLogFile_MirrorMessagesToStdErr
#error '_LCLLogFile_MirrorMessagesToStdErr' must be defined in LCLLogFileConfig.h
#endif
#ifndef _LCLLogFile_EscapeLineFeeds
#error '_LCLLogFile_EscapeLineFeeds' must be defined in LCLLogFileConfig.h
#endif
#ifndef _LCLLogFile_MaxMessageSizeInCharacters
#error '_LCLLogFile_MaxMessageSizeInCharacters' must be defined in LCLLogFileConfig.h
#endif
#ifndef _LCLLogFile_ShowFileNames
#error '_LCLLogFile_ShowFileNames' must be defined in LCLLogFileConfig.h
#endif
#ifndef _LCLLogFile_ShowLineNumbers
#error '_LCLLogFile_ShowLineNumbers' must be defined in LCLLogFileConfig.h
#endif
#ifndef _LCLLogFile_ShowFunctionNames
#error '_LCLLogFile_ShowFunctionNames' must be defined in LCLLogFileConfig.h
#endif
//
// Fields.
//
// A lock which is held when the log file is used, opened, etc.
static NSRecursiveLock *_LCLLogFile_lock = nil;
// A handle to the current log file, if opened.
static volatile FILE *_LCLLogFile_fileHandle = NULL;
// YES, if logging is active.
static volatile BOOL _LCLLogFile_isActive = NO;
// YES, if log messages should be appended to an existing log file.
static BOOL _LCLLogFile_appendToExistingLogFile = NO;
// YES, if log messages should be mirrored to stderr.
static BOOL _LCLLogFile_mirrorToStdErr = NO;
// YES, if '\\' and '\n' characters should be escaped in log messages.
static BOOL _LCLLogFile_escapeLineFeeds = NO;
// YES, if the file name should be shown.
static BOOL _LCLLogFile_showFileName = NO;
// YES, if the line number should be shown.
static BOOL _LCLLogFile_showLineNumber = NO;
// YES, if the function name should be shown.
static BOOL _LCLLogFile_showFunctionName = NO;
// Max size of a log message (without prefixes).
static NSUInteger _LCLLogFile_maxMessageSize = 0;
// Max size of log file.
static size_t _LCLLogFile_fileSizeMax = 0;
// Current size of log file.
static size_t _LCLLogFile_fileSize = 0;
// Paths of log files.
static NSString *_LCLLogFile_filePath = nil;
static const char *_LCLLogFile_filePath_c = NULL;
static NSString *_LCLLogFile_filePath0 = nil;
static const char *_LCLLogFile_filePath0_c = NULL;
// The process id.
static pid_t _LCLLogFile_processId = 0;
// The log level headers we use.
const char * const _LCLLogFile_levelHeader[] = {
"-",
"C",
"E",
"W",
"I",
"D",
"T"
};
@implementation LCLLogFile
//
// Initialization.
//
// No instances, please.
+(id)alloc {
[LCLLogFile doesNotRecognizeSelector:_cmd];
return nil;
}
// Initializes the class.
+ (void)initialize {
// perform initialization only once
if (self != [LCLLogFile class])
return;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// create the lock
_LCLLogFile_lock = [[NSRecursiveLock alloc] init];
// get the process id
_LCLLogFile_processId = getpid();
// get the max file size, at least 4k
_LCLLogFile_fileSizeMax = (_LCLLogFile_MaxLogFileSizeInBytes);
if (_LCLLogFile_fileSizeMax < 4 * 1024) {
_LCLLogFile_fileSizeMax = 4 * 1024;
}
// get whether we should append to an existing log file
_LCLLogFile_appendToExistingLogFile = (_LCLLogFile_AppendToExistingLogFile);
// get whether we should mirror log messages to stderr
_LCLLogFile_mirrorToStdErr = (_LCLLogFile_MirrorMessagesToStdErr);
// get whether we should escape '\\' and '\n' characters in log messages
_LCLLogFile_escapeLineFeeds = (_LCLLogFile_EscapeLineFeeds);
// get max size of a log message
_LCLLogFile_maxMessageSize = (_LCLLogFile_MaxMessageSizeInCharacters);
// get whether we should show file names
_LCLLogFile_showFileName = (_LCLLogFile_ShowFileNames);
// get whether we should show line numbers
_LCLLogFile_showLineNumber = (_LCLLogFile_ShowLineNumbers);
// get whether we should show function names
_LCLLogFile_showFunctionName = (_LCLLogFile_ShowFunctionNames);
// get the full path of the log file
NSString *path = (_LCLLogFile_LogFilePath);
// create log file paths
_LCLLogFile_filePath = nil;
_LCLLogFile_filePath_c = NULL;
_LCLLogFile_filePath0 = nil;
_LCLLogFile_filePath0_c = NULL;
if (path != nil) {
// standardize the given path
path = [path stringByStandardizingPath];
// create parent paths
NSString *parentpath = [path stringByDeletingLastPathComponent];
[[NSFileManager defaultManager] createDirectoryAtPath:parentpath
withIntermediateDirectories:YES
attributes:nil
error:NULL];
// create the path of the backup file
NSString *path0 = [path stringByAppendingString:@".0"];
// create the paths' file system representations
CFIndex path_c_max_len = CFStringGetMaximumSizeOfFileSystemRepresentation((CFStringRef)path);
CFIndex path0_c_max_len = CFStringGetMaximumSizeOfFileSystemRepresentation((CFStringRef)path);
char *path_c = malloc(path_c_max_len);
char *path0_c = malloc(path0_c_max_len);
Boolean path_fsr_created = CFStringGetFileSystemRepresentation((CFStringRef)path, path_c, path_c_max_len);
Boolean path0_fsr_created = CFStringGetFileSystemRepresentation((CFStringRef)path0, path0_c, path0_c_max_len);
// create local copies of the paths
if (path_fsr_created && path0_fsr_created) {
_LCLLogFile_filePath = [path copy];
_LCLLogFile_filePath_c = strdup(path_c);
_LCLLogFile_filePath0 = [path0 copy];
_LCLLogFile_filePath0_c = strdup(path0_c);
}
free(path_c);
free(path0_c);
}
// creation of paths failed? fall back to stderr
if (_LCLLogFile_filePath_c == NULL) {
NSLog(@"error: invalid log file path '%@'", path);
_LCLLogFile_mirrorToStdErr = YES;
}
// log file size is zero
_LCLLogFile_fileSize = 0;
[pool release];
}
//
// Logging methods.
//
// Creates the log message prefix.
static NSString *_LCLLogFile_prefix(const char *identifier_c, uint32_t level,
const char *path_c, uint32_t line,
const char *function_c) {
// get settings
const int show_file = _LCLLogFile_showFileName;
const int show_line = _LCLLogFile_showLineNumber;
const int show_function = _LCLLogFile_showFunctionName;
// get file name from path
const char *file_c = NULL;
if (show_file) {
file_c = path_c != NULL ? strrchr(path_c, '/') : NULL;
file_c = (file_c != NULL) ? (file_c + 1) : (path_c);
}
// get line
char line_c[11];
if (show_line) {
snprintf(line_c, sizeof(line_c), "%u", line);
line_c[sizeof(line_c) - 1] = '\0';
}
// get the level header
char level_ca[11];
const char *level_c;
if (level < sizeof(_LCLLogFile_levelHeader)/sizeof(const char *)) {
// a known level, e.g. E, W, I
level_c = _LCLLogFile_levelHeader[level];
} else {
// unknown level, use the level number
snprintf(level_ca, sizeof(level_ca), "%u", level);
level_c = level_ca;
}
// create prefix
NSString *prefix = [NSString stringWithFormat:@" %u:%x %s %s%s%s%s%s%s%s ",
/* */
/* %u */ _LCLLogFile_processId,
/* %x */ mach_thread_self(),
/* */
/* %s */ level_c,
/* */
/* %s */ identifier_c,
/* %s */ show_file ? ":" : "",
/* %s */ show_file ? file_c : "",
/* %s */ show_line ? ":" : "",
/* %s */ show_line ? line_c : "",
/* %s */ show_function ? ":" : "",
/* %s */ show_function ? function_c : ""
/* */
];
return prefix;
}
// Writes the given log message to the log file (internal).
static void _LCLLogFile_log(const char *identifier_c, uint32_t level,
const char *path_c, uint32_t line,
const char *function_c,
NSString *message) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// create the prefix
NSString *prefix = _LCLLogFile_prefix(identifier_c, level, path_c, line, function_c);
// restrict size of log message
if (_LCLLogFile_maxMessageSize != 0) {
NSUInteger message_len = [message length];
if (message_len > _LCLLogFile_maxMessageSize) {
message = [message substringToIndex:_LCLLogFile_maxMessageSize];
}
}
// escape '\\' and '\n' characters
if (_LCLLogFile_escapeLineFeeds) {
NSMutableString *emessage = [[[NSMutableString alloc] initWithCapacity:[message length] * 2] autorelease];
[emessage appendString:message];
[emessage replaceOccurrencesOfString:@"\\" withString:@"\\\\" options:0 range:NSMakeRange(0, [emessage length])];
[emessage replaceOccurrencesOfString:@"\n" withString:@"\\n" options:0 range:NSMakeRange(0, [emessage length])];
message = emessage;
}
// create C strings
const char *prefix_c = [prefix UTF8String];
const char *message_c = [message UTF8String];
// get size of log entry
const int time_c_len = 24;
const int backslash_n_len = 1;
size_t entry_len = time_c_len + strlen(prefix_c) + strlen(message_c) + backslash_n_len;
// under lock protection ...
[_LCLLogFile_lock lock];
{
FILE *filehandle = (FILE *)_LCLLogFile_fileHandle;
// rotate the log file if required
if (filehandle) {
if (_LCLLogFile_fileSize + entry_len > _LCLLogFile_fileSizeMax) {
[LCLLogFile rotate];
[LCLLogFile open];
filehandle = (FILE *)_LCLLogFile_fileHandle;
}
}
// get current time
struct timeval now;
struct tm now_tm;
char time_c[time_c_len];
gettimeofday(&now, NULL);
localtime_r(&now.tv_sec, &now_tm);
snprintf(time_c, sizeof(time_c), "%04d-%02d-%02d %02d:%02d:%02d.%03d",
now_tm.tm_year + 1900,
now_tm.tm_mon + 1,
now_tm.tm_mday,
now_tm.tm_hour,
now_tm.tm_min,
now_tm.tm_sec,
now.tv_usec / 1000);
// write the log message
if (filehandle) {
// increase file size
_LCLLogFile_fileSize += entry_len;
// write current time and log message
fprintf(filehandle, "%s%s%s\n", time_c, prefix_c, message_c);
// flush the file
fflush(filehandle);
}
// mirror to stderr?
if (_LCLLogFile_mirrorToStdErr) {
# ifndef __LCLLogFile_stderr
# define __LCLLogFile_stderr stderr
# endif
fprintf(__LCLLogFile_stderr, "%s%s%s\n", time_c, prefix_c, message_c);
}
}
// ... done
[_LCLLogFile_lock unlock];
[pool release];
}
// Writes the given log message to the log file (message).
+ (void)logWithIdentifier:(const char *)identifier level:(uint32_t)level
path:(const char *)path line:(uint32_t)line
function:(const char *)function
message:(NSString *)message {
// open the log file
if (!_LCLLogFile_isActive) {
[LCLLogFile open];
}
// write log message if the log file is opened or mirroring is enabled
if (_LCLLogFile_fileHandle || _LCLLogFile_mirrorToStdErr) {
// write log message
_LCLLogFile_log(identifier, level, path, line, function, message);
}
}
// Writes the given log message to the log file (format and va_list var args).
+ (void)logWithIdentifier:(const char *)identifier level:(uint32_t)level
path:(const char *)path line:(uint32_t)line
function:(const char *)function
format:(NSString *)format args:(va_list)args {
// open the log file
if (!_LCLLogFile_isActive) {
[LCLLogFile open];
}
// write log message if the log file is opened or mirroring is enabled
if (_LCLLogFile_fileHandle || _LCLLogFile_mirrorToStdErr) {
// create log message
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
// write log message
_LCLLogFile_log(identifier, level, path, line, function, message);
// release local objects
[message release];
}
}
// Writes the given log message to the log file (format and ... var args).
+ (void)logWithIdentifier:(const char *)identifier level:(uint32_t)level
path:(const char *)path line:(uint32_t)line
function:(const char *)function
format:(NSString *)format, ... {
// open the log file
if (!_LCLLogFile_isActive) {
[LCLLogFile open];
}
// write log message if the log file is opened or mirroring is enabled
if (_LCLLogFile_fileHandle || _LCLLogFile_mirrorToStdErr) {
// create log message
va_list args;
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
// write log message
_LCLLogFile_log(identifier, level, path, line, function, message);
// release local objects
[message release];
}
}
//
// Configuration.
//
// Returns the path of the log file.
+ (NSString *)path {
return _LCLLogFile_filePath;
}
// Returns the path of the backup log file.
+ (NSString *)path0 {
return _LCLLogFile_filePath0;
}
// Returns whether log messages get appended to an existing log file on startup.
+ (BOOL)appendsToExistingLogFile {
return _LCLLogFile_appendToExistingLogFile;
}
// Returns/sets the maximum size of the log file (as defined by
// _LCLLogFile_MaxLogFileSizeInBytes).
+ (size_t)maxSize {
return _LCLLogFile_fileSizeMax;
}
+ (void)setMaxSize:(size_t)value {
[_LCLLogFile_lock lock];
{
_LCLLogFile_fileSizeMax = value;
if (_LCLLogFile_fileSizeMax < 4 * 1024) {
_LCLLogFile_fileSizeMax = 4 * 1024;
}
}
[_LCLLogFile_lock unlock];
}
// Returns/sets whether log messages are mirrored to stderr.
+ (BOOL)mirrorsToStdErr {
return _LCLLogFile_mirrorToStdErr;
}
+ (void)setMirrorsToStdErr:(BOOL)value {
[_LCLLogFile_lock lock];
{
_LCLLogFile_mirrorToStdErr = value;
}
[_LCLLogFile_lock unlock];
}
// Returns/sets whether ('\\' and) '\n' line feed characters are escaped in
// log messages.
+ (BOOL)escapesLineFeeds {
return _LCLLogFile_escapeLineFeeds;
}
+ (void)setEscapesLineFeeds:(BOOL)value {
[_LCLLogFile_lock lock];
{
_LCLLogFile_escapeLineFeeds = value;
}
[_LCLLogFile_lock unlock];
}
// Returns/sets the maximum size of a log message in characters (without
// prefixes). The value 0 indicates that there is no maximum size for log
// messages.
+ (NSUInteger)maxMessageSize {
return _LCLLogFile_maxMessageSize;
}
+ (void)setMaxMessageSize:(NSUInteger)value {
[_LCLLogFile_lock lock];
{
_LCLLogFile_maxMessageSize = value;
}
[_LCLLogFile_lock unlock];
}
// Returns/sets whether file names are shown.
+ (BOOL)showsFileNames {
return _LCLLogFile_showFileName;
}
+ (void)setShowsFileNames:(BOOL)value {
[_LCLLogFile_lock lock];
{
_LCLLogFile_showFileName = value;
}
[_LCLLogFile_lock unlock];
}
// Returns/sets whether line numbers are shown.
+ (BOOL)showsLineNumbers {
return _LCLLogFile_showLineNumber;
}
+ (void)setShowsLineNumbers:(BOOL)value {
[_LCLLogFile_lock lock];
{
_LCLLogFile_showLineNumber = value;
}
[_LCLLogFile_lock unlock];
}
// Returns/sets whether function names are shown.
+ (BOOL)showsFunctionNames {
return _LCLLogFile_showFunctionName;
}
+ (void)setShowsFunctionNames:(BOOL)value {
[_LCLLogFile_lock lock];
{
_LCLLogFile_showFunctionName = value;
}
[_LCLLogFile_lock unlock];
}
//
// Status information and internals.
//
// Returns the current size of the log file.
+ (size_t)size {
size_t sz = 0;
[_LCLLogFile_lock lock];
{
// get the size
sz = _LCLLogFile_fileSize;
}
[_LCLLogFile_lock unlock];
return sz;
}
// Returns the version of LCLLogFile.
+ (NSString *)version {
#define __lcl_version_to_string( _text) __lcl_version_to_string0(_text)
#define __lcl_version_to_string0(_text) #_text
return @__lcl_version_to_string(_LCLLOGFILE_VERSION_MAJOR)
"." __lcl_version_to_string(_LCLLOGFILE_VERSION_MINOR)
"." __lcl_version_to_string(_LCLLOGFILE_VERSION_BUILD)
"" _LCLLOGFILE_VERSION_SUFFIX;
}
// Opens the log file.
+ (void)open {
[_LCLLogFile_lock lock];
{
if (_LCLLogFile_fileHandle == NULL) {
// size of log file is 0
_LCLLogFile_fileSize = 0;
if (_LCLLogFile_isActive || !_LCLLogFile_appendToExistingLogFile) {
// create a new log file
_LCLLogFile_fileHandle = NULL;
if (_LCLLogFile_filePath_c != NULL) {
_LCLLogFile_fileHandle = fopen(_LCLLogFile_filePath_c, "w");
}
} else {
// append to existing log file, get size from file
_LCLLogFile_fileHandle = NULL;
if (_LCLLogFile_filePath_c != NULL) {
_LCLLogFile_fileHandle = fopen(_LCLLogFile_filePath_c, "a");
}
// try to get size of existing log file
struct stat stat_c;
if (_LCLLogFile_filePath_c != NULL && stat(_LCLLogFile_filePath_c, &stat_c) == 0) {
_LCLLogFile_fileSize = (size_t)stat_c.st_size;
}
}
// logging is active
_LCLLogFile_isActive = YES;
}
}
[_LCLLogFile_lock unlock];
}
// Closes the log file.
+ (void)close {
[_LCLLogFile_lock lock];
{
// close the log file
FILE *filehandle = (FILE *)_LCLLogFile_fileHandle;
if (filehandle != NULL) {
fclose(filehandle);
_LCLLogFile_fileHandle = NULL;
}
// log file size is zero
_LCLLogFile_fileSize = 0;
}
[_LCLLogFile_lock unlock];
}
// Resets the log file.
+ (void)reset {
[_LCLLogFile_lock lock];
{
// close the log file
[LCLLogFile close];
// unlink existing log files
if (_LCLLogFile_filePath_c != NULL) {
unlink(_LCLLogFile_filePath_c);
}
if (_LCLLogFile_filePath0_c != NULL) {
unlink(_LCLLogFile_filePath0_c);
}
// logging is not active
_LCLLogFile_isActive = NO;
}
[_LCLLogFile_lock unlock];
}
// Rotates the log file.
+ (void)rotate {
[_LCLLogFile_lock lock];
{
// close the log file
[LCLLogFile close];
// keep a copy of the current log file
if (_LCLLogFile_filePath_c != NULL && _LCLLogFile_filePath0_c != NULL) {
rename(_LCLLogFile_filePath_c, _LCLLogFile_filePath0_c);
}
}
[_LCLLogFile_lock unlock];
}
//
// Methods for creating log file paths.
//
// Returns a default path for a log file which is based on the Info.plist
// files which are associated with this class. The returned path has the form
// ~/Library/Logs/<main>/<this>.log
// where
// <main> is the name (or identifier) of the application's main bundle, and
// <this> is the name (or identifier) of the bundle to which this LCLLogFile
// class belongs.
// This method is a convenience method which calls defaultPathWithPathPrefix
// with the prefix ~/Library/Logs and the given fallback path.
+ (NSString *)defaultPathInHomeLibraryLogsOrPath:(NSString *)path {
return [LCLLogFile defaultPathWithPathPrefix:
[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Logs"]
orPath:path];
}
// Returns a default path for a log file which is based on the Info.plist
// files which are associated with this class. The returned path has the form
// <path>/<main>/<this>.log
// where
// <path> is the given path prefix,
// <main> is the name (or identifier) of the application's main bundle, and
// <this> is the name (or identifier) of the bundle to which this LCLLogFile
// class belongs.
// If the name or identifier cannot be retrieved from the main bundle, the
// returned default path has the form
// <path>/<this>/<this>.<pid>.log
// where
// <pid> is the current process id.
// If the name or identifier cannot be retrieved from the bundle which
// corresponds to this LCLLogFile class, or if the given path prefix <path> is
// nil, the given fallback path is returned.
+ (NSString *)defaultPathWithPathPrefix:(NSString *)pathPrefix
orPath:(NSString *)path {
// get the main bundle and the bundle which corresponds to this class
NSBundle *pathBundle = [NSBundle mainBundle];
NSBundle *fileBundle = [NSBundle bundleForClass:[LCLLogFile class]];
NSString *pathComponent = [LCLLogFile defaultPathComponentFromPathBundle:pathBundle
fileBundle:fileBundle
orPathComponent:nil];
if (pathPrefix != nil && pathComponent != nil) {
return [pathPrefix stringByAppendingPathComponent:pathComponent];
} else {
return path;
}
}
// Returns a default path component for a log file which is based on the given
// bundles' Info.plist files. The returned path has the form
// <path>/<file>.log
// where
// <path> is the name (or identifier) of the given path bundle, and
// <file> is the name (or identifier) of the given file bundle.
// If the name or identifier cannot be retrieved from the path bundle, the
// returned default path has the form
// <file>/<file>.<pid>.log
// where
// <pid> is the current process id.
// If the name or identifier cannot be retrieved from the file bundle, the
// given fallback path component is returned.
+ (NSString *)defaultPathComponentFromPathBundle:(NSBundle *)pathBundle
fileBundle:(NSBundle *)fileBundle
orPathComponent:(NSString *)pathComponent {
NSString *pathName = [LCLLogFile nameOrIdentifierFromBundle:pathBundle
orString:nil];
NSString *fileName = [LCLLogFile nameOrIdentifierFromBundle:fileBundle
orString:nil];
if (pathName != nil && fileName != nil) {
// we have a path name and a file name
return [NSString stringWithFormat:@"%@/%@.log", pathName, fileName];
} else if (pathName == nil && fileName != nil) {
// we don't have a path name, but a file name, use the file name as
// the path name and append the pid to the file name to avoid collisions
return [NSString stringWithFormat:@"%@/%@.%u.log", fileName, fileName, getpid()];
} else {
// no information from the bundles, fail
return pathComponent;
}
}
// Returns the name from the given bundle's Info.plist file. If the name doesn't
// exist, the bundle's identifier is returned. If the identifier doesn't exist,
// the given fallback string is returned.
+ (NSString *)nameOrIdentifierFromBundle:(NSBundle *)bundle
orString:(NSString *)string {
id bundleName = [bundle objectForInfoDictionaryKey:@"CFBundleName"];
if (bundleName != nil && [bundleName isKindOfClass:[NSString class]]) {
return (NSString *)bundleName;
}
NSString *bundleIdentifier = [bundle bundleIdentifier];
if (bundleIdentifier != nil) {
return bundleIdentifier;
}
return string;
}
@end