Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add support for dio #688

Merged
merged 21 commits into from
Jan 14, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
pull_request:
paths-ignore:
- 'logging/**'
- 'dio/**'
defaults:
run:
shell: bash
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/dio.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: sentry-dio
on:
push:
branches:
- main
pull_request:
paths-ignore:
- 'logging/**'
- 'flutter/**'
defaults:
run:
shell: bash
jobs:
build:
name: Build ${{matrix.sdk}} on ${{matrix.os}}
runs-on: ${{ matrix.os }}
timeout-minutes: 20
defaults:
run:
working-directory: ./dio
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
sdk: [stable, beta]
exclude:
- os: macos-latest
sdk: beta
steps:
- uses: dart-lang/setup-dart@v1
with:
sdk: ${{ matrix.sdk }}
- uses: actions/checkout@v2
# coverage with 'chrome' platform hangs the build
- name: Test (VM and browser)
run: |
dart pub get
dart test -p chrome
dart test -p vm --coverage=coverage
dart pub run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.packages --report-on=lib

- uses: codecov/codecov-action@v2
if: runner.os == 'Linux'
with:
name: sentry
file: ./dio/coverage/lcov.info

- uses: VeryGoodOpenSource/very_good_coverage@v1.2.0
if: runner.os == 'Linux'
with:
path: "./dio/coverage/lcov.info"
min_coverage: 81

analyze:
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: ./dio
steps:
- uses: dart-lang/setup-dart@v1
with:
sdk: stable
- uses: actions/checkout@v2
- run: |
dart pub get
dart analyze --fatal-infos
dart format --set-exit-if-changed ./

package-analysis:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v2
- uses: axel-op/dart-package-analyzer@v3
id: analysis
with:
githubToken: ${{ secrets.GITHUB_TOKEN }}
relativePath: dio/
- name: Check scores
env:
TOTAL: ${{ steps.analysis.outputs.total }}
TOTAL_MAX: ${{ steps.analysis.outputs.total_max }}
run: |
PERCENTAGE=$(( $TOTAL * 100 / $TOTAL_MAX ))
if (( $PERCENTAGE < 100 ))
then
echo Score too low!
exit 1
fi
1 change: 1 addition & 0 deletions .github/workflows/flutter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
pull_request:
paths-ignore:
- 'logging/**'
- 'dio/**'
defaults:
run:
shell: bash
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/logging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ on:
branches:
- main
pull_request:
paths-ignore:
- 'dio/**'
- 'flutter/**'
defaults:
run:
shell: bash
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Unreleased

