Как NSLog в файл
можно ли писать каждый NSLog
не только в консоль, а в файл тоже? Я хочу подготовить это без замены NSLog
на someExternalFunctionForLogging
.
это будет реальная проблема, чтобы заменить все NSLog
. Может быть, есть возможность для разбора данных из консоли или перехвата сообщений?
9 ответов:
Вариант 1: Используйте ASL
NSLog выводит журнал в ASL (версия syslog от Apple) и консоль, что означает, что он уже записывается в файл на вашем Mac при использовании симулятора iPhone. Если вы хотите прочитать его, откройте консоль приложения.приложение и введите имя приложения в поле Фильтр. Чтобы сделать то же самое в вашем устройстве iPhone, вам нужно будет использовать API ASL и сделать некоторое кодирование.
Вариант 2: запись в файл
допустим, вы работаете на симулятор и вы не хотите использовать консоль.приложение. Вы можете перенаправить поток ошибок в файл по своему вкусу с помощью freopen:
freopen([path cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
Смотрите это объяснение и пример проекта для сведения.или вы можете переопределить NSLog с помощью пользовательской функции с помощью макроса. Например, добавьте этот класс в свой проект:
// file Log.h #define NSLog(args...) _Log(@"DEBUG ", __FILE__,__LINE__,__PRETTY_FUNCTION__,args); @interface Log : NSObject void _Log(NSString *prefix, const char *file, int lineNumber, const char *funcName, NSString *format,...); @end // file Log.m #import "Log.h" @implementation Log void _Log(NSString *prefix, const char *file, int lineNumber, const char *funcName, NSString *format,...) { va_list ap; va_start (ap, format); format = [format stringByAppendingString:@"\n"]; NSString *msg = [[NSString alloc] initWithFormat:[NSString stringWithFormat:@"%@",format] arguments:ap]; va_end (ap); fprintf(stderr,"%s%50s:%3d - %s",[prefix UTF8String], funcName, lineNumber, [msg UTF8String]); [msg release]; } @end
и импортировать его в проект широкий добавив следующий код
<application>-Prefix.pch
:#import "Log.h"
теперь каждый вызов NSLog будет замените с вашей изготовленной на заказ функцией без потребности касаться вашему существующему коду. Однако функция выше-это только печать на консоль. Чтобы добавить вывод файла, добавьте эту функцию выше _Log:
void append(NSString *msg){ // get path to Documents/somefile.txt NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *path = [documentsDirectory stringByAppendingPathComponent:@"logfile.txt"]; // create if needed if (![[NSFileManager defaultManager] fileExistsAtPath:path]){ fprintf(stderr,"Creating file at %s",[path UTF8String]); [[NSData data] writeToFile:path atomically:YES]; } // append NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:path]; [handle truncateFileAtOffset:[handle seekToEndOfFile]]; [handle writeData:[msg dataUsingEncoding:NSUTF8StringEncoding]]; [handle closeFile]; }
и добавьте эту строку ниже fprintf в функции _Log:
append(msg);
запись файлов также работает на вашем устройстве iPhone, но файл будет создан в каталоге внутри него, и вы не сможете получить доступ, если не добавите код для отправки его обратно на ваш mac или не покажете его на экране просмотр внутри вашего приложения или использование iTunes для добавления каталога документов.
есть легче подход. Вот метод, который перенаправляет
NSLog
вывод в файл в приложении . Это может быть полезно, если вы хотите протестировать свое приложение за пределами своей студии разработки, отключенной от вашего mac.ObjC:
- (void)redirectLogToDocuments { NSArray *allPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [allPaths objectAtIndex:0]; NSString *pathForLog = [documentsDirectory stringByAppendingPathComponent:@"yourFile.txt"]; freopen([pathForLog cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr); }
Swift:
func redirectLogToDocuments() { let allPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) let documentsDirectory = allPaths.first! let pathForLog = documentsDirectory.stringByAppendingString("/yourFile.txt") freopen(pathForLog.cStringUsingEncoding(NSASCIIStringEncoding)!, "a+", stderr) }
после выполнения этого метода все выходные данные генерируются
NSLog
(ObjC) илиOrganizer
, просмотрите файлы приложения и сохранитеApplication Data
где-то в файловой системе, чем просто найдите .
перевел ответ Яакла на Свифт, разместив его здесь в любом случае кто-то нуждается в нем так же
запустите этот код где-нибудь в вашем приложении, с этого момента он хранит все выходные данные NSLog() в файле, в каталоге документов.
let docDirectory: NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as NSString let logpath = docDirectory.stringByAppendingPathComponent("YourFileName.txt") freopen(logpath.cStringUsingEncoding(NSASCIIStringEncoding)!, "a+", stderr)
дополнительно: как найти лог-файл в Xcode:
Вы можете просто получить доступ к журналу из Xcode: Windows > устройства > выберите приложение > InfoWheelButton > загрузить контейнер. Просмотр файла с помощью finder: нажмите правую кнопку мыши на файл > показать содержимое пакета > appdata > документы > и там файлы
Я нашел самое простое решение проблемы: вход в файл на iPhone . Нет необходимости менять какой-либо код NSLog или изменять сам регистратор, просто добавьте эти 4 строки в свои didFinishLaunchingWithOptions и убедитесь, что в ваших настройках сборки live release не будет активирован (я добавил флаг LOG2FILE для этого).
#ifdef LOG2FILE #if TARGET_IPHONE_SIMULATOR == 0 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsDirectory = [paths objectAtIndex:0]; NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"console.log"]; freopen([logPath cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr); #endif #endif
ОК! во-первых, я хочу поблагодарить Эван-Мулавски. Вот мое решение, может быть, это будет полезно для кого-то:
в AppDelegate я добавляю функцию:
void logThis(NSString* Msg, ...) { NSArray* findingMachine = [Msg componentsSeparatedByString:@"%"]; NSString* outputString = [NSString stringWithString:[findingMachine objectAtIndex:0]]; va_list argptr; va_start(argptr, Msg); for(int i = 1; i < [findingMachine count]; i++) { if ([[findingMachine objectAtIndex:i] hasPrefix:@"i"]||[[findingMachine objectAtIndex:i] hasPrefix:@"d"]) { int argument = va_arg(argptr, int); /* next Arg */ outputString = [outputString stringByAppendingFormat:@"%i", argument]; NSRange range; range.location = 0; range.length = 1; NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:@""]; outputString = [outputString stringByAppendingString:tmpStr]; } else if ([[findingMachine objectAtIndex:i] hasPrefix:@"@"]) { id argument = va_arg(argptr, id); // add argument and next patr of message outputString = [outputString stringByAppendingFormat:@"%@", argument]; NSRange range; range.location = 0; range.length = 1; NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:@""]; outputString = [outputString stringByAppendingString:tmpStr]; } else if ([[findingMachine objectAtIndex:i] hasPrefix:@"."]) { double argument = va_arg(argptr, double); // add argument and next patr of message outputString = [outputString stringByAppendingFormat:@"%f", argument]; NSRange range; range.location = 0; range.length = 3; NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:@""]; outputString = [outputString stringByAppendingString:tmpStr]; } else if ([[findingMachine objectAtIndex:i] hasPrefix:@"f"]) { double argument = va_arg(argptr, double); // add argument and next patr of message outputString = [outputString stringByAppendingFormat:@"%f", argument]; NSRange range; range.location = 0; range.length = 1; NSString* tmpStr = [[findingMachine objectAtIndex:i] stringByReplacingCharactersInRange:range withString:@""]; outputString = [outputString stringByAppendingString:tmpStr]; } else { outputString = [outputString stringByAppendingString:@"%"]; outputString = [outputString stringByAppendingString:[findingMachine objectAtIndex:i]]; } } va_end(argptr); NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); NSString * filePath = [[paths objectAtIndex:0]stringByAppendingPathComponent:@"logFile.txt"]; NSError* theError = nil; NSString * fileString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&theError]; if (theError != nil||[fileString length]==0) { fileString = [NSString stringWithString:@""]; } fileString = [fileString stringByAppendingFormat:@"\n%@",outputString]; if(![fileString writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&theError]) { NSLog(@"Loging problem"); } NSLog(@"%@",outputString); }
и, затем используйте "заменить для всех" NSLog - > logThis. Этот код адаптирован для моего приложения. Его можно расширить для различных нужд.
Thnks для помощи.
Это то, что я использую и хорошо работает:
http://parmanoir.com/Redirecting_NSLog_to_a_file
надеюсь, что это помогает.
Я просто опубликую его здесь ради содержания
- (BOOL)redirectNSLog { // Create log file [@"" writeToFile:@"/NSLog.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil]; id fileHandle = [NSFileHandle fileHandleForWritingAtPath:@"/NSLog.txt"]; if (!fileHandle) return NSLog(@"Opening log failed"), NO; [fileHandle retain]; // Redirect stderr int err = dup2([fileHandle fileDescriptor], STDERR_FILENO); if (!err) return NSLog(@"Couldn't redirect stderr"), NO; return YES; }
Swift 2.0:
добавьте их в Appdelegate didFinishLaunchWithOptions.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { var paths: Array = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) let documentsDirectory: String = paths[0] let logPath: String = documentsDirectory.stringByAppendingString("/console.log") if (isatty(STDERR_FILENO) == 0) { freopen(logPath, "a+", stderr) freopen(logPath, "a+", stdin) freopen(logPath, "a+", stdout) } print(logPath) return true }
доступ к консоли.журнал :
когда путь журнал будет распечатан на Лога в Xcode, выберите путь, щелкните правой кнопкой мыши, выбрать сервис - разгадывайте в Finder и откройте файл консоли.журнал
Я немного поработал с ответом Элвина Джорджа.
чтобы держать размеры файлов журнала под контролем я реализовал (быстрый и грязный) решение "10 поколений файлов журнала" и добавить функцию, чтобы удалить их позже
каждый раз, когда приложение запускается, он создает новый файл журнала с индексом "0". На выходе файл(ы) будет переименован с индексом выше, чем раньше. Индекс " 10 " будет удален.
Итак, каждый запуск дает вам новый файл журнала, максимум 10 поколения
может быть, это не самый элегантный способ сделать это, но работает для меня в течение последних недель очень хорошо, так как мне нужно долгое время регистрировать "с mac"
// ----------------------------------------------------------------------------------------------------------- // redirectConsoleToFile() // // does two things // 1) redirects "stderr", "stdin" and "stdout" to a logfile // 2) deals with old/existing files to keep up to 10 generations of the logfiles // tested with IOS 9.4 and Swift 2.2 func redirectConsoleToFile() { // Instance of a private filemanager let myFileManger = NSFileManager.defaultManager() // the path of the documnts directory of the app let documentDirectory: String = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first! // maximum number of logfiles let maxNumberOfLogFiles: Int = 10 // look if the max number of files already exist var logFilePath : String = documentDirectory.stringByAppendingString("/Console\(maxNumberOfLogFiles).log") var FlagOldFileNoProblem: Bool = true if myFileManger.fileExistsAtPath(logFilePath) == true { // yes, max number of files reached, so delete the oldest one do { try myFileManger.removeItemAtPath(logFilePath) } catch let error as NSError { // something went wrong print("ERROR deleting old logFile \(maxNumberOfLogFiles): \(error.description)") FlagOldFileNoProblem = false } } // test, if there was a problem with the old file if FlagOldFileNoProblem == true { // loop over all possible filenames for i in 0 ..< maxNumberOfLogFiles { // look, if an old file exists, if so, rename it with an index higher than before logFilePath = documentDirectory.stringByAppendingString("/Console\((maxNumberOfLogFiles - 1) - i).log") if myFileManger.fileExistsAtPath(logFilePath) == true { // there is an old file let logFilePathNew = documentDirectory.stringByAppendingString("/WayAndSeeConsole\(maxNumberOfLogFiles - i).log") do { // rename it try myFileManger.moveItemAtPath(logFilePath, toPath: logFilePathNew) } catch let error as NSError { // something went wrong print("ERROR renaming logFile: (i = \(i)), \(error.description)") FlagOldFileNoProblem = false } } } } // test, if there was a problem with the old files if FlagOldFileNoProblem == true { // No problem so far, so try to delete the old file logFilePath = documentDirectory.stringByAppendingString("/Console0.log") if myFileManger.fileExistsAtPath(logFilePath) == true { // yes, it exists, so delete it do { try myFileManger.removeItemAtPath(logFilePath) } catch let error as NSError { // something went wrong print("ERROR deleting old logFile 0: \(error.description)") } } } // even if there was a problem with the files so far, we redirect logFilePath = documentDirectory.stringByAppendingString("/Console0.log") if (isatty(STDIN_FILENO) == 0) { freopen(logFilePath, "a+", stderr) freopen(logFilePath, "a+", stdin) freopen(logFilePath, "a+", stdout) displayDebugString(DEBUG_Others, StringToAdd: "stderr, stdin, stdout redirected to \"\(logFilePath)\"") } else { displayDebugString(DEBUG_Others, StringToAdd: "stderr, stdin, stdout NOT redirected, STDIN_FILENO = \(STDIN_FILENO)") } } // ----------------------------------------------------------------------------------------------------------- // cleanupOldConsoleFiles() // // delete all old consolfiles func cleanupOldConsoleFiles() { // Instance of a private filemanager let myFileManger = NSFileManager.defaultManager() // the path of the documnts directory of the app let documentDirectory: String = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first! // maximum number of logfiles let maxNumberOfLogFiles: Int = 10 // working string var logFilePath: String = "" // loop over all possible filenames for i in 0 ... maxNumberOfLogFiles { // look, if an old file exists, if so, rename it with an index higher than before logFilePath = documentDirectory.stringByAppendingString("/Console\(i).log") if myFileManger.fileExistsAtPath(logFilePath) == true { // Yes, file exist, so delete it do { try myFileManger.removeItemAtPath(logFilePath) } catch let error as NSError { // something went wrong print("ERROR deleting old logFile \"\(i)\": \(error.description)") } } } }
Swift 4 version
let docDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let logpathe = docDirectory.appendingPathComponent("Logerr.txt") freopen(logpathe.path.cString(using: .ascii)!, "a+", stderr) let logpatho = docDirectory.appendingPathComponent("Logout.txt") freopen(logpatho.path.cString(using: .ascii)!, "a+", stdout)
выход из Swift
print()
будетstdout