Node.js 모범 사례 예외 처리


755

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

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

node.js에서 포착되지 않은 예외를 처리하는 일반적인 모범 사례를 보여주는 포인터 / 기사를 고맙게 생각합니다.


11
포착되지 않은 예외는 발생하지 않아야합니다. 그들이 충돌 할 때 전체 응용 프로그램을 다시 시작하는 프로그램을 사용하는 경우 (nodemon, forever, supervisor)
Raynos

116
비동기 코드의 모든 부분을 ​​내부에 넣지 않고 모든 라이브러리에try .. catch 대해이 작업이 수행 되는지
Dan

13
+1 Dan 처음에는 모든 라이브러리 가 약간 과장된 것으로 생각했습니다. "만"모든 "스레드 진입 점"을 코드의 try / catches로 래핑해야하기 때문입니다. 하지만 더 신중하게 생각하고, 어떤 LIB가있을 수 setTimeout또는 setInterval코드에 의해 체포 할 수없는 어딘가에 깊이 묻혀 종류 또는 무언가를.
Eugene Beresovsky

8
@EugeneBeresovksy Dan은 옳지 만 uncaughtExceptions가 발생할 때 유일한 안전한 옵션은 앱을 다시 시작하는 것입니다. 즉, 앱이 다운되어 앱에 대해 수행 할 수있는 작업이 없습니다. 건설적인 작업을 수행하려는 경우 새로운 실험적인 v0.8 도메인 기능을 구현하여 충돌을 기록하고 클라이언트에 5xx 응답을 보낼 수 있습니다.
ostergaard

1
@Dan try .. catch에 모든 콜백 함수를 포함하더라도 catch 오류를 보장하지는 않습니다. 필요한 모듈이 자체 바이너리를 사용하는 경우 비정상적으로 충돌 할 수 있습니다. 나는 phantomjs-node에서 이런 일이 발생했으며 잡을 수없는 오류에 실패했습니다 (필요한 바이너리에 대해 일종의 프로세스 검사를 수행하지는 않았지만 결코 추구하지는 않았습니다).
Trindaz

답변:


737

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

안전하게 "throwing"오류

문자 그대로 오류를 발생시키는 대신 가능한 한 잡히지 않은 오류를 피하는 것이 이상적입니다. 대신 코드 아키텍처에 따라 다음 방법 중 하나를 사용하여 오류를 안전하게 "throw"할 수 있습니다.

  • 동기 코드의 경우 오류가 발생하면 오류를 리턴하십시오.

    // 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 문에 래핑되지 않은 장소에서 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

5
감사합니다 Raynos. 당신은의 악을 설명하는 소스가 try catch있습니까? 증거로 백업하고 싶습니다. 동기화 예제도 수정했습니다.
balupton

2
이 답변은 더 이상 유효하지 않습니다. 도메인은이 문제를 해결합니다 (node.js 권장)
Gabriel Llamas 2013

5
@balupton 오류 처리를 위해 오류가 발생해야합니다. 반드시 피해야합니다. 앱이나 다른 것들의 실행을 중단시키는 것은 없습니다. Java 및 대부분의 다른 현대 언어는 예외를 완벽하게 지원합니다. 여기에서 일부 흉내 낸 게시물을 읽은 후의 유일한 결론은 사람들이 잘 이해하지 못하고 두려워한다는 것입니다. 불확실한 의심을 두려워하십시오. 이 논쟁은 적어도 20 년 전에 예외에 찬성하여 결정적으로 결정되었습니다.
enl8enmentnow

22
이제 도메인은 io.js에서 더 이상 사용되지 않습니다 . " 이 모듈은 더 이상 사용되지 않습니다. 대체 API가 완료되면이 모듈은 완전히 사용되지 않습니다 ... 도메인에서 제공하는 기능을 절대적으로 사용해야하는 사용자는 당분간 도메인에 의존 할 수 있습니다. 앞으로 다른 솔루션으로 마이그레이션해야합니다. "
Timothy Gu

5
도메인 API는 이제 사용되지 않습니다 ? 그들은 대체 API를 언급합니다. 누군가 언제 나올지, 그리고 어떻게 보일지 아십니까?
UpTheCreek

95

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


Node.JS 오류 처리 우수 사례


Number1 : 비동기 오류 처리에 약속 사용

TL; DR : 콜백 스타일에서 비동기 오류를 처리하는 것이 아마도 가장 빠른 방법 일 것입니다 (일명 운명의 피라미드). 코드에 줄 수있는 최고의 선물은 try-catch와 같이 매우 작고 친숙한 코드 구문을 제공하는 평판 좋은 약속 라이브러리를 사용하는 것입니다.

그렇지 않은 경우 : 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){ 
                    ...
                });
            });
        });
    });
});

블로그 인용구 : "약속에 문제가 있습니다." (블로그 pouchdb에서 키워드 "노드 약속"으로 11 위)

