你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

采样替代 - 适用于 Java 的 Azure Monitor Application Insights

注意

从 3.5.0 开始,提供正式版的采样替代功能。

采样替代允许你替代默认采样百分比,例如:

  • 将采样百分比设置为 0(或某个小值)以检查干扰运行状况。
  • 将采样百分比设置为 0(或一些小值)以调用干扰依赖项。
  • 对于重要的请求类型(例如,/login),将采样百分比设置为 100,即使默认的采样配置为低于此值也是如此。

术语

在了解采样替代之前,应了解术语“范围”。 范围是一个表示下列事项的通用术语:

  • 传入请求。
  • 传出依赖项(例如,对另一个服务的远程调用)。
  • 进程内依赖项(例如,由服务的子组件所做的工作)。

对于采样替代,这些范围组件非常重要:

  • 属性

范围属性表示给定请求或依赖项的标准属性和自定义属性。

入门

首先,创建一个名为“applicationinsights.json”的配置文件。 将其保存在与“applicationinsights-agent-*.jar”相同的目录中。 请使用以下模版。

{
  "connectionString": "...",
  "sampling": {
    "percentage": 10,
    "overrides": [
      {
        "telemetryType": "request",
        "attributes": [
          ...
        ],
        "percentage": 0
      },
      {
        "telemetryType": "request",
        "attributes": [
          ...
        ],
        "percentage": 100
      }
    ]
  }
}

工作原理

telemetryType (Application Insights 3.4.0 中的 telemetryKind) 必须是 requestdependencytrace (日志) 或 exception

当范围启动时,将使用范围的类型和当时存在于其上的属性来检查是否有任何采样替代匹配项。

匹配可以是 strictregexp。 正则表达式匹配是针对整个属性值执行的,因此如果要匹配其中任何位置包含 abc 的值,则需要使用 .*abc.*。 采样替代可以指定多个属性条件,在这种情况下,所有属性条件都必须匹配,才能使采样替代匹配。

如果其中一个采样替代匹配,则使用其采样百分比来确定是否对该范围进行采样。

只使用第一个匹配的采样替代。

如果没有匹配的采样替代:

  • 如果是跟踪中的第一个范围,则使用顶级采样配置
  • 如果不是跟踪中的第一个范围,则使用父采样决策。

可用于采样的 Span 属性

OpenTelemetry 范围属性是自动收集的,基于 OpenTelemetry 语义约定

你还能以编程方式添加范围属性,并将其用于采样。

注意

若要查看 Application Insights Java 为应用程序捕获的确切属性集,请将自诊断级别设置为调试,并查找以文本“导出 Span”开头的调试消息。

注意

只有在范围开始时设置的属性才可用于采样,因此无法通过 OpenTelemetry Java 扩展来筛选稍后捕获的 http.response.status_code 或请求持续时间等属性。 下面是一个示例扩展,它根据请求持续时间筛选范围

注意

使用遥测处理器添加的属性不可用于采样。

用例

禁止收集运行状况检查的遥测数据

这一示例会取消收集所有对 /health-checks 的请求的遥测数据。

这一示例也会取消收集通常在 /health-checks 下收集的任何下游范围(依赖项)。

{
  "connectionString": "...",
  "sampling": {
    "overrides": [
      {
        "telemetryType": "request",
        "attributes": [
          {
            "key": "url.path",
            "value": "/health-check",
            "matchType": "strict"
          }
        ],
        "percentage": 0
      }
    ]
  }
}

禁止收集干扰性依赖项调用的遥测数据

这一示例会取消收集所有 GET my-noisy-key redis 调用的遥测数据。

{
  "connectionString": "...",
  "sampling": {
    "overrides": [
      {
        "telemetryType": "dependency",
        "attributes": [
          {
            "key": "db.system",
            "value": "redis",
            "matchType": "strict"
          },
          {
            "key": "db.statement",
            "value": "GET my-noisy-key",
            "matchType": "strict"
          }
        ],
        "percentage": 0
      }
    ]
  }
}

收集重要请求类型的所有遥测数据

这一示例会收集 /login 的全部遥测数据。

由于下游范围(依赖项)遵循父级的采样决策(缺少该下游范围的任何采样替代),因此也会为所有“/login”请求收集这些范围。

{
  "connectionString": "...",
  "sampling": {
    "percentage": 10
  },
  "sampling": {
    "overrides": [
      {
        "telemetryType": "request",
        "attributes": [
          {
            "key": "url.path",
            "value": "/login",
            "matchType": "strict"
          }
        ],
        "percentage": 100
      }
    ]
  }
}

公开范围属性以禁止 SQL 依赖项调用

此示例逐步讲解查找可用属性以抑制干扰 SQL 调用的体验。 下面的查询描述了过去 30 天内的不同 SQL 调用和关联的记录计数:

dependencies
| where timestamp > ago(30d)
| where name == 'SQL: DB Query'
| summarize count() by name, operation_Name, data
| sort by count_ desc
SQL: DB Query    POST /Order             DECLARE @MyVar varbinary(20); SET @MyVar = CONVERT(VARBINARY(20), 'Hello World');SET CONTEXT_INFO @MyVar;    36712549    
SQL: DB Query    POST /Receipt           DECLARE @MyVar varbinary(20); SET @MyVar = CONVERT(VARBINARY(20), 'Hello World');SET CONTEXT_INFO @MyVar;    2220248    
SQL: DB Query    POST /CheckOutForm      DECLARE @MyVar varbinary(20); SET @MyVar = CONVERT(VARBINARY(20), 'Hello World');SET CONTEXT_INFO @MyVar;    554074    
SQL: DB Query    GET /ClientInfo         DECLARE @MyVar varbinary(20); SET @MyVar = CONVERT(VARBINARY(20), 'Hello World');SET CONTEXT_INFO @MyVar;    37064

