Development Tip

파일에 NSLog하는 방법

yourdevel 2020. 12. 13. 11:16
반응형

파일에 NSLog하는 방법


NSLog콘솔뿐만 아니라 파일에도 모두 쓸 수 있습니까? NSLog바꾸지 않고 준비하고 싶습니다 someExternalFunctionForLogging.

모두 교체하는 것이 진짜 문제가 될 것 NSLog입니다. 콘솔에서 데이터를 구문 분석하거나 메시지를 포착 할 가능성이 있습니까?


옵션 1 : ASL 사용

NSLog는 ASL (Apple의 syslog 버전) 및 콘솔에 로그를 출력합니다. 즉, iPhone 시뮬레이터를 사용할 때 이미 Mac의 파일에 쓰고 있음을 의미합니다. 읽으려면 애플리케이션 Console.app을 열고 필터 필드에 애플리케이션 이름을 입력합니다. iPhone 장치에서 동일한 작업을 수행하려면 ASL API를 사용하고 일부 코딩을 수행해야합니다.

옵션 2 : 파일에 쓰기

시뮬레이터에서 실행 중이고 Console.app을 사용하고 싶지 않다고 가정 해 보겠습니다. 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];
}

_Log 함수의 fprintf 아래에 다음 줄을 추가합니다.

append(msg);

파일 쓰기는 iPhone 장치에서도 작동하지만 파일은 내부 디렉토리에 생성되며 코드를 추가하여 Mac으로 다시 보내거나 내부의보기에 표시하지 않으면 액세스 할 수 없습니다. 앱을 사용하거나 iTunes를 사용하여 문서 디렉토리를 추가하십시오.


훨씬 더 쉽게 접근 방식은. 다음은 NSLog출력을 애플리케이션 Documents폴더 의 파일로 리디렉션하는 방법입니다 . 이는 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);
}

빠른:

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) 또는 print(Swift)에 의해 생성 된 모든 출력이 지정된 파일로 전달됩니다. 저장된 파일을 열려면 단순히 폴더 를 찾아 보는 것보다 Organizer응용 프로그램의 파일을 찾아보고 Application Data파일 시스템에 Documents저장하십시오.


문제에 대한 가장 간단한 해결책을 찾았습니다 . iPhone의 파일에 로깅 . NSLog 코드를 변경하거나 로거 자체를 변경할 필요가 없습니다.이 4 줄을 didFinishLaunchingWithOptions에 추가하고 빌드 설정에서 라이브 릴리스가 활성화되지 않는지 확인하십시오 (이를 위해 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

JaakL의 답변을 Swift로 번역하여 다른 사람도 필요로하는 경우 여기에 게시했습니다.

이 코드를 앱 어딘가에서 실행하면 그 순간부터 모든 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> 다운로드 컨테이너. 파인더로 파일보기 : 파일에서 마우스 오른쪽 버튼 클릭> 패키지 내용보기> appdata> 문서> 파일이 있습니다.


확인! 먼저 Evan-Mulawski에게 감사드립니다. 내 해결책은 다음과 같습니다. 누군가에게 도움이 될 것입니다.

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

그런 다음 "replace for all"NSLog-> logThis를 사용하십시오. 이 코드는 내 앱에 맞게 조정되었습니다. 다양한 요구에 맞게 확장 할 수 있습니다.


도움을 청합니다.


이것이 내가 사용하고 잘 작동하는 것입니다.

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
}

console.log 액세스 :

로그 경로가 Xcode 로그 영역에 인쇄되면 경로를 선택하고 마우스 오른쪽 버튼을 클릭 한 다음 Finder에서 Services-Reaveal을 선택하고 console.log 파일을 엽니 다.


Swift 4 버전

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)

스위프트의 출력이 print()될 것이다stdout


나는 Alvin George의 대답으로 약간 일했습니다.

로그 파일 크기를 제어하기 위해 "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)")
            }
        }
    }
}

참고URL : https://stackoverflow.com/questions/7271528/how-to-nslog-into-a-file

반응형