"… 그리고 실제로 콜백은 훨씬 더 사악한 일을합니다. 스택은 우리가 프로그래밍 언어에서 당연한 것으로 간주하는 스택을 박탈합니다. 스택없이 코드를 작성하는 것은 브레이크 페달없이 자동차를 운전하는 것과 매우 흡사합니다. . 당신이 그것을 도달하고 거기 때까지 당신이 그것을 필요로하는지 심하게 모르고 약속의 요점은 우리가 비동기 갔을 때 우리가 잃어버린 언어 기본 백업 제공하는 것입니다. 반환, 던져, 스택을하지만 약속을 활용하려면 약속을 올바르게 사용하는 방법을 알아야합니다. "


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

TL; DR : 오류를 문자열 또는 사용자 정의 유형으로 발생시키는 코드를 보는 것이 일반적입니다 . 이는 오류 처리 논리와 모듈 간의 상호 운용성을 복잡하게합니다. 약속 거부, 예외 발생 또는 오류 발생 여부 – Node.JS 내장 오류 개체를 사용하면 균일 성이 향상되고 오류 정보가 손실되지 않습니다.

그렇지 않은 경우 : 일부 모듈을 실행할 때 어떤 유형의 오류가 반환되는지 확실하지 않으면 향후 예외에 대해 추론하고 처리하기가 훨씬 어렵습니다. 심지어 가치를 정의하기 위해 사용자 정의 유형을 사용하면 스택 추적과 같은 중요한 오류 정보가 손실 될 수 있습니다!

코드 예-올바르게 수행

    //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 번 : 운영 오류와 프로그래머 오류 구별

TL : DR : 작업 오류 (예 : 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 인 블로그)

" ... JavaScript에서 throw가 작동하는 방식의 본질 상, 참조를 유출하거나 정의되지 않은 다른 취성 상태를 만들지 않고 안전하게"중지 된 곳을 "찾을 수있는 방법은 거의 없습니다. 던진 오류 프로세스를 종료하는 것입니다 . 물론, 정상적인 웹 서버에서, 당신은 많은 연결이 열려있을 수 있습니다, 그리고 오류가 다른 사람에 의해 촉발 되었기 때문에 갑자기 그 아래를 종료하는 것이 합리적이 아니다. 더 나은 접근 방법이다 "다른 사람들이 정상적인 시간을 끝내도록하고 그 작업자의 새 요청을 듣지 못하게하고 오류를 발생시킨 요청에 오류 응답을 보냅니다."


Number4 : 미들웨어를 통하지 않고 중앙에서 오류를 처리

TL; DR : 관리 대상 메일 및 로깅과 같은 오류 처리 논리는 오류가 발생할 때 모든 엔드 포인트 (예 : Express 미들웨어, 크론 작업, 단위 테스트)가 호출하는 중앙 집중식 전용 개체에 캡슐화되어야합니다.

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

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

//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 오류 처리")

"… 여러 레벨의 스택에서 동일한 오류를 처리하게 될 수 있습니다. 이는 하위 수준이 오류를 호출자에게 전파하여 오류를 호출자에게 전파하는 것 외에는 유용한 작업을 수행 할 수 없을 때 발생합니다. 작업을 다시 시도하거나 사용자에게 오류를보고하는 등의 방법으로 최상위 응답자 만 적절한 응답을 알고 있지만 모든 오류를 단일 최상위 수준으로보고하려고하는 것은 아닙니다. 콜백 자체는 어떤 컨텍스트에서 오류가 발생했는지 알 수 없기 때문에 "


Number5 : Swagger를 사용하여 문서 API 오류

TL; DR : API 호출자에게 오류가 발생할 수있는 오류를 알려주므로 충돌없이 오류를 신중하게 처리 할 수 ​​있습니다. 일반적으로 Swagger와 같은 REST API 문서 프레임 워크를 사용하여 수행됩니다.

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

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

… 오류를 처리하는 방법에 대해 이야기했지만 새 함수를 작성할 때 함수를 호출 한 코드에 오류를 어떻게 전달합니까? … 어떤 오류가 발생할 수 있는지 또는 무슨 뜻인지 모르는 경우 우연히 프로그램을 수정할 수 없습니다. 따라서 새로운 함수를 작성하는 경우 호출자에게 어떤 오류가 발생할 수 있고 어떤 조치를 취해야하는지 알려야합니다.


6 번 : 낯선 사람이 마을에 올 때 프로세스를 정상적으로 종료하십시오

