Cómo iniciar sesión NSLog en un archivo

¿Es posible escribir cada NSLog no solo en la consola, sino también en un archivo? Quiero preparar esto sin reemplazar NSLog en someExternalFunctionForLogging .

Será un verdadero problema reemplazar todo NSLog . ¿Tal vez hay una posibilidad de analizar datos desde la consola o capturar mensajes?

Opción 1: usar ASL

Las salidas NSLog se conectan a ASL (la versión de syslog de Apple) y a la consola, lo que significa que ya está escribiendo en un archivo en su Mac cuando usa el simulador de iPhone. Si quiere leerlo, abra la aplicación Console.app y escriba el nombre de su aplicación en el campo de filtro. Para hacer lo mismo en su dispositivo iPhone, necesitaría usar la API ASL y hacer algo de encoding.

Opción 2: escribir en un archivo

Supongamos que se está ejecutando en el simulador y que no desea usar Console.app. Puede redirigir la secuencia de error a un archivo de su agrado usando freopen:
freopen([path cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
Vea esta explicación y proyecto de muestra para más detalles.

O puede anular NSLog con una función personalizada usando una macro. Ejemplo, agregue esta clase a su proyecto:

 // 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 

E impórtelo a todo el proyecto agregando lo siguiente a su -Prefix.pch :

 #import "Log.h" 

Ahora cada llamada a NSLog será reemplazada con su función personalizada sin la necesidad de tocar su código existente. Sin embargo, la función anterior solo se imprime en la consola. Para agregar salida de archivo, agregue esta función arriba _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]; } 

y agregue esta línea debajo de fprintf en la función _Log:

 append(msg); 

La escritura de archivos también funciona en su dispositivo iPhone, pero el archivo se creará en un directorio dentro de él, y no podrá acceder a menos que agregue código para enviarlo de nuevo a su Mac, o lo muestre en una vista dentro de su aplicación, o use iTunes para agregar el directorio de documentos.

Hay un enfoque mucho más fácil . Aquí está el método que redirige la salida NSLog a un archivo en Documents carpeta Documents la aplicación. Esto puede ser útil cuando quiera probar su aplicación fuera de su estudio de desarrollo, desenchufada de su 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); } 

Rápido:

 func redirectLogToDocuments() { let allPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) let documentsDirectory = allPaths.first! let pathForLog = documentsDirectory.stringByAppendingString("/yourFile.txt") freopen(pathForLog.cStringUsingEncoding(NSASCIIStringEncoding)!, "a+", stderr) } 

Después de ejecutar este método, todos los resultados generados por NSLog (ObjC) o print (Swift) se reenviarán al archivo especificado. Para que su archivo guardado abra Organizer , explore los archivos de la aplicación y guarde los Application Data en algún lugar de su sistema de archivos, que simplemente vaya a Documents carpeta Documents .

Traducí la respuesta de JaakL a Swift , posteándola aquí en cualquier caso, alguien más también lo necesita

Ejecute este código en algún lugar de su aplicación, desde ese momento almacena todos los resultados de NSLog () en un archivo, en el directorio de documentos.

 let docDirectory: NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as NSString let logpath = docDirectory.stringByAppendingPathComponent("YourFileName.txt") freopen(logpath.cStringUsingEncoding(NSASCIIStringEncoding)!, "a+", stderr) 

Extra: cómo encontrar el archivo de registro con Xcode:
Simplemente puede acceder al registro desde Xcode: Windows> Dispositivos> Elija su aplicación> InfoWheelButton> descargar contenedor. Vea el archivo con el buscador: haga clic con el botón derecho del mouse en el archivo> muestre el contenido del paquete> appdata> documentos> Y allí están los archivos

Encontré la solución más simple al problema: iniciar sesión en un archivo en el iPhone . No es necesario cambiar ningún código NSLog o cambiar el registrador en sí mismo, simplemente agregue estas 4 líneas a su didFinishLaunchingWithOptions y asegúrese de que en su configuración de comstackción esa versión en vivo no lo tenga activado (agregué el indicador LOG2FILE para esto).

 #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 

¡De acuerdo! En primer lugar, quiero agradecer a Evan-Mulawski. Aquí está mi solución, tal vez será útil para alguien:

En AppDelegate agrego Función:

 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); } 

y, luego use "reemplazar para todos" NSLog -> logThis. Este código está adaptado para mi aplicación. Se puede ampliar para diferentes necesidades.


Gracias por la ayuda.

Esto es lo que uso y funciona bien:

http://parmanoir.com/Redirecting_NSLog_to_a_file

Espero eso ayude.

Voy a publicarlo aquí por el bien del contenido

 - (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:

Añádalos a 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 } 

Accediendo a console.log:

Cuando la ruta del registro se imprima en el Área de registro de Xcode, seleccione la ruta, haga clic con el botón derecho, seleccione Servicios: Reaveal en el Finder y abra el archivo console.log

Trabajé un poco con la respuesta de Alvin George.

Para mantener el tamaño de los archivos de registro bajo control, implementé (rápido y sucio) una solución de “10 generaciones de archivos de registro” y agregué un func para eliminarlos más adelante.

Cada vez que se inicia la aplicación, generará un nuevo archivo de registro con un índice “0”. Los archivos que salen se renombrarán con un índice más alto que antes. El índice “10” será eliminado.

Por lo tanto, cada inicio le proporciona un nuevo archivo de registro, un máximo de 10 generaciones

Puede que no sea la manera más elegante de hacerlo, pero funciona muy bien para mí durante las últimas semanas, ya que necesito un largo tiempo de registro “fuera del 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)") } } } }