SpitfireYük Testi ve Gözlemlenebilirlikv1.1.0 · Yenilikler

Sürüm: 1.1.0Bu dokümantasyon Spitfire 1.1.0 içindir.

Flutter uygulamasını OTel ile bağlamak

Flutter (Android, iOS, web, masaüstü): HTTP çağrıları trace olarak; backend'e traceparent ile bağlanır. Değerler: Servisleri OTel ile bağlamak: genel bakış.

Sürüm tablosu

Flutter Dart Paket Sürüm
3.22 ve sonrası 3.4 ve sonrası dartastic_opentelemetry 0.12.0
3.22 ve sonrası 3.4 ve sonrası http 1.6.0
3.19 ve öncesi 3.3 ve öncesi desteklenmiyor Flutter 3.22'ye yükseltin

Sürümünüzü bulun: flutter --version

OpenTelemetry'nin resmi Dart SDK'sı yok. dartastic_opentelemetry bir topluluk paketidir (Apache-2.0, CNCF'e bağışlanma sürecinde); sürümünü sabitleyin. Yalnızca trace.

Doğrulandı: 2026-11-04 (kod Flutter 3.47.5, Dart 3.13.4 ile denendi). Kaynak: pub.dev dartastic_opentelemetry, kaynak kod ve README, OTel blog: Dart ve Flutter, pub.dev http, Flutter --dart-define-from-file.

Gerekenler

  • Spitfire adresi https://<spitfire> (HTTPS: Android ve iOS düz HTTP'yi reddeder)
  • Servis adı <service-name>, sürüm <service-version>, ortam <environment>
  • Uygulama klasörü (burada /srv/app, pubspec.yaml dosyasının olduğu yer)

Flutter

  1. Anahtarı oluşturun: Gözlemlenebilirlik → Tarayıcı ve uygulama anahtarı; İzin verilen kökenler: web derlemesinin adresi, yalnızca Android ve iOS için boş; sfrum_… anahtarını kopyalayın (Gözlemlenebilirlik).

  2. Paketleri ekleyin:

bash
cd /srv/app && flutter pub add 'dartastic_opentelemetry:0.12.0' 'http:1.6.0'
  1. /srv/app/lib/otel.dart dosyasını oluşturun, yapıştırın:
dart
// IS_OTEL_ENABLED=true: OTel on; any other value: off.
import 'package:dartastic_opentelemetry/dartastic_opentelemetry.dart';
import 'package:http/http.dart' as http;

const otelOn = String.fromEnvironment('IS_OTEL_ENABLED') == 'true';

Future<void> initOtel() async {
  if (!otelOn) return;
  await OTel.initialize(
    serviceName: const String.fromEnvironment('OTEL_SERVICE_NAME'),
    serviceVersion: const String.fromEnvironment('OTEL_SERVICE_VERSION'),
    resourceAttributes: OTel.attributesFromMap({'deployment.environment': const String.fromEnvironment('OTEL_ENVIRONMENT')}),
    spanKind: SpanKind.client,
    sampler: ParentBasedSampler(TraceIdRatioSampler(double.parse(const String.fromEnvironment('OTEL_TRACES_SAMPLER_ARG', defaultValue: '0.1')))),
    spanProcessor: BatchSpanProcessor(OtlpHttpSpanExporter(OtlpHttpExporterConfig(
      endpoint: const String.fromEnvironment('OTEL_TRACES_ENDPOINT'),
      headers: {'authorization': 'Bearer ${const String.fromEnvironment('OTEL_KEY')}'},
    ))),
    detectPlatformResources: false,
    enableMetrics: false,
    enableLogs: false,
  );
}

/// An http.Client whose requests are client spans carrying traceparent.
class OtelHttpClient extends http.BaseClient {
  final http.Client _inner = http.Client();
  final _propagator = W3CTraceContextPropagator();

  @override
  Future<http.StreamedResponse> send(http.BaseRequest request) async {
    if (!otelOn) return _inner.send(request);
    final span = OTel.tracer().startSpan('HTTP ${request.method}', kind: SpanKind.client,
        attributes: OTel.attributesFromMap({'http.request.method': request.method, 'url.full': request.url.toString()}));
    try {
      return await OTel.withSpanAsync(span, () async {
        _propagator.inject(Context.current, request.headers, _HeaderSetter(request.headers));
        final res = await _inner.send(request);
        span.addAttributes(OTel.attributesFromMap({'http.response.status_code': res.statusCode}));
        return res;
      });
    } catch (e, st) {
      span.recordException(e, stackTrace: st);
      span.setStatus(SpanStatusCode.Error, e.toString());
      rethrow;
    } finally {
      span.end();
    }
  }
}

class _HeaderSetter implements TextMapSetter<String> {
  _HeaderSetter(this._headers);
  final Map<String, String> _headers;

  @override
  void set(String key, String value) => _headers[key] = value;
}
  1. /srv/app/lib/main.dart içinde main async olsun, OTel ilk başlasın:
dart
import 'otel.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await initOtel();
  runApp(const MyApp());
}
  1. API çağrılarınızı trace'li client ile gönderin:
dart
final api = OtelHttpClient();
final res = await api.get(Uri.parse('https://api.example.com/orders'));
  1. /srv/app/otel.json dosyasını oluşturun, yapıştırın:
json
{
  "IS_OTEL_ENABLED": "true",
  "OTEL_SERVICE_NAME": "<service-name>",
  "OTEL_SERVICE_VERSION": "<service-version>",
  "OTEL_ENVIRONMENT": "<environment>",
  "OTEL_TRACES_ENDPOINT": "https://<spitfire>/otlp/v1/traces",
  "OTEL_KEY": "sfrum_…",
  "OTEL_TRACES_SAMPLER_ARG": "0.1"
}
  1. Bu dosyayla derleyin (iOS: ipa; web: web):
bash
cd /srv/app && flutter build apk --release --dart-define-from-file=otel.json

Doğrulama

  1. Spitfire anahtarı bir uygulamadan (Origin olmadan) kabul ediyor:
bash
curl -s -o /dev/null -w '%{http_code}' -X POST https://<spitfire>/otlp/v1/traces -H 'Authorization: Bearer sfrum_…' -H 'Content-Type: application/json' -d '{}' | grep -q '^200
  

 && echo OK
  1. Kod derleniyor:
bash
cd /srv/app && flutter analyze lib/otel.dart lib/main.dart && echo OK

Kapatma ve kaldırma

  1. Kapatma: otel.json içinde "IS_OTEL_ENABLED": "false" yazın, yeniden derleyin.
  2. Kaldırma:
bash
cd /srv/app && flutter pub remove dartastic_opentelemetry && rm -f lib/otel.dart otel.json

Sık karşılaşılan sorunlar

Belirti Çözüm
Span gelmiyor --dart-define-from-file=otel.json ile derleyin; flutter run için de gerekir
401 Anahtar sfrum_… olmalı, açık olmalı; OTEL_KEY değeri Bearer olmadan yazılır
Web derlemesi: 403 origin not allowed Web derlemesinin adresini İzin verilen kökenler listesine ekleyin
429 Anahtarın Dakikada gönderim değerini artırın ya da OTEL_TRACES_SAMPLER_ARG değerini düşürün
version solving failed Flutter 3.22 ve sonrası
Uygulama ve backend span'leri tek trace değil Çağrıları OtelHttpClient ile gönderin

İlgili sayfalar