Development Tip

Node.js 모범 사례 예외 처리

yourdevel 2020. 9. 29. 18:48
반응형

Node.js 모범 사례 예외 처리


며칠 전에 node.js를 사용해보기 시작했습니다. 내 프로그램에서 처리되지 않은 예외가있을 때마다 노드가 종료된다는 것을 깨달았습니다. 이것은 처리되지 않은 예외가 발생하고 컨테이너가 여전히 요청을받을 수있을 때 작업자 스레드 만 죽는 일반적인 서버 컨테이너와 다릅니다. 이것은 몇 가지 질문을 제기합니다.

  • 인가 process.on('uncaughtException')그것을 방지하는 유일한 효과적인 방법은?
  • process.on('uncaughtException')뿐만 아니라 비동기 프로세스를 실행하는 동안 처리되지 않은 예외를 잡을?
  • 잡히지 않은 예외의 경우 활용할 수있는 이미 빌드 된 모듈 (예 : 이메일 전송 또는 파일 쓰기)이 있습니까?

node.js에서 잡히지 않은 예외를 처리하는 일반적인 모범 사례를 보여줄 포인터 / 기사를 감사하겠습니다.


업데이트 : Joyent는 이제 자체 가이드를 가지고 있습니다. 다음 정보는 요약에 가깝습니다.

안전하게 "발생"오류

이상적으로는 가능한 한 포착되지 않은 오류를 피하는 것이 좋습니다. 문자 그대로 오류를 던지는 대신 코드 아키텍처에 따라 다음 방법 중 하나를 사용하여 오류를 안전하게 "던질"할 수 있습니다.

  • 동기 코드의 경우 오류가 발생하면 오류를 반환합니다.

    // Define divider as a syncrhonous function
    var divideSync = function(x,y) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by returning it
            return new Error("Can't divide by zero")
        }
        else {
            // no error occured, continue on
            return x/y
        }
    }
    
    // Divide 4/2
    var result = divideSync(4,2)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/2=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/2='+result)
    }
    
    // Divide 4/0
    result = divideSync(4,0)
    // did an error occur?
    if ( result instanceof Error ) {
        // handle the error safely
        console.log('4/0=err', result)
    }
    else {
        // no error occured, continue on
        console.log('4/0='+result)
    }
    
  • 콜백 기반 (즉, 비동기) 코드의 경우 콜백의 첫 번째 인수는입니다. err오류가 발생하면 오류가 발생 err하고 오류가 발생하지 않으면 err입니다 null. 다른 모든 인수는 다음 인수를 따릅니다 err.

    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }
    
    divide(4,2,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/2=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/2='+result)
        }
    })
    
    divide(4,0,function(err,result){
        // did an error occur?
        if ( err ) {
            // handle the error safely
            console.log('4/0=err', err)
        }
        else {
            // no error occured, continue on
            console.log('4/0='+result)
        }
    })
    
  • 들어 다사 다난 오류가 대신 오류를 던지는 어디서나 발생할 수 있습니다 코드, 화재 error이벤트를 대신 :

    // Definite our Divider Event Emitter
    var events = require('events')
    var Divider = function(){
        events.EventEmitter.call(this)
    }
    require('util').inherits(Divider, events.EventEmitter)
    
    // Add the divide function
    Divider.prototype.divide = function(x,y){
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by emitting it
            var err = new Error("Can't divide by zero")
            this.emit('error', err)
        }
        else {
            // no error occured, continue on
            this.emit('divided', x, y, x/y)
        }
    
        // Chain
        return this;
    }
    
    // Create our divider and listen for errors
    var divider = new Divider()
    divider.on('error', function(err){
        // handle the error safely
        console.log(err)
    })
    divider.on('divided', function(x,y,result){
        console.log(x+'/'+y+'='+result)
    })
    
    // Divide
    divider.divide(4,2).divide(4,0)
    

안전하게 "잡기"오류

