这是indexloc提供的服务,不要输入任何密码
Skip to content

Add a gRPC logger (Restored) #389

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open

Add a gRPC logger (Restored) #389

wants to merge 3 commits into from

Conversation

Frezyx
Copy link
Owner

@Frezyx Frezyx commented Jun 22, 2025

As discussed in this issue: #201 (comment).

This PR adds a gRPC logger. This logger was inspired by the http logger and works very similarly. The logger can be used to instrument a gRPC client, allowing to transparently log gRPC calls.
The logger obfuscates authorization tokens by default (I find it really insecure to write tokens to the logs), but this can be disabled if needs be.
For now, it only supports unary RPCs, but adding streaming RPCs shouldn't be too difficult.

Summary by Sourcery

Add a standalone gRPC logger package for Talker to transparently log unary RPC calls with request, response, and error details, including optional authorization token obfuscation.

New Features:

  • Introduce TalkerGrpcLogger interceptor to log unary gRPC requests, responses, and errors
  • Enable default obfuscation of authorization tokens in metadata with a toggleable option

Build:

  • Include pubspec.yaml and analysis_options.yaml to configure package dependencies and lints

Documentation:

  • Add README with usage instructions, preview, and token obfuscation details
  • Provide an example application demonstrating integration with TalkerGrpcLogger

Tests:

  • Add initial test stub to verify package setup

Chores:

  • Scaffold package structure with library export, changelog, and .gitignore

@Frezyx Frezyx added enhancement New feature or request work_in_progress Сurrently under work addons Related to addons/bridge packages like dio_logger and bloc_logger v5 Related to new package big release 5.0.0 labels Jun 22, 2025
Copy link
Contributor

sourcery-ai bot commented Jun 22, 2025

Reviewer's Guide

This PR introduces a new talker_grpc_logger package that provides structured logging for unary gRPC calls by defining log classes and a client interceptor, scaffolded with full package setup and documentation.

Sequence diagram for gRPC unary call logging with TalkerGrpcLogger

sequenceDiagram
    participant Client
    participant TalkerGrpcLogger
    participant Talker
    participant gRPC_Server

    Client->>TalkerGrpcLogger: Make unary gRPC call
    TalkerGrpcLogger->>Talker: logTyped(GrpcRequestLog)
    TalkerGrpcLogger->>gRPC_Server: Forward request
    gRPC_Server-->>TalkerGrpcLogger: Response or Error
    alt Success
        TalkerGrpcLogger->>Talker: logTyped(GrpcResponseLog)
        TalkerGrpcLogger-->>Client: Return response
    else Error
        TalkerGrpcLogger->>Talker: logTyped(GrpcErrorLog)
        TalkerGrpcLogger-->>Client: Return error
    end
Loading

File-Level Changes

Change Details Files
Add structured gRPC log classes
  • Define GrpcRequestLog with JSON header formatting and token obfuscation
  • Define GrpcErrorLog capturing error details, duration, and obfuscated headers
  • Define GrpcResponseLog capturing response durations
packages/talker_grpc_logger/lib/src/grpc_logs.dart
Implement TalkerGrpcLogger interceptor
  • Log requests before invocation using GrpcRequestLog
  • Measure and log successful responses with GrpcResponseLog
  • Catch and log errors with GrpcErrorLog
  • Stub streaming interceptor for future support
packages/talker_grpc_logger/lib/src/talker_grpc_logger_base.dart
Scaffold new Dart package
  • Add README with usage examples and token obfuscation details
  • Define pubspec.yaml with dependencies (grpc, talker)
  • Include example application demonstrating interceptor setup
  • Add basic test file and CHANGELOG
  • Configure analysis_options and .gitignore
  • Provide library export file
packages/talker_grpc_logger/README.md
packages/talker_grpc_logger/pubspec.yaml
packages/talker_grpc_logger/example/talker_grpc_logger_example.dart
packages/talker_grpc_logger/test/talker_grpc_logger_test.dart
packages/talker_grpc_logger/CHANGELOG.md
packages/talker_grpc_logger/analysis_options.yaml
packages/talker_grpc_logger/.gitignore
packages/talker_grpc_logger/lib/talker_grpc_logger.dart

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

@Frezyx Frezyx linked an issue Jun 22, 2025 that may be closed by this pull request
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 @Frezyx - I've reviewed your changes - here's some feedback:

  • The generated test in test/talker_grpc_logger_test.dart still references a non‐existent Awesome class — replace this placeholder with real unit tests that verify TalkerGrpcLogger’s behavior.
  • The interceptStreaming method only prints to console; consider either implementing streaming RPC logging or throwing a clear “not supported” error rather than leaving a no-op.
  • You’re passing a title into each Grpc*Log constructor but then overriding the title getter with a constant string; remove the unused constructor parameter or make the getter use it to avoid confusion.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The generated test in test/talker_grpc_logger_test.dart still references a non‐existent `Awesome` class — replace this placeholder with real unit tests that verify TalkerGrpcLogger’s behavior.
- The interceptStreaming method only prints to console; consider either implementing streaming RPC logging or throwing a clear “not supported” error rather than leaving a no-op.
- You’re passing a `title` into each Grpc*Log constructor but then overriding the `title` getter with a constant string; remove the unused constructor parameter or make the getter use it to avoid confusion.

## Individual Comments

