2014-06-06 13 views
6

Sono abituato a pensare in Java e sto cercando di capire come node.js. Il mio programma ha bisogno di registrare le informazioni quando le cose vanno male, e trovo che devo inserire un sacco di codice boilerplate nel mio programma node.js per ottenere ciò che otterrei gratuitamente in Java."Call stack" per i callback in node.js

La mia domanda si riduce a:

  • c'è un/modo più semplice non boilerplate per ottenere informazioni stack-come in una catena di callback? e/o
  • Sono incolpevole di non riuscire a comprendere correttamente node.js, e di provare a forzare node.js asincrono ad essere più simile a Java sincrono?

Java Esempio

Ecco un programma di Noddy Java che cerca (e non) per connettersi a un database Mongo: import java.net.UnknownHostException;

import com.mongodb.Mongo; 

public class Test { 

    public static void main(final String[] args) throws UnknownHostException { 
     final Mongo mongo = a(); 
    } 

    private static Mongo a() throws UnknownHostException { 
     return b(); 
    } 

    private static Mongo b() throws UnknownHostException { 
     return c(); 
    } 

    private static Mongo c() throws UnknownHostException { 
     return new Mongo("non-existent host"); 
    } 

} 

... che dà questo output di stack utile:

Exception in thread "main" java.net.UnknownHostException: non-existent host 
at java.net.Inet6AddressImpl.lookupAllHostAddr(Native Method) 
at java.net.InetAddress$1.lookupAllHostAddr(Unknown Source) 
at java.net.InetAddress.getAddressesFromNameService(Unknown Source) 
at java.net.InetAddress.getAllByName0(Unknown Source) 
at java.net.InetAddress.getAllByName(Unknown Source) 
at java.net.InetAddress.getAllByName(Unknown Source) 
at java.net.InetAddress.getByName(Unknown Source) 
at com.mongodb.ServerAddress.updateInetAddress(ServerAddress.java:204) 
at com.mongodb.ServerAddress.<init>(ServerAddress.java:73) 
at com.mongodb.ServerAddress.<init>(ServerAddress.java:46) 
at com.mongodb.Mongo.<init>(Mongo.java:138) 
at Test.c(Test.java:20) 
at Test.b(Test.java:16) 
at Test.a(Test.java:12) 
at Test.main(Test.java:8) 