하지만 가끔 어딘가에 오류를 발생시키는 코드가 여전히있을 수 있으며,이 코드는 포착되지 않은 예외로 이어질 수 있으며 안전하게 포착하지 못하면 애플리케이션의 잠재적 인 충돌이 발생할 수 있습니다. 코드 아키텍처에 따라 다음 방법 중 하나를 사용하여이를 포착 할 수 있습니다.

  • 오류가 발생한 위치를 알고 있으면 해당 섹션을 node.js 도메인 에 래핑 할 수 있습니다.

    var d = require('domain').create()
    d.on('error', function(err){
        // handle the error safely
        console.log(err)
    })
    
    // catch the uncaught errors in this asynchronous or synchronous code block
    d.run(function(){
        // the asynchronous or synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    })
    
  • 오류가 발생한 위치가 동기 코드이고 어떤 이유로 든 도메인을 사용할 수없는 경우 (아마도 이전 버전의 노드) try catch 문을 사용할 수 있습니다.

    // catch the uncaught errors in this synchronous code block
    // try catch statements only work on synchronous code
    try {
        // the synchronous code that we want to catch thrown errors on
        var err = new Error('example')
        throw err
    } catch (err) {
        // handle the error safely
        console.log(err)
    }
    

    그러나 try...catch비동기 적으로 발생한 오류는 포착되지 않으므로 비동기 코드에서 사용하지 않도록주의하십시오 .

    try {
        setTimeout(function(){
            var err = new Error('example')
            throw err
        }, 1000)
    }
    catch (err) {
        // Example error won't be caught here... crashing our app
        // hence the need for domains
    }
    

    try..catch비동기 코드와 함께 작업하려면 Node 7.4 이상을 실행할 때 async/await기본적으로 사용 하여 비동기 함수를 작성할 수 있습니다.

    주의해야 할 또 다른 사항 try...catch은 다음 try과 같이 명령문 내부에서 완료 콜백을 래핑 할 위험이 있다는 것입니다 .

    var divide = function(x,y,next) {
        // if error condition?
        if ( y === 0 ) {
            // "throw" the error safely by calling the completion callback
            // with the first argument being the error
            next(new Error("Can't divide by zero"))
        }
        else {
            // no error occured, continue on
            next(null, x/y)
        }
    }
    
    var continueElsewhere = function(err, result){
            throw new Error('elsewhere has failed')
    }
    
    try {
            divide(4, 2, continueElsewhere)
            // ^ the execution of divide, and the execution of 
            //   continueElsewhere will be inside the try statement
    }
    catch (err) {
            console.log(err.stack)
            // ^ will output the "unexpected" result of: elsewhere has failed
    }
    

    이 문제는 코드가 더 복잡 해짐에 따라 수행하기 매우 쉽습니다. 따라서 도메인을 사용하거나 오류를 반환하여 (1) 비동기 코드에서 포착되지 않은 예외를 피하고 (2) 원하지 않는 실행 포착 시도를 방지하는 것이 가장 좋습니다. JavaScript의 비동기 이벤트 머신 스타일 대신 적절한 스레딩을 허용하는 언어에서는 이는 문제가되지 않습니다.

  • 마지막으로 도메인이나 try catch 문에 래핑되지 않은 위치에서 포착되지 않은 오류가 발생하는 경우 uncaughtException리스너 를 사용하여 애플리케이션이 충돌하지 않도록 만들 수 있습니다 (하지만 그렇게하면 애플리케이션이 알 수없는 상태 가 될 수 있습니다. ) :

    // catch the uncaught errors that weren't wrapped in a domain or try catch statement
    // do not use this in modules, but only in applications, as otherwise we could have multiple of these bound
    process.on('uncaughtException', function(err) {
        // handle the error safely
        console.log(err)
    })
    
    // the asynchronous or synchronous code that emits the otherwise uncaught error
    var err = new Error('example')
    throw err
    

다음은 선택한 블로그 게시물의 코드 예제 및 인용문을 포함하여이 주제에 대한 다양한 소스의 요약 및 큐 레이션입니다. 모범 사례의 전체 목록은 여기에서 찾을 수 있습니다.


Node.JS 오류 처리 모범 사례


Number1 : 비동기 오류 처리를위한 약속 사용

요약 : 콜백 스타일에서 비동기 오류를 처리하는 것이 아마도 지옥으로가는 가장 빠른 방법 일 것입니다 (일명 둠의 피라미드). 코드에 줄 수있는 가장 좋은 선물은 try-catch와 같이 훨씬 간결하고 친숙한 코드 구문을 제공하는 평판이 좋은 promise 라이브러리를 대신 사용하는 것입니다.

그렇지 않으면 : Node.JS 콜백 스타일 인 function (err, response)는 오류 처리와 캐주얼 코드의 혼합, 과도한 중첩 및 어색한 코딩 패턴으로 인해 유지 관리 할 수없는 코드에 대한 유망한 방법입니다.

코드 예-좋음

doWork()
.then(doWork)
.then(doError)
.then(doWork)
.catch(errorHandler)
.then(verify);

코드 예제 안티 패턴 – 콜백 스타일 오류 처리

getData(someParameter, function(err, result){
    if(err != null)
      //do something like calling the given callback function and pass the error
    getMoreData(a, function(err, result){
          if(err != null)
            //do something like calling the given callback function and pass the error
        getMoreData(b, function(c){ 
                getMoreData(d, function(e){ 
                    ...
                });
            });
        });
    });
});

