Sentry-javascript: Funções do Google Cloud - Configuração do Sentry Client

Criado em 18 jun. 2019  ·  35Comentários  ·  Fonte: getsentry/sentry-javascript

Oi equipe, amo o Sentinela, obrigado!

Estou usando o Google Cloud Functions com Typescript para um novo projeto e adoraria usar o Sentry para capturar erros.

A partir deste comentário , parece que o Cloud Functions não permite que terceiros se conectem ao manipulador de erros global do Node.

Isso ainda é preciso? Posso fazer com que a biblioteca Sentry JS funcione com o Google Cloud Functions?

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

Todos 35 comentários

Ei, obrigado! :)

Isso ainda é preciso? Posso fazer com que a biblioteca Sentry JS funcione com o Google Cloud Functions?

De acordo com https://github.com/googleapis/nodejs-error-reporting#agging -and-reporting-application-wide-uncaught-errors e https://github.com/googleapis/nodejs-error-reporting#unhandled - rejeições é possível fazê-lo agora, no entanto, eu ainda não testei.

Posso fazer isso em alguns dias, mas você pode dar uma olhada se tiver algum tempo livre :)

Obrigado @kamilogorek. Interessante!

Não tenho certeza de como isso interage com o Google Cloud Functions. Estou no meio do planejamento técnico agora, então não vou testar isso em breve. Mas eu vou deixar você saber se / quando eu fizer. :)

No momento, estamos apenas agrupando todas as nossas Cloud Functions em:

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

Mas não estamos vendo o código-fonte ou outro contexto no Sentry.

Existe uma maneira melhor de fazer essa integração?

@vpontis acabei de testar eu mesmo e a resolução de contexto parece funcionar bem. Qual é a sua função/configuração?

image

Oi @kamilogorek obrigado por ajudar!

Eu repliquei o que você obtém com o Hello World, então isso é ótimo.

Mas não há variáveis ​​de contexto. Como você não pode ver o valor das variáveis ​​no escopo? Ou isso é apenas um recurso do Python?

image


Além disso, parece que os erros mais complicados que estou recebendo são quando estou fazendo gravações/leituras no banco de dados e há algum erro em um fluxo/evento.

Por exemplo, na captura de tela abaixo, não consigo nem dizer de qual função foi chamada.

Qual é a sua recomendação? Existe uma maneira de obter um StackTrace mais longo que inclua a chamada do manipulador de função de origem? Ou só preciso adicionar mais farinha de rosca?

image


Além disso, alguma idéia do que está acontecendo com esses erros de código-fonte não encontrado?

image

Mas não há variáveis ​​de contexto. Como você não pode ver o valor das variáveis ​​no escopo? Ou isso é apenas um recurso do Python?

É um recurso apenas do Python. JS não fornece um mecanismo para implementar esse recurso, infelizmente.

Por exemplo, na captura de tela abaixo, não consigo nem dizer de qual função foi chamada. Qual é a sua recomendação? Existe uma maneira de obter um StackTrace mais longo que inclua a chamada do manipulador de função de origem? Ou só preciso adicionar mais farinha de rosca?

Não há como dizer o que chamou essa função específica, pois foi acionada por um soquete. E como você mencionou, a melhor maneira de rastrear isso é usar breadcrumbs ao realizar consultas de banco de dados, chamadas de proxy ou solicitações de serviço externo.

E como as instâncias sem servidor têm vida longa, você pode chamar:

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

para obter uma ficha limpa.

Além disso, alguma idéia do que está acontecendo com esses erros de código-fonte não encontrado?

Desative Enable JavaScript source fetching nas configurações de seus projetos, por exemplo. https://sentry.io/settings/kamil-ogorek/projects/testing-project/
É um aplicativo de nó sem servidor, então não faz sentido fazer isso.

É um recurso apenas do Python. JS não fornece um mecanismo para implementar esse recurso, infelizmente.

Droga. Eu amei isso sobre Python!

Super útil, obrigado Kamil. Implementarei essas alterações e entrarei em contato aqui se tiver mais dúvidas. Tenho certeza que este problema será útil para outras pessoas que usam o Sentry em JS sem servidor.

Impressionante! Fecharei o problema para fins de triagem, mas sinta-se à vontade para me enviar um ping a qualquer momento e reabri-lo se necessário! :)

@kamilogorek Adicionei Breadcrumbs no Google Cloud Functions e acho que estou vendo breadcrumbs de diferentes chamadas de função.

Isso faz sentido? Como posso limitar os breadcrumbs a serem apenas de uma solicitação HTTP com o Google Cloud Functions?

Faz sentido, mas você pode me mostrar o código de exemplo que você usa em suas funções de nuvem? Assim fica mais fácil entender tudo.

@kamilogorek você foi muito útil!

Mudamos para usar o Koa com Docker/Node. Então temos a integração Koa configurada assim: https://docs.sentry.io/platforms/node/koa/

Está funcionando muito bem, exceto para breadcrumbs, estamos vendo breadcrumbs para todas as solicitações no servidor, não apenas breadcrumbs vinculados à solicitação atual.

Existe uma maneira de corrigir isso?

Existe uma maneira de corrigir isso?

Existe, mas preciso testá-lo antes de fornecer o código aqui :)
Vou tentar voltar para você em ~ 1-2 dias.

@kamilogorek você é uma lenda absoluta meu amigo

@vpontis então a única coisa que você realmente precisa fazer é criar uma instância de domínio em um de seus middlewares. Uma vez lá, o SDK irá detectá-lo e usá-lo para separar os contextos. Você também pode mover parseRequest para lá. (exemplo baseado no 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 aceita algumas opções que você pode usar para escolher quais dados de solicitação você deseja extrair - https://github.com/getsentry/sentry-javascript/blob/f71c17426c7053d46fe3e2e35e77c564749d0eb7/packages/node/src/handlers .ts#L177

Obrigado @kamilogorek!

Alguns pensamentos:

  1. Você pode vincular o nó real req e res ao domínio que está armazenado em ctx.req e `ctx.res

  2. Você pode passar no Node req para parseRequest

  3. Por que você chama ctx.app.emit("error", err, ctx); depois de pegar o erro manualmente?

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

Não seria um caso em que você deseja apenas retornar a resposta e não passar o erro para o manipulador de erros centralizado?

  1. Em Koa next é async . Isso causará algum problema dentro de um nó domain.run(...)

Deixe-me saber se isso faz sentido. Isso já é super, super útil. Vou testar ainda esta semana.

Ah re 3, vejo que você está apenas copiando isso do exemplo, então vou ignorar essa parte :). Testando agora...

Hmm, estou tendo problemas para fazer isso funcionar. Eu acho que é devido à mistura de retornos de chamada de domínio e funções async no Koa.

Além disso, parece que os domínios nem funcionam no nó 12 (estou planejando atualizar em breve para obter suporte a rastreamento de pilha assíncrono).

De qualquer forma, não entendo como esse código com domínio funciona, então hesito em colocá-lo em uma parte crucial do aplicativo.

Existe outra maneira de colocar o "hub" atual em ctx e chamar addBreadcrumb alguma forma associado à solicitação atual? Não tenho certeza de como os hubs e o tratamento de mensagens funcionam no Sentry ...

Eu tenho esse código funcionando (estas são duas funções de middleware sucessivas), mas não me sinto confortável com _por que_ ele 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);
  }
};

Estou chamando isso de noite. Não obtive uma solução funcional que me agradasse.

Eu também percebi que a maioria das minhas migalhas de pão são de instruções console.log que são automaticamente capturadas pelo Sentry. Então eu preciso descobrir como colocar essas migalhas de pão no escopo certo ...

Eu vasculhei o código e os domínios parecem resolver esse problema. Mas os domínios não parecem confiáveis ​​com funções assíncronas e vão desaparecer em breve...

Existe outra maneira de colocar o "hub" atual no ctx e chamar addBreadcrumb de alguma forma associado à solicitação atual?

Você pode atribuir o hub diretamente ao ctx , mas não funcionará para breadcrumbs capturados automaticamente, pois eles exigem Sentry.getCurrentHub() para retornar o hub, o breadcrumb deve ser atribuído. E uma das maneiras de detectá-lo é usando domain.active .

Mas os domínios não parecem confiáveis ​​com funções assíncronas e vão desaparecer em breve...

Infelizmente, eles estão desaparecendo desde dezembro de 2014 , sem substituição clara (há ganchos assíncronos, mas também não são perfeitos) à vista, assim como não foram removidos do núcleo do nó nesses 5 anos.

Estamos brincando com zone.js agora, pois isso ajudaria tremendamente, mas ainda é um PoC enorme por enquanto e não podemos dizer quando ou mesmo se vamos usá-lo para substituir domínios.

Olá @kamilogorek ,

Estou tentando capturar erros não tratados no Sentry e tentei o que você mencionou em https://github.com/getsentry/sentry-javascript/issues/2122#issuecomment -503440087

Mas acho que o google não permite que você se conecte ao process.on('uncaughtException') , não consegui registrar nenhum erro no Sentry usando essa abordagem.

Existe alguma outra maneira que você recomendaria experimentar, envolver cada corpo de função em um bloco try catch não parece ser o caminho ideal a seguir.

Nós fornecemos um método wrap , mas não acho muito melhor do que tentar/pegar tbh.

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

// becomes

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

Parece-me que vale a pena manter esse problema em aberto como uma solicitação de recurso para oferecer suporte às funções do firebase/google cloud de maneira mais simples.

Fiquei incrivelmente impressionado com a facilidade de configuração no lado do cliente e decepcionado quando percebi que configurar o lado do servidor seria muito mais complexo. Existe algum plano para melhorar a experiência no GCP (funções especificamente)?

@goleary Você se importaria de abrir uma nova edição descrevendo exatamente o que você gostaria de ver?
Este tópico já é bastante grande e difícil de seguir.

Obrigado

Ainda não existe uma maneira mais simples de configurar isso? Idealmente, poder configurá-lo uma vez globalmente, sem precisar configurá-lo para todas as funções?

@marcospgp não, e não haverá, infelizmente, pois o próprio Google não fornece um mecanismo que permita isso. Veja o próprio repórter - https://cloud.google.com/error-reporting/docs/setup/nodejs ele também usa chamadas manuais.

Hmm interessante, eu configurei as funções de nuvem do Sentry on Firebase (que usa funções de nuvem do google nos bastidores) e acabei de receber um relatório de erros - então parece funcionar!

Aqui está meu index.js, onde está todo o código do 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 foi o index.js acima capaz de enviar exceções não capturadas dentro de ./cloud-functions/function1 e ./cloud-functions/function2 para o Sentry, ou você teve que logar ativamente no Sentry dentro desses arquivos?

Acabei de tentar a solução @marcospgp , mas não parece estar registrando exceções não capturadas, ele deve estar registrando-as manualmente (usando sentry.captureException() ?)

O mesmo que @goleary aqui, nada é registrado.

Também parece que .wrap() não está mais disponível.

Isso significa que devemos encapsular manualmente todo o código dentro de try/catch?

@Dinduks é isso que estou fazendo. A sobrecarga é um pouco irritante, mas ser capaz de usar o sentry vale o esforço em relação ao registro da função de nuvem do Firebase (não é ótimo).

@Dinduks wrap ainda está lá, veja: https://github.com/getsentry/sentry-javascript/blob/master/packages/browser/src/exports.ts#L40
No entanto, lembre-se de que ele executa a função imediatamente e retorna o valor de retorno. Portanto, não tenho certeza se isso é mais útil do que o try/catch regular, que permite que você também faça alguma ação de fallback do usuário.

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

Eu não acho que seja uma boa ideia, mas eu criei um wrapper que se parece com isso.

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

Será usado da seguinte forma.

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

Gostaria de saber se existe uma maneira melhor.

@kamilogorek
Estou lutando com isso também.

Consegui Sentry.init() com sucesso e tenho meus problemas quando envolvo todo o meu código em uma instrução try {} catch {} e uma chamada manual para Sentry.captureException e Sentry.flush() .
No entanto, não consigo obter nada relatado se eu remover a instrução try/catch .
O mesmo vale para o monitoramento de desempenho, onde não estou recebendo nada, a menos que eu crie manualmente uma transação com Sentry.startTransaction() no início da função.

Isso é esperado?
Existe alguma maneira de enviar erros não tratados para o Sentry?
Caso contrário, isso significa que a guia de desempenho sempre definirá a taxa de falha em 0%? (porque pegamos o erro e informamos manualmente, a transação está sendo fechada corretamente, portanto tem um status ok?)

@axelvaindal ainda não damos suporte ao monitoramento de desempenho para serverless. Quanto a esta pergunta:

Existe alguma maneira de enviar erros não tratados para o Sentry?

Então não, não realmente, já que o GCF não fornece uma maneira de se conectar a exceções/rejeições não tratadas, então não podemos interceptar isso. Você precisa envolver seus manipuladores (veja o comentário acima do seu) para capturá-lo manualmente.

Você também pode ler nossa implementação do manipulador AWSLambda para obter algumas ideias de como melhorar esse trecho - https://github.com/getsentry/sentry-javascript/blob/master/packages/serverless/src/awslambda.ts

Esta página foi útil?
0 / 5 - 0 avaliações