sentry.dart 4.26 KB
Newer Older
1
// Copyright (C) 2019  Wilko Manger
Wilko Manger's avatar
Wilko Manger committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
//
// This file is part of Pattle.
//
// Pattle is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Pattle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with Pattle.  If not, see <https://www.gnu.org/licenses/>.

import 'dart:async';
19
import 'dart:convert';
Wilko Manger's avatar
Wilko Manger committed
20

21
import 'package:chopper/chopper.dart';
Wilko Manger's avatar
Wilko Manger committed
22 23
import 'package:flutter/foundation.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
24
import 'package:matrix_sdk/matrix_sdk.dart' as matrix;
Wilko Manger's avatar
Wilko Manger committed
25 26 27 28 29 30 31 32 33 34
import 'package:sentry/sentry.dart';
import 'package:package_info/package_info.dart';
import 'package:device_info/device_info.dart';
import 'dart:io' show Platform;

SentryClient _sentry;

Future<void> _reportError(dynamic error, dynamic stackTrace) async {
  print('Caught error: $error');
  if (_isInDebugMode) {
35 36 37 38 39 40 41 42 43

    if (error is Response) {
      print('statusCode: ${error.statusCode}');
      print('headers: ${error.headers}');
      print('body: ${error.body}');
    } else if (error is matrix.MatrixException) {
      print('body: ${error.body}');
    }

44 45 46 47
    if (stackTrace != null) {
      print(stackTrace);
    }

Wilko Manger's avatar
Wilko Manger committed
48
  } else {
49
    if (error is Response) {
50 51 52 53 54 55 56
      var body;
      try {
        body = json.decode(error.body);
      } on FormatException {
        body = error.body?.toString();
      }

57 58 59 60 61 62
      _sentry.capture(
        event: Event(
          exception: error,
          stackTrace: stackTrace,
          extra: {
            'status_code': error.statusCode,
63 64
            'headers': error.headers,
            'body': body
65 66 67
          }
        )
      );
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
    } else if (error is matrix.MatrixException) {
      _sentry.capture(
        event: Event(
          exception: error,
          stackTrace: stackTrace,
          extra: {
            'body': error.body,
          }
        )
      );
    } else {
      _sentry.captureException(
        exception: error,
        stackTrace: stackTrace,
      );
83
    }
Wilko Manger's avatar
Wilko Manger committed
84 85 86 87 88 89 90
  }
}

bool get _isInDebugMode {
  bool inDebugMode = false;

  // Set to true if running debug mode (where asserts are evaluated)
Wilko Manger's avatar
Wilko Manger committed
91
  assert(inDebugMode = true);
Wilko Manger's avatar
Wilko Manger committed
92 93 94 95

  return inDebugMode;
}

96
Future<Event> get _environment async {
Wilko Manger's avatar
Wilko Manger committed
97 98
  final deviceInfo = DeviceInfoPlugin();

99 100 101 102
  User user;
  Os os;
  Device device;

Wilko Manger's avatar
Wilko Manger committed
103
  if (Platform.isAndroid) {
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    final info = await deviceInfo.androidInfo;

    user = User(
      id: info.androidId
    );

    os = Os(
      name: 'Android',
      version: info.version.release,
      build: info.version.sdkInt.toString(),
    );

    device = Device(
      model: info.model,
      manufacturer: info.manufacturer,
      brand: info.brand,
      simulator: !info.isPhysicalDevice
    );

Wilko Manger's avatar
Wilko Manger committed
123
  } else if (Platform.isIOS){
124 125 126 127 128 129 130 131
    final info = await deviceInfo.iosInfo;

    user = User(
      id: info.identifierForVendor
    );

    os = Os(
      name: 'iOS',
Wilko Manger's avatar
Wilko Manger committed
132
      version: info.systemVersion,
133 134 135 136
    );

    device = Device(
      family: info.model,
Wilko Manger's avatar
Wilko Manger committed
137
      model: info.name,
138 139
      simulator: !info.isPhysicalDevice
    );
Wilko Manger's avatar
Wilko Manger committed
140
  }
141 142 143 144 145 146

  final packageInfo = await PackageInfo.fromPlatform();

  return Event(
    release: packageInfo.version,
    userContext: user,
Wilko Manger's avatar
Wilko Manger committed
147
    environment: 'production',
148 149 150 151 152 153 154 155 156
    contexts: Contexts(
      device: device,
      os: os,
      app: App(
        build: packageInfo.buildNumber,
        buildType: DotEnv().env['BUILD_TYPE'],
      )
    )
  );
Wilko Manger's avatar
Wilko Manger committed
157 158 159 160 161 162 163
}

Future<void> init() async {
  await DotEnv().load();

  _sentry = SentryClient(
    dsn: DotEnv().env['SENTRY_DSN'],
164
    environmentAttributes: await _environment
Wilko Manger's avatar
Wilko Manger committed
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
  );

  FlutterError.onError = (FlutterErrorDetails details) {
    if (_isInDebugMode) {
      FlutterError.dumpErrorToConsole(details);
    } else {
      // Report to zone
      Zone.current.handleUncaughtError(details.exception, details.stack);
    }
  };
}

void wrap(Function run) {
  runZoned<Future<void>>(() async {
    run();
  }, onError: (error, stackTrace) {
    _reportError(error, stackTrace);
  });
}