Zum Hauptinhalt springen
Um herauszufinden, welche bestimmten Codeabschnitte Performance-Probleme verursachen, ist es nützlich, Ihrer Anwendung benutzerdefinierte Instrumentierung hinzuzufügen. Dies ermöglicht es uns, bessere Aufschlüsselungen darüber zu erstellen, welcher Code am langsamsten läuft und auf welchen Aktionstyp die meiste Zeit verwendet wurde. Benutzerdefinierte Instrumentierung ist auf zwei Arten möglich: mit Funktions-Decoratoren und Instrumentierungs-Helferfunktionen. Die Funktions-Decoratoren sind am einfachsten zu verwenden, aber weniger flexibel als die Instrumentierungs-Helferfunktionen. Diese Kurzanleitung hilft Ihnen beim Einrichten benutzerdefinierter Instrumentierung. Weitere Details zur Verwendung bestimmter Helfer finden Sie in den Hex-Dokumenten für das AppSignal-Paket.
Hinweis: Stellen Sie sicher, dass Sie AppSignal integriert haben, bevor Sie Ihrer Anwendung benutzerdefinierte Instrumentierung hinzufügen, wenn sie nicht automatisch durch eine unserer unterstützten Integrationen integriert wird.
Hinweis: Diese Seite beschreibt nur, wie Sie Performance-Instrumentierung zu Ihrem Code hinzufügen. Um Fehler zu verfolgen, lesen Sie bitte unseren Leitfaden zur Ausnahmebehandlung.

Appsignal.instrument/2-3

Die Funktion instrument/2 wird verwendet, um Instrumentierung hinzuzufügen, indem ein Codeabschnitt in eine Span eingewickelt wird. Eine Span wird schließlich zu einem Sample oder zu einem Event im Sample einer anderen Span in AppSignal.

Spans zu Traces hinzufügen

Im folgenden Beispiel haben wir einen Phoenix-Controller mit einer index/2-Funktion, die eine langsame Funktion aufruft. Die Funktion slow wird mit der Funktion Appsignal.instrument/2 instrumentiert, die sie als separates Event in dieser Phoenix-Anfrage erfasst. Sie wird auf AppSignal.com in der Event-Timeline dieses Samples angezeigt, um mehr Einblick zu geben, wo die meiste Zeit während der Anfrage verbracht wurde.
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
Hier umschließen wir den Inhalt unserer Funktion mit einem Aufruf von Appsignal.instrument3 und übergeben "slow" als Namen für das Event.

Neue Traces starten

Im Phoenix-Beispiel wurde bereits ein AppSignal-Trace gestartet, dank der First-Party-Unterstützung für Phoenix im AppSignal-Paket. Nicht alle Frameworks und Pakete werden derzeit direkt unterstützt und starten automatisch Traces. Dasselbe gilt für Ihre eigenen reinen Elixir-Anwendungen. Wie beim Hinzufügen von Spans zu bereits instrumentierten Traces wird eine neue Root-Span mit der Funktion Appsignal.instrument/2 erstellt:
defmodule AppsignalElixirExample do
  def example_fn do
    Appsignal.instrument("instrument", fn ->
      :timer.sleep(500)
    end)
  end
end
Dieses Beispiel erstellt ein Sample namens “instrument” im “background”-Namespace in AppSignal. Der Name wird auch als Kategoriename für die Haupt-Span verwendet. Um einen anderen Kategorienamen zu verwenden, verwenden Sie stattdessen instrument/3:
defmodule AppsignalElixirExample do
  def example_fn do
    Appsignal.instrument("instrument", "call.instrument", fn ->
      :timer.sleep(500)
    end)
  end
end
Wenn Sie eine Funktion übergeben, die ein Argument annimmt, ruft die Funktion instrument/2-3 sie mit der geöffneten Span auf, um weitere Anpassungen zu ermöglichen. Siehe die Dokumentation auf hex.pm für alle verfügbaren Span-Funktionen.
defmodule AppsignalElixirExample do
  def example_fn do
    Appsignal.instrument("Build complicated SQL query", "prepare_query.sql", fn span ->
      # Example of building a complex SQL query in the instrument block and performing it
      query = "SOME complicate SQL query"
      # The body is only set here because it's the value being calculated and instrumented in this function
      Appsignal.Span.set_sql(span, query)
      Database.perform_query(query)
    end)
  end
end

Ausnahmebehandlung

Um Fehler mithilfe benutzerdefinierter Instrumentierung zu melden, lesen Sie bitte mehr in unserem Leitfaden zur Ausnahmebehandlung.

Funktions-Decoratoren

Mit dem Decorator-Modul Appsignal.Instrumentation.Decorators ist es möglich, Ihren Elixir-Anwendungen benutzerdefinierte Instrumentierung hinzuzufügen, ohne den Inhalt der Funktionen zu ändern.

Transaktions-Events