TL; DR : 알 수없는 오류가 발생하면 (개발자 오류, 모범 사례 번호 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 : 성숙한 로거를 사용하여 오류 가시성 향상

TL; DR : Winston, Bunyan 또는 Log4J와 같은 성숙한 로깅 도구 세트는 오류 발견 및 이해 속도를 높입니다. 따라서 console.log는 잊어 버리십시오.

그렇지 않은 경우 : console.logs를 통해 감추거나 쿼리 도구 나 적절한 로그 뷰어를 사용하지 않고 지저분한 텍스트 파일을 통해 수동으로 늦게까지 업무에 바쁠 수 있습니다

코드 예-작동중인 Winston 로거

//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 제품을 사용한 오류 및 다운 타임 발견

TL; DR : 모니터링 및 성능 제품 (일명 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 "


위의 단축 버전입니다- 더 많은 모범 사례 및 예제를 참조하십시오.


30

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

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


4
+1이 링크는 도움이됩니다. 나는 여전히 node.js의 맥락에서 모범 사례와 "우아한 재시작"의 의미를 찾고 있습니다.
momo

이 문맥에서 "우아한 재시작"에 대한 나의 이해는 본질적으로 nponeccop가 제안하는 것입니다. 프로세스를 죽이고, 처음부터 실행중인 모든 프로세스를 재시작하십시오.
Ilkka

그 링크에 감사드립니다! 정말 유용합니다!
SatheeshJM

이것은 좋은 대답입니다. 그러나 첫 번째 예제에서 오류를 반환하는 것에 동의하지 않습니다. 를 반환하면 Error반환 값을 다형성으로 만들어 함수의 의미를 불필요하게 방해합니다. 또한, 0 다이빙하여 이미함으로써 자바 스크립트 처리 Infinity, -Infinity또는 NaN, 값 어디에 typeof === 'number'. 로 확인할 수 있습니다 !isFinite(value). 일반적으로 함수에서 오류를 반환하지 않는 것이 좋습니다. 일관된 의미로 특수한 비다 형성 값을 던지거나 반환하는 코드 가독성 및 유지 관리 측면에서 더 좋습니다.
wprl

링크가 끊어졌습니다. downforeveryoneorjustme.com/debuggable.com
Kev

13

nodejs 도메인nodejs 에서 오류를 처리하는 가장 최신 방법입니다. 도메인은 전통적으로 발생 된 객체뿐만 아니라 오류 / 기타 이벤트를 모두 캡처 할 수 있습니다. 도메인은 인터셉트 메소드를 통해 첫 번째 인수로 전달 된 오류로 콜백을 처리하는 기능도 제공합니다.

일반적인 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'을 인쇄하지 않습니다.

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

throw 된 예외를 올바르게 처리하는 라이브러리 이외의 많은 라이브러리를 찾지 못했습니다. 예를 들어, jQuery는 연기하지 않습니다. "실패"핸들러는 'then'핸들러에서 예외를 얻지 못합니다.


Javascript의 올바른 약속 사양을 Promises / A +라고합니다. github.com/promises-aplus/promises-spec/blob/master/… 에서 구현 목록을 볼 수 있습니다 . 베어 Promises / A +는 실제로 사용할 수 없습니다. Promises / A +는 여전히 라이브러리가 스스로 해결할 수있는 많은 실질적인 문제를 남깁니다. 그러나 오류 전파, 결정적 실행 순서 및 스택 오버플로로부터의 안전성과 같은 필수 요소가 보장됩니다.
Esailija


11

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

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


2
도메인 모듈은 공식적으로 더 이상 사용되지 않습니다. nodejs.org/api/domain.html
MattSidor

3

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

아래는 github 페이지에서 인용 한 것입니다.

발생 된 예외는 포착되어 다음 함수에 대한 첫 번째 인수로 전달됩니다. 콜백 함수를 기본 함수에 인라인하지 않으면 예외가 발생하지 않습니다. 한 번의 포착되지 않은 예외로 인해 전체 서버가 다운 될 수 있으므로 JS 서버는 장기 실행 노드에 매우 중요합니다.

또한 단계를 사용하여 스크립트 실행을 제어하여 마지막 단계와 같이 정리 섹션을 가질 수 있습니다. 예를 들어 노드에 빌드 스크립트를 작성하고 작성하는 데 걸린 시간을보고하려는 경우 마지막 단계는 마지막 콜백을 발굴하지 않고 수행 할 수 있습니다.


3

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에서 설명한 접근 방식의 조합입니다.


오류가 발생하는 대신, 일부 개발자는 Rust의 결과 개념을 사용하여 실패가 알려진 가능성이있는 경우 OK 또는 Fail 을 리턴 할 것을 권장합니다 . 이렇게하면 실패가 예기치 않은 오류와 분리됩니다. 이것의 하나의 JS 구현은 r-result 입니다.
joeytwiddle

앱 전체의 디자인 결정입니다. 오류를 반환하는 개념은 대체로 동일하고 시작하기 간단하지만 (추가 종속성 없음) 덜 명시 적입니다 ( 결과 는 실패를 처리해야 할 때 고통스럽게 알 수 있습니다). 불필요하게 건축되었습니다.
joeytwiddle

1

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

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


1

여기에서는 오류 잡기가 매우 잘 설명되어 있지만 오류를보고 문제를 해결할 수 있도록 오류를 어딘가에 로그 아웃하는 것이 좋습니다.

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와 같은 도구를 사용하거나 영원히 사용하면 앱에서 충돌이 발생하고 로그 아웃 한 후 큰 문제없이 재부팅 할 수 있습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.