설정
Flutter 앱이 플랫폼마다 갖춰야 하는 네이티브 설정(sophonz-config.json, Sophonz-Info.plist), 서비스 식별 방식, Dart에서 지정할 수 있는 모든 옵션을 정리합니다.
데이터를 어디로 보내고 어느 서비스의 데이터로 기록할지는 네이티브 설정이 정합니다. Android는 sophonz-config.json, iOS는 Sophonz-Info.plist이며, 둘 다 Dart에서는 바꿀 수 없습니다. Dart 쪽은 Flutter 계층이 무엇을 수집하고 라우트 시간을 어떻게 잴지만 결정합니다.
한눈에 보기
| 설정 | Android (sophonz-config.json) | iOS (Sophonz-Info.plist) |
|---|---|---|
| 수집기 URL | sdk_config.ingest.collector_url | CollectorURL |
| 서비스 키 | sdk_config.ingest.service_key | AppKey |
| 서비스 이름 | 애플리케이션 ID | AppName |
| 프로젝트 | sdk_config.ingest.service_namespace | Project |
| 배포 환경 | sdk_config.ingest.deployment_environment | DeploymentEnvironment |
| 프레임워크 | sdk_config.app_framework: "flutter" | fromPlist(platform: .flutter) |
| 읽는 시점 | 빌드 시점, Gradle 플러그인이 읽음 | 실행 시점, Sophonz.Options.fromPlist가 읽음 |
서비스 식별
모든 스팬과 로그에는 어느 서비스의 데이터인지 알려 주는 리소스 속성이 붙습니다.
| 리소스 속성 | 의미 |
|---|---|
service.key | 서비스 키. 수집기는 이 값으로 테넌트와 앱을 찾습니다 |
service.name | 앱 이름. Android는 애플리케이션 ID, iOS는 AppName이며 없으면 <번들 ID>:<프로세스 이름> |
service.namespace | 프로젝트 |
deployment.environment.name | production, staging 같은 배포 환경 |
Android와 iOS 빌드를 하나의 앱으로 보려면 두 플랫폼에 같은 서비스 키와 프로젝트를 넣으세요.
WARNING — 서비스 키가 없으면 데이터도 없습니다
수집기는 서비스 키를 강제합니다. 키가 없거나 등록되지 않은 텔레메트리는 오류 없이 버려집니다. 앱은 정상적으로 시작하고 수집하고 업로드까지 하지만 Sophonz에는 아무것도 나타나지 않습니다. 한 플랫폼의 데이터가 보이지 않으면 먼저 그 플랫폼의 키를 확인하세요.
환경을 나눌 때는 앱마다 서비스 키를 하나로 두고, 아래처럼 빌드마다 deployment_environment / DeploymentEnvironment만 바꿉니다.
Android
sophonz-config.json
앱 모듈에 파일을 만듭니다. Gradle 플러그인이 빌드 시점에 SDK에 넣으므로, 내용을 바꾸면 다시 빌드해야 합니다.
{
"sdk_config": {
"app_framework": "flutter",
"ingest": {
"collector_url": "https://in.sophonz.ai",
"service_key": "sk_replace_me",
"service_namespace": "my-project",
"deployment_environment": "production"
}
}
}| 키 | 기본값 | 효과 |
|---|---|---|
sdk_config.app_framework | 없음 | flutter로 지정해 Flutter 앱으로 보고되게 합니다 |
sdk_config.ingest.collector_url | https://in.sophonz.ai | 수집기 기본 URL. SDK가 /v1/traces, /v1/logs를 붙입니다 |
sdk_config.ingest.service_key | 없음 | service.key로 전송. 없으면 아무것도 받아들여지지 않습니다 |
sdk_config.ingest.service_namespace | 없음 | service.namespace로 전송 |
sdk_config.ingest.deployment_environment | 없음 | deployment.environment.name으로 전송 |
ndk_enabled | true | 네이티브(C/C++) 크래시 수집. Flutter 엔진이 네이티브 코드이므로 켜 두세요 |
app_id, api_token | 없음 | 텔레메트리가 아니라 매핑 파일 업로드용 자격 증명입니다. 릴리스 빌드 참고 |
ANR, WebView, 탭, 네트워크 같은 Android SDK의 수집 옵션도 같은 파일에 넣습니다. 목록은 Android 설정 레퍼런스에 있습니다.
CAUTION — 모르는 키는 빌드를 실패시킵니다
파서는 알 수 없는 키를 거부합니다. 오타 하나로도 빌드가 멈추므로, 이 페이지나 Android 설정 레퍼런스에 있는 키만 쓰세요.
빌드 타입·플레이버별 설정
파일은 src/<variant>/, 플레이버 조합, 개별 플레이버, src/<build type>/, src/main/ 순으로 찾으며 처음 찾은 파일을 씁니다. 그래서 디버그 빌드는 다른 환경으로 보고하게 할 수 있습니다.
{
"sdk_config": {
"app_framework": "flutter",
"ingest": {
"service_key": "sk_replace_me",
"service_namespace": "my-project",
"deployment_environment": "development"
}
}
}flutter build apk --flavor staging으로 빌드하면 android/app/src/staging/의 파일을 씁니다.
키를 저장소에 커밋하지 않기
collector_url과 service_key는 빌드 시점의 환경 변수로 넘길 수 있습니다.
SOPHONZ_SERVICE_KEY=sk_... flutter build appbundle --release| 변수 | 대체하는 키 |
|---|---|
SOPHONZ_SERVICE_KEY | sdk_config.ingest.service_key |
SOPHONZ_COLLECTOR_URL | sdk_config.ingest.collector_url |
파일에 값이 있으면 환경 변수보다 파일이 우선하므로, 환경 변수를 쓸 때는 파일에서 키를 빼 두세요.
iOS
Sophonz-Info.plist
ios/Sophonz-Info.plist를 만들고 Xcode에서 Runner 타깃에 추가합니다(File > Add Files to "Runner", Runner 타깃 체크). 앱 번들에 리소스로 들어가야 합니다.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CollectorURL</key>
<string>https://in.sophonz.ai</string>
<key>AppKey</key>
<string>sk_replace_me</string>
<key>Project</key>
<string>my-project</string>
<key>DeploymentEnvironment</key>
<string>production</string>
</dict>
</plist>| 키 | 필수 | 효과 |
|---|---|---|
CollectorURL | 예 | 수집기 기본 URL. /v1/...을 붙이지 마세요 |
AppKey | 예 | 서비스 키. service.key로 전송 |
AppName | 아니요 | service.name으로 전송. 없으면 <번들 ID>:<프로세스 이름> |
Project | 아니요 | service.namespace로 전송 |
DeploymentEnvironment | 아니요 | deployment.environment.name으로 전송. 기본값 production |
CAUTION — 설정이 비어 있으면 SDK가 시작되지 않습니다
Sophonz.Options.fromPlist는 CollectorURL이나 AppKey가 없거나 비어 있으면 nil을 돌려줍니다. 값이 $(SOMETHING) 형태로 남아 있어도 없는 것으로 봅니다. Xcode는 타깃의 Info.plist에서만 빌드 설정을 치환하고, 리소스로 복사되는 plist는 건드리지 않기 때문입니다. 이 파일에는 실제 값을 넣거나, Copy Bundle Resources 전에 빌드 단계에서 파일을 생성하세요.
NOTE — AppName은 최신 Apple SDK가 필요합니다
AppName은 Apple SDK 1.0.0 이후 빌드부터 읽습니다. 1.0.0에서는 무시되고 번들에서 만든 기본 이름이 서비스 이름이 됩니다. 포털은 프로젝트와 service.name으로 앱을 찾으므로, 나중에 이름을 바꾸면 기존 앱 페이지와 데이터가 분리됩니다.
시작 옵션
fromPlist는 나머지 시작 옵션을 인자로 받습니다.
let captureServices = CaptureServicesOptionsBuilder()
.addDefaults()
.addPushNotificationCaptureService(withOptions: .init())
.build()
if let options = Sophonz.Options.fromPlist(
platform: .flutter,
captureServices: captureServices,
crashReporter: .sophonz,
logLevel: .default
) {
try? Sophonz.start(options: options)
}| 인자 | 기본값 | 설명 |
|---|---|---|
platform | .native | .flutter를 넘깁니다 |
captureServices | .default() | 푸시 알림 수집은 기본값에 없으므로 위처럼 추가합니다. 전체 목록은 iOS 계측 참고 |
crashReporter | .sophonz | 크래시 수집을 끄려면 .none |
logLevel | .default | SDK 자체의 콘솔 로그 수준 |
otel | nil | 직접 만든 익스포터를 담은 Sophonz.OTelOptions. iOS에서 다른 곳으로 내보내는 유일한 방법입니다 |
plist 대신 코드로 설정하려면 Sophonz.Options.withCollector(url:appKey:project:deploymentEnvironment:platform:)를 사용합니다. $(...)가 치환되는 타깃의 Info.plist에서 값을 읽는 식으로, plist를 생성하지 않고도 빌드 구성마다 값을 바꿀 수 있습니다.
Dart
Dart에는 수집기나 키 설정이 없습니다. 존재하는 옵션은 다음이 전부입니다.
| 옵션 | 위치 | 기본값 |
|---|---|---|
action | Sophonz.instance.start | 없음 |
routeSettingsExtractor | SophonzNavigationObserver, SophonzGoRouterObserver | route.settings |
screenLoadConfig.recencyThreshold | 두 옵저버, SophonzScreenLoadConfig로 지정 | 1초 |
router | SophonzGoRouterObserver | 없음 |
internalClient | SophonzHttpClient | 새 http.Client() |
endpoint, headers, timeoutSeconds | addSpanExporter, addLogRecordExporter | 네이티브 SDK 타임아웃 |
느린 프레임, 멈춘 프레임, 아이솔레이트 멈춤의 기준값은 0.1.0에서 고정되어 있어 바꿀 수 없습니다. 값은 계측에 있습니다.
start
await Sophonz.instance.start(action: () => runApp(const MyApp()));action을 넘기면 start는 FlutterError.onError를 감싸고(기존 핸들러는 유지하고 기록 후 호출합니다), PlatformDispatcher.instance.onError를 설정한 뒤 action을 실행합니다. action이 없으면 오류 핸들러를 설치하지 않으므로 나중에 직접 설치합니다.
await Sophonz.instance.start();
await Sophonz.instance.installErrorHandlers(() => runApp(const MyApp()));CAUTION — PlatformDispatcher.onError는 이어 붙지 않고 교체됩니다
FlutterError.onError는 기존 핸들러를 유지하지만 PlatformDispatcher.instance.onError는 덮어씁니다. start 전에 설정한 핸들러는 더 이상 실행되지 않고, start 후에 설정하면 Sophonz가 그 오류를 받지 못합니다. 둘 다 필요하면 start 후에 직접 핸들러를 설정하고 그 안에서 Sophonz.instance.logDartError(error, stack)를 호출하세요.
내비게이션
SophonzNavigationObserver(
routeSettingsExtractor: (route) => route.settings,
screenLoadConfig: const SophonzScreenLoadConfig(
recencyThreshold: Duration(milliseconds: 500),
),
)| 옵션 | 효과 |
|---|---|
routeSettingsExtractor | 이름을 기록할 RouteSettings를 돌려줍니다. null을 돌려주면 그 라우트는 추적하지 않습니다 |
screenLoadConfig.recencyThreshold | 화면 로드의 시작으로 볼 터치가 얼마나 최근이어야 하는지. 더 오래된 터치면 push 시점을 시작으로 씁니다 |
라우트 이름, go_router, 옵저버별 기록 내용은 내비게이션에서 설명합니다.
추가 익스포터 (Android 전용)
Sophonz.instance.addSpanExporter(
endpoint: 'https://otlp.example.com/v1/traces',
headers: [{'x-api-key': 'token'}],
timeoutSeconds: 10,
);
Sophonz.instance.addLogRecordExporter(
endpoint: 'https://otlp.example.com/v1/logs',
);
await Sophonz.instance.start(action: () => runApp(const MyApp()));| 파라미터 | 설명 |
|---|---|
endpoint | /v1/traces 또는 /v1/logs까지 포함한 전체 OTLP/HTTP URL. 빈 문자열은 무시됩니다 |
headers | 헤더마다 항목 하나짜리 맵을 담은 리스트 |
timeoutSeconds | 내보내기 타임아웃. 생략하면 네이티브 기본값 |
Sophonz로 보내는 것에 더해 추가로 내보내는 것이며, 대체하지 않습니다.
CAUTION — 익스포터는 네이티브 SDK가 시작되기 전에 전달돼야 합니다
익스포터는 Dart에 쌓였다가 start()가 실행될 때 네이티브로 넘어갑니다. start() 이후의 호출은 무시됩니다. Application.onCreate()에서 이미 Android SDK를 시작했다면 전달된 익스포터도 너무 늦게 도착해 무시되므로, 둘을 함께 쓰려면 앱 시작 시간 측정을 포기하거나 Kotlin에서 Sophonz.start(this) 전에 익스포터를 등록해야 합니다. iOS에는 아예 전달되지 않으므로 시작 옵션의 otel:을 사용하세요.