Im folgenden Beispiel haben wir einen Phoenix-Controller mit einer index/2-Funktion, die eine langsame Funktion aufruft. Die Funktion slow wird mit dem AppSignal- transaction_event-Decorator instrumentiert, der sie als separates Event in dieser Phoenix- Anfrage erfasst. Sie wird auf AppSignal.com in der Event-Timeline dieses Transaktions- Samples angezeigt, um mehr Einblick zu geben, wo die meiste Zeit während der Anfrage verbracht wurde.
# Phoenix controller example
defmodule PhoenixExample.PageController do
  use PhoenixExample.Web, :controller
  # Include this
  use Appsignal.Instrumentation.Decorators

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

  # Decorate this function to add custom instrumentation
  @decorate transaction_event()
  defp slow do
    :timer.sleep(1000)
  end
end
Wenn Sie bestimmte Events unter derselben Event-Gruppe gruppieren möchten (andere Gruppen sind phoenix_controller, phoenix_render, ecto usw.), können Sie dem transaction_event-Decorator auch einen Gruppennamen übergeben.
@decorate transaction_event("github_api")
defp get_data_from_github do
  # Third-party API call
end
Dies erstellt ein Event get_data_from_github.github_api in der Event-Timeline. Weitere Informationen darüber, wie Event-Namen verwendet werden, finden Sie in unseren Event-Benennungsrichtlinien.

Transaktionen

Im Phoenix-Beispiel wurde bereits eine AppSignal-Transaktion gestartet, dank der First-Party-Unterstützung für Phoenix im AppSignal-Paket. Nicht alle Frameworks und Pakete werden derzeit direkt unterstützt und starten automatisch Transaktionen. Dasselbe gilt für Ihre eigenen reinen Elixir-Anwendungen. Um transaction_event-Decoratoren zu verfolgen, müssen wir zuvor eine AppSignal-Transaktion starten. Wir können eine Transaktion mit dem Funktions-Decorator transaction starten.
# Pure Elixir example
defmodule FunctionDecoratorsExample do
  # Include this
  use Appsignal.Instrumentation.Decorators

  # No transaction is started beforehand like in Phoenix, so we need to start
  # it ourselves.
  @decorate transaction()
  def call do
    slow()
    # ...
  end

  # Decorate this function to add custom instrumentation
  @decorate transaction_event()
  defp slow do
    :timer.sleep(1000)
  end
end
Hinweis: Stellen Sie bei reinen Elixir-Anwendungen sicher, dass die AppSignal- Anwendung gestartet wird, bevor Sie eine Transaktion starten. Weitere Informationen finden Sie in unserer Anleitung zur Integration von AppSignal.

Namespaces

Um zwischen HTTP-Anfragen und Hintergrund-Jobs zu unterscheiden, können wir der Transaktion einen Namespace übergeben, sobald wir sie starten. Die folgenden zwei Namespaces sind offizielle, von AppSignal unterstützte Namespaces.
  • http_request – der Standard – wird als „web“-Namespace bezeichnet
  • background_job – erstellt den „background“-Namespace
defmodule FunctionDecoratorsExample do
  # Include this
  use Appsignal.Instrumentation.Decorators

  # No namespace argument defaults to `:http_request`
  @decorate transaction()
  def web_function do
    # do stuff
  end

  # The "background" namespace
  @decorate transaction(:background_job)
  def background_function do
    # do stuff
  end
end
Weitere Informationen darüber, was Namespaces sind, finden Sie in unserer Dokumentation zu Namespaces.

Benutzerdefinierte Namespaces

Sie können auch eigene Namespaces erstellen, um Transaktionen in einem separaten Teil Ihrer Anwendung wie einem Verwaltungsbereich zu verfolgen. Dies gruppiert alle Transaktionen mit diesem Namespace in einem separaten Abschnitt auf AppSignal.com, sodass langsame Admin-Controller die Durchschnittswerte der Geschwindigkeit Ihrer Anwendung nicht beeinträchtigen.
@decorate transaction(:admin)
def some_function do
  # do stuff
end

Phoenix-Channels

Es gibt einen benutzerdefinierten Funktions-Decorator für Phoenix-Channels. Dieser Decorator soll vor die handle_in/3-Funktion eines Phoenix.Channel-Moduls gesetzt werden.
defmodule FunctionDecoratorsExample.MyChannel do
  # Include this
  use Appsignal.Instrumentation.Decorators

  # Add this channel function decorator
  @decorate channel_action()
  def handle_in("ping", _payload, socket) do
    # your code here..
  end
end
Channel-Events werden unter dem „background“-Namespace angezeigt und zeigen das Channel-Modul und das verwendete Action-Argument.

Spans manuell erstellen und schließen

In einigen Fällen kann es nützlich sein, Spans manuell zu verwalten. Sie können die Span-API verwenden, um z. B. Aufgaben zu instrumentieren:
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