Skip to content

Comments

feat(drift): add talker_drift_logger package and integrate Drift logg…#426

Open
becandier wants to merge 3 commits intoFrezyx:masterfrom
becandier:feat/talker-drift-logger
Open

feat(drift): add talker_drift_logger package and integrate Drift logg…#426
becandier wants to merge 3 commits intoFrezyx:masterfrom
becandier:feat/talker-drift-logger

Conversation

@becandier
Copy link

@becandier becandier commented Sep 16, 2025

Добавил новый пакет - логгер для Drift (talker_drift_logger) на базе Talker. В самом talker подвез ключи для drift (query/result/error/transaction/batch) и дефолтные тайтлы/цвета; в talker_flutter прописал эти цвета в TalkerScreen, чтобы логи были не серые.

Интерцептор наследуется от QueryInterceptor и перехватывает все базовые операции (select/insert/update/delete/custom), а также транзакции и батчи. Пишет в Talker: запрос, результат (с временем, количеством строк и срезом данных), и ошибку. Всё настраивается через TalkerDriftLoggerSettings: можно включать/выключать печать аргументов/результатов, ограничивать объёмы, маскировать поля, фильтровать запросы/ошибки. Подключение - просто NativeDatabase(...).interceptWith(TalkerDriftLogger(...)).

Есть базовые тесты и CI; в корневом README добавил пакет и краткую секцию.

Summary by Sourcery

Add talker_drift_logger package to log Drift database operations through Talker, register new drift log keys and colors in Talker core and Flutter UI, and include configuration settings, documentation, examples, tests, and CI setup.

New Features:

  • Introduce talker_drift_logger package with a QueryInterceptor for logging Drift SQL operations, transactions, and batches.
  • Provide TalkerDriftLoggerSettings to customize logging behavior, including argument/result printing, obfuscation, filtering, and styling.
  • Register new drift-specific log keys and default titles/colors in talker and talker_flutter packages.

Enhancements:

  • Integrate colored Drift logs into TalkerFlutter UI for better readability.

CI:

  • Add GitHub Actions workflow for testing the talker_drift_logger package.

Documentation:

  • Update root README to include talker_drift_logger and add package README with usage, settings, and example.
  • Add initial changelog and example project for talker_drift_logger.

Tests:

  • Add interceptor tests for logging queries, results, and errors.

…ing\n\n- Core: add Drift keys to talker (talker_key.dart), default titles/colors (settings.dart)\n- UI: add TalkerScreen colors for Drift keys (talker_flutter)\n- Package: talker_drift_logger with interceptor, settings, logs; tests; example; README\n- CI: add workflow, align with repo; remove min_coverage override to match other packages\n- Repo docs: update root README with package row and section\n\nBREAKING CHANGE: none
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Sep 16, 2025

Reviewer's Guide

This PR introduces a new talker_drift_logger package for logging Drift database operations via Talker, extends core Talker to support Drift log keys and colors, updates documentation, and adds tests, examples, and CI for the new package.

Sequence diagram for logging a Drift SELECT query with TalkerDriftLogger

sequenceDiagram
  participant App
  participant Drift
  participant TalkerDriftLogger
  participant Talker

  App->>Drift: runSelect(statement, args)
  Drift->>TalkerDriftLogger: runSelect(statement, args)
  TalkerDriftLogger->>Talker: logCustom(DriftQueryLog)
  TalkerDriftLogger->>Drift: executor.runSelect(statement, args)
  Drift-->>TalkerDriftLogger: rows
  TalkerDriftLogger->>Talker: logCustom(DriftResultLog)
  TalkerDriftLogger-->>Drift: rows
  Drift-->>App: rows
Loading

Sequence diagram for logging a Drift transaction (begin/commit/rollback)