从上述结果中可以看到,所有操作在 data 字段中共享相同的值:DECLARE @MyVar varbinary(20); SET @MyVar = CONVERT(VARBINARY(20), 'Hello World');SET CONTEXT_INFO @MyVar;。 所有这些记录之间的通用性使其成为采样替代的良好候选项。

通过将自我诊断设置为调试,输出中将显示以下日志条目:

2023-10-26 15:48:25.407-04:00 DEBUG c.m.a.a.i.exporter.AgentSpanExporter - exporting span: SpanData{spanContext=ImmutableSpanContext...

这些日志中感兴趣的区域是“属性”部分:

{
  "attributes": {
    "data": {
      "thread.name": "DefaultDatabaseBroadcastTransport: MessageReader thread",
      "thread.id": 96,
      "db.connection_string": "apache:",
      "db.statement": "DECLARE @MyVar varbinary(20); SET @MyVar = CONVERT(VARBINARY(20), 'Hello World');SET CONTEXT_INFO @MyVar;",
      "db.system": "other_sql",
      "applicationinsights.internal.item_count": 1
    }
  }
}

使用该输出,可以配置类似于以下示例的采样替代,用于筛选干扰 SQL 调用:

{
  "connectionString": "...",
  "preview": {
    "sampling": {
      "overrides": [
        {
          "telemetryType": "dependency",
          "attributes": [
            {
              "key": "db.statement",
              "value": "DECLARE @MyVar varbinary(20); SET @MyVar = CONVERT(VARBINARY(20), 'Hello World');SET CONTEXT_INFO @MyVar;",
              "matchType": "strict"
            }
          ],
          "percentage": 0
        }
      ]
    }
  }
}

应用更改后,以下查询允许我们确定这些依赖项上次引入 Application Insights 的时间:

dependencies
| where timestamp > ago(30d)
| where data contains 'DECLARE @MyVar'
| summarize max(timestamp) by data
| sort by max_timestamp desc
DECLARE @MyVar varbinary(20); SET @MyVar = CONVERT(VARBINARY(20), 'Hello World');SET CONTEXT_INFO @MyVar;    11/13/2023 8:52:41 PM 

禁止收集日志的遥测数据

使用 SL4J 可以添加日志属性:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class MdcClass {

  private static final Logger logger = LoggerFactory.getLogger(MdcClass.class);

  void method {
	
    MDC.put("key", "value");
    try {
       logger.info(...); // Application log to remove
    finally {
       MDC.remove("key"); // In a finally block in case an exception happens with logger.info
    }
	
  }
  
}

然后,可以删除包含所添加的属性的日志:

{
  "sampling": {
    "overrides": [
      {
        "telemetryType": "trace",
        "percentage": 0,
        "attributes": [
          {
            "key": "key",
            "value": "value",
            "matchType": "strict"
          }
        ]
      }
    ]
  }
}

禁止收集 Java 方法的遥测数据

我们将向 Java 方法添加范围,然后使用采样重写来删除此范围。

我们首先添加 opentelemetry-instrumentation-annotations 依赖项:

    <dependency>
      <groupId>io.opentelemetry.instrumentation</groupId>
      <artifactId>opentelemetry-instrumentation-annotations</artifactId>
    </dependency>

现在,我们可以将 WithSpan 注释添加到执行 SQL 请求的 Java 方法:

package org.springframework.samples.petclinic.vet;

@Controller
class VetController {

	private final VetRepository vetRepository;

	public VetController(VetRepository vetRepository) {
		this.vetRepository = vetRepository;
	}

	@GetMapping("/vets.html")
	public String showVetList(@RequestParam(defaultValue = "1") int page, Model model) {
		Vets vets = new Vets();
		Page<Vet> paginated = findPaginated(page);
		vets.getVetList().addAll(paginated.toList());
		return addPaginationModel(page, paginated, model);
	}

	@WithSpan
	private Page<Vet> findPaginated(int page) {
		int pageSize = 5;
		Pageable pageable = PageRequest.of(page - 1, pageSize);
		return vetRepository.findAll(pageable);  // Execution of SQL requests
	}

以下采样重写配置允许删除 WithSpan 注释添加的范围:

  "sampling": {
    "overrides": [
      {
        "telemetryType": "dependency",
        "attributes": [
          {
            "key": "code.function",
            "value": "findPaginated",
            "matchType": "strict"
          }
        ],
        "percentage": 0
      }
    ]
  }

属性值是 Java 方法的名称。

此配置将删除 findPaginated 方法创建的所有遥测数据。 不会为来自 findPaginated 方法的 SQL 执行创建 SQL 依赖项。

以下配置将删除具有 WithSpan 注释的 VetController 类的方法发出的所有遥测数据:

 "sampling": {
    "overrides": [
      {
        "telemetryType": "dependency",
        "attributes": [
          {
            "key": "code.namespace",
            "value": "org.springframework.samples.petclinic.vet.VetController",
            "matchType": "strict"
          }
        ],
        "percentage": 0
      }
    ]
  }

故障排除

如果使用了 regexp,而采样替代不起作用,请尝试使用 .* 正则表达式。 如果采样现在起作用,则表示第一个正则表达式有问题,请阅读此正则表达式文档

如果它不适用于 .*,则 application-insights.json file 中可能存在语法问题。 请查看 Application Insights 日志,留意是否存在警告消息。