### Comment 1
<location> `packages/talker_grpc_logger/lib/src/grpc_logs.dart:38` </location>
<code_context>
+    // Add the headers to the log message, but obfuscate the token if
+    // necessary.
+    final Map<String, String> headers = {};
+    options.metadata.forEach((key, value) {
+      if (obfuscateToken && key.toLowerCase() == 'authorization') {
+        headers[key] = 'Bearer [obfuscated]';
+      } else {
+        headers[key] = value;
+      }
+    });
+
+    try {
</code_context>

<issue_to_address>
Assumes all metadata values are strings, which may not always be the case.

gRPC metadata values can be non-strings (e.g., List<String>), so this code may fail or log incorrectly if such values are present. Please add type checks or handle non-string values appropriately.
</issue_to_address>

### Comment 2
<location> `packages/talker_grpc_logger/lib/src/grpc_logs.dart:51` </location>
<code_context>
+        final prettyHeaders = encoder.convert(headers);
+        msg += '\nHeaders: $prettyHeaders';
+      }
+    } catch (_) {
+      // TODO: add handling can`t convert
+    }
+    return msg;
</code_context>

<issue_to_address>
Catching all exceptions without handling or logging may obscure errors.

Please log the exception or provide a fallback message in the catch block to aid debugging if header conversion fails.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    } catch (_) {
      // TODO: add handling can`t convert
    }
    return msg;
=======
    } catch (e, stackTrace) {
      msg += '\nHeaders: <Failed to convert headers: $e>';
      // Optionally, log the stack trace if a logger is available:
      // logger.error('Failed to convert headers', e, stackTrace);
    }
    return msg;
>>>>>>> REPLACE

</suggested_fix>

### Comment 3
<location> `packages/talker_grpc_logger/lib/src/talker_grpc_logger_base.dart:29` </location>
<code_context>
+    DateTime startTime = DateTime.now();
+    final response = invoker(method, request, options);
+
+    response.then((r) {
+      Duration elapsedTime = DateTime.now().difference(startTime);
+      _talker.logTyped(GrpcResponseLog(method.path,
+          method: method, response: r, durationMs: elapsedTime.inMilliseconds));
+    }).catchError((e) {
+      Duration elapsedTime = DateTime.now().difference(startTime);
+      _talker.logTyped(GrpcErrorLog(method.path,
</code_context>

<issue_to_address>
Error handling assumes all errors are GrpcError instances.

If 'e' is not a GrpcError, passing it directly to GrpcErrorLog may cause a runtime error. Please add a type check and handle unexpected error types accordingly.
</issue_to_address>

### Comment 4
<location> `packages/talker_grpc_logger/lib/src/talker_grpc_logger_base.dart:52` </location>
<code_context>
+      Stream<Q> requests,
+      CallOptions options,
+      ClientStreamingInvoker<Q, R> invoker) {
+    print('interceptStreaming');
+
+    return invoker(method, requests, options);
</code_context>

<issue_to_address>
Debug print statement left in production code.

Please remove the print statement or replace it with appropriate logging if necessary.
</issue_to_address>

### Comment 5
<location> `packages/talker_grpc_logger/README.md:98` </location>
<code_context>
+false`:
+
+```dart
+TalkerGrpcLogger(talker: talker, obfuscateToken: true)
+```
</code_context>

<issue_to_address>
Code snippet shows enabling obfuscation, not disabling as described.

Update the code snippet to use `obfuscateToken: false` to match the description above.
</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.

Comment on lines +38 to +44
options.metadata.forEach((key, value) {
if (obfuscateToken && key.toLowerCase() == 'authorization') {
headers[key] = 'Bearer [obfuscated]';
} else {
headers[key] = value;
}
});
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): Assumes all metadata values are strings, which may not always be the case.

gRPC metadata values can be non-strings (e.g., List), so this code may fail or log incorrectly if such values are present. Please add type checks or handle non-string values appropriately.

Comment on lines +51 to +54
} catch (_) {
// TODO: add handling can`t convert
}
return msg;
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): Catching all exceptions without handling or logging may obscure errors.

Please log the exception or provide a fallback message in the catch block to aid debugging if header conversion fails.

Suggested change
} catch (_) {
// TODO: add handling can`t convert
}
return msg;
} catch (e, stackTrace) {
msg += '\nHeaders: <Failed to convert headers: $e>';
// Optionally, log the stack trace if a logger is available:
// logger.error('Failed to convert headers', e, stackTrace);
}
return msg;

Comment on lines +29 to +33
response.then((r) {
Duration elapsedTime = DateTime.now().difference(startTime);
_talker.logTyped(GrpcResponseLog(method.path,
method: method, response: r, durationMs: elapsedTime.inMilliseconds));
}).catchError((e) {
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): Error handling assumes all errors are GrpcError instances.

If 'e' is not a GrpcError, passing it directly to GrpcErrorLog may cause a runtime error. Please add a type check and handle unexpected error types accordingly.

Stream<Q> requests,
CallOptions options,
ClientStreamingInvoker<Q, R> invoker) {
print('interceptStreaming');
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: Debug print statement left in production code.

Please remove the print statement or replace it with appropriate logging if necessary.

false`:

```dart
TalkerGrpcLogger(talker: talker, obfuscateToken: true)
Copy link
Contributor

Choose a reason for hiding this comment

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

issue: Code snippet shows enabling obfuscation, not disabling as described.

Update the code snippet to use obfuscateToken: false to match the description above.

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 enhancement New feature or request v5 Related to new package big release 5.0.0 work_in_progress Сurrently under work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Creation of a gRPC logger
2 participants