(In particolare, le ultime 4 linee Show Me "ciò che stava accadendo" nel mio codice al momento si è verificato l'errore di Mongo .)

Node.js Esempio

Ecco il mio tentativo di ri-scrivere il mio programma nel node.js:

a(function (err, mongo) { 
    if (err) { 
     console.log("Something went wrong in main"); 
     console.log(err); 
    } 
}); 

function a(callback) { 
    b(function (err, mongo) { 
     if (err) { 
      console.log("Something went wrong in a()"); 
      return callback(err); 
     } 

     return callback(null, mongo); 
    }); 
} 

function b(callback) { 
    c(function (err, mongo) { 
     if (err) { 
      console.log("Something went wrong in b()"); 
      return callback(err); 
     } 

     return callback(null, mongo); 
    }); 
} 

function c(callback) { 
    var MongoClient = require('mongodb').MongoClient; 
    return MongoClient.connect('mongodb://non-existent host/', function (err, mongo) { 
     if (err) { 
      console.log("Something went wrong in c()"); 
      return callback(err); 
     } 

     return callback(null, mongo); 
    }); 
} 

... che dà questo output:

Something went wrong in c() 
Something went wrong in b() 
Something went wrong in a() 
Something went wrong in main 
[Error: failed to connect to [non-existent host:27017]] 

Ma per ottenere questo risultato, devo mettere in un sacco di codice standard per tutto il mio programma, che sta per essere un dolore alla polizia come il mio programma diventa più grande e ho un intero team di sviluppo.

Posso ottenere questo output simile allo stack in un altro modo? È improbabile aspettarsi questo tipo di output?

+0

promesse potrebbe essere quello che state cercando; trasmettono il tipico successo e fallimento dei processi (asincroni) in modo coerente. –

risposta

2

promesse sono esattamente quello che stai cercando (riportare le caratteristiche dello stack al codice asincrono)

var Promise = require("bluebird"); 
var mongodb = require("mongodb"); 
// enable long stack traces, bluebird specific 
Promise.longStackTraces(); 
// promisify mongodb so that it returns promises, also bluebird specific 
Promise.promisifyAll(mongodb); 
// raise stack limit, feature of v8/node.js 
Error.stackTraceLimit = 100; 


function c() { 
    var MongoClient = require("mongodb").MongoClient; 
    return MongoClient.connectAsync('mongodb://non-existent host/') 
} 

function b() { 
    return c() 
} 

function a() { 
    return b() 
} 

a().then(function(connection) { 

}); 

dà:

Possibly unhandled Error: failed to connect to [non-existent host:27017] 
    at null.<anonymous> (/home/petka/bluebird/node_modules/mongodb/lib/mongodb/connection/server.js:546:74) 
    at EventEmitter.emit (events.js:106:17) 
    at null.<anonymous> (/home/petka/bluebird/node_modules/mongodb/lib/mongodb/connection/connection_pool.js:150:15) 
    at EventEmitter.emit (events.js:98:17) 
    at Socket.<anonymous> (/home/petka/bluebird/node_modules/mongodb/lib/mongodb/connection/connection.js:533:10) 
    at Socket.EventEmitter.emit (events.js:95:17) 
    at net.js:830:16 
From previous event: 
    at Function.connectAsync (eval at makeNodePromisifiedEval (/home/petka/bluebird/js/main/promisify.js:199:12), <anonymous>:7:21) 
    at c (/home/petka/bluebird/throwaway.js:10:28) 
    at b (/home/petka/bluebird/throwaway.js:14:16) 
    at a (/home/petka/bluebird/throwaway.js:18:16) 
    at Object.<anonymous> (/home/petka/bluebird/throwaway.js:21:5) 
    at Module._compile (module.js:456:26) 
    at Object.Module._extensions..js (module.js:474:10) 
    at Module.load (module.js:356:32) 
    at Function.Module._load (module.js:312:12) 
    at Function.Module.runMain (module.js:497:10) 
    at startup (node.js:119:16) 
    at node.js:902:3 

È possibile utilizzare catch (chiamato così perché funziona come un dichiarazione di cattura reale) in un unico luogo:

a().catch(function(e) { 
     //handle e 
}); 

Anche le funzioni specifiche bluebird aggiunte per la cattura:

catture basa sono supportati anche dal momento che è solo un metodo:

a().catch(SyntaxError, function(e) { 

}); 

predicato può essere un costruttore di errore o di una funzione predicato

// define a predicate for IO errors 
function IOError(e) { 
    return "code" in Object(e); 
} 
+0

Penso che questa funzione "dal precedente evento" della tua libreria sia ciò che dovresti pubblicizzare qui. OP si lamenta di dover scrivere manualmente 'log (" qualcosa è andato storto a ... ")' ovunque, non è lui? – Bergi

+0

@Beri acconsento, rimuoverò la versione del registro – Esailija

+0

Si potrebbe anche voler sottolineare che questa non è una proprietà intrinseca delle promesse, ma una funzionalità di implementazione di alcune librerie. – Bergi

0

Come sull'utilizzo dello stack da l'errore:

function trace(err) 
{ 
    console.log(err); 
    var stack = new Error().stack; 
    console.log(stack); 
} 


a(function (err, mongo) { 
    if (err) 
    trace(err) 
}); 

function a(callback) { 
    b(function (err, mongo) { 
     if (err) 
     return callback(err); 
     else 
     return callback(null, mongo); 
    }); 
} 

function b(callback) { 
    c(function (err, mongo) { 
     if (err) 
     return callback(err); 
     else 
     return callback(null, mongo); 
    }); 
} 

function c(callback) { 
    var MongoClient = require('mongodb').MongoClient; 
    return MongoClient.connect('mongodb://nohost/', function (err, mongo) { 
     if (err) 
     return callback(err); 
     else 
     return callback(null, mongo); 
    }); 
} 

Uscita

[Error: failed to connect to [non-existent host:27017]] 
Error 
    at trace (/myfolder/a.js:4:14) 
    at /myfolder/a.js:11:2 
    at /myfolder/a.js:17:10 
    at /myfolder/a.js:26:10 
    at /myfolder/a.js:36:10 
    at /myfolder/node_modules/mongodb/lib/mongodb/mongo_client.js:406:11 
    at process._tickCallback (node.js:339:11) 

Ora non è dettagliato come quello in Java, non fornisce nomi di funzioni, ma aiuta a identificare la catena di esecuzione.

+0

... a meno che i metodi che si desidera nella traccia dello stack ('a',' b', 'c') utilizzino funzioni di ordine superiore per generare i callback. In tal caso otterresti solo i numeri di riga delle funzioni del generatore. – Bergi

0

Longjohn sembra essere molto popolare ora un giorno per ottenere tracce di stack lunghi in node.js

http://blog.nodejitsu.com/npmawesome-full-stack-traces-with-longjohn/

http://www.mattinsler.com/post/26396305882/announcing-longjohn-long-stack-traces-for-node-js

Ecco un elenco di altre tali moduli che possono essere utili per identificare quello che ti serve

https://nodejsmodules.org/tags/stack

Esempio di codice:

var longjohn = require('longjohn') 
var stackTrace = require('stack-trace'); 
longjohn.empty_frame = 'ASYNC CALLBACKS :'; 
a(); 

function a() { 

    b(); 
}; 

function b() { 
    c(); 
} 

function c() { 

    setTimeout(function() { 

     throw new Error("My Custom Error"); 

    }, 1000); 

} 

process.on('uncaughtException', function(err) { 

    //in JSON format with the help of stack-trace module; 
    console.error(stackTrace.parse(err)); 

    //in plain text format 
    console.error(err.stack.trim()); 

    process.exit(1); 

}); 

uscita in formato testo con Longjohn:

Error: My Custom Error 
    at [object Object].<anonymous> (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:19:9) 
    at listOnTimeout (timers.js:110:15) 
ASYNC CALLBACKS : 
    at c (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:17:2) 
    at b (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:12:2) 
    at a (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:8:2) 
    at Object.<anonymous> (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:4:1) 
    at Module._compile (module.js:456:26) 
    at Module._extensions..js (module.js:474:10) 
    at Module.load (module.js:356:32) 
    at Module._load (module.js:312:12) 

uscita in formato testo normale senza Longjohn:

Error: My Custom Error 
    at null._onTimeout (/Users/nav/Documents/workspace/stack_trace/hello-world-server.njs:19:9) 
    at Timer.listOnTimeout [as ontimeout] (timers.js:110:15) 
Problemi correlati