sequenceDiagram
  participant App
  participant Drift
  participant TalkerDriftLogger
  participant Talker

  App->>Drift: beginTransaction()
  Drift->>TalkerDriftLogger: beginTransaction(parent)
  TalkerDriftLogger->>Talker: logCustom(DriftTransactionLog)
  TalkerDriftLogger-->>Drift: TransactionExecutor

  App->>Drift: commitTransaction(inner)
  Drift->>TalkerDriftLogger: commitTransaction(inner)
  TalkerDriftLogger->>Talker: logCustom(DriftTransactionLog)
  TalkerDriftLogger-->>Drift: (commit complete)

  App->>Drift: rollbackTransaction(inner)
  Drift->>TalkerDriftLogger: rollbackTransaction(inner)
  TalkerDriftLogger->>Talker: logCustom(DriftTransactionLog)
  TalkerDriftLogger-->>Drift: (rollback complete)
Loading

Class diagram for TalkerDriftLogger and related log classes

classDiagram
  class TalkerDriftLogger {
    - Talker _talker
    + TalkerDriftLoggerSettings settings
    + Future<T> _run<T>(...)
    + bool _accept(...)
    + bool _acceptError(...)
    + TransactionExecutor beginTransaction(...)
    + Future<void> commitTransaction(...)
    + Future<void> rollbackTransaction(...)
    + Future<void> runBatched(...)
    + Future<int> runInsert(...)
    + Future<int> runUpdate(...)
    + Future<int> runDelete(...)
    + Future<void> runCustom(...)
    + Future<List<Map<String, Object?>>> runSelect(...)
  }
  TalkerDriftLogger --|> QueryInterceptor
  TalkerDriftLogger --> TalkerDriftLoggerSettings
  TalkerDriftLogger --> Talker

  class TalkerDriftLoggerSettings {
    + bool enabled
    + LogLevel logLevel
    + bool printArgs
    + bool printResults
    + int? resultRowLimit
    + int? resultMaxChars
    + bool printTransaction
    + bool printBatch
    + Set<String> obfuscateColumns
    + Set<Pattern> obfuscatePatterns
    + AnsiPen? queryPen
    + AnsiPen? resultPen
    + AnsiPen? errorPen
    + AnsiPen? transactionPen
    + AnsiPen? batchPen
    + bool Function(String, List<Object?>)? statementFilter
    + bool Function(Object)? errorFilter
    + String Function(List<Map<String, Object?>>)? resultPrinter
    + String Function(List<Object?>)? argsPrinter
    + copyWith(...)
  }

  class DriftQueryLog {
    + List<Object?> args
    + TalkerDriftLoggerSettings settings
    + AnsiPen pen
    + String key
    + LogLevel logLevel
    + String generateTextMessage(...)
  }
  DriftQueryLog --|> TalkerLog

  class DriftResultLog {
    + TalkerDriftLoggerSettings settings
    + int? durationMs
    + int? rowCount
    + List<Map<String, Object?>>? rows
    + int? affected
    + AnsiPen pen
    + String key
    + LogLevel logLevel
    + String generateTextMessage(...)
  }
  DriftResultLog --|> TalkerLog

  class DriftErrorLog {
    + TalkerDriftLoggerSettings settings
    + Object dbError
    + List<Object?> args
    + int? durationMs
    + AnsiPen pen
    + String key
    + LogLevel logLevel
    + String generateTextMessage(...)
  }
  DriftErrorLog --|> TalkerLog

  class DriftTransactionLog {
    + TalkerDriftLoggerSettings settings
    + int? durationMs
    + AnsiPen pen
    + String key
    + LogLevel logLevel
    + String generateTextMessage(...)
  }
  DriftTransactionLog --|> TalkerLog

  class DriftBatchLog {
    + TalkerDriftLoggerSettings settings
    + AnsiPen pen
    + String key
    + LogLevel logLevel
    + String generateTextMessage(...)
  }
  DriftBatchLog --|> TalkerLog

  TalkerDriftLogger --> DriftQueryLog
  TalkerDriftLogger --> DriftResultLog
  TalkerDriftLogger --> DriftErrorLog
  TalkerDriftLogger --> DriftTransactionLog
  TalkerDriftLogger --> DriftBatchLog
Loading

File-Level Changes

Change Details Files
Root documentation updated to include the new Drift logger
  • Added a new table entry and section for talker_drift_logger in the root README
  • Inserted basic usage snippets and Getting Started instructions
  • Updated the Table of Contents to reference the new section
README.md
Extended Talker core modules with Drift log support
  • Registered new Drift log keys in settings and talker_key
  • Added default titles and ANSI colors for query, result, error, transaction, batch
  • Integrated those colors into the Flutter TalkerScreen theme
packages/talker/lib/src/settings.dart
packages/talker/lib/src/talker_key.dart
packages/talker_flutter/lib/src/ui/theme/talker_screen_theme.dart
Refined the flutter package pubspec formatting
  • Standardized quote style for sdk and flutter version constraints
  • Removed an extraneous blank line
packages/talker_flutter/pubspec.yaml
Implemented the talker_drift_logger package interceptor and log types
  • Created TalkerDriftLogger extending QueryInterceptor with stopwatch wrappers for all operations
  • Logged SQL, results, errors, transactions, and batches using custom TalkerLog subclasses
  • Built JSON masking, truncation, and formatting utilities
packages/talker_drift_logger/lib/talker_drift_logger_interceptor.dart
packages/talker_drift_logger/lib/drift_logs.dart
packages/talker_drift_logger/lib/talker_drift_logger_settings.dart
packages/talker_drift_logger/lib/talker_drift_logger.dart
Added docs, examples, tests, and CI for talker_drift_logger
  • Added package-level README, CHANGELOG, analysis options, and pubspec
  • Provided example app demonstrating interceptor usage
  • Wrote unit tests for query/result and error logging
  • Configured a GitHub Actions workflow for CI
packages/talker_drift_logger/README.md
packages/talker_drift_logger/CHANGELOG.md
packages/talker_drift_logger/pubspec.yaml
packages/talker_drift_logger/analysis_options.yaml
packages/talker_drift_logger/test/interceptor_test.dart
packages/talker_drift_logger/example/lib/main.dart
.github/workflows/talker_drift_logger.yaml

Possibly linked issues

  • Fix logs formating #1: PR adds a talker_drift_logger package, fulfilling the issue's request for a drift package by providing logging capabilities.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey there - I've reviewed your changes - here's some feedback:

  • In TalkerDriftLogger constructor, consider using the predefined TalkerKey.drift* constants when registering log keys instead of hardcoded strings to avoid typos and ensure consistency.
  • The interceptor methods for insert, update, delete, and custom all share similar patterns; extracting the common logic into a reusable private helper would reduce duplication and improve maintainability.
  • The cosmetic changes to talker_flutter/pubspec.yaml (changing quote styles) seem unrelated to the drift logger feature; consider reverting these to keep the diff focused.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In TalkerDriftLogger constructor, consider using the predefined TalkerKey.drift* constants when registering log keys instead of hardcoded strings to avoid typos and ensure consistency.
- The interceptor methods for insert, update, delete, and custom all share similar patterns; extracting the common logic into a reusable private helper would reduce duplication and improve maintainability.
- The cosmetic changes to talker_flutter/pubspec.yaml (changing quote styles) seem unrelated to the drift logger feature; consider reverting these to keep the diff focused.

## Individual Comments