블로그 인용문 : " Promise에 문제가 있습니다" (블로그 pouchdb에서 "Node Promises"키워드에 대해 11 위)

"… 사실 콜백은 훨씬 더 사악한 일을합니다. 이는 우리가 프로그래밍 언어에서 일반적으로 당연하게 여기는 스택을 빼앗 깁니다. 스택없이 코드를 작성하는 것은 브레이크 페달없이 자동차를 운전하는 것과 매우 유사합니다. 당신이 그것에 도달하고 거기에 없을 때까지 당신이 그것을 얼마나 절실하게 필요로하는지 깨닫지 마십시오. 약속의 요점은 우리가 비동기로 갔을 때 잃어버린 언어의 기본 인 리턴, 던지기, 스택을 우리에게 돌려주는 것입니다. 그러나 당신은 Promise를 활용하려면 Promise를 올바르게 사용하는 방법을 알아야합니다. "


Number2 : 기본 제공 오류 개체 만 사용

요약 : 오류를 문자열 또는 사용자 지정 유형으로 발생시키는 코드를 보는 것은 매우 일반적입니다 . 이는 오류 처리 논리와 모듈 간의 상호 운용성을 복잡하게 만듭니다. 약속 거부, 예외 발생 또는 오류 발생 여부에 관계없이 Node.JS 내장 Error 객체를 사용하면 일관성이 향상되고 오류 정보 손실이 방지됩니다.

그렇지 않으면 : 일부 모듈을 실행할 때 어떤 유형의 오류가 반환되는지 불확실하므로 다가오는 예외에 대해 추론하고 처리하기가 훨씬 더 어려워집니다. 사용자 지정 유형을 사용하여 오류를 설명하면 스택 추적과 같은 중요한 오류 정보가 손실 될 수 있습니다.

코드 예제-올바르게 수행

    //throwing an Error from typical function, whether sync or async
 if(!productToAdd)
 throw new Error("How can I add new product when no value provided?");

//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));

//'throwing' an Error from a Promise
 return new promise(function (resolve, reject) {
 DAL.getProduct(productToAdd.id).then((existingProduct) =>{
 if(existingProduct != null)
 return reject(new Error("Why fooling us and trying to add an existing product?"));

코드 예제 안티 패턴

//throwing a String lacks any stack trace information and other important properties
if(!productToAdd)
    throw ("How can I add new product when no value provided?");

블로그 인용문 : "문자열은 오류가 아닙니다." (블로그 개발사에서 "Node.JS 오류 개체"키워드에 대해 6 위)

"… 오류 대신 문자열을 전달하면 모듈 간의 상호 운용성이 감소합니다. 오류 검사를 수행하거나 오류에 대해 더 많이 알고 싶은 API와의 계약을 깨뜨 립니다. 생성자에게 전달 된 메시지를 보유하는 것 외에 최신 JavaScript 엔진의 흥미로운 속성 .. "


3 번 : 운영 오류와 프로그래머 오류 구분

요약 : 작업 오류 (예 : API가 잘못된 입력을 수신함)는 오류 영향을 완전히 이해하고 신중하게 처리 할 수있는 알려진 사례를 나타냅니다. 반면에 프로그래머 오류 (예 : 정의되지 않은 변수 읽기 시도)는 응용 프로그램을 정상적으로 다시 시작해야하는 알 수없는 코드 오류를 나타냅니다.

그렇지 않으면 : 오류가 나타날 때 항상 응용 프로그램을 다시 시작할 수 있지만 사소하고 예측 된 오류 (작동 오류)로 인해 ~ 5000 명의 온라인 사용자를 중단시키는 이유는 무엇입니까? 그 반대도 이상적이지 않습니다. 알 수없는 문제 (프로그래머 오류)가 발생했을 때 응용 프로그램을 유지하면 예기치 않은 동작이 발생할 수 있습니다. 둘을 차별화하면 재치있게 행동하고 주어진 상황에 따라 균형 잡힌 접근 방식을 적용 할 수 있습니다.

코드 예제-올바르게 수행

    //throwing an Error from typical function, whether sync or async
 if(!productToAdd)
 throw new Error("How can I add new product when no value provided?");

//'throwing' an Error from EventEmitter
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));

//'throwing' an Error from a Promise
 return new promise(function (resolve, reject) {
 DAL.getProduct(productToAdd.id).then((existingProduct) =>{
 if(existingProduct != null)
 return reject(new Error("Why fooling us and trying to add an existing product?"));

코드 예제-오류를 작동 가능 (신뢰 됨)으로 표시

//marking an error object as operational 
var myError = new Error("How can I add new product when no value provided?");
myError.isOperational = true;

//or if you're using some centralized error factory (see other examples at the bullet "Use only the built-in Error object")
function appError(commonType, description, isOperational) {
    Error.call(this);
    Error.captureStackTrace(this);
    this.commonType = commonType;
    this.description = description;
    this.isOperational = isOperational;
};

throw new appError(errorManagement.commonErrors.InvalidInput, "Describe here what happened", true);

//error handling code within middleware
process.on('uncaughtException', function(error) {
    if(!error.isOperational)
        process.exit(1);
});

블로그 인용 : "그렇지 않으면 상태를 위험에 빠뜨릴 수 있습니다"(블로그에서 디버그 가능, "Node.JS uncaught exception"키워드에 대해 3 위)

" … 자바 스크립트에서 throw가 작동하는 방식의 특성상 참조를 누출하거나 다른 종류의 정의되지 않은 취성 상태를 생성하지 않고 안전하게"중단 한 부분을 선택 "할 수있는 방법은 거의 없습니다. 던진 오류는 프로세스를 종료하는 것 입니다. 물론 일반적인 웹 서버에서는 많은 연결이 열려있을 수 있으며 다른 사람에 의해 오류가 발생했기 때문에 연결을 갑작스럽게 종료하는 것은 합리적이지 않습니다. 오류를 유발 한 요청에 오류 응답을 보내고 나머지는 정상 시간에 완료하고 해당 작업자의 새 요청 수신을 중지합니다. "


4 번 : 미들웨어를 통해 중앙에서 오류 처리

TL은, DR : 오류가 들어올 때와 같은 관리 및 로깅에 메일로 로직을 처리 오류는 전용 중앙 집중식 개체의 모든 엔드 포인트 (예를 들면 익스프레스 미들웨어, cron 작업, 단위 테스트) 호출을 캡슐화한다.

그렇지 않으면 : 한 곳에서 오류를 처리하지 않으면 코드 중복이 발생하고 잘못 처리 된 오류가 발생할 수 있습니다.

코드 예-일반적인 오류 흐름

//DAL layer, we don't handle errors here
DB.addDocument(newCustomer, (error, result) => {
    if (error)
        throw new Error("Great error explanation comes here", other useful parameters)
});

//API route code, we catch both sync and async errors and forward to the middleware
try {
    customerService.addNew(req.body).then(function (result) {
        res.status(200).json(result);
    }).catch((error) => {
        next(error)
    });
}
catch (error) {
    next(error);
}

//Error handling middleware, we delegate the handling to the centrzlied error handler
app.use(function (err, req, res, next) {
    errorHandler.handleError(err).then((isOperationalError) => {
        if (!isOperationalError)
            next(err);
    });
});

블로그 인용문 : "때때로 하위 수준은 호출자에게 오류를 전파하는 것 외에는 유용한 작업을 수행 할 수 없습니다."(Joyent 블로그에서 "Node.JS 오류 처리"키워드에 대해 1 위)

"… 여러 수준의 스택에서 동일한 오류를 처리하게 될 수 있습니다. 이는 하위 수준에서 오류를 호출자에게 전파하고 호출자에게 오류를 전파하는 등 유용한 작업을 수행 할 수 없을 때 발생합니다. 종종, 작업을 재 시도하거나 사용자에게 오류를보고하는 등 적절한 응답이 무엇인지는 최상위 호출자 만 알고 있습니다. 그렇다고 모든 오류를 단일 최상위 수준에보고해야한다는 의미는 아닙니다. 콜백 자체는 오류가 발생한 컨텍스트를 알 수 없기 때문입니다. "


Number5 : Swagger를 사용한 문서 API 오류

요약 : API 호출자에게 어떤 오류가 반환 될 수 있는지 알려 주면 충돌없이 신중하게 처리 할 수 ​​있습니다. 이것은 일반적으로 Swagger와 같은 REST API 문서 프레임 워크로 수행됩니다.

그렇지 않으면 API 클라이언트가 이해할 수없는 오류를 수신했기 때문에 충돌하고 다시 시작하기로 결정할 수 있습니다. 참고 : API 호출자는 본인 일 수 있습니다 (마이크로 서비스 환경에서 매우 일반적 임).

블로그 인용문 : "발신자에게 어떤 오류가 발생할 수 있는지 알려야합니다"(Joyent 블로그에서 "Node.JS 로깅"키워드에 대해 1 위)

… 오류를 처리하는 방법에 대해 이야기했지만 새 함수를 작성할 때 함수를 호출 한 코드에 오류를 어떻게 전달합니까? … 어떤 오류가 발생할 수 있는지 모르거나 그 의미를 모르는 경우 프로그램이 우연한 경우를 제외하고는 정확할 수 없습니다. 따라서 새 함수를 작성하는 경우 호출자에게 어떤 오류가 발생할 수 있으며 어떤 오류가 발생하는지 알려 주어야합니다.


Number6 : 낯선 사람이 마을에 오면 절차를 우아하게 종료

요약 : 알 수없는 오류가 발생하는 경우 (개발자 오류, 모범 사례 번호 3 참조)-애플리케이션 건전성에 대한 불확실성이 있습니다. 일반적인 관행은 Forever 및 PM2와 같은 '다시 시작'도구를 사용하여 신중하게 프로세스를 다시 시작하는 것을 제안합니다.

그렇지 않은 경우 : 익숙하지 않은 예외가 포착되면 일부 개체가 결함 상태 (예 : 전역 적으로 사용되고 일부 내부 오류로 인해 더 이상 이벤트를 발생하지 않는 이벤트 이미 터)에있을 수 있으며 향후 모든 요청이 실패하거나 미친 듯이 동작 할 수 있습니다.

코드 예제-충돌 여부 결정

//deciding whether to crash when an uncaught exception arrives
//Assuming developers mark known operational errors with error.isOperational=true, read best practice #3
process.on('uncaughtException', function(error) {
 errorManagement.handler.handleError(error);
 if(!errorManagement.handler.isTrustedError(error))
 process.exit(1)
});


//centralized error handler encapsulates error-handling related logic 
function errorHandler(){
 this.handleError = function (error) {
 return logger.logError(err).then(sendMailToAdminIfCritical).then(saveInOpsQueueIfCritical).then(determineIfOperationalError);
 }

 this.isTrustedError = function(error)
 {
 return error.isOperational;
 }

블로그 인용문 : "오류 처리에 대한 생각에는 세 가지 학교가 있습니다"(블로그 jsrecipes에서)

… 오류 처리에 대한 생각은 주로 세 가지입니다. 1. 응용 프로그램을 중단하고 다시 시작합니다. 2. 가능한 모든 오류를 처리하고 충돌하지 마십시오. 3. 둘 사이의 균형 잡힌 접근


Number7 : 성숙한 로거를 사용하여 오류 가시성을 높입니다.

요약 : Winston, Bunyan 또는 Log4J와 같은 성숙한 로깅 도구 세트는 오류 발견 및 이해를 가속화합니다. 그러니 console.log는 잊어 버리세요.

그렇지 않으면 : console.logs를 훑어 보거나 쿼리 도구 나 적절한 로그 뷰어없이 지저분한 텍스트 파일을 수동으로 훑어 보면 늦게까지 업무에 바쁠 수 있습니다.

코드 예제-작동중인 Winston logger

//your centralized logger object
var logger = new winston.Logger({
 level: 'info',
 transports: [
 new (winston.transports.Console)(),
 new (winston.transports.File)({ filename: 'somefile.log' })
 ]
 });

//custom code somewhere using the logger
logger.log('info', 'Test Log Message with some parameter %s', 'some parameter', { anything: 'This is metadata' });

블로그 인용문 : "로거에 대한 몇 가지 요구 사항을 식별 할 수 있습니다."(블로그 strongblog에서)

… (로거에 대한) 몇 가지 요구 사항을 식별 할 수 있습니다. 1. 각 로그 라인에 타임 스탬프를 찍습니다. 이것은 매우 자명합니다. 각 로그 항목이 언제 발생했는지 알 수 있어야합니다. 2. 로깅 형식은 기계뿐만 아니라 사람도 쉽게 소화 할 수 있어야합니다. 3. 여러 구성 가능한 대상 스트림을 허용합니다. 예를 들어, 하나의 파일에 추적 로그를 쓰고 있지만 오류가 발생하면 동일한 파일에 기록한 다음 오류 파일에 기록하고 동시에 이메일을 보낼 수 있습니다.


Number8 : APM 제품을 사용하여 오류 및 다운 타임 발견

요약 : 모니터링 및 성능 제품 (일명 APM)은 코드베이스 또는 API를 사전에 측정하여 누락 된 오류, 충돌 및 느린 부분을 자동으로 강조 표시 할 수 있습니다.

그렇지 않으면 : API 성능과 다운 타임을 측정하는 데 많은 노력을 기울일 수 있습니다 . 실제 시나리오에서 가장 느린 코드 부분이 무엇인지, 그리고 이것이 UX에 어떤 영향을 미치는지 결코 알지 못할 것입니다.

블로그 인용 : "APM 제품 세그먼트"(블로그 출처 : Yoni Goldberg)

"… APM 제품은 3 개의 주요 세그먼트로 구성됩니다. 1. 웹 사이트 또는 API 모니터링 – HTTP 요청을 통해 가동 시간과 성능을 지속적으로 모니터링하는 외부 서비스. 몇 분 안에 설정할 수 있습니다. 다음은 Pingdom, Uptime Robot, New Relic 2 등 몇 가지 경쟁 업체입니다. . 코드 계측 – 느린 코드 감지, 예외 통계, 성능 모니터링 등의 기능을 활용하기 위해 애플리케이션 내에 에이전트를 포함해야하는 제품군입니다. 다음은 몇 가지 선택된 경쟁자입니다 : New Relic, App Dynamics 3. 운영 인텔리전스 대시 보드 –이러한 제품 라인은 애플리케이션 성능을 쉽게 파악하는 데 도움이되는 메트릭과 선별 된 콘텐츠로 운영 팀을 촉진하는 데 중점을 둡니다. 여기에는 일반적으로 여러 정보 소스 (애플리케이션 로그, DB 로그, 서버 로그 등) 및 선행 대시 보드 설계 작업을 집계하는 작업이 포함됩니다. 다음은 선택된 경쟁자입니다 : Datadog, Splunk "


위는 축약 된 버전입니다. 여기에서 더 많은 모범 사례 및 예제를 참조하십시오.


잡히지 않은 예외를 잡을 수 있지만 사용이 제한됩니다. 참조 http://debuggable.com/posts/node-js-dealing-with-uncaught-exceptions:4c933d54-1428-443c-928d-4e1ecbdd56cb를

monit, forever또는 upstart노드 프로세스가 충돌 할 때 다시 시작하는 데 사용할 수 있습니다. 정상적인 종료가 가장 좋습니다 (예 : 포착되지 않은 예외 처리기에 모든 메모리 내 데이터 저장).


nodejs 도메인nodejs 에서 오류를 처리하는 가장 최신 방법입니다. 도메인은 오류 / 기타 이벤트는 물론 전통적으로 던진 개체를 모두 캡처 할 수 있습니다. 도메인은 또한 intercept 메소드를 통해 첫 번째 인수로 전달 된 오류가있는 콜백을 처리하는 기능을 제공합니다.

일반적인 try / catch 스타일 오류 처리와 마찬가지로 일반적으로 오류가 발생하면 오류를 발생시키고 나머지 코드에 영향을주지 않도록 오류를 격리하려는 영역을 차단하는 것이 가장 좋습니다. 이러한 영역을 "차단"하는 방법은 격리 된 코드 블록으로 함수를 사용하여 domain.run을 호출하는 것입니다.

동기 코드에서는 위의 내용으로 충분합니다. 오류가 발생하면이를 통과 시키거나이를 잡아서 처리하여 되 돌리는 데 필요한 데이터를 되돌립니다.

try {  
  //something
} catch(e) {
  // handle data reversion
  // probably log too
}

비동기 콜백에서 오류가 발생하면 데이터 롤백 (공유 상태, 데이터베이스와 같은 외부 데이터 등)을 완전히 처리 할 수 ​​있어야합니다. 또는 예외가 발생했음을 나타 내기 위해 무언가를 설정해야합니다. 해당 플래그에 관심이있는 곳에서는 콜백이 완료 될 때까지 기다려야합니다.

var err = null;
var d = require('domain').create();
d.on('error', function(e) {
  err = e;
  // any additional error handling
}
d.run(function() { Fiber(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(err != null) {
    // handle data reversion
    // probably log too
  }

})});

위의 코드 중 일부는 추악하지만 스스로 패턴을 만들어 더 예쁘게 만들 수 있습니다. 예 :

var specialDomain = specialDomain(function() {
  // do stuff
  var future = somethingAsynchronous();
  // more stuff

  future.wait(); // here we care about the error
  if(specialDomain.error()) {
    // handle data reversion
    // probably log too
  } 
}, function() { // "catch"
  // any additional error handling
});

업데이트 (2013-09) :

위에서 나는 미래를 인라인으로 기다릴 수 있도록 섬유 의미론 을 암시하는 미래를 사용합니다 . 이것은 실제로 모든 것에 대해 전통적인 try-catch 블록을 사용할 수있게합니다 -제가 가장 좋은 방법이라고 생각합니다. 그러나 항상이 작업을 수행 할 수있는 것은 아닙니다 (예 : 브라우저에서) ...

광섬유 의미론이 필요하지 않은 미래도 있습니다 (그러면 일반 브라우저 JavaScript에서 작동 함). 이것들은 선물, 약속 또는 지연이라고 부를 수 있습니다 (여기서부터는 선물 만 언급하겠습니다). 평범한 자바 스크립트 퓨처 라이브러리를 사용하면 퓨처간에 오류를 전파 할 수 있습니다. 이러한 라이브러리 중 일부만 던져진 미래를 올바르게 처리 할 수 ​​있도록 허용하므로주의하십시오.

예 :

returnsAFuture().then(function() {
  console.log('1')
  return doSomething() // also returns a future

}).then(function() {
  console.log('2')
  throw Error("oops an error was thrown")

}).then(function() {
  console.log('3')

}).catch(function(exception) {
  console.log('handler')
  // handle the exception
}).done()

이것은 조각이 비동기 적이지만 정상적인 try-catch를 모방합니다. 다음과 같이 인쇄됩니다.

1
2
handler

해당 흐름을 방해하는 예외가 발생했기 때문에 '3'이 인쇄되지 않습니다.

블루 버드 약속을 살펴보십시오.

던져진 예외를 적절히 처리하는 라이브러리 외에는 많은 다른 라이브러리를 찾지 못했습니다. 예를 들어 jQuery는 지연되지 않습니다. "fail"핸들러는 예외가 발생하지 않습니다. 제 생각에는 거래 차단기 인 'then'핸들러에서 예외가 발생합니다.


나는 최근 http://snmaynard.com/2012/12/21/node-error-handling/ 에서 이에 대해 썼습니다 . 버전 0.8에서 node의 새로운 기능은 도메인이며 모든 형태의 오류 처리를 하나의 더 쉬운 관리 양식으로 결합 할 수 있습니다. 내 게시물에서 읽을 수 있습니다.

또한 Bugsnag 와 같은 것을 사용 하여 포착되지 않은 예외를 추적하고 이메일, 채팅방을 통해 알림을 받거나 포착되지 않은 예외에 대한 티켓을 만들 수 있습니다 (저는 Bugsnag의 공동 설립자입니다).


Step.js 라이브러리 를 추가 하면 항상 다음 단계 함수에 전달하여 예외를 처리하는 데 도움이됩니다. 따라서 이전 단계에서 오류를 확인하는 기능을 마지막 단계로 가질 수 있습니다. 이 접근 방식은 오류 처리를 크게 단순화 할 수 있습니다.

아래는 github 페이지의 인용문입니다.

throw 된 모든 예외는 포착되어 다음 함수의 첫 번째 인수로 전달됩니다. 콜백 함수를 기본 함수에 중첩하지 않는 한 포착되지 않은 예외가 발생하지 않도록 방지합니다. 포착되지 않은 단일 예외가 전체 서버를 다운시킬 수 있기 때문에 이것은 장기 실행 node.JS 서버에 매우 중요합니다.

또한 단계를 사용하여 스크립트 실행을 제어하여 마지막 단계로 정리 섹션을 가질 수 있습니다. 예를 들어 Node에 빌드 스크립트를 작성하고 작성하는 데 걸린 시간을보고하려는 경우 마지막 단계에서이를 수행 할 수 있습니다 (마지막 콜백을 파헤 치려고 시도하는 대신).


try-catch를 사용하는 것이 적절할 수있는 한 가지 예는 forEach 루프를 사용할 때입니다. 동기식이지만 동시에 내부 범위에서 return 문을 사용할 수 없습니다. 대신 try and catch 접근 방식을 사용하여 적절한 범위에서 Error 개체를 반환 할 수 있습니다. 중히 여기다:

function processArray() {
    try { 
       [1, 2, 3].forEach(function() { throw new Error('exception'); }); 
    } catch (e) { 
       return e; 
    }
}

위의 @balupton에서 설명한 접근 방식의 조합입니다.


얼마 전에이 게시물을 읽은 후 API / 기능 수준에서 예외 처리를 위해 도메인을 사용하는 것이 안전한지 궁금했습니다. 필자가 작성한 각 비동기 함수에서 예외 처리 코드를 단순화하는 데 사용하고 싶었습니다. 내 우려는 각 기능에 새 도메인을 사용하면 상당한 오버 헤드가 발생한다는 것입니다. 내 숙제는 최소한의 오버 헤드가 있고 어떤 상황에서 try catch보다 도메인에서 성능이 실제로 더 좋다는 것을 나타내는 것 같습니다.

http://www.lighthouselogic.com/#/using-a-new-domain-for-each-async-function-in-node/


오류 포착은 여기에서 매우 잘 설명되었지만 오류를보고 수정할 수 있도록 어딘가에 오류를 로그 아웃하는 것을 기억하는 것이 좋습니다.

Bunyan은 NodeJS 용으로 널리 사용되는 로깅 프레임 워크입니다. console.log를 피하는 한 로컬 디버깅에 유용한 여러 출력 위치에 쓰기를 지원합니다. 도메인의 오류 처리기에서 오류를 로그 파일로 내보낼 수 있습니다.

var log = bunyan.createLogger({
  name: 'myapp',
  streams: [
    {
      level: 'error',
      path: '/var/tmp/myapp-error.log'  // log ERROR to this file
    }
  ]
});

검사 할 오류 및 / 또는 서버가 많으면 시간이 많이 걸릴 수 있으므로 오류를 함께 그룹화하거나 둘 다 함께 사용하려면 Raygun (면책 조항, 저는 Raygun에서 일함)과 같은 도구를 살펴 보는 것이 좋습니다. Raygun을 도구로 사용하기로 결정한 경우 설정도 매우 쉽습니다.

var raygunClient = new raygun.Client().init({ apiKey: 'your API key' });
raygunClient.send(theError);

PM2 또는 영원히와 같은 도구를 사용하면 앱이 충돌하고 발생한 일을 로그 아웃하고 큰 문제없이 재부팅 할 수 있어야합니다.


Ubuntu (Upstart)에서 서비스를 사용하려는 경우 : upstart, monit 및 forever.js를 사용하여 Ubuntu 11.04의 Node as a service

참고 URL : https://stackoverflow.com/questions/7310521/node-js-best-practice-exception-handling

반응형