Sentry-javascript: Google Cloud-Funktionen – Sentry-Client einrichten

Erstellt am 18. Juni 2019  ·  35Kommentare  ·  Quelle: getsentry/sentry-javascript

Hallo Team, liebe Sentry, danke!

Ich verwende Google Cloud Functions mit Typescript für ein neues Projekt und würde gerne Sentry verwenden, um Fehler zu erfassen.

Aus diesem Kommentar geht hervor, dass Cloud Functions Drittanbieter nicht in den globalen Fehlerhandler von Node einklinken lässt.

Ist das noch korrekt? Kann ich die Sentry JS-Bibliothek mit Google Cloud Functions zum Laufen bringen?

Verwandte: https://forum.sentry.io/t/google-cloud-functions-client-setup/1970

Alle 35 Kommentare

Hey danke! :)

Ist das noch korrekt? Kann ich die Sentry JS-Bibliothek mit Google Cloud Functions zum Laufen bringen?

Gemäß https://github.com/googleapis/nodejs-error-reporting#catcher -and-reporting-application-wide-uncaught-errors und https://github.com/googleapis/nodejs-error-reporting#unhandled - Ablehnungen ist jetzt möglich, habe ich aber noch nicht selbst getestet.

Ich kann das in ein paar Tagen tun, aber du kannst es ausprobieren, wenn du etwas Freizeit hast :)

Danke @kamilogorek. Interessant!

Ich bin mir nicht sicher, wie das mit Google Cloud Functions interagiert. Ich bin gerade mitten in der technischen Planung, komme also nicht so schnell zum Testen. Aber ich werde euch wissen lassen, ob/wann ich es tue. :)

Im Moment verpacken wir nur alle unsere Cloud-Funktionen in:

try () {
...
} catch (e) {
  Sentry.captureException(e);
}

Aber wir sehen weder den Quellcode noch anderen Kontext in Sentry.

Gibt es einen besseren Weg, diese Integration zu bewerkstelligen?

@vpontis hat es gerade selbst getestet und die Kontextauflösung scheint gut zu funktionieren. Was ist Ihre Funktion/Konfiguration?

image

Hallo @kamilogorek, danke für die Hilfe!

Ich habe das, was Sie mit Hello World bekommen, repliziert, also ist das großartig.

Aber es gibt keine Kontextvariablen. Sie können den Wert der Variablen im Gültigkeitsbereich nicht sehen? Oder ist das nur ein Python-Feature?

image


Außerdem sieht es so aus, als ob die komplizierteren Fehler, die ich erhalte, auftreten, wenn ich Schreib-/Lesezugriffe auf die Datenbank mache und bei einem Stream/Ereignis ein Fehler auftritt.

Zum Beispiel kann ich im Screenshot unten nicht einmal sagen, von welcher Funktion es aufgerufen wurde.

Was ist Ihre Empfehlung? Gibt es eine Möglichkeit, einen längeren StackTrace zu erhalten, der den Aufruf des ursprünglichen Funktionshandlers enthält? Oder muss ich einfach mehr Semmelbrösel hinzufügen?

image


Haben Sie auch eine Idee, was mit diesen nicht gefundenen Fehlern im Quellcode los ist?

image

Aber es gibt keine Kontextvariablen. Sie können den Wert der Variablen im Gültigkeitsbereich nicht sehen? Oder ist das nur ein Python-Feature?

Es ist nur eine Python-Funktion. JS bietet leider keinen Mechanismus zur Implementierung dieser Funktion.

Zum Beispiel kann ich im Screenshot unten nicht einmal sagen, von welcher Funktion es aufgerufen wurde. Was ist Ihre Empfehlung? Gibt es eine Möglichkeit, einen längeren StackTrace zu erhalten, der den Aufruf des ursprünglichen Funktionshandlers enthält? Oder muss ich einfach mehr Semmelbrösel hinzufügen?

Es gibt keine Möglichkeit zu sagen, wie diese spezielle Funktion aufgerufen wurde, da sie von einem Socket ausgelöst wurde. Und wie Sie bereits erwähnt haben, können Sie dies am besten verfolgen, indem Sie Breadcrumbs verwenden, wenn Sie DB-Abfragen, Proxy-Aufrufe oder externe Dienstanfragen durchführen.