### Comment 1
<location> `packages/talker_drift_logger/lib/talker_drift_logger_interceptor.dart:50` </location>
<code_context>
+
+  bool _accept(String statement, List<Object?> args) {
+    if (!settings.enabled) return false;
+    final filter = settings.statementFilter;
+    if (filter == null) return true;
+    try {
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Consider handling exceptions from statementFilter more explicitly.

If statementFilter throws, the error is swallowed and false is returned. Logging or surfacing the error would help with debugging misconfigurations and unexpected behavior.

Suggested implementation:

```
  bool _accept(String statement, List<Object?> args) {
    if (!settings.enabled) return false;
    final filter = settings.statementFilter;
    if (filter == null) return true;
    try {

```

```
    try {
      return filter(statement, args);
    } catch (e, stackTrace) {
      // Log the error for debugging misconfigurations and unexpected behavior
      print('Error in statementFilter: $e\n$stackTrace');
      return false;
    }

```
</issue_to_address>

### Comment 2
<location> `packages/talker_drift_logger/lib/talker_drift_logger_interceptor.dart:268` </location>
<code_context>
+    if (accepted) {
+      _talker.logCustom(DriftQueryLog(statement, args: args, settings: settings));
+    }
+    int? durationMs;
+    return _run<List<Map<String, Object?>>>(
+      statement,
</code_context>

<issue_to_address>
**issue (bug_risk):** Storing durationMs outside the async context may lead to race conditions.

Because durationMs is set in the onDone callback but used in the .then block, its value may be inconsistent if the future resolves before the callback. To prevent this, pass durationMs directly to DriftResultLog within the callback or refactor to eliminate shared mutable state.
</issue_to_address>

### Comment 3
<location> `packages/talker_drift_logger/lib/drift_logs.dart:119` </location>
<code_context>
+  final int? durationMs;
+  final int? rowCount;
+  final List<Map<String, Object?>>? rows;
+  final int? affected;
+
+  @override
</code_context>

<issue_to_address>
**suggestion:** The 'affected' field in DriftResultLog is not consistently set.

Please ensure 'affected' is set for all applicable operations, or remove it if it's not needed to maintain clarity.

Suggested implementation:

```
    this.durationMs,
    this.rowCount,
    this.rows,
  }) : super(statement);

  final TalkerDriftLoggerSettings settings;
  final int? durationMs;
  final int? rowCount;
  final List<Map<String, Object?>>? rows;

```

```

```

If you want to keep the `affected` field, you must ensure it is set for all applicable operations (e.g., in the constructors or factory methods that create `DriftResultLog` instances for INSERT, UPDATE, DELETE). If you decide to keep it, review all usages and ensure it is always set when relevant.
</issue_to_address>

### Comment 4
<location> `packages/talker_drift_logger/lib/talker_drift_logger_settings.dart:70` </location>
<code_context>
+  final String Function(List<Map<String, Object?>> rows)? resultPrinter;
+  final String Function(List<Object?> args)? argsPrinter;
+
+  TalkerDriftLoggerSettings copyWith({
+    bool? enabled,
+    LogLevel? logLevel,
</code_context>

<issue_to_address>
**suggestion:** copyWith does not allow unsetting nullable fields.

Currently, it's not possible to set a nullable field to null using copyWith. Please update the method to allow explicitly passing null for nullable fields.

Suggested implementation:

```
  final bool Function(String statement, List<Object?> args)? statementFilter;
  final bool Function(Object error)? errorFilter;
  final String Function(List<Map<String, Object?>> rows)? resultPrinter;
  final String Function(List<Object?> args)? argsPrinter;

  static const _unset = Object();

  TalkerDriftLoggerSettings copyWith({
    bool? enabled,
    LogLevel? logLevel,
    bool? printArgs,
    bool? printResults,
    int? resultRowLimit,
    int? resultMaxChars,
    bool? printTransaction,
    bool? printBatch,
    Set<String>? obfuscateColumns,
    Set<Pattern>? obfuscatePatterns,
    AnsiPen? queryPen,
    AnsiPen? resultPen,
    Object? statementFilter = _unset,
    Object? errorFilter = _unset,
    Object? resultPrinter = _unset,
    Object? argsPrinter = _unset,
    Object? errorPen = _unset,
    Object? transactionPen = _unset,
    Object? batchPen = _unset,
=======

```

```
    Set<String>? obfuscateColumns,
    Set<Pattern>? obfuscatePatterns,
    AnsiPen? queryPen,
    AnsiPen? resultPen,
    // Add other nullable fields here as needed

```

```
  }) {
    return TalkerDriftLoggerSettings(
      enabled: enabled ?? this.enabled,
      logLevel: logLevel ?? this.logLevel,
      printArgs: printArgs ?? this.printArgs,
      printResults: printResults ?? this.printResults,
      resultRowLimit: resultRowLimit ?? this.resultRowLimit,
      resultMaxChars: resultMaxChars ?? this.resultMaxChars,
      printTransaction: printTransaction ?? this.printTransaction,
      printBatch: printBatch ?? this.printBatch,
      obfuscateColumns: obfuscateColumns ?? this.obfuscateColumns,
      obfuscatePatterns: obfuscatePatterns ?? this.obfuscatePatterns,
      queryPen: queryPen ?? this.queryPen,
      resultPen: resultPen ?? this.resultPen,
      statementFilter: identical(statementFilter, _unset)
          ? this.statementFilter
          : statementFilter as bool Function(String, List<Object?>)?,
      errorFilter: identical(errorFilter, _unset)
          ? this.errorFilter
          : errorFilter as bool Function(Object)?,
      resultPrinter: identical(resultPrinter, _unset)
          ? this.resultPrinter
          : resultPrinter as String Function(List<Map<String, Object?>>)?,
      argsPrinter: identical(argsPrinter, _unset)
          ? this.argsPrinter
          : argsPrinter as String Function(List<Object?>)?,
      errorPen: identical(errorPen, _unset)
          ? this.errorPen
          : errorPen as AnsiPen?,
      transactionPen: identical(transactionPen, _unset)
          ? this.transactionPen
          : transactionPen as AnsiPen?,
      batchPen: identical(batchPen, _unset)
          ? this.batchPen
          : batchPen as AnsiPen?,
    );
  }

```

If there are other nullable fields in your class, you should add them to the `copyWith` signature and update the constructor call in the same way.
Update all usages of `copyWith` to use the new API, e.g. `copyWith(resultPrinter: null)` to unset.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.


bool _accept(String statement, List<Object?> args) {
if (!settings.enabled) return false;
final filter = settings.statementFilter;
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Consider handling exceptions from statementFilter more explicitly.

If statementFilter throws, the error is swallowed and false is returned. Logging or surfacing the error would help with debugging misconfigurations and unexpected behavior.

Suggested implementation:

  bool _accept(String statement, List<Object?> args) {
    if (!settings.enabled) return false;
    final filter = settings.statementFilter;
    if (filter == null) return true;
    try {

    try {
      return filter(statement, args);
    } catch (e, stackTrace) {
      // Log the error for debugging misconfigurations and unexpected behavior
      print('Error in statementFilter: $e\n$stackTrace');
      return false;
    }

if (accepted) {
_talker.logCustom(DriftQueryLog(statement, args: args, settings: settings));
}
int? durationMs;
Copy link
Contributor

Choose a reason for hiding this comment

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

issue (bug_risk): Storing durationMs outside the async context may lead to race conditions.

Because durationMs is set in the onDone callback but used in the .then block, its value may be inconsistent if the future resolves before the callback. To prevent this, pass durationMs directly to DriftResultLog within the callback or refactor to eliminate shared mutable state.

final int? durationMs;
final int? rowCount;
final List<Map<String, Object?>>? rows;
final int? affected;
Copy link
Contributor

Choose a reason for hiding this comment

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

suggestion: The 'affected' field in DriftResultLog is not consistently set.

Please ensure 'affected' is set for all applicable operations, or remove it if it's not needed to maintain clarity.

Suggested implementation:

    this.durationMs,
    this.rowCount,
    this.rows,
  }) : super(statement);

  final TalkerDriftLoggerSettings settings;
  final int? durationMs;
  final int? rowCount;
  final List<Map<String, Object?>>? rows;


If you want to keep the affected field, you must ensure it is set for all applicable operations (e.g., in the constructors or factory methods that create DriftResultLog instances for INSERT, UPDATE, DELETE). If you decide to keep it, review all usages and ensure it is always set when relevant.

…rove filters error handling; fix runSelect duration race\n\n- Keys: replace hardcoded strings with TalkerKey.drift*\n- Filters: surface statementFilter/errorFilter exceptions via talker.handle\n- Select: measure duration inside async block to avoid race
@codecov-commenter
Copy link

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.14%. Comparing base (1ed15ab) to head (b0800c7).
⚠️ Report is 100 commits behind head on master.
❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #426      +/-   ##
==========================================
- Coverage   98.63%   91.14%   -7.49%     
==========================================
  Files           3       13      +10     
  Lines         146      271     +125     
==========================================
+ Hits          144      247     +103     
- Misses          2       24      +22     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@listepo listepo mentioned this pull request Oct 19, 2025
@Frezyx Frezyx added consideration Awaiting a decision on implementation addons Related to addons/bridge packages like dio_logger and bloc_logger labels Dec 2, 2025
@Frezyx Frezyx linked an issue Dec 2, 2025 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

addons Related to addons/bridge packages like dio_logger and bloc_logger consideration Awaiting a decision on implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Integration for drift

3 participants