帮助文档 Logo
平台使用
阿里云
百度云
移动云
智算服务
教育生态
登录 →
帮助文档 Logo
平台使用 阿里云 百度云 移动云 智算服务 教育生态
登录
  1. 首页
  2. 阿里云
  3. 日志服务
  4. 操作指南
  5. 日志应用
  6. 全栈可观测
  7. Trace
  8. 接入Trace数据
  9. 新接入方案
  10. 通过OpenTelemetry接入FlutterDart Trace数据

通过OpenTelemetry接入FlutterDart Trace数据

  • 新接入方案
  • 发布于 2025-04-22
  • 0 次阅读
文档编辑
文档编辑

本文介绍通过OpenTelemetry将Flutter、Dart应用的Trace数据接入到日志服务的操作步骤。

前提条件

已创建Trace实例。具体操作,请参见创建Trace实例。

步骤一:SDK集成

  1. 创建Flutter SDK。具体操作,请参见安装Flutter SDK。

  2. 在项目根目录下执行以下命令,导入opentelemetry-dart SDK模块。

    flutter pub add opentelemetry_sls

    导入成功后,项目的pubspec.yaml文件中将增加以下信息,并隐式执行flutter pub get命令。

    dependencies:
     opentelemetry_sls: ^0.15.1
  3. 在指定的Dart文件中导入opentelemetry-dart模块。

    import 'package:opentelemetry_sls/api.dart';
    import 'package:opentelemetry_sls/sdk.dart' as otel_sdk;

步骤二:初始化SDK

在使用SDK前,需要先完成初始化。

  TracerProvider? _provider;
  Tracer? _tracer;

  void _initOTelSDK() {
    const project = "qs-demos";
    const endpoint = "cn-beijing.log.aliyuncs.com";
    const instanceId = "sls-mall";
    const accessKeyId = "your access key id";
    const accessKeySecret = "your access key secret";
    final exporter =
        otel_sdk.CollectorExporter(Uri.parse("https://${project}.${endpoint}/opentelemetry/v1/traces"), headers: {
      "x-sls-otel-project": "${project}",
      "x-sls-otel-instance-id": "${instanceId}",
      "x-sls-otel-ak-id": "${accessKeyId}",
      "x-sls-otel-ak-secret": "${accessKeySecret}"
    });
    final processor = otel_sdk.BatchSpanProcessor(exporter);
    final simpleProcessor = otel_sdk.SimpleSpanProcessor(otel_sdk.ConsoleExporter());
    _provider = otel_sdk.TracerProviderBase(
        processors: [processor, simpleProcessor],
        resource: otel_sdk.Resource([
          Attribute.fromString("service.name", "main"),
          Attribute.fromString("service.namespace", "flutter"),
          Attribute.fromString("service.version", "1.0.0"),
          Attribute.fromString("deployment.environment", "dev"),
        ]));
  }

变量

说明

示例

${endpoint}

服务入口是访问一个Project及其内部数据的URL,日志服务提供私网域名和公网域名。更多信息,请参见服务入口。

cn-hangzhou.log.aliyuncs.com

${project}

日志服务Project名称,更多信息,请参见管理Project。

test-project

${instance}

Trace服务实例ID。更多信息,请参见创建Trace实例。

test-traces

${access-key-id}

AccessKey ID用于标识用户,更多信息,请参见访问密钥。

建议您遵循最小化原则,按需授予RAM用户必要的权限。关于授权的具体操作,请参见创建RAM用户及授权,RAM自定义授权示例。

无

${access-key-secret}

AccessKey Secret是用户用于加密签名字符串和日志服务用来验证签名字符串的密钥,必须保密。

无

${service.namespace}

服务归属的命名空间。

order

${service}

服务名,根据您的实际场景配置。

payment

${version}

服务版本号,建议按照va.b.c格式定义。

v1.0.0

${environment}

部署环境,例如测试环境、生产环境。

pre

步骤三:使用SDK

创建Tracer

建议根据不同的业务场景来创建Tracer。创建Tracer时需要传入instrumentation scope name,利于按照scope区分不同的Trace数据。

Tracer? _tracer = _provider!.getTracer('hello-otel-dart');

创建基本Span

Span代表事务中的操作,每个Span都封装了操作名称、起止时间戳、属性信息、事件信息和Context信息等。

final span = _tracer!.startSpan("operation");
// do stuff
// ...
span.end();

创建嵌套Span

当您希望为嵌套操作关联Span时,可通过以下方式进行关联。

final parent = _tracer!.startSpan("parent operation");
Context.current.withSpan(parent).execute(() {
  final child = _tracer!.startSpan("child operation");
  // do stuff
  // ...
  child.end();
});
parent.end();

创建带属性的Span

您可以通过属性在Span上提供特定操作的上下文信息。例如执行结果、关联的其他业务信息等。

final span = _tracer!.startSpan("GET /resource/catalog", kind: SpanKind.client);
span.setAttribute(Attribute.fromString("http.method", "GET"));
span.setAttribute(Attribute.fromString("http.url", "your http url"));
// do stuff
// ...
span.end();

给Span添加状态

Span包含StatusCode.unset、StatusCode.ok、StatusCode.errir三个状态,分别表示默认状态、成功状态、操作包含错误。

final span = _tracer!.startSpan("operation");

span.setStatus(StatusCode.error, description: "something error");
// 也可以捕获异常信息
try {
  _throwException();
} on Exception catch (e) {
  span.recordException(e);
}

span.end();

传播上下文信息

OpenTelemetry提供了一种基于文本的方法,传播上下文信息。此处为使用Dart http库发出HTTP GET请求的示例。

import 'package:http/http.dart' as http;

final traceContextPropagator = otel_sdk.W3CTraceContextPropagator();
final textMapSetter = HttpClientTextMapSetter();
final headers = <String, String>{};

final httpSpan = _tracer!.startSpan("start http request");
Context.current.withSpan(httpSpan).execute(() {
  traceContextPropagator.inject(Context.current, headers, textMapSetter);
  final client = http.Client();
  client.get(Uri.parse("http://sls-mall.caa227ac081f24f1a8556f33d69b96c99.cn-beijing.alicontainer.com/catalogue"),
             headers: headers);
});
httpSpan.end();

目前,OpenTelemetry SDK支持按照 W3C Trace Context标准传播上下文信息。关于信息,请参见w3c_trace_context_propagator类。

更多 OpenTelemetry SDK使用信息,请参考官方文档。

完整示例

下述示例表示使用OpenTelemetry SDK采集Flutter应用程序的Trace数据。

// ignore: depend_on_referenced_packages
import 'package:http/http.dart' as http;

import 'package:flutter/material.dart';
import 'package:opentelemetry_sls/api.dart';
import 'package:opentelemetry_sls/sdk.dart' as otel_sdk;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'OTel Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class HttpClientTextMapSetter implements TextMapSetter<Map> {
  @override
  void set(Map carrier, String key, String value) {
    carrier[key] = value;
  }
}

class _MyHomePageState extends State<MyHomePage> {
  TracerProvider? _provider;
  Tracer? _tracer;
  final traceContextPropagator = otel_sdk.W3CTraceContextPropagator();

  void _initOTelSDK() {
    const project = "qs-demos";
    const endpoint = "cn-beijing.log.aliyuncs.com";
    final exporter =
        otel_sdk.CollectorExporter(Uri.parse("https://$project.$endpoint/opentelemetry/v1/traces"), headers: {
      "x-sls-otel-project": "$project",
      "x-sls-otel-instance-id": "sls-mall",
      "x-sls-otel-ak-id": "",
      "x-sls-otel-ak-secret": ""
    });
    final processor = otel_sdk.BatchSpanProcessor(exporter);
    final simpleProcessor = otel_sdk.SimpleSpanProcessor(otel_sdk.ConsoleExporter());
    _provider = otel_sdk.TracerProviderBase(
        processors: [processor, simpleProcessor],
        resource: otel_sdk.Resource([
          Attribute.fromString("service.name", "main"),
          Attribute.fromString("service.namespace", "flutter"),
          Attribute.fromString("service.version", "1.0.0"),
          Attribute.fromString("deployment.environment", "dev"),
        ]));

    _tracer = _provider!.getTracer('hello-otel-dart');
  }

  void _simpleSpan() {
    final span = _tracer!.startSpan("operation");
    // do stuff
    // ...
    span.end();
  }