Und da serverlose Instanzen langlebig sind, können Sie Folgendes aufrufen:

Sentry.configureScope(scope => scope.clear())
// or
Sentry.configureScope(scope => scope.clearBreadcrumbs())

um eine saubere Weste zu bekommen.

Haben Sie auch eine Idee, was mit diesen nicht gefundenen Fehlern im Quellcode los ist?

Deaktivieren Sie Enable JavaScript source fetching in Ihren Projekteinstellungen, z. https://sentry.io/settings/kamil-ogorek/projects/testing-project/
Es ist eine serverlose Node-App, also macht es keinen Sinn, das zu tun.

Es ist nur eine Python-Funktion. JS bietet leider keinen Mechanismus zur Implementierung dieser Funktion.

Verdammt. Ich liebte das an Python!

Super hilfreich, danke Kamil. Ich werde diese Änderungen implementieren und mich hier bei Ihnen melden, wenn ich weitere Fragen habe. Ich bin sicher, dass dieses Problem für andere Leute hilfreich sein wird, die Sentry auf JS Serverless verwenden.

Fantastisch! Ich werde das Problem aus Triagegründen schließen, aber Sie können mich jederzeit anpingen, und ich werde es bei Bedarf wieder öffnen! :)

@kamilogorek Ich habe Breadcrumbs zu Google Cloud Functions hinzugefügt und glaube, ich sehe Breadcrumbs von verschiedenen Funktionsaufrufen.

Ist das sinnvoll? Wie kann ich die Breadcrumbs auf nur eine HTTP-Anfrage mit Google Cloud-Funktionen beschränken?

Es macht Sinn, aber können Sie mir Beispielcode zeigen, den Sie in Ihren Cloud-Funktionen verwenden? Auf diese Weise wäre es einfacher, alles zu verstehen.

@kamilogorek du warst wirklich hilfreich!

Wir sind auf die Verwendung von Koa mit Docker / Node umgestiegen. Also haben wir die Koa-Integration so eingerichtet: https://docs.sentry.io/platforms/node/koa/

Es funktioniert ziemlich gut, mit Ausnahme von Breadcrumbs, wir sehen Breadcrumbs für alle Anfragen auf dem Server, nicht nur Breadcrumbs, die an die aktuelle Anfrage gebunden sind.

Gibt es eine Möglichkeit, das zu beheben?

Gibt es eine Möglichkeit, das zu beheben?

Es gibt, aber ich muss es testen, bevor ich den Code hier bereitstelle :)
Ich werde versuchen, mich in ca. 1-2 Tagen bei Ihnen zu melden.

@kamilogorek du bist eine absolute legende mein freund

