Sentry-javascript: Funciones de Google Cloud: configuración del cliente Sentry

Creado en 18 jun. 2019  ·  35Comentarios  ·  Fuente: getsentry/sentry-javascript

Hola equipo, amor Sentry, ¡gracias!

Estoy usando Google Cloud Functions con Typescript para un nuevo proyecto y me encantaría usar Sentry para capturar errores.

A partir de este comentario , parece que Cloud Functions no permite que terceros se conecten al controlador de errores global de Node.

¿Sigue siendo exacto? ¿Puedo hacer que la biblioteca Sentry JS funcione con Google Cloud Functions?

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

Todos 35 comentarios

¡Hey gracias! :)

¿Sigue siendo exacto? ¿Puedo hacer que la biblioteca Sentry JS funcione con Google Cloud Functions?

De acuerdo con https://github.com/googleapis/nodejs-error-reporting# catch -and-reporting-application-wide-uncaught-errors y https://github.com/googleapis/nodejs-error-reporting#unhandled - rechazos es posible hacerlo ahora, sin embargo, aún no lo he probado yo mismo.

Puedo hacerlo en unos días, pero puedes darle una vuelta si tienes algo de tiempo libre :)

Gracias @kamilogorek. ¡Interesante!

No estoy seguro de cómo interactúa con Google Cloud Functions. Estoy en medio de la planificación técnica ahora, así que no voy a probar eso pronto. Pero te dejaré saber si / cuando lo haga. :)

En este momento, solo estamos envolviendo todas nuestras funciones de la nube en:

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

Pero no estamos viendo el código fuente u otro contexto en Sentry.

¿Hay una mejor manera de hacer esta integración?

@vpontis lo probé yo mismo y la resolución de contexto parece funcionar bien. ¿Cuál es su función/configuración?

image

Hola @kamilogorek gracias por ayudar!

He replicado lo que obtienes con Hello World, así que eso es genial.

Pero no hay variables de contexto. ¿Como si no pudieras ver el valor de las variables en el alcance? ¿O es solo una característica de Python?

image


Además, parece que los errores más complicados que obtengo son cuando hago escrituras/lecturas en la base de datos y hay algún error en una secuencia/evento.

Por ejemplo, en la captura de pantalla a continuación, ni siquiera puedo decir desde qué función se llamó.

¿Cuál es tu recomendación? ¿Hay alguna forma de obtener un StackTrace más largo que incluya la llamada del controlador de la función de origen? ¿O solo necesito agregar más pan rallado?

image


Además, ¿alguna idea de lo que está pasando con estos errores de código fuente no encontrado?

image

Pero no hay variables de contexto. ¿Como si no pudieras ver el valor de las variables en el alcance? ¿O es solo una característica de Python?

Es una característica única de Python. Lamentablemente, JS no proporciona un mecanismo para implementar esta función.

Por ejemplo, en la captura de pantalla a continuación, ni siquiera puedo decir desde qué función se llamó. ¿Cuál es tu recomendación? ¿Hay alguna forma de obtener un StackTrace más largo que incluya la llamada del controlador de la función de origen? ¿O solo necesito agregar más pan rallado?

No hay forma de saber qué llamó a esta función específica, ya que fue activada por un socket. Y como mencionó, la mejor manera de rastrear esto es usar migas de pan cuando realiza consultas de base de datos, llamadas de proxy o solicitudes de servicios externos.

Y debido a que las instancias sin servidor son de larga duración, puede llamar:

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

para hacer borrón y cuenta nueva.

Además, ¿alguna idea de lo que está pasando con estos errores de código fuente no encontrado?

Desactive Enable JavaScript source fetching en la configuración de sus proyectos, p. https://sentry.io/settings/kamil-ogorek/projects/testing-project/
Es una aplicación de nodo sin servidor, por lo que no tiene sentido hacerlo.

Es una característica única de Python. Lamentablemente, JS no proporciona un mecanismo para implementar esta función.

Maldita sea. ¡Me encantó esto de Python!

Súper útil, gracias Kamil. Implementaré esos cambios y me pondré en contacto con usted aquí si tengo más preguntas. Estoy seguro de que este problema será útil para otras personas que usen Sentry en JS sin servidor.

¡Increíble! Cerraré el problema por el bien de la clasificación, pero siéntete libre de enviarme un ping en cualquier momento y lo volveré a abrir si es necesario. :)

@kamilogorek Agregué migas de pan en Google Cloud Functions y creo que estoy viendo migas de pan de diferentes llamadas a funciones.

¿Tiene sentido? ¿Cómo puedo limitar las migas de pan para que sean solo de una solicitud HTTP con Google Cloud Functions?

Tiene sentido, sin embargo, ¿puede mostrarme el código de ejemplo que usa en sus funciones en la nube? De esta manera sería más fácil entender todo.

@kamilogorek , ¡has sido de gran ayuda!

Cambiamos a usar Koa con Docker/Node. Así que tenemos la integración de Koa configurada así: https://docs.sentry.io/platforms/node/koa/

Funciona bastante bien excepto por las migas de pan, estamos viendo migas de pan para todas las solicitudes en el servidor, no solo las migas de pan vinculadas a la solicitud actual.

¿Hay alguna manera de arreglar eso?

¿Hay alguna manera de arreglar eso?

Lo hay, pero necesito probarlo antes de proporcionar el código aquí :)
Intentaré comunicarme con usted en aproximadamente 1 o 2 días.

@kamilogorek eres una leyenda absoluta mi amigo

@vpontis , por lo que lo único que realmente debe hacer es crear una instancia de dominio en uno de sus middlewares. Una vez que esté allí, el SDK lo detectará y lo usará para separar los contextos. También puede mover parseRequest allí. (ejemplo basado en 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 acepta algunas opciones que puede usar para elegir qué datos de solicitud desea extraer: https://github.com/getsentry/sentry-javascript/blob/f71c17426c7053d46fe3e2e35e77c564749d0eb7/packages/node/src/handlers .ts#L177

¡Gracias @kamilogorek!

Algunos pensamientos:

  1. Puede vincular el nodo real req y res al dominio que se almacena en ctx.req y `ctx.res

  2. Puede pasar en el Nodo req a parseRequest

  3. ¿Por qué llama a ctx.app.emit("error", err, ctx); después de detectar manualmente el error?

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

¿No sería ese un caso en el que solo desea devolver la respuesta y no pasar el error al controlador de errores centralizado?

  1. En Koa next es async . ¿Causará eso algún problema dentro de un nodo? domain.run(...)

Déjame saber si eso tiene sentido. Esto ya es súper, súper útil. Lo probaré más tarde esta semana.

Ah re 3, veo que solo estás copiando eso del ejemplo, así que ignoraré esa parte :). Probando ahora...

Hmm, tengo problemas para hacer que esto funcione. Creo que se debe a la combinación de devoluciones de llamada de dominio y funciones async en Koa.

También parece que los dominios ni siquiera funcionan en el Nodo 12 (estoy planeando actualizarme pronto para obtener soporte de seguimiento de pila asíncrona).

De cualquier manera, no entiendo cómo funciona este código con dominio, así que dudo en ponerlo en una parte crucial de la aplicación.

¿Hay otra forma de poner el "concentrador" actual en ctx y llamar a addBreadcrumb asociado de alguna manera con la solicitud actual? No estoy muy seguro de cómo funcionan los concentradores y el manejo de mensajes en Sentry...

Tengo este código funcionando (estas son dos funciones de middleware sucesivas) pero no me siento cómodo con _por qué_ funciona...

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

Lo estoy llamando una noche. No obtuve una solución funcional con la que estuviera contento.

También me di cuenta de que la mayoría de mis migas de pan provienen de declaraciones de console.log que Sentry captura automáticamente. Así que necesito descubrir cómo hacer que estas migas de pan estén en el alcance correcto...

Revisé el código y parece que los dominios resuelven este problema. Pero los dominios no parecen confiables con funciones asíncronas y desaparecerán pronto...

¿Hay otra forma de poner el "concentrador" actual en ctx y llamar a addBreadcrumb asociado de alguna manera con la solicitud actual?

Puede asignar el concentrador directamente al ctx , sin embargo, no funcionará para las migas de pan capturadas automáticamente, ya que requieren Sentry.getCurrentHub() para devolver el concentrador, al que se debe asignar la migas de pan. Y una de las formas en que lo detecta es usando domain.active .

Pero los dominios no parecen confiables con funciones asíncronas y desaparecerán pronto...

Desafortunadamente, van a desaparecer desde diciembre de 2014 , sin un reemplazo claro (hay ganchos asíncronos, pero tampoco son perfectos) a la vista, y tampoco se han eliminado del núcleo del nodo en esos 5 años.

Estamos jugando con zone.js ahora, ya que sería de gran ayuda, pero todavía es un gran PoC por ahora y no podemos decir cuándo o incluso si alguna vez lo usaremos para reemplazar dominios.

Hola @kamilogorek ,

Estoy tratando de capturar errores no manejados en Sentry y probé lo que mencionó en https://github.com/getsentry/sentry-javascript/issues/2122#issuecomment -503440087

Pero supongo que Google no te permite conectarte a process.on('uncaughtException') , no pude registrar ningún error en Sentry usando ese enfoque.

¿Hay alguna otra forma en que recomendaría probar? Envolver cada cuerpo de función en un bloque try catch no parece la forma ideal de hacerlo.

Proporcionamos un método wrap , pero no creo que sea mucho mejor que probar/atrapar tbh.

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

// becomes

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

Me parece que vale la pena mantener abierto este problema como una solicitud de función para admitir las funciones de Firebase/Google Cloud de una manera más simple.

Quedé increíblemente impresionado con la facilidad de configuración en el lado del cliente y me decepcionó cuando me di cuenta de que configurar el lado del servidor sería mucho más complejo. ¿Hay algún plan para mejorar la experiencia en GCP (funciones específicamente)?

@goleary ¿Le importaría abrir un nuevo número que describa exactamente lo que le gustaría ver?
Este hilo ya es bastante grande y difícil de seguir.

Gracias

¿Todavía no hay una forma más sencilla de configurar esto? ¿Lo ideal sería poder configurarlo una vez globalmente, sin tener que configurarlo para cada función?

@marcospgp no, y desafortunadamente no lo habrá, ya que el propio Google no proporciona un mecanismo que lo permita. Vea su propio reportero: https://cloud.google.com/error-reporting/docs/setup/nodejs , también usa llamadas manuales.

Hmm, interesante, configuré Sentry en las funciones de la nube de Firebase (que usa las funciones de la nube de Google detrás de escena) y acabo de recibir un informe de error, ¡así que parece funcionar!

Aquí está mi index.js, donde está todo el código de Sentry:

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 ¿ fue el index.js anterior capaz de enviar excepciones no detectadas dentro ./cloud-functions/function1 y ./cloud-functions/function2 a Sentry, o tuvo que iniciar sesión activamente en Sentry dentro de esos archivos?

Acabo de probar la solución @marcospgp , pero no parece estar registrando excepciones no detectadas, debe estar registrándolas manualmente (usando sentry.captureException() ?)

Igual que @goleary aquí, no se registra nada.

También parece que .wrap() ya no está disponible.

¿Significa que debemos envolver manualmente todo el código dentro de try/catches?

@Dinduks eso es lo que estoy haciendo. La sobrecarga es un poco molesta, pero vale la pena el esfuerzo de usar Sentry en lugar del registro de la función de nube de Firebase (no muy bueno).

@Dinduks wrap todavía está allí, consulte: https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/exports.ts#L40
Sin embargo, tenga en cuenta que ejecuta la función inmediatamente y le devuelve el valor de retorno. Por lo tanto, no estoy seguro de si eso es más útil que el intento/captura normal, que también le permite realizar algunas acciones de reserva del usuario.

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

No creo que sea una buena idea, pero he creado un envoltorio que se ve así.

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

Se utilizará de la siguiente manera.

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

Me gustaría saber si hay una mejor manera.

@kamilogorek
Estoy luchando con esto también.

He logrado Sentry.init() y tengo mis problemas cuando envuelvo todo mi código en una instrucción try {} catch {} y una llamada manual a Sentry.captureException y Sentry.flush() .
Sin embargo, no puedo obtener ningún informe si elimino la instrucción try/catch .
Lo mismo ocurre con la supervisión del rendimiento en la que no obtengo nada a menos que cree manualmente una transacción con Sentry.startTransaction() al comienzo de la función.

¿Es esto esperado?
¿Hay alguna forma de enviar los errores no controlados a Sentry?
Si no, ¿significa esto que la pestaña de rendimiento siempre establecerá la tasa de fallas en 0%? (debido a que detectamos el error y lo informamos manualmente, la transacción se está cerrando correctamente, por lo tanto, ¿tiene un estado correcto?)

@axelvaindal todavía no admitimos la supervisión del rendimiento sin servidor. En cuanto a esta pregunta:

¿Hay alguna forma de enviar los errores no controlados a Sentry?

Entonces no, en realidad no, ya que GCF no proporciona una forma de conectarse con excepciones/rechazos no controlados, por lo que no podemos interceptarlos. Debe envolver sus controladores (vea el comentario sobre el suyo) para poder capturarlo manualmente.

También puede leer nuestra implementación del controlador AWSLambda para obtener algunas ideas sobre cómo mejorar ese fragmento: https://github.com/getsentry/sentry-javascript/blob/master/packages/serverless/src/awslambda.ts

¿Fue útil esta página
0 / 5 - 0 calificaciones