Sentry(v20.12.1) K8S 雲原生架構探索,JavaScript 效能監控之管理 Transactions

為少發表於2021-01-21

系列

  1. Sentry-Go SDK 中文實踐指南
  2. 一起來刷 Sentry For Go 官方文件之 Enriching Events
  3. Snuba:Sentry 新的搜尋基礎設施(基於 ClickHouse 之上)
  4. Sentry 10 K8S 雲原生架構探索,Vue App 1 分鐘快速接入
  5. Sentry(v20.12.1) K8S雲原生架構探索,玩轉前/後端監控與事件日誌大資料分析,高效能高可用+可擴充套件可伸縮叢集部署
  6. Sentry(v20.12.1) K8S 雲原生架構探索,Sentry JavaScript SDK 三種安裝載入方式
  7. Sentry(v20.12.1) K8S 雲原生架構探索,SENTRY FOR JAVASCRIPT SDK 配置詳解
  8. Sentry(v20.12.1) K8S 雲原生架構探索, SENTRY FOR JAVASCRIPT 手動捕獲事件基本用法
  9. Sentry(v20.12.1) K8S 雲原生架構探索,SENTRY FOR JAVASCRIPT Source Maps 詳解
  10. Sentry(v20.12.1) K8S 雲原生架構探索,SENTRY FOR JAVASCRIPT 故障排除
  11. Sentry(v20.12.1) K8S 雲原生架構探索,1分鐘上手 JavaScript 效能監控

Automatic Instrumentation

要自動捕獲 transactions,必須首先在應用程式中啟用跟蹤。

@sentry/tracing 包提供了一個 BrowserTracing 整合,以新增 automatic instrumentation 來監視瀏覽器應用程式的效能。

What Automatic Instrumentation Provides

BrowserTracing 整合為每個頁面 load 和 navigation 事件建立一個新 transaction,併為在開啟這些 transactions 時發生的每個 XMLHttpRequestfetch 請求建立一個 child span。進一步瞭解 traces, transactions, and spans。

Enable Automatic Instrumentation