@vpontis , also ist das Einzige, was Sie wirklich tun müssen, eine Domäneninstanz in einer Ihrer Middlewares zu erstellen. Sobald es dort ist, wird SDK es erkennen und verwenden, um die Kontexte zu trennen. Sie können auch parseRequest dorthin verschieben. (Beispiel basierend auf https://github.com/koajs/examples/blob/master/errors/app.js )

const Sentry = require("@sentry/node");
const Koa = require("koa");
const app = (module.exports = new Koa());
const domain = require("domain");

Sentry.init({
  // ...
});

app.use(async function(ctx, next) {
  const local = domain.create();
  local.add(ctx);
  local.on("error", next);
  local.run(() => {
    Sentry.configureScope(scope => {
      scope.addEventProcessor(event => Sentry.Handlers.parseRequest(event, ctx.request));
    });
    next();
  });
});

app.use(async function(ctx, next) {
  try {
    await next();
  } catch (err) {
    // some errors will have .status
    // however this is not a guarantee
    ctx.status = err.status || 500;
    ctx.type = "html";
    ctx.body = "<p>Something <em>exploded</em>, please contact Maru.</p>";

    // since we handled this manually we'll
    // want to delegate to the regular app
    // level error handling as well so that
    // centralized still functions correctly.
    ctx.app.emit("error", err, ctx);
  }
});

// response

app.use(async function() {
  throw new Error("boom boom");
});

// error handler

app.on("error", function(err) {
  Sentry.captureException(err);
});

if (!module.parent) app.listen(3000);

parseRequest akzeptiert einige Optionen, die Sie verwenden möchten, um auszuwählen, welche Anforderungsdaten Sie extrahieren möchten - https://github.com/getsentry/sentry-javascript/blob/f71c17426c7053d46fe3e2e35e77c564749d0eb7/packages/node/src/handlers .ts#L177

Danke @kamilogorek!

Einige Gedanken:

  1. Sie können die aktuellen Knoten req und res an die Domain binden, die auf ctx.req und `ctx.res gespeichert sind

  2. Sie können den Knoten req bis parseRequest übergeben

  3. Warum rufen Sie ctx.app.emit("error", err, ctx); , nachdem Sie den Fehler manuell abgefangen haben?

    // since we handled this manually we'll
    // want to delegate to the regular app
    // level error handling as well so that
    // centralized still functions correctly.
    ctx.app.emit("error", err, ctx);

Wäre das nicht ein Fall, in dem Sie nur die Antwort zurückgeben und den Fehler nicht an den zentralen Fehlerbehandler weitergeben möchten?

  1. In Koa sind next async . Wird dies zu Problemen innerhalb eines Knotens domain.run(...) führen

Lassen Sie mich wissen, ob das Sinn macht. Das ist schon super, super hilfreich. Ich werde es später diese Woche testen.

Ah zu 3. Ich sehe, Sie kopieren das nur aus dem Beispiel, also werde ich diesen Teil ignorieren :). Jetzt testen...

Hmm, ich habe Probleme, das zum Laufen zu bringen. Ich denke, es liegt an der Vermischung von Domain-Callbacks und async -Funktionen in Koa.

Außerdem sieht es so aus, als würden Domänen in Node 12 nicht einmal funktionieren (ich plane ein baldiges Upgrade, um asynchrone Stack-Trace-Unterstützung zu erhalten).

Wie auch immer, ich verstehe nicht, wie dieser Code mit der Domäne funktioniert, daher zögere ich, ihn in einen entscheidenden Teil der App einzufügen.

Gibt es eine andere Möglichkeit, den aktuellen "Hub" auf ctx zu setzen und addBreadcrumb aufzurufen, die irgendwie mit der aktuellen Anfrage verbunden sind? Ich bin mir nicht ganz sicher, wie Hubs und Nachrichtenbehandlung in Sentry funktionieren ...

Ich habe diesen Code zum Laufen gebracht (das sind zwei aufeinanderfolgende Middleware-Funktionen), aber ich fühle mich nicht wohl mit _warum_ es funktioniert ...

export const setSentryDomain = async (ctx, next) => {
  await new Promise(async (resolve, reject) => {
    const local = domain.create();

    local.add(ctx.req);
    local.add(ctx.res);

    local.run(async () => {
      Sentry.configureScope((scope) => {
        scope.addEventProcessor((event) => Sentry.Handlers.parseRequest(event, ctx.req));
      });

      await next();
      resolve();
    });
  });
};

export const catchErrors = async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    console.log('got error');
    ctx.app.emit('error', err, ctx);
  }
};

Ich rufe es eine Nacht an. Ich habe keine funktionierende Lösung bekommen, mit der ich zufrieden war.

Mir ist auch aufgefallen, dass die meisten meiner Breadcrumbs aus console.log -Anweisungen stammen, die automatisch von Sentry erfasst werden. Also muss ich herausfinden, wie ich diese Breadcrumbs in den richtigen Bereich bringe ...

Ich habe den Code und die Domains durchstöbert, es klingt, als würde es dieses Problem lösen. Aber Domänen mit asynchronen Funktionen scheinen nicht zuverlässig zu sein und sie verschwinden bald ...

Gibt es eine andere Möglichkeit, den aktuellen "Hub" auf ctx zu setzen und addBreadcrumb aufzurufen, die irgendwie mit der aktuellen Anfrage verbunden ist?

Sie können den Hub direkt dem ctx zuweisen, dies funktioniert jedoch nicht für automatisch erfasste Breadcrumbs, da diese Sentry.getCurrentHub() benötigen, um den Hub zurückzugeben, dem der Breadcrumb zugewiesen werden sollte. Und eine der Möglichkeiten, es zu erkennen, ist die Verwendung von domain.active .

Aber Domänen mit asynchronen Funktionen scheinen nicht zuverlässig zu sein und sie verschwinden bald ...

Leider verschwinden sie seit Dezember 2014 , ohne dass ein klarer Ersatz in Sicht ist (es gibt asynchrone Hooks, aber sie sind auch nicht perfekt), und sie wurden in diesen 5 Jahren nicht aus dem Kern des Knotens entfernt.

Wir spielen jetzt mit zone.js herum, da es enorm helfen würde, aber es ist im Moment immer noch ein riesiger PoC und kann nicht sagen, wann oder ob wir es jemals verwenden werden, um Domains zu ersetzen.

Hallo @kamilogorek ,

Ich versuche, nicht behandelte Fehler in Sentry zu erfassen, und habe ausprobiert, was Sie in https://github.com/getsentry/sentry-javascript/issues/2122#issuecomment -503440087 erwähnt haben

Aber ich denke, Google lässt Sie nicht mit process.on('uncaughtException') , ich konnte mit diesem Ansatz keine Fehler bei Sentry protokollieren.

Gibt es eine andere Möglichkeit, die Sie zum Ausprobieren empfehlen würden, scheint es nicht der ideale Weg zu sein, jeden Funktionskörper in einen Try-Catch-Block zu packen.

Wir bieten eine wrap -Methode an, aber ich glaube nicht, dass sie viel besser ist als try/catch tbh.

exports.helloBackground = (data, context) => {
  return `Hello ${data.name || 'World'}!`;
};

// becomes

exports.helloBackground = (data, context) => {
  return Sentry.wrap(() => {
    return `Hello ${data.name || 'World'}!`;
  })
};

Es scheint mir, dass es sich lohnen könnte, dieses Problem als Funktionsanforderung offen zu halten, um Firebase-/Google-Cloud-Funktionen auf einfachere Weise zu unterstützen.

Ich war unglaublich beeindruckt von der einfachen Einrichtung auf der Clientseite und enttäuscht, als mir klar wurde, dass die Einrichtung auf der Serverseite viel komplexer sein würde. Gibt es Pläne, die Erfahrung auf der GCP (insbesondere Funktionen) zu verbessern?

@goleary Würde es Ihnen etwas ausmachen, eine neue Ausgabe zu eröffnen, in der genau beschrieben wird, was Sie sehen möchten?
Dieser Thread ist schon ziemlich groß und schwer zu verfolgen.

Danke

Gibt es immer noch keine einfachere Möglichkeit, dies einzurichten? Idealerweise einmal global einstellen können, ohne es für jede Funktion einstellen zu müssen?

@marcospgp nein, und das wird es leider nicht geben, da Google selbst keinen Mechanismus bereitstellt, der dies zulässt. Siehe ihren eigenen Reporter - https://cloud.google.com/error-reporting/docs/setup/nodejs , der auch manuelle Aufrufe verwendet.

Hmm interessant, ich habe Sentry auf Firebase-Cloud-Funktionen eingerichtet (die hinter den Kulissen Google-Cloud-Funktionen verwenden) und ich habe gerade einen Fehlerbericht erhalten - es scheint also zu funktionieren!

Hier ist meine index.js, in der sich der gesamte Sentry-Code befindet:

const admin = require("firebase-admin");
const functions = require("firebase-functions");
const Sentry = require("@sentry/node");

/**
 * Set up Sentry for error reporting
 */
if (functions.config().sentry && functions.config().sentry.dsn) {
  Sentry.init({ dsn: functions.config().sentry.dsn });
} else {
  console.warn(
    "/!\\ sentry.dsn environment variable not found. Skipping setting up Sentry..."
  );
}

admin.initializeApp();

const { function1 } = require("./cloud-functions/function1");
const { function2 } = require("./cloud-functions/function2");

module.exports = {
  function1,
  function2
};

@marcospgp war das obige index.js in der Lage, nicht erfasste Ausnahmen innerhalb ./cloud-functions/function1 und ./cloud-functions/function2 an Sentry zu senden, oder mussten Sie sich innerhalb dieser Dateien aktiv bei Sentry anmelden?

Ich habe gerade die @marcospgp- Lösung ausprobiert, aber es scheint keine nicht erfassten Ausnahmen zu protokollieren, er muss sie manuell protokollieren (mit sentry.captureException() ?).

Genau wie bei @goleary hier wird nichts protokolliert.

Außerdem scheint .wrap() nicht mehr verfügbar zu sein.

Bedeutet das, dass wir den gesamten Code manuell in try/catches einschließen sollten?

@ Dinduks das mache ich. Der Overhead ist ein wenig nervig, aber die Verwendung von Sentry ist den Aufwand über die Protokollierung der Firebase-Cloud-Funktion wert (nicht großartig).

@Dinduks wrap ist noch da, siehe: https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/exports.ts#L40
Beachten Sie jedoch, dass es die Funktion sofort ausführt und Ihnen den Rückgabewert zurückgibt. Ich bin mir also nicht sicher, ob das hilfreicher ist als normales Try/Catch, mit dem Sie auch einige Fallback-Aktionen für Benutzer ausführen können.

const myHandler = (req, res) => Sentry.wrap(() => {
  someFunctionThatCanBreak(req);
  return res.send(200);
});

Ich denke nicht, dass es eine gute Idee ist, aber ich habe einen Wrapper erstellt, der so aussieht.

import * as Sentry from "@sentry/node";

export const sentryWrapper = (f) => {
  return async function () {
    try {
      // eslint-disable-next-line prefer-rest-params
      return await f.apply(this, arguments);
    } catch (e) {
      Sentry.captureException(e);
      await Sentry.flush(2000);
      throw new Error(e);
    }
  };
};

Es wird wie folgt verwendet.

export const getChannelVideoTest = functions.https.onRequest(
  sentryWrapper(async (req, res) => {
    someFunctionThatCanBreak(req);
    return res.send(200);
  }),
);

Ich würde gerne wissen, ob es einen besseren Weg gibt.

@kamilogorek
Damit habe ich auch zu kämpfen.

Ich habe erfolgreich Sentry.init() und ich habe meine Probleme, wenn ich meinen gesamten Code in eine try {} catch {} Anweisung und einen manuellen Aufruf von Sentry.captureException und Sentry.flush() .
Ich kann jedoch nichts gemeldet bekommen, wenn ich die try/catch -Anweisung entferne.
Gleiches gilt für die Leistungsüberwachung, bei der ich nichts erhalte, es sei denn, ich erstelle manuell eine Transaktion mit Sentry.startTransaction() am Anfang der Funktion.

Wird dies erwartet?
Gibt es eine Möglichkeit, unbehandelte Fehler an Sentry zu senden?
Wenn nicht, bedeutet dies, dass die Registerkarte Leistung die Fehlerrate immer auf 0 % setzt? (Da wir den Fehler abfangen und manuell melden, wird die Transaktion ordnungsgemäß geschlossen, hat also einen OK-Status ?)

@axelvaindal Wir unterstützen noch keine Leistungsüberwachung für Serverless. Zu dieser Frage:

Gibt es eine Möglichkeit, unbehandelte Fehler an Sentry zu senden?

Dann nein, nicht wirklich, da GCF keine Möglichkeit bietet, sich in unbehandelte Ausnahmen/Zurückweisungen einzuklinken, also können wir das nicht abfangen. Sie müssen Ihre Handler umwickeln (siehe Kommentar über Ihrem), um sie manuell abzufangen.

Sie können auch unsere AWSLambda-Handler-Implementierung lesen, um einige Ideen zur Verbesserung dieses Snippets zu erhalten – https://github.com/getsentry/sentry-javascript/blob/master/packages/serverless/src/awslambda.ts

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen