LoginSignup
7
2

Flutter retrofit(dio)でログを出す

Last updated at Posted at 2024-02-17

Flutterに限らずですが、retrofitを用いたAPI通信をする場合にログを出力しながら
デバッグしていきたい場面があると思います。
今回はサンプルとしてホットペッパーグルメAPIを用いながら解説していきます。

ちなみに、retrofitは以下のようにAPI通信に関わるデータの変換処理を簡易的にしてくれるパッケージです。
build_runnerを実行すると、.g.dartファイルが生成され、めんどくさい変換処理を担ってくれます。
あくまで変換を簡易的にしてくれるもので、実際に通信を簡易的にしてくれるのはdioパッケージの役割です。

@RestApi()
abstract class HotpepperApiClient {
  factory HotpepperApiClient(AppDio dio, {String? baseUrl}) =
      _HotpepperApiClient;

  @GET('')
  Future<String> fetchHotpepperInfo({
    @Query('key') required String apiKey,
    @Query('format') required String format,
    @Query('lat') required double latitude,
    @Query('lng') required double longitude,
    @Query('range') required int range,
  });
}

(今回はAppDioがメインテーマになるので後ほど解説します)

使い方はこんな感じです。

final hotpepperClient = HotpepperApiClient(AppDio());
await hotpepperClient.fetchHotpepperInfo(
  apiKey: 'xxxxxx',
  format: 'json',
  latitude: 35.351532,
  longitude: 139.845848,
  range: 1,
);

このretrofit / dioはとても便利なのですが、実際にAPI通信のリクエストとレスポンスでどう言うデータを送受信しているかは設定しないと確認できません。以下で提示するようにDioMixinを継承してカスタムすることが可能です。

import 'package:dio/dio.dart';

class AppDio extends DioMixin {
  factory AppDio() {
    final instance = _instance;
    if (instance != null) {
      return instance;
    }
    final dio = AppDio._();
    final options = BaseOptions(
      baseUrl: 'https://webservice.recruit.co.jp/hotpepper/gourmet/v1',
      connectTimeout: const Duration(seconds: 15),
      headers: {
        'accept': '*/*',
        'Accept-Language': 'ja',
        'X-Api-Version': '2.0',
        'Content-Type': 'application/json',
      },
      contentType: 'application/json',
    );
    dio
      ..options = options
      ..httpClientAdapter = HttpClientAdapter();
    dio.interceptors.add(
      InterceptorsWrapper(
        onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
          print('######## Request Log ########');
          print('--> ${options.method} ${options.path}');
          print('Full URL: ${options.uri}');
          print('Headers: ${options.headers}');
          print('Query Parameters: ${options.queryParameters}');
          print('Request Data: ${options.data}');
          return handler.next(options);
        },
        onResponse: (Response response, ResponseInterceptorHandler handler) {
          print('######## Response Log ########');
          print('<-- ${response.statusCode} ${response.requestOptions.path}');
          print('Headers: ${response.headers}');
          print('Response Data: ${response.data}');
          return handler.next(response);
        },
        onError: (DioException e, ErrorInterceptorHandler handler) {
          print('######## Error Log ########');
          print('<-- Error --> ${e.message}');
          return handler.next(e);
        },
      ),
    );
    _instance = dio;
    return dio;
  }

  AppDio._();

  static AppDio? _instance;
}

それを、冒頭のコードと同じになってしまいますがretrofitのコードに指定すればOKです

@RestApi()
abstract class HotpepperApiClient {
  factory HotpepperApiClient(AppDio dio, {String? baseUrl}) =
      _HotpepperApiClient;

  @GET('')
  Future<String> fetchHotpepperInfo({
    @Query('key') required String apiKey,
    @Query('format') required String format,
    @Query('lat') required double latitude,
    @Query('lng') required double longitude,
    @Query('range') required int range,
  });
}

サンプルなのでprint関数を用いていますが、おそらくlintで警告が出てしまうと思います。
そのため、適宜以下のようなログ用のパッケージを導入して置き換えてみてください。

ただし、Dioパッケージは便利なものでデフォルトでもLogInterceptorというクラスが用意されているためこちらを用いてもいいですね。
手動でログをカスタムする書き方を提示しまいしたが、ある程度のフォーマットであれば以下のような記述でも問題ないと思います。

dio.interceptors.add(
-   InterceptorsWrapper(
-     onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
-       debugPrint('######## Request Log ########');
-       debugPrint('--> ${options.method} ${options.path}');
-       debugPrint('Full URL: ${options.uri}');
-       debugPrint('Headers: ${options.headers}');
-       debugPrint('Query Parameters: ${options.queryParameters}');
-       debugPrint('Request Data: ${options.data}');
-       return handler.next(options);
-     },
-     onResponse:
-         (Response<dynamic> response, ResponseInterceptorHandler handler) {
-       debugPrint('######## Response Log ########');
-       debugPrint(
-         '<-- ${response.statusCode} ${response.requestOptions.path}',
-       );
-       debugPrint('Headers: ${response.headers}');
-       debugPrint('Response Data: ${response.data}');
-       return handler.next(response);
-     },
-     onError: (DioException e, ErrorInterceptorHandler handler) {
-       debugPrint('######## Error Log ########');
-       debugPrint('<-- Error --> ${e.message}');
-       return handler.next(e);
-     },
-   ),
- );
+ dio.interceptors.add(LogInterceptor(
+   request: true,
+   requestHeader: true,
+   requestBody: true,
+   responseHeader: true,
+   responseBody: true,
+ ));
7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2