* Feat: Add support for [Dio](https://pub.dev/packages/dio) (#688)

# 6.3.0-alpha.1

* Feat: Automatically create transactions when navigating between screens (#643)
Expand Down
1 change: 1 addition & 0 deletions dart/lib/sentry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export 'src/transport/transport.dart';
export 'src/integration.dart';
export 'src/event_processor.dart';
export 'src/http_client/sentry_http_client.dart';
export 'src/http_client/sentry_http_client_error.dart';
marandaneto marked this conversation as resolved.
Show resolved Hide resolved
export 'src/sentry_attachment/sentry_attachment.dart';
export 'src/sentry_user_feedback.dart';
// tracing
Expand Down
10 changes: 10 additions & 0 deletions dio/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Files and directories created by pub.
.dart_tool/
.packages

# Conventional directory for build outputs.
build/

# Omit committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
1 change: 1 addition & 0 deletions dio/CHANGELOG.md
60 changes: 60 additions & 0 deletions dio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<p align="center">
<a href="https://sentry.io" target="_blank" align="center">
<img src="https://sentry-brand.storage.googleapis.com/sentry-logo-black.png" width="280">
</a>
<br />
</p>

Sentry integration for `dio` package
===========

| package | build | pub | likes | popularity | pub points |
| ------- | ------- | ------- | ------- | ------- | ------- |
| sentry | [![build](https://github.com/getsentry/sentry-dart/workflows/sentry-dio/badge.svg?branch=main)](https://github.com/getsentry/sentry-dart/actions?query=workflow%3Asentry-dio) | [![pub package](https://img.shields.io/pub/v/sentry_dio.svg)](https://pub.dev/packages/sentry_dio) | [![likes](https://badges.bar/sentry_dio/likes)](https://pub.dev/packages/sentry_dio/score) | [![popularity](https://badges.bar/sentry_dio/popularity)](https://pub.dev/packages/sentry_dio/score) | [![pub points](https://badges.bar/sentry_dio/pub%20points)](https://pub.dev/packages/sentry_dio/score)

Integration for the [`dio`](https://pub.dev/packages/dio) package.

#### Flutter

For Flutter applications there's [`sentry_flutter`](https://pub.dev/packages/sentry_flutter) which builds on top of this package.
That will give you native crash support (for Android and iOS), [release health](https://docs.sentry.io/product/releases/health/), offline caching and more.

#### Usage

- Sign up for a Sentry.io account and get a DSN at http://sentry.io.

- Follow the installing instructions on [pub.dev](https://pub.dev/packages/sentry/install).

- Initialize the Sentry SDK using the DSN issued by Sentry.io:

```dart
import 'package:sentry/sentry.dart';
import 'package:sentry_dio/sentry_dio.dart';

Future<void> main() async {
await Sentry.init(
(options) {
options.dsn = 'https://example@sentry.io/example';
},
appRunner: initDio, // Init your App.
);
}

void initDio() {
final dio = Dio();
// Make sure this is the last initialization method,
// otherwise you might override Sentrys configuration.
dio.addSentry(...);
}
```

Dependending on you configuration, this can add performance tracing,
http breadcrumbs and automatic recording of bad http requests.

#### Resources

* [![Documentation](https://img.shields.io/badge/documentation-sentry.io-green.svg)](https://docs.sentry.io/platforms/dart/)
* [![Forum](https://img.shields.io/badge/forum-sentry-green.svg)](https://forum.sentry.io/c/sdks)
* [![Discord](https://img.shields.io/discord/621778831602221064)](https://discord.gg/Ww9hbqr)
* [![Stack Overflow](https://img.shields.io/badge/stack%20overflow-sentry-green.svg)](https://stackoverflow.com/questions/tagged/sentry)
* [![Twitter Follow](https://img.shields.io/twitter/follow/getsentry?label=getsentry&style=social)](https://twitter.com/intent/follow?screen_name=getsentry)
34 changes: 34 additions & 0 deletions dio/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
include: package:lints/recommended.yaml

analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
language:
strict-raw-types: true
errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: error
# treat missing returns as a warning (not a hint)
missing_return: error
# allow having TODOs in the code
todo: ignore
# allow self-reference to deprecated members (we do this because otherwise we have
# to annotate every member in every test, assert, etc, when we deprecate something)
deprecated_member_use_from_same_package: warning
# ignore sentry/path on pubspec as we change it on deployment
invalid_dependency: ignore
unnecessary_import: ignore
exclude:
- example/**

linter:
rules:
- prefer_final_locals
- public_member_api_docs
- prefer_single_quotes
- prefer_relative_imports
- unnecessary_brace_in_string_interps
- implementation_imports
- require_trailing_commas
- unawaited_futures
17 changes: 17 additions & 0 deletions dio/example/example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:dio/dio.dart';
import 'package:sentry/sentry.dart';
import 'package:sentry_dio/sentry_dio.dart';

Future<void> main() async {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we make a pub project like https://github.com/getsentry/sentry-dart/tree/main/dart/example?
so we can compile and run easily on CI as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I've also included it in the Flutter example app, maybe that's already enough?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mm I guess because of paths-ignore, we'd not have a failed CI when changes don't integrate well?
I still kinda prefer the pub project facility, but it's a personal opinion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can do that in a different PR, so we don't block it.

await Sentry.init(
(options) {
options.dsn = 'https://example@sentry.io/example';
},
appRunner: initDio, // Init your App.
);
}

void initDio() {
final dio = Dio();
dio.httpClientAdapter = SentryHttpClientAdapter();
}
3 changes: 3 additions & 0 deletions dio/lib/sentry_dio.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
library sentry_dio;

export 'src/sentry_dio_extension.dart';
74 changes: 74 additions & 0 deletions dio/lib/src/breadcrumb_client_adapter.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// ignore_for_file: strict_raw_type

import 'dart:typed_data';

import 'package:dio/dio.dart';
import 'package:sentry/sentry.dart';

/// A [Dio](https://pub.dev/packages/dio)-package compatible HTTP client adapter
/// which records requests as breadcrumbs.
///
/// Remarks:
/// If this client is used as a wrapper, a call to close also closes the
/// given client.
class BreadcrumbClientAdapter extends HttpClientAdapter {
// ignore: public_member_api_docs
BreadcrumbClientAdapter({required HttpClientAdapter client, Hub? hub})
: _hub = hub ?? HubAdapter(),
_client = client;

final HttpClientAdapter _client;
final Hub _hub;

@override
Future<ResponseBody> fetch(
RequestOptions options,
Stream<Uint8List>? requestStream,
Future? cancelFuture,
) async {
// See https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/

var requestHadException = false;
int? statusCode;
String? reason;
int? responseBodySize;
ueman marked this conversation as resolved.
Show resolved Hide resolved

final stopwatch = Stopwatch();
stopwatch.start();

try {
final response =
await _client.fetch(options, requestStream, cancelFuture);

statusCode = response.statusCode;
reason = response.statusMessage;
final contentLengthHeader = response.headers['content-length'];
if (contentLengthHeader != null && contentLengthHeader.isNotEmpty) {
final headerValue = contentLengthHeader.first;
responseBodySize = int.tryParse(headerValue);
}

return response;
} catch (_) {
requestHadException = true;
rethrow;
} finally {
stopwatch.stop();

final breadcrumb = Breadcrumb.http(
level: requestHadException ? SentryLevel.error : SentryLevel.info,
url: options.uri,
method: options.method,
statusCode: statusCode,
reason: reason,
requestDuration: stopwatch.elapsed,
responseBodySize: responseBodySize,
);

_hub.addBreadcrumb(breadcrumb);
}
}

@override
void close({bool force = false}) => _client.close(force: force);
}
Loading