要啟用此自動跟蹤,請在 SDK 配置選項中包含 BrowserTracing 整合。(請注意,使用 ESM 模組時,主要的 @sentry/* import 必須先於 @sentry/tracing import。)

配置完成後,在 sentry.io 中檢視 transactions 時,您將同時看到 pageloadnavigation

ESM

// If you're using one of our integration packages, like `@sentry/react` or `@sentry/angular`,
// substitute its name for `@sentry/browser` here
import * as Sentry from "@sentry/browser";
import { Integrations as TracingIntegrations } from "@sentry/tracing"; // Must import second

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",

  integrations: [
    new Integrations.BrowserTracing({
      tracingOrigins: ["localhost", "my-site-url.com", /^\//],
      // ... other options
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

CDN

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",

  integrations: [
    new Sentry.Integrations.BrowserTracing({
      tracingOrigins: ["localhost", "my-site-url.com", /^\//],
      // ... other options
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

Configuration Options

您可以將許多不同的選項傳遞給 BrowserTracing 整合(作為 {optionName: value} 形式的物件),但是它具有合理的預設值。有關所有可能的選項,請參見 TypeDocs。

tracingOrigins

tracingOrigins 的預設值是 ['localhost', /^\//]。 JavaScript SDK 將 sentry-trace header 附加到其目標包含列表中的字串或匹配列表中的正規表示式的所有傳出的 XHR/fetch 請求。 如果您的前端向另一個域發出請求,則需要在其中新增它,以將 sentry-trace header 傳播到後端服務,這是將 transactions 連結在一起作為單個跟蹤的一部分所必需的。tracingOrigins 選項與整個請求 URL 匹配,而不僅僅是域。使用更嚴格的正規表示式來匹配 URL 的某些部分,可以確保請求不用不必要地附加 sentry-trace header。

例如:

  • 前端應用程式是從 example.com 提供的
  • 後端服務由 api.example.com 提供
  • 前端應用程式對後端進行 API 呼叫
  • 因此,該選項需要這樣配置:new Integrations.BrowserTracing({tracingOrigins: ['api.example.com']})
  • 現在,向 api.example.com 發出的 XHR/fetch 請求將獲得附加的 sentry-trace header
Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    new Integrations.BrowserTracing({
      tracingOrigins: ["localhost", "my-site-url.com"],
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

您將需要配置您的 Web 伺服器 CORS 以允許 sentry-trace header。該配置可能類似於 "Access-Control-Allow-Headers: sentry-trace",但是該配置取決於您的設定。如果您不允許使用 sentry-trace header,則該請求可能會被阻止。

beforeNavigate

對於 pageloadnavigation transactions,BrowserTracing 整合使用瀏覽器的 window.location API 生成 transaction 名稱。要自定義 pageloadnavigation transactions 的名稱,您可以向 BrowserTracing 整合提供 beforeNavigate 選項。該選項允許您修改 transaction 名稱以使其更通用,例如,名為 GET /users/12312012GET /users/11212012 的 transactions 都可以重新命名為 GET /users/:userid,以便他們可以在一起。

import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";

Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    new Integrations.BrowserTracing({
      beforeNavigate: context => {
        return {
          ...context,
          // You could use your UI's routing library to find the matching
          // route template here. We don't have one right now, so do some basic
          // parameter replacements.
          name: location.pathname
            .replace(/\d+/g, "<digits>")
            .replace(/[a-f0-9]{32}/g, "<hash>"),
        };
      },
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

shouldCreateSpanForRequest

此函式可用於過濾掉不需要的 spans,例如 XHR 的執行狀況檢查或類似的檢查。 預設情況下,shouldCreateSpanForRequest 已經過濾掉了除了 tracingOrigins 中定義的內容以外的所有內容。

import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";
Sentry.init({
  dsn: "https://examplePublicKey@o0.ingest.sentry.io/0",
  integrations: [
    new Integrations.BrowserTracing({
      shouldCreateSpanForRequest: url => {
        // Do not create spans for outgoing requests to a `/health/` endpoint
        return !url.match(/\/health\/?$/);
      },
    }),
  ],

  // We recommend adjusting this value in production, or using tracesSampler
  // for finer control
  tracesSampleRate: 1.0,
});

Manual Instrumentation

要手動捕獲 transactions,必須首先在應用程式中啟用跟蹤。

要手動 instrument 程式碼的某些區域,可以建立 transactions 來捕獲它們。

這對於所有 JavaScript SDK(後端和前端)均有效,並且獨立於 ExpressHttpBrowserTracing 整合而工作。

const transaction = Sentry.startTransaction({ name: "test-transaction" });
const span = transaction.startChild({ op: "functionX" }); // This function returns a Span
// functionCallX
span.finish(); // Remember that only finished spans will be sent with the transaction
transaction.finish(); // Finishing the transaction will send it to Sentry

例如,如果要為頁面上的使用者互動建立 transaction,請執行以下操作:

// Let's say this function is invoked when a user clicks on the checkout button of your shop
shopCheckout() {
  // This will create a new Transaction for you
  const transaction = Sentry.startTransaction('shopCheckout');
  // set the transaction on the scope so it picks up any errors
  hub.configureScope(scope => scope.setSpan(transaction));

  // Assume this function makes an xhr/fetch call
  const result = validateShoppingCartOnServer();

  const span = transaction.startChild({
    data: {
      result
    },
    op: 'task',
    description: `processing shopping cart result`,
  });
  processAndValidateShoppingCart(result);
  span.finish();

  transaction.finish();
}

這個例子將傳送一個 transaction shopCheckout 到 Sentry。 交易將包含一個 task span,該 span 衡量 processAndValidateShoppingCart 花費了多長時間。最後,對 transaction.finish() 的呼叫將完成transaction 並將其傳送給 Sentry。

在為非同步操作建立 spans 時,您還可以利用 Promises。但是請記住,必須在呼叫 transaction.finish() 之前將其 span 包含在事務中。

例如:

function processItem(item, transaction) {
  const span = transaction.startChild({
    op: "http",
    description: `GET /items/:item-id`,
  });

  return new Promise((resolve, reject) => {
    http.get(`/items/${item.id}`, response => {
      response.on("data", () => {});
      response.on("end", () => {
        span.setTag("http.status_code", response.statusCode);
        span.setData("http.foobarsessionid", getFoobarSessionid(response));
        span.finish();
        resolve(response);
      });
    });
  });
}

Connect Backend and Frontend Transactions

要將後端和前端 transactions 連線到單個一致的跟蹤中,Sentry 使用 trace_id 值,該值在前端和後端之間傳播。根據情況,此 ID 可以在請求 header 或 HTML <meta> 標記中傳輸。以這種方式連結 transactions 使您可以在 Sentry UI 中在它們之間進行導航,因此您可以更好地瞭解系統的不同部分如何相互影響。您可以在我們的分散式跟蹤文件中瞭解有關此模型的更多資訊。

Pageload

在前端和後端都啟用跟蹤並利用自動前端 instrumentation 功能時,可以將前端上自動生成的 pageload transaction 與後端上的為頁面服務提供請求的 transaction 相連線。因為在瀏覽器中執行的 JavaScript 程式碼無法讀取當前頁面的響應 headers,所以 trace_id 必須在響應本身中傳輸,尤其是在從後端傳送的 HTML <head> 中的 <meta> 標籤中。

<html>
  <head>
    <meta name="sentry-trace" content="{{ span.toTraceparent() }}" />
    <!-- ... -->
  </head>
</html>

name 屬性必須是字串 "sentry-trace"content 屬性必須由後端的 Sentry SDK 使用 span.toTraceparent()(或等效項,取決於後端平臺)生成。這保證了將為每個請求生成一個新的唯一值。

span 引用是為 HTML 提供服務的 transaction,或其任何 child spans。 它定義了 pageload transaction 的父級。

一旦資料被包含在 <meta> 標籤中,我們的 BrowserTracing 整合將自動獲取資料並將其連結到在 pageload 時生成的 transaction。(請注意,它不會連結到自動生成的 navigation transactions,即不需要重新載入整個頁面的 transaction。每個 transaction 都是後端不同請求 transaction 的結果,因此應具有唯一的 trace_id。)

載入頁面後,它發出的任何請求(以及後端產生的任何請求)都通過請求 header 連結。

就像上面討論的 <meta> 標籤一樣,標題的名稱是 sentry-trace,其值是通過呼叫 span.toTraceparent()(或等效的)來獲得的,其中 span 是相關 transaction 或其任何子項。

Sentry 的所有與跟蹤相關的整合(BrowserTracingHttpExpress)都會針對它們生成的所有 transactions 和 spans 自動生成或拾取並傳播此 header。在手動建立 transaction 或 span 的任何情況下,您都可以自己附加和讀取 header,這樣做很有意義。

Control Data Truncation

當前,每個標籤的最大字元數限制為200個字元。超過200個字元限制的標籤將被截斷,丟失潛在的重要資訊。要保留此資料,您可以將資料拆分為多個標籤。

例如,一個200多個字元標記的請求:

https://empowerplant.io/api/0/projects/ep/setup_form/?user_id=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=161803398874989484820458683436563811772030917980576

上面200個字元以上的請求將被截斷為:

https://empowerplant.io/api/0/projects/ep/setup_form/?user_id=314159265358979323846264338327&tracking_id=EasyAsABC123OrSimpleAsDoReMi&product_name=PlantToHumanTranslator&product_id=1618033988749894848

相反,使用 span.set_tagspan.set_data 會使用結構化後設資料保留此查詢的詳細資訊。這可以通過 baseUrlendpointparameters 完成:

const baseUrl = "https://empowerplant.io";
const endpoint = "/api/0/projects/ep/setup_form";
const parameters = {
  user_id: 314159265358979323846264338327,
  tracking_id: "EasyAsABC123OrSimpleAsDoReMi",
  product_name: PlantToHumanTranslator,
  product_id: 161803398874989484820458683436563811772030917980576,
};

const span = transaction.startChild({
  op: "request",
  description: "setup form",
});

span.setTag("baseUrl", baseUrl);
span.setTag("endpoint", endpoint);
span.setData("parameters", parameters);
// you may also find some parameters to be valuable as tags
span.setData("user_id", parameters.user_id);
http.get(`${base_url}/${endpoint}/`, (data = parameters));

Group Transactions

Sentry 捕獲 transactions 時,將為它們分配一個 transaction 名稱。該名稱通常由 Sentry SDK 根據您使用的框架整合自動生成。如果您無法利用自動 transaction 生成(或想要自定義 transaction 名稱的生成方式),則可以使用,在使用配置初始化 SDK 時註冊的全域性事件處理器。

在 node.js 應用程式中執行此操作的示例:

import { addGlobalEventProcessor } from "@sentry/node";

addGlobalEventProcessor(event => {
  // if event is a transaction event
  if (event.type === "transaction") {
    event.transaction = sanitizeTransactionName(event.transaction);
  }
  return event;
});

對於使用 BrowserTracing 整合的瀏覽器 JavaScript 應用程式,beforeNavigate 選項可用於根據 URL 更好地將 navigation/pageload transactions 分組在一起。

import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";

Sentry.init({
  // ...
  integrations: [
    new Integrations.BrowserTracing({
      beforeNavigate: context => {
        return {
          ...context,
          // You could use your UI's routing library to find the matching
          // route template here. We don't have one right now, so do some basic
          // parameter replacements.
          name: location.pathname
            .replace(/\d+/g, "<digits>")
            .replace(/[a-f0-9]{32}/g, "<hash>"),
        };
      },
    }),
  ],
});

Retrieve an Active Transaction

如果要將 Spans 附加到已在進行中的 transaction 中,例如在對 transaction 進行分組時,可以使用 Sentry.getCurrentHub().getScope().getTransaction()。當 scope 中有正在執行的 transaction 時,此函式將返回一個 Transaction 物件,否則它將返回 undefined。如果您使用的是 BrowserTracing 整合,則預設情況下,我們會將 transaction 附加到 Scope,因此您可以執行以下操作:

function myJsFunction() {
  const transaction = Sentry.getCurrentHub()
    .getScope()
    .getTransaction();
  if (transaction) {
    let span = transaction.startChild({
      op: "encode",
      description: "parseAvatarImages",
    });
    // Do something
    span.finish();
  }
}

中文文件陸續同步到:

我是為少。
微信:uuhells123。
公眾號:黑客下午茶。
謝謝點贊支援???!

相關文章