  void _nestedSpan() {
    final parent = _tracer!.startSpan("parent operation");
    Context.current.withSpan(parent).execute(() {
      final child = _tracer!.startSpan("child operation");
      // do stuff
      // ...
      child.end();
    });
    parent.end();
  }

  void _spanWithAttribute() {
    final span = _tracer!.startSpan("GET /resource/catalog", kind: SpanKind.client);
    span.setAttribute(Attribute.fromString("http.method", "GET"));
    span.setAttribute(Attribute.fromString("http.url", "your http url"));
    // do stuff
    // ...
    span.end();
  }

  void _spanWithStatus() {
    final span = _tracer!.startSpan("operation");

    span.setStatus(StatusCode.error, description: "something error");
    // 也可以捕获异常信息
    try {
      _throwException();
    } on Exception catch (e) {
      span.recordException(e);
    }

    span.end();
  }

  void _throwException() {
    throw Exception("Something bad happened!");
  }

  void _propagateContext() {
    final textMapSetter = HttpClientTextMapSetter();
    final headers = <String, String>{};

    final httpSpan = _tracer!.startSpan("start http request");
    Context.current.withSpan(httpSpan).execute(() {
      traceContextPropagator.inject(Context.current, headers, textMapSetter);
      final client = http.Client();
      client.get(Uri.parse("http://sls-mall.caa227ac081f24f1a8556f33d69b96c99.cn-beijing.alicontainer.com/catalogue"),
          headers: headers);
    });
    httpSpan.end();
  }

  @override
  Widget build(BuildContext context) {
    Color color = Theme.of(context).primaryColor;
    _initOTelSDK();

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        children: [
          _buildButton(color, 'init', _initOTelSDK),
          _buildButton(color, 'simple span', _simpleSpan),
          _buildButton(color, 'nested span', _nestedSpan),
          _buildButton(color, 'span with attribute', _spanWithAttribute),
          _buildButton(color, 'span with status', _spanWithStatus),
          _buildButton(color, 'propagate context', _propagateContext),
        ],
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  Widget _buildButton(Color color, String label, VoidCallback? onPressed) {
    return Row(
      mainAxisSize: MainAxisSize.max,
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Expanded(
            flex: 1,
            child: Container(
              margin: const EdgeInsets.only(left: 16, top: 8, right: 16),
              child: TextButton(
                  onPressed: onPressed,
                  style: ButtonStyle(
                      shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(12))),
                      side: MaterialStateProperty.all(BorderSide(color: color, width: 0.67)),
                      backgroundColor: MaterialStateProperty.all(Colors.transparent),
                      padding:
                          MaterialStateProperty.all(const EdgeInsets.only(left: 12, top: 6, right: 12, bottom: 6))),
                  child: Text(
                    label,
                    style: TextStyle(fontSize: 22, fontWeight: FontWeight.w400, color: color),
                  )),
            )),
      ],
    );
  }
}

相关文章

通过OpenTelemetry接入Java Trace数据 2025-04-22 10:45

本文介绍通过OpenTelemetry Java SDK将Java应用的Trace数据接入到日志服务的操作步骤。 前提条件

通过OpenTelemetry接入Golang Trace数据 2025-04-22 10:45

本文介绍通过OpenTelemetry Golang SDK将Golang应用的Trace数据接入到日志服务的操作步骤。 前提条件

通过OpenTelemetry接入Python Trace数据 2025-04-22 10:45

本文介绍通过OpenTelemetry Python SDK将Python应用的Trace数据接入到日志服务的操作步骤。 前提条件

通过OpenTelemetry接入Node.js Trace数据 2025-04-22 10:45

本文介绍通过opentelemetry-js SDK将Node.js应用的Trace数据接入到日志服务的操作步骤。 前提条件

通过OpenTelemetry接入C# Trace数据 2025-04-22 10:45

本文介绍通过OpenTelemetry .NET SDK将C#应用的Trace数据接入到日志服务的操作步骤。 前提条件

通过OpenTelemetry接入Rust Trace数据 2025-04-22 10:45

本文介绍通过OpenTelemetry Rust SDK将Rust应用的Trace数据接入到日志服务的操作步骤。 前提条件

目录
Copyright © 2025 your company All Rights Reserved. Powered by 博智数字服务平台.
闽ICP备08105208号-1