Pular para o conteúdo principal
Para descobrir quais trechos específicos de código estão causando problemas de desempenho, é útil adicionar instrumentação personalizada à sua aplicação. Isso nos permite criar breakdowns melhores sobre quais códigos rodam mais devagar e em que tipo de ação foi gasta a maior parte do tempo. A instrumentação personalizada é possível de duas formas: usando function decorators e funções helper de instrumentação. Os function decorators são mais fáceis de usar, mas são menos flexíveis do que as funções helper de instrumentação. Este guia curto ajudará você a configurar a instrumentação personalizada. Mais detalhes sobre o uso de certos helpers podem ser encontrados nos Hex docs do pacote AppSignal.
Nota: Certifique-se de ter integrado o AppSignal antes de adicionar instrumentação personalizada à sua aplicação se ela não estiver integrada automaticamente por uma das nossas integrações suportadas.
Nota: Esta página descreve apenas como adicionar instrumentação de desempenho ao seu código. Para rastrear erros, leia nosso guia de tratamento de exceções.

Appsignal.instrument/2-3

A função instrument/2 é usada para adicionar instrumentação envolvendo um trecho de código em um span. Um span eventualmente se torna uma amostra, ou um evento na amostra de outro span no AppSignal.

Adicionar spans a traces

No exemplo a seguir, temos um controller Phoenix com uma função index/2 que chama uma função lenta. A função slow é instrumentada usando a função Appsignal.instrument/2, que a registra como um evento separado nessa requisição Phoenix. Ela aparecerá no AppSignal.com na timeline de eventos desta amostra para fornecer mais insight sobre onde a maior parte do tempo foi gasta durante a requisição.
defmodule AppsignalPhoenixExampleWeb.PageController do
  use AppsignalPhoenixExampleWeb, :controller

  def index(conn, _params) do
    slow()
    render(conn, "index.html")
  end

  defp slow do
    Appsignal.instrument("slow", fn ->
      :timer.sleep(1000)
    end)
  end
end
Aqui, envolvemos o conteúdo da nossa função com uma chamada a Appsignal.instrument3, e passamos "slow" como o nome do evento.

Iniciando novos traces

No exemplo do Phoenix, um trace do AppSignal já foi iniciado, graças ao suporte de primeira classe ao Phoenix no pacote AppSignal. Nem todos os frameworks e pacotes são suportados diretamente no momento e iniciam automaticamente traces. O mesmo vale para suas próprias aplicações Elixir puras. Como ao adicionar spans a traces já instrumentados, um novo root span é criado usando a função Appsignal.instrument/2:
defmodule AppsignalElixirExample do
  def example_fn do
    Appsignal.instrument("instrument", fn ->
      :timer.sleep(500)
    end)
  end
end
Este exemplo cria uma amostra chamada “instrument” no namespace “background” no AppSignal. O nome também será usado como o nome da categoria para o main span. Para usar um nome de categoria diferente, use instrument/3 em vez disso:
defmodule AppsignalElixirExample do
  def example_fn do
    Appsignal.instrument("instrument", "call.instrument", fn ->
      :timer.sleep(500)
    end)
  end
end
Ao passar uma função que recebe um argumento, a função instrument/2-3 a chama com o span aberto para permitir mais personalização. Veja os docs em hex.pm para todas as funções de Span disponíveis.
defmodule AppsignalElixirExample do
  def example_fn do
    Appsignal.instrument("Build complicated SQL query", "prepare_query.sql", fn span ->
      # Exemplo de construção de uma consulta SQL complexa dentro do bloco instrument e sua execução
      query = "SOME complicate SQL query"
      # O body é definido aqui apenas porque é o valor sendo calculado e instrumentado nesta função
      Appsignal.Span.set_sql(span, query)
      Database.perform_query(query)
    end)
  end
end

Tratamento de exceções

Para reportar erros usando instrumentação personalizada, leia mais em nosso guia de tratamento de exceções.

Function decorators

Usando o módulo de decorator Appsignal.Instrumentation.Decorators, é possível adicionar instrumentação personalizada às suas aplicações Elixir sem alterar o conteúdo das funções.

Eventos de transação

No exemplo a seguir, temos um controller Phoenix com uma função index/2 que chama uma função lenta. A função slow é instrumentada usando o decorator transaction_event do AppSignal, que a registra como um evento separado nesta requisição Phoenix. Ela aparecerá no AppSignal.com na timeline de eventos desta amostra de transação para fornecer mais insight sobre onde a maior parte do tempo foi gasta durante a requisição.
# Exemplo de controller Phoenix
defmodule PhoenixExample.PageController do
  use PhoenixExample.Web, :controller
  # Inclua isto
  use Appsignal.Instrumentation.Decorators

  def index(conn, _params) do
    slow()
    render conn, "index.html"
  end

  # Decore esta função para adicionar instrumentação personalizada
  @decorate transaction_event()
  defp slow do
    :timer.sleep(1000)
  end
end
Se você quiser agrupar certos eventos sob o mesmo grupo de eventos (outros grupos são phoenix_controller, phoenix_render, ecto, etc.), você também pode fornecer um nome de grupo ao decorator transaction_event.
@decorate transaction_event("github_api")
defp get_data_from_github do
  # Chamada de API de terceiros
end
Isso criará um evento get_data_from_github.github_api na timeline de eventos. Para mais informações sobre como os nomes de eventos são usados, leia nossas diretrizes de nomenclatura de eventos.

Transações

No exemplo do Phoenix, uma transação AppSignal já foi iniciada, graças ao suporte de primeira classe ao Phoenix no pacote AppSignal. Nem todos os frameworks e pacotes são suportados diretamente no momento e iniciam automaticamente transações. O mesmo vale para suas próprias aplicações Elixir puras. Para rastrear decorators transaction_event, precisaremos iniciar uma transação AppSignal previamente. Podemos iniciar uma transação com o function decorator transaction.
# Exemplo em Elixir puro
defmodule FunctionDecoratorsExample do
  # Inclua isto
  use Appsignal.Instrumentation.Decorators

  # Nenhuma transação é iniciada previamente como no Phoenix, então precisamos iniciá-la
  # nós mesmos.
  @decorate transaction()
  def call do
    slow()
    # ...
  end

  # Decore esta função para adicionar instrumentação personalizada
  @decorate transaction_event()
  defp slow do
    :timer.sleep(1000)
  end
end
Nota: Ao usar aplicações Elixir puras, certifique-se de que a aplicação AppSignal foi iniciada antes de iniciar uma transação. Para mais informações, veja como integrar o AppSignal.

Namespaces

Para diferenciar entre requisições HTTP e background jobs, podemos passar um namespace para a transação assim que iniciá-la. Os dois namespaces a seguir são namespaces oficiais suportados pelo AppSignal.
  • http_request - o padrão - é chamado de namespace “web”
  • background_job - cria o namespace “background”
defmodule FunctionDecoratorsExample do
  # Inclua isto
  use Appsignal.Instrumentation.Decorators

  # Sem argumento de namespace, o padrão é `:http_request`
  @decorate transaction()
  def web_function do
    # faça coisas
  end

  # O namespace "background"
  @decorate transaction(:background_job)
  def background_function do
    # faça coisas
  end
end
Para mais informações sobre o que são namespaces, veja nossa documentação de namespaces.

Namespaces personalizados

Você também pode criar seus próprios namespaces para rastrear transações em uma parte separada da sua aplicação, como um painel administrativo. Isso agrupará todas as transações com esse namespace em uma seção separada no AppSignal.com para que controllers admin lentos não interfiram nas médias de velocidade da sua aplicação.
@decorate transaction(:admin)
def some_function do
  # faça coisas
end

Phoenix channels

Há um function decorator personalizado para Phoenix channels. Este decorator deve ser colocado antes da função handle_in/3 de um módulo Phoenix.Channel.
defmodule FunctionDecoratorsExample.MyChannel do
  # Inclua isto
  use Appsignal.Instrumentation.Decorators

  # Adicione este function decorator de channel
  @decorate channel_action()
  def handle_in("ping", _payload, socket) do
    # seu código aqui..
  end
end
Eventos de channels serão exibidos sob o namespace “background”, mostrando o módulo do channel e o argumento de ação no qual ele é usado.

Criando e fechando spans manualmente

Em alguns casos, pode ser útil lidar com spans manualmente. Você pode usar a API de spans para instrumentar tasks, por exemplo:
def index(conn, _params) do
  current = Appsignal.Tracer.current_span()
  fn ->
    span = "http_request"
    |> Appsignal.Tracer.create_span(current)
    |> Appsignal.Span.set_name("name")
    |> Appsignal.Span.set_attribute("appsignal:category", "category")
    :timer.sleep(1000)
    Appsignal.Tracer.close_span(span)
  end
  |> Task.async()
  |> Task.await()
  render(conn, "index.html")
end