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

[SLT-141] feat(metrics): multiple exports #3099

Merged
merged 33 commits into from
Sep 11, 2024
Merged

[SLT-141] feat(metrics): multiple exports #3099

merged 33 commits into from
Sep 11, 2024

Conversation

golangisfun123
Copy link
Collaborator

@golangisfun123 golangisfun123 commented Sep 4, 2024

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced a multi-exporter for OpenTelemetry trace data, allowing simultaneous dispatch to multiple backends.
    • Added support for additional OTLP exporters, enhancing flexibility in data exporting configurations.
  • Bug Fixes

    • Improved error handling during exporter creation for the OpenTelemetry metrics handler.
  • Documentation

    • Enhanced documentation regarding environment variables for configuring multiple exporters in OpenTelemetry, including new variables and their descriptions.

Copy link
Contributor

coderabbitai bot commented Sep 4, 2024

Walkthrough

The changes introduce a multi-exporter for OpenTelemetry trace data, enabling simultaneous span exports to multiple OTLP exporters. Enhancements to the metrics handler include support for multiple OTLP exporters and improved client creation logic. Documentation updates clarify the use of new environment variables for configuring exporters.

Changes

Files Change Summary
core/metrics/multiexporter.go, core/metrics/multiexporter_test.go Introduced multiexporter.go for simultaneous span exports to multiple OTLP exporters and added corresponding tests.
core/metrics/otlp.go, core/metrics/export_test.go, core/metrics/otlp_test.go Added support for multiple OTLP exporters, refactored client creation, and introduced utility functions with tests.
core/metrics/README.md Enhanced documentation regarding new environment variables for configuring multiple OTLP exporters.
core/go.mod Added direct dependency on google.golang.org/grpc v1.64.0 and removed the indirect dependency.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant OTLPHandler
    participant MultiExporter
    participant Exporter1
    participant Exporter2

    User->>OTLPHandler: Send Trace Data
    OTLPHandler->>MultiExporter: Export Spans
    MultiExporter->>Exporter1: Export Spans
    MultiExporter->>Exporter2: Export Spans
    Exporter1-->>MultiExporter: Success/Failure
    Exporter2-->>MultiExporter: Success/Failure
    MultiExporter-->>OTLPHandler: Export Result
    OTLPHandler-->>User: Acknowledge
Loading

Poem

🐇
In fields of code, I hop with glee,
New exporters dance, oh joy for me!
With spans that fly to every place,
Telemetry's grace, a swift embrace.
So let us cheer, both near and far,
For changes bright, like a shining star!
🌟


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added go Pull requests that update Go code size/s labels Sep 4, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between dfb97a8 and 9632578.

Files selected for processing (3)
  • core/metrics/multiexporter.go (1 hunks)
  • core/metrics/otlp.go (5 hunks)
  • core/metrics/rookout.go (1 hunks)
Additional comments not posted (11)
core/metrics/rookout.go (2)

1-1: Verify the reason for the build constraint and its impact.

The build constraint restricts the compatibility of the code to Go versions 1.16 through 1.22, excluding 1.23. This change could potentially impact users who may be using Go 1.23 or later.

Please provide more context on the reason for introducing this build constraint. Also, consider the impact on users and document it appropriately.


6-7: LGTM!

The change in the import section improves clarity.

core/metrics/multiexporter.go (4)

11-13: LGTM!

The code changes are approved.


18-22: LGTM!

The code changes are approved.


25-33: Verify the error handling behavior.

The current implementation returns an error immediately if any of the exporters fail to export spans. This may not be the desired behavior in all cases, as it prevents the remaining exporters from receiving the spans.

Consider the following alternative approaches:

  1. Log the error and continue exporting to the remaining exporters.
  2. Accumulate the errors and return them as a single error after attempting to export to all the exporters.

Discuss with the team and choose the approach that aligns with the project's error handling strategy.


36-44: Verify the error handling behavior.

The current implementation returns an error immediately if any of the exporters fail to shut down. This may not be the desired behavior in all cases, as it prevents the remaining exporters from being properly shut down.

Consider the following alternative approaches:

  1. Log the error and continue shutting down the remaining exporters.
  2. Accumulate the errors and return them as a single error after attempting to shut down all the exporters.

Discuss with the team and choose the approach that aligns with the project's error handling strategy.

core/metrics/otlp.go (5)

31-39: Refactoring improves modularity and error handling.

The refactoring of the Start method to use the buildClientFromTransport function for creating the OTLP clients is a great improvement. It enhances the modularity and readability of the code by abstracting the client creation logic into a separate function. The addition of a secondary client allows for more flexible data exporting configurations.

Moreover, the improved error handling, which returns specific errors when client creation fails, enhances the robustness of the code by providing more informative error messages.


52-62: MultiExporter enhances flexibility in data exporting.

The introduction of the MultiExporter, which combines the primary and secondary OTLP exporters, is a valuable addition. It allows for simultaneous span exports to multiple OTLP exporters, providing flexibility in data exporting configurations.

Furthermore, the initialization of the baseHandler with the MultiExporter and the configuration of maximum queue size and export batch size parameters improves the control over the exporting behavior, enabling fine-tuning of the exporting process.


108-109: New constant for secondary OTLP transport improves maintainability.

The addition of the otlpTransportEnvSecondary constant for the secondary OTLP transport environment variable is a good practice. It provides a clear and maintainable way to reference the secondary transport, making the code more readable and less prone to errors.

Keeping the existing otlpTransportEnv constant unchanged ensures backward compatibility.


120-129: New function improves code modularity and error handling.

The introduction of the buildClientFromTransport function is a great addition to the codebase. It abstracts the client creation logic based on the transport type, improving code modularity and reusability. The function takes an otlpTransportType as input and returns an otlptrace.Client and an error, providing a clear and consistent interface.

The use of a switch statement to handle different transport types makes the code more readable and maintainable. Additionally, returning an error for unknown transport types enhances error handling and prevents unexpected behavior.


145-148: New constants for default values improve readability and maintainability.

The introduction of the defaultMaxQueueSize and defaultMaxExportBatch constants is a good practice. It provides clear and maintainable default values for the maximum queue size and export batch size, respectively.

Using constants instead of hardcoded values improves code readability and makes it easier to modify these values in the future if needed. It also ensures consistency throughout the codebase.

Copy link

cloudflare-workers-and-pages bot commented Sep 4, 2024

Deploying sanguine-fe with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0e3a5ae
Status: ✅  Deploy successful!
Preview URL: https://3f23bdc3.sanguine-fe.pages.dev
Branch Preview URL: https://multiple-exports.sanguine-fe.pages.dev

View logs

Copy link

codecov bot commented Sep 4, 2024

Codecov Report

Attention: Patch coverage is 70.73171% with 48 lines in your changes missing coverage. Please review.

Project coverage is 32.90317%. Comparing base (651ce66) to head (0e3a5ae).
Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
core/metrics/otlp.go 70.63492% 30 Missing and 7 partials ⚠️
core/metrics/multiexporter.go 71.05263% 9 Missing and 2 partials ⚠️
Additional details and impacted files
@@                 Coverage Diff                 @@
##              master       #3099         +/-   ##
===================================================
- Coverage   33.38834%   32.90317%   -0.48517%     
===================================================
  Files            343         534        +191     
  Lines          20603       33398      +12795     
  Branches          82          82                 
===================================================
+ Hits            6879       10989       +4110     
- Misses         13147       21431       +8284     
- Partials         577         978        +401     
Flag Coverage Δ
cctp-relayer 31.97848% <ø> (ø)
core 58.80282% <70.73171%> (?)
ethergo 47.18923% <ø> (+0.05277%) ⬆️
git-changes-action 23.48315% <ø> (?)
omnirpc 33.23904% <ø> (ø)
opbot 0.49261% <ø> (ø)
promexporter 6.82171% <ø> (ø)
screener-api 29.45990% <ø> (?)
scribe 18.18182% <ø> (-0.06433%) ⬇️
tools 30.55118% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 9632578 and c762c4c.

Files selected for processing (1)
  • core/metrics/otlp.go (5 hunks)
Additional comments not posted (5)
core/metrics/otlp.go (5)

31-39: LGTM!

The changes improve error handling and abstract the client creation logic.


41-47: LGTM!

The changes introduce support for a secondary OTLP exporter and handle errors appropriately.


55-60: LGTM!

The changes integrate the secondary exporter into the multi-exporter setup and handle errors appropriately.


62-70: LGTM!

The changes integrate the multi-exporter into the baseHandler and provide clearer configuration options.


128-137: LGTM!

The buildClientFromTransport function improves the modularity and robustness of the client creation process.

@golangisfun123 golangisfun123 changed the title feat(metrics): multiple exports [SLT-141] feat(metrics): multiple exports Sep 4, 2024
@trajan0x trajan0x linked an issue Sep 5, 2024 that may be closed by this pull request
@@ -101,6 +125,17 @@ const (
otlpTransportGRPC // grpc
)

func buildClientFromTransport(transport otlpTransportType) (otlptrace.Client, error) {
Copy link
Contributor

@trajan0x trajan0x Sep 5, 2024

Choose a reason for hiding this comment

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

Relevant docs:

exporters general doc
exporter envs these are used for construction of otlp client

sent a loom but tl;dr, at the moment you don't actually allow different urls b/c any otlptracehttp.NewClient(), or otlptracehttp.NewClient(), is going to use the defualt enviornment variable name

Seperately, make sure you add new enviornment variables to metrics/README.md

return fmt.Errorf("could not create client: %w", err)
}

secondaryClient, err := buildClientFromTransport(
Copy link
Contributor

Choose a reason for hiding this comment

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

we should be able to tolerate cases where user doesn't define a secondary transport

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between c762c4c and e831fe9.

Files selected for processing (1)
  • core/metrics/otlp.go (5 hunks)
Additional context used
GitHub Check: codecov/patch
core/metrics/otlp.go

[warning] 39-40: core/metrics/otlp.go#L39-L40
Added lines #L39 - L40 were not covered by tests


[warning] 49-49: core/metrics/otlp.go#L49
Added line #L49 was not covered by tests


[warning] 59-60: core/metrics/otlp.go#L59-L60
Added lines #L59 - L60 were not covered by tests


[warning] 132-132: core/metrics/otlp.go#L132
Added line #L132 was not covered by tests


[warning] 136-137: core/metrics/otlp.go#L136-L137
Added lines #L136 - L137 were not covered by tests

Additional comments not posted (4)
core/metrics/otlp.go (4)

31-49: Verify handling of missing secondary transport.

The initialization of client and secondaryClient uses environment variables with sensible defaults. The error handling is robust, but ensure that the application gracefully handles cases where the secondary transport is not defined, as suggested in a previous review.

Consider testing or documenting the behavior when OTEL_EXPORTER_OTLP_TRANSPORT_SECONDARY is not set.

Tools
GitHub Check: codecov/patch

[warning] 39-40: core/metrics/otlp.go#L39-L40
Added lines #L39 - L40 were not covered by tests


[warning] 49-49: core/metrics/otlp.go#L49
Added line #L49 was not covered by tests


62-72: Approve the creation and integration of multiExporter.

The multiExporter is effectively created and integrated with the baseHandler, utilizing both the primary and secondary exporters. This setup enhances the modularity and robustness of the metrics handling.


156-158: Approve the addition of export configuration constants.

The introduction of defaultMaxQueueSize and defaultMaxExportBatch provides clear and sensible defaults for managing the exporter's performance and resource usage.


154-158: Approve the graceful shutdown handling.

The handleShutdown function is well-implemented, using a derived context with a timeout to ensure a graceful shutdown. The approach of waiting a bit before shutting down to allow the last spans to be sent is prudent.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Outside diff range, codebase verification and nitpick comments (1)
core/metrics/README.md (1)

15-21: Typographical errors in the documentation.

There are several instances of the word "environment" being misspelled as "enviornment" throughout the document. Consistency and correctness in documentation are crucial for professional quality and user comprehension.

Please correct all instances of this typographical error to maintain the quality of the documentation.

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between e831fe9 and 68def3c.

Files selected for processing (2)
  • core/metrics/README.md (1 hunks)
  • core/metrics/otlp.go (5 hunks)
Additional context used
GitHub Check: codecov/patch
core/metrics/otlp.go

[warning] 67-67: core/metrics/otlp.go#L67
Added line #L67 was not covered by tests


[warning] 138-139: core/metrics/otlp.go#L138-L139
Added lines #L138 - L139 were not covered by tests


[warning] 142-143: core/metrics/otlp.go#L142-L143
Added lines #L142 - L143 were not covered by tests

core/metrics/otlp.go Outdated Show resolved Hide resolved
core/metrics/otlp.go Outdated Show resolved Hide resolved
Comment on lines 15 to 21
We do our best to support enviornment variables specified in the [Otel Spec](https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/) and have added a few of our own. This was to allow for multiple exporter backends for traces, as otel clients only allow for one URL. The relevant multi exporter code is in `multiexporter.go`, and simply wraps multiple otel clients.

The additional environment variables to note are:
| Enviornment Variable | Description | Default |
|------------------------------------------|-------------------------------------------|---------|
| `OTEL_EXPORTER_OTLP_TRANSPORT_PRIMARY` | Primary exporter URL to send traces to. | `true` |
| `OTEL_EXPORTER_OTLP_TRANSPORT_SECONDARY` | Secondary exporter URL to send traces to. | `8080` |
Copy link
Contributor

Choose a reason for hiding this comment

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

Clarify the default values for new environment variables.

The documentation introduces two new environment variables, OTEL_EXPORTER_OTLP_TRANSPORT_PRIMARY and OTEL_EXPORTER_OTLP_TRANSPORT_SECONDARY. However, the default values listed seem incorrect:

  • OTEL_EXPORTER_OTLP_TRANSPORT_PRIMARY is listed with a default of true, which is unusual for a URL.
  • OTEL_EXPORTER_OTLP_TRANSPORT_SECONDARY is listed with a default of 8080, which typically represents a port, not a URL.

Please revise the default values to reflect appropriate URLs or clarify if these are placeholders.


Enhance the explanation of the multi-exporter functionality.

The section explaining the multi-exporter functionality could benefit from a more detailed explanation or example. The current text mentions that the multiexporter.go file "simply wraps multiple otel clients," but it does not explain how this is achieved or how users can configure or utilize this functionality effectively.

Consider adding a code snippet or a more detailed step-by-step guide on setting up multiple exporters using the provided environment variables.


Inconsistent information regarding support for profiling.

The table mentions that OTLP does not support profiling directly but can do so through Pyroscope by specifying the PYROSCOPE_ENDPOINT environment variable. However, there is no further explanation or reference to Pyroscope in the OTLP section, which might confuse users about how to set up profiling support.

It would be beneficial to include a brief explanation or cross-reference to the Pyroscope section within the OTLP documentation to clarify how profiling can be enabled.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 68def3c and 51284b9.

Files selected for processing (1)
  • core/metrics/otlp.go (5 hunks)
Additional comments not posted (4)
core/metrics/otlp.go (4)

31-49: Refactor client creation logic to handle secondary transport conditionally.

The current implementation always attempts to create a secondary client, even if the environment variable for the secondary transport is not set. This could lead to unnecessary error handling or exceptions when the secondary transport is not intended to be used.

Consider refactoring the logic to check if the secondary transport environment variable is set before attempting to create the secondary client. This approach aligns with the previous reviewer's suggestion to tolerate cases where the user doesn't define a secondary transport.

Here's a proposed refactor to conditionally handle the secondary client creation:

- secondaryClient, err = getClient(otlpTransportEnvSecondary)
- if err != nil {
-     return fmt.Errorf("could not create secondary client: %w", err)
- }
+ if core.GetEnv(otlpTransportEnvSecondary, "") != "" {
+     secondaryClient, err = getClient(otlpTransportEnvSecondary)
+     if err != nil {
+         return fmt.Errorf("could not create secondary client: %w", err)
+     }
+ }

This comment has been previously made and is still valid.


57-67: Ensure test coverage for multi-exporter logic.

The introduction of the multi-exporter logic is a significant enhancement. However, static analysis indicates that the line where the multi-exporter is created with only the primary exporter (line 67) is not covered by tests.

It's crucial to ensure that both scenarios (with and without the secondary exporter) are covered by unit tests to verify that the multi-exporter behaves as expected in all configurations.

Consider adding unit tests to cover these scenarios to ensure robust error handling and functionality verification.

Tools
GitHub Check: codecov/patch

[warning] 67-67: core/metrics/otlp.go#L67
Added line #L67 was not covered by tests

This comment has been previously made and is still valid.

123-125: Clarify the use of new environment variables in documentation.

The introduction of new constants for environment variables (otlpTransportEnvPrimary and otlpTransportEnvSecondary) is a key part of this feature. However, it's important to ensure that these new variables are well-documented.

As per the previous reviewer's suggestion, add these environment variables to the metrics/README.md to ensure that users are aware of how to configure the primary and secondary transports.

Would you like me to help update the documentation or open a GitHub issue to track this task?
This comment has been previously made and is still valid.


136-149: Improve error handling in buildClientFromTransport.

The function buildClientFromTransport is crucial for creating clients based on the transport type. However, static analysis indicates that the error handling for unknown transport types (lines 142-143) is not covered by tests.

This lack of coverage could lead to untested paths in production, potentially resulting in runtime errors if an unsupported transport type is accidentally used.

Enhance the robustness of this function by adding unit tests to cover these error scenarios. Additionally, consider logging or monitoring these errors to aid in troubleshooting and maintaining the system's stability.

Tools
GitHub Check: codecov/patch

[warning] 138-139: core/metrics/otlp.go#L138-L139
Added lines #L138 - L139 were not covered by tests

[warning] 142-143: core/metrics/otlp.go#L142-L143
Added lines #L142 - L143 were not covered by tests

This comment has been previously made and is still valid.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 7

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 51284b9 and f8a2048.

Files selected for processing (2)
  • core/metrics/multiexporter.go (1 hunks)
  • core/metrics/otlp.go (5 hunks)
Additional context used
GitHub Check: codecov/patch
core/metrics/multiexporter.go

[warning] 31-36: core/metrics/multiexporter.go#L31-L36
Added lines #L31 - L36 were not covered by tests


[warning] 38-38: core/metrics/multiexporter.go#L38
Added line #L38 was not covered by tests


[warning] 46-47: core/metrics/multiexporter.go#L46-L47
Added lines #L46 - L47 were not covered by tests

core/metrics/otlp.go

[warning] 35-38: core/metrics/otlp.go#L35-L38
Added lines #L35 - L38 were not covered by tests


[warning] 43-44: core/metrics/otlp.go#L43-L44
Added lines #L43 - L44 were not covered by tests


[warning] 48-49: core/metrics/otlp.go#L48-L49
Added lines #L48 - L49 were not covered by tests


[warning] 62-63: core/metrics/otlp.go#L62-L63
Added lines #L62 - L63 were not covered by tests


[warning] 65-65: core/metrics/otlp.go#L65
Added line #L65 was not covered by tests


[warning] 152-153: core/metrics/otlp.go#L152-L153
Added lines #L152 - L153 were not covered by tests


[warning] 156-157: core/metrics/otlp.go#L156-L157
Added lines #L156 - L157 were not covered by tests

Additional comments not posted (4)
core/metrics/multiexporter.go (4)

11-15: Interface Definition Review: MultiExporter

The MultiExporter interface is well-defined, extending tracesdk.SpanExporter and including an AddExporter method. This design supports the dynamic addition of exporters, which is crucial for the flexibility of exporting to multiple backends.


17-19: Struct Definition Review: multiExporter

The multiExporter struct is simple and effective, holding a slice of *otlptrace.Exporter. This structure supports the intended functionality of managing multiple exporters.


21-28: Function Implementation Review: NewMultiExporter

The NewMultiExporter function is implemented correctly to instantiate multiExporter with a variable number of otlptrace.Exporter instances. This flexibility allows easy setup of multiple export targets.


52-55: Method Implementation Review: AddExporter

The AddExporter method correctly appends a new exporter to the slice. This method is straightforward and effectively allows the dynamic addition of exporters at runtime.

Comment on lines 31 to 36
func (m *multiExporter) ExportSpans(ctx context.Context, ss []tracesdk.ReadOnlySpan) error {
for _, exporter := range m.exporters {
err := exporter.ExportSpans(ctx, ss)
if err != nil {
return fmt.Errorf("could not export spans: %w", err)
}
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 5, 2024

Choose a reason for hiding this comment

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

Test Coverage Issue

The static analysis tool has flagged several lines in the ExportSpans and Shutdown methods as not covered by tests. This lack of coverage could lead to undetected issues in production.

It is crucial to add unit tests covering these methods to ensure their functionality under various scenarios, especially error handling.

Would you like assistance in writing these tests or perhaps a suggestion on how to structure them?

Also applies to: 38-38, 46-47

Tools
GitHub Check: codecov/patch

[warning] 31-36: core/metrics/multiexporter.go#L31-L36
Added lines #L31 - L36 were not covered by tests

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

@trajan0x, I've reviewed the changes, and the test coverage issue has been resolved. The ExportSpans and Shutdown methods are now covered in the TestMultiExporter function, ensuring that their functionality is tested. Great work on addressing this!

Analysis chain


Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Fetch the diff file to inspect the changes made by the user.
gh pr diff 3099 --path core/metrics/multiexporter_test.go

Length of output: 411


Script:

#!/bin/bash
# List the files changed in the pull request to confirm if core/metrics/multiexporter_test.go is present.
gh pr diff 3099 --name-only

Length of output: 162


Script:

#!/bin/bash
# Fetch the diff for core/metrics/multiexporter_test.go to inspect the changes.
gh pr diff 3099 -- core/metrics/multiexporter_test.go

Length of output: 92


Script:

#!/bin/bash
# Fetch the entire diff for the pull request and filter for core/metrics/multiexporter_test.go.
gh pr diff 3099 | awk '/core\/metrics\/multiexporter_test.go/,/^diff/'

Length of output: 1604

Comment on lines 30 to 39
// ExportSpans exports a batch of spans.
func (m *multiExporter) ExportSpans(ctx context.Context, ss []tracesdk.ReadOnlySpan) error {
for _, exporter := range m.exporters {
err := exporter.ExportSpans(ctx, ss)
if err != nil {
return fmt.Errorf("could not export spans: %w", err)
}
}
return nil
}
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 5, 2024

Choose a reason for hiding this comment

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

Method Implementation Review: ExportSpans

The ExportSpans method iterates over each exporter and attempts to export spans. If an error occurs during export, it immediately returns the error, halting further exports. This could be reconsidered to attempt all exports before returning an error, which would ensure that a failure in one exporter doesn't prevent others from receiving data.

Consider modifying the error handling to allow all exporters a chance to receive spans before returning an error. This could be achieved by collecting all errors and returning a combined error if necessary.

-		if err != nil {
-			return fmt.Errorf("could not export spans: %w", err)
-		}
+		if err != nil {
+			// Collect errors from all exporters
+			allErrors = append(allErrors, err)
+		}
+	}
+	if len(allErrors) > 0 {
+		return fmt.Errorf("could not export spans to some exporters: %v", allErrors)
+	}

Committable suggestion was skipped due to low confidence.

Tools
GitHub Check: codecov/patch

[warning] 31-36: core/metrics/multiexporter.go#L31-L36
Added lines #L31 - L36 were not covered by tests


[warning] 38-38: core/metrics/multiexporter.go#L38
Added line #L38 was not covered by tests

Copy link
Contributor

Choose a reason for hiding this comment

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

@golangisfun123 thoughts in desirable behavior?

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines 41 to 50
// Shutdown notifies the exporter of a pending halt to operations.
func (m *multiExporter) Shutdown(ctx context.Context) error {
for _, exporter := range m.exporters {
err := exporter.Shutdown(ctx)
if err != nil {
return fmt.Errorf("could not stop exporter: %w", err)
}
}
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Method Implementation Review: Shutdown

Similar to ExportSpans, the Shutdown method stops at the first error encountered. This could potentially leave some exporters running if one fails to shut down properly.

Consider implementing a similar error aggregation strategy as suggested for ExportSpans to ensure all exporters are attempted to be shut down before returning an error.

-		if err != nil {
-			return fmt.Errorf("could not stop exporter: %w", err)
-		}
+		if err != nil {
+			// Collect errors from all exporters
+			allErrors = append(allErrors, err)
+		}
+	}
+	if len(allErrors) > 0 {
+		return fmt.Errorf("could not stop some exporters: %v", allErrors)
+	}

Committable suggestion was skipped due to low confidence.

Tools
GitHub Check: codecov/patch

[warning] 46-47: core/metrics/multiexporter.go#L46-L47
Added lines #L46 - L47 were not covered by tests

Comment on lines 57 to 75
// create the multi-exporter with optional secondary exporter
multiExporter := NewMultiExporter(exporter)
if secondaryClient != nil {
secondaryExporter, err := otlptrace.New(ctx, secondaryClient)
if err != nil {
return fmt.Errorf("failed to create secondary otlp exporter: %w", err)
}
multiExporter.AddExporter(secondaryExporter)
}

n.baseHandler = newBaseHandler(
n.buildInfo,
tracesdk.WithBatcher(
multiExporter,
tracesdk.WithMaxQueueSize(defaultMaxQueueSize),
tracesdk.WithMaxExportBatchSize(defaultMaxExportBatch),
),
tracesdk.WithSampler(tracesdk.AlwaysSample()),
)
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure test coverage for multi-exporter logic.

The introduction of the multi-exporter logic is a significant enhancement. However, static analysis indicates that the line where the multi-exporter is created with only the primary exporter (line 67) is not covered by tests.

It's crucial to ensure that both scenarios (with and without the secondary exporter) are covered by unit tests to verify that the multi-exporter behaves as expected in all configurations.

Consider adding unit tests to cover these scenarios to ensure robust error handling and functionality verification.

Tools
GitHub Check: codecov/patch

[warning] 67-67: core/metrics/otlp.go#L67
Added line #L67 was not covered by tests

Tools
GitHub Check: codecov/patch

[warning] 62-63: core/metrics/otlp.go#L62-L63
Added lines #L62 - L63 were not covered by tests


[warning] 65-65: core/metrics/otlp.go#L65
Added line #L65 was not covered by tests

Comment on lines 145 to 159
// buildClientFromTransport creates a new OTLP client based on the transport type and url.
func buildClientFromTransport(transport otlpTransportType, url string) (otlptrace.Client, error) {
if url == "" {
return nil, fmt.Errorf("no url specified")
}

switch transport {
case otlpTransportHTTP:
return otlptracehttp.NewClient(otlptracehttp.WithEndpointURL(url)), nil
case otlpTransportGRPC:
return otlptracegrpc.NewClient(otlptracegrpc.WithEndpointURL(url)), nil
default:
return nil, fmt.Errorf("unknown transport type: %s", transport.String())
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve error handling in buildClientFromTransport.

The function buildClientFromTransport is crucial for creating clients based on the transport type. However, static analysis indicates that the error handling for unknown transport types (lines 156-157) is not covered by tests.

This lack of coverage could lead to untested paths in production, potentially resulting in runtime errors if an unsupported transport type is accidentally used.

Enhance the robustness of this function by adding unit tests to cover these error scenarios. Additionally, consider logging or monitoring these errors to aid in troubleshooting and maintaining the system's stability.

Tools
GitHub Check: codecov/patch

[warning] 156-157: core/metrics/otlp.go#L156-L157
Added lines #L156 - L157 were not covered by tests

Tools
GitHub Check: codecov/patch

[warning] 152-153: core/metrics/otlp.go#L152-L153
Added lines #L152 - L153 were not covered by tests


[warning] 156-157: core/metrics/otlp.go#L156-L157
Added lines #L156 - L157 were not covered by tests

Comment on lines 121 to 123
otlpTransportEnvDefault = "OTEL_EXPORTER_OTLP_TRANSPORT"
otlpTransportEnvPrimary = "OTEL_EXPORTER_OTLP_TRANSPORT_PRIMARY"
otlpTransportEnvSecondary = "OTEL_EXPORTER_OTLP_TRANSPORT_SECONDARY"
Copy link
Contributor

Choose a reason for hiding this comment

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

Clarify the use of new environment variables in documentation.

The introduction of new constants for environment variables (otlpTransportEnvPrimary and otlpTransportEnvSecondary) is a key part of this feature. However, it's important to ensure that these new variables are well-documented.

As per the previous reviewer's suggestion, add these environment variables to the metrics/README.md to ensure that users are aware of how to configure the primary and secondary transports.

Would you like me to help update the documentation or open a GitHub issue to track this task?

Comment on lines 31 to 49
isDefaultConfig := core.GetEnv(otlpTransportEnvDefault, "") != ""

var client, secondaryClient otlptrace.Client
if isDefaultConfig {
client, err = getClient(otlpTransportEnvDefault)
if err != nil {
return fmt.Errorf("could not create default client: %w", err)
}
} else {
// instantiate both the primary and secondary clients
client, err = getClient(otlpTransportEnvPrimary)
if err != nil {
return fmt.Errorf("could not create primary client: %w", err)
}

secondaryClient, err = getClient(otlpTransportEnvSecondary)
if err != nil {
return fmt.Errorf("could not create secondary client: %w", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Refactor client creation logic to handle secondary transport conditionally.

The current implementation always attempts to create a secondary client, even if the environment variable for the secondary transport is not set. This could lead to unnecessary error handling or exceptions when the secondary transport is not intended to be used.

Consider refactoring the logic to check if the secondary transport environment variable is set before attempting to create the secondary client. This approach aligns with the previous reviewer's suggestion to tolerate cases where the user doesn't define a secondary transport.

Here's a proposed refactor to conditionally handle the secondary client creation:

- secondaryClient, err = getClient(otlpTransportEnvSecondary)
- if err != nil {
-     return fmt.Errorf("could not create secondary client: %w", err)
- }
+ if core.GetEnv(otlpTransportEnvSecondary, "") != "" {
+     secondaryClient, err = getClient(otlpTransportEnvSecondary)
+     if err != nil {
+         return fmt.Errorf("could not create secondary client: %w", err)
+     }
+ }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
isDefaultConfig := core.GetEnv(otlpTransportEnvDefault, "") != ""
var client, secondaryClient otlptrace.Client
if isDefaultConfig {
client, err = getClient(otlpTransportEnvDefault)
if err != nil {
return fmt.Errorf("could not create default client: %w", err)
}
} else {
// instantiate both the primary and secondary clients
client, err = getClient(otlpTransportEnvPrimary)
if err != nil {
return fmt.Errorf("could not create primary client: %w", err)
}
secondaryClient, err = getClient(otlpTransportEnvSecondary)
if err != nil {
return fmt.Errorf("could not create secondary client: %w", err)
}
isDefaultConfig := core.GetEnv(otlpTransportEnvDefault, "") != ""
var client, secondaryClient otlptrace.Client
if isDefaultConfig {
client, err = getClient(otlpTransportEnvDefault)
if err != nil {
return fmt.Errorf("could not create default client: %w", err)
}
} else {
// instantiate both the primary and secondary clients
client, err = getClient(otlpTransportEnvPrimary)
if err != nil {
return fmt.Errorf("could not create primary client: %w", err)
}
if core.GetEnv(otlpTransportEnvSecondary, "") != "" {
secondaryClient, err = getClient(otlpTransportEnvSecondary)
if err != nil {
return fmt.Errorf("could not create secondary client: %w", err)
}
}
Tools
GitHub Check: codecov/patch

[warning] 35-38: core/metrics/otlp.go#L35-L38
Added lines #L35 - L38 were not covered by tests


[warning] 43-44: core/metrics/otlp.go#L43-L44
Added lines #L43 - L44 were not covered by tests


[warning] 48-49: core/metrics/otlp.go#L48-L49
Added lines #L48 - L49 were not covered by tests

// MultiExporter is an interface that allows exporting spans to multiple OTLP trace exporters.
type MultiExporter interface {
tracesdk.SpanExporter
AddExporter(exporter *otlptrace.Exporter)
Copy link
Contributor

Choose a reason for hiding this comment

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

should be using SpanExporter instead since it allows any exporter to be used rather than just otlptrace epxorter.

For example we can use tracetest.NewInMemoryExporter for testing (we already do this here)

)

// MultiExporter is an interface that allows exporting spans to multiple OTLP trace exporters.
type MultiExporter interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

should have a test

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 1d82704 and 48f2afc.

Files selected for processing (3)
  • core/go.mod (2 hunks)
  • core/metrics/multiexporter_test.go (1 hunks)
  • core/metrics/otlp.go (5 hunks)
Files skipped from review as they are similar to previous changes (1)
  • core/metrics/multiexporter_test.go
Additional context used
GitHub Check: codecov/patch
core/metrics/otlp.go

[warning] 49-49: core/metrics/otlp.go#L49
Added line #L49 was not covered by tests


[warning] 54-54: core/metrics/otlp.go#L54
Added line #L54 was not covered by tests


[warning] 173-173: core/metrics/otlp.go#L173
Added line #L173 was not covered by tests


[warning] 177-178: core/metrics/otlp.go#L177-L178
Added lines #L177 - L178 were not covered by tests

GitHub Check: Lint (core)
core/metrics/otlp.go

[failure] 53-53:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


[failure] 193-193:
exported: exported function WithURL should have comment or be unexported (revive)


[failure] 202-202:
exported: exported function WithInsecure should have comment or be unexported (revive)

Additional comments not posted (4)
core/metrics/otlp.go (3)

32-70: LGTM! The refactored Start method improves modularity and flexibility.

The changes to the Start method introduce support for multiple exporters, allowing for more flexible data exporting configurations. The creation of a primary exporter and the loop to add additional exporters based on environment variables enhance the modularity of the exporter setup.

The use of a multiExporter to combine all created exporters and integrate it into the baseHandler is a clean approach to handle multiple export paths.

Tools
GitHub Check: codecov/patch

[warning] 49-49: core/metrics/otlp.go#L49
Added line #L49 was not covered by tests


[warning] 54-54: core/metrics/otlp.go#L54
Added line #L54 was not covered by tests

GitHub Check: Lint (core)

[failure] 53-53:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


136-159: Add test coverage for the new makeOTLPExporter function.

The makeOTLPExporter function is a good addition that abstracts the exporter creation logic based on the transport type and URL. It improves code reusability and maintainability.

However, static analysis indicates that some lines in this function are not covered by tests:

Tools
GitHub Check: codecov/patch

[warning] 173-173: core/metrics/otlp.go#L173
Added line #L173 was not covered by tests

To ensure the robustness of this critical function, please add unit tests to cover all possible scenarios, including error cases.


162-180: Add test coverage for error handling in buildClientFromTransport.

The buildClientFromTransport function is a nice addition that creates OTLP clients based on the transport type and options. The use of a switch statement to handle different transport types makes the code readable and maintainable.

However, static analysis indicates that the error handling for unknown transport types is not covered by tests:

Tools
GitHub Check: codecov/patch

[warning] 177-178: core/metrics/otlp.go#L177-L178
Added lines #L177 - L178 were not covered by tests

To ensure the robustness of this function, please add unit tests to cover the scenario where an unknown transport type is provided. This will help prevent untested paths in production.

Tools
GitHub Check: codecov/patch

[warning] 173-173: core/metrics/otlp.go#L173
Added line #L173 was not covered by tests


[warning] 177-178: core/metrics/otlp.go#L177-L178
Added lines #L177 - L178 were not covered by tests

core/go.mod (1)

66-66: LGTM!

The changes to the google.golang.org/grpc dependency are approved. Making the dependency explicit improves clarity in dependency management and indicates a direct use of gRPC functionalities, which may enhance the project's capabilities for remote procedure calls and service communication.

@github-actions github-actions bot added size/m and removed size/s labels Sep 11, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Outside diff range and nitpick comments (4)
core/metrics/export_test.go (1)

4-5: LGTM, but add tests.

The code changes are approved.

However, consider adding tests for this function to ensure it behaves as expected.

core/metrics/otlp.go (3)

32-70: Refactoring of the Start method looks good!

The changes to support multiple exporters and the introduction of the multi-exporter improve the flexibility and modularity of the metrics handler. The use of environment variables for configuration is a good practice.

Consider adding more detailed logging statements to aid in debugging and monitoring the creation and integration of exporters.

Tools
GitHub Check: codecov/patch

[warning] 49-49: core/metrics/otlp.go#L49
Added line #L49 was not covered by tests


[warning] 54-54: core/metrics/otlp.go#L54
Added line #L54 was not covered by tests

GitHub Check: Lint (core)

[failure] 53-53:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


136-162: The new makeOTLPExporter function looks good!

Abstracting the exporter creation logic into a separate function improves code reusability. The use of getEnvSuffix allows for flexible configuration based on suffixed environment variables, and calling buildClientFromTransport separates the client creation logic from the exporter creation.

Consider adding more detailed error messages to aid in debugging. For example:

- return nil, fmt.Errorf("could not create exporter: url is empty")
+ return nil, fmt.Errorf("could not create exporter: url is empty for suffix %q", envSuffix)
- return nil, fmt.Errorf("could not create client from transport: %w", err)
+ return nil, fmt.Errorf("could not create client from transport %s: %w", transport, err)

265-268: The new constants defaultMaxQueueSize and defaultMaxExportBatch look good!

Defining these constants improves code readability and maintainability by providing clear and centralized configuration options for the exporter. Using them in the Start method ensures consistent default values for the queue size and export batch size.

Consider adding comments to describe the purpose and recommended values for these constants. For example:

const (
	// defaultMaxQueueSize is the default maximum queue size for the OTLP exporter.
	// Recommended value: 1000000
	defaultMaxQueueSize = 1000000

	// defaultMaxExportBatch is the default maximum export batch size for the OTLP exporter.
	// Recommended value: 2000
	defaultMaxExportBatch = 2000
)
Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 48f2afc and 4db816f.

Files selected for processing (3)
  • core/metrics/export_test.go (1 hunks)
  • core/metrics/otlp.go (5 hunks)
  • core/metrics/otlp_test.go (1 hunks)
Additional context used
GitHub Check: codecov/patch
core/metrics/otlp.go

[warning] 49-49: core/metrics/otlp.go#L49
Added line #L49 was not covered by tests


[warning] 54-54: core/metrics/otlp.go#L54
Added line #L54 was not covered by tests


[warning] 176-176: core/metrics/otlp.go#L176
Added line #L176 was not covered by tests


[warning] 180-181: core/metrics/otlp.go#L180-L181
Added lines #L180 - L181 were not covered by tests

GitHub Check: Lint (core)
core/metrics/otlp.go

[failure] 53-53:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


[failure] 196-196:
exported: exported function WithURL should have comment or be unexported (revive)


[failure] 205-205:
exported: exported function WithInsecure should have comment or be unexported (revive)


[failure] 220-220:
exported: exported function WithHeaders should have comment or be unexported (revive)


[failure] 239-239:
Magic number: 2, in detected (mnd)


[failure] 240-240:
Magic number: 2, in detected (mnd)

Additional comments not posted (1)
core/metrics/otlp_test.go (1)

9-71: LGTM!

The test function TestHeadersToMap is well-structured and follows the table-driven testing approach. It covers various scenarios and correctly compares the result with the expected output map using reflect.DeepEqual. The test cases are comprehensive and provide meaningful error messages when the result does not match the expected output.

The code changes are approved.

Comment on lines +164 to +183
// buildClientFromTransport creates a new OTLP client based on the transport type and url.
func buildClientFromTransport(transport otlpTransportType, options ...Option) (otlptrace.Client, error) {
to := transportOptions{}
for _, option := range options {
if err := option(&to); err != nil {
return nil, fmt.Errorf("could not apply option: %w", err)
}
}

// TODO: make sure url is validated

switch transport {
case otlpTransportHTTP:
return otlptracehttp.NewClient(to.httpOptions...), nil
case otlpTransportGRPC:
return otlptracegrpc.NewClient(to.grpcOptions...), nil
default:
return nil, fmt.Errorf("unknown transport type: %s", transport.String())
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The new buildClientFromTransport function looks good!

Separating the client creation logic based on the transport type makes the code modular and extensible. The use of a switch statement makes it easy to add support for new transport types in the future, and applying the provided options allows for flexible configuration of the transport client.

However, static analysis tools have reported a lack of test coverage for certain lines in this function.

Please add unit tests to cover the following scenarios:

  • Creating an HTTP transport client (line 176)
  • Creating a gRPC transport client (line 178)
  • Handling an unknown transport type (lines 180-181)

This will ensure that all code paths in the function are properly tested.

Tools
GitHub Check: codecov/patch

[warning] 176-176: core/metrics/otlp.go#L176
Added line #L176 was not covered by tests


[warning] 180-181: core/metrics/otlp.go#L180-L181
Added lines #L180 - L181 were not covered by tests

Comment on lines 196 to 227
func WithURL(url string) Option {
return func(o *transportOptions) error {
o.httpOptions = append(o.httpOptions, otlptracehttp.WithEndpointURL(url))
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithEndpointURL(url))

return nil
}
}

func WithInsecure(isInsecure bool) Option {
return func(o *transportOptions) error {
if isInsecure {
o.httpOptions = append(o.httpOptions, otlptracehttp.WithInsecure())
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithInsecure())
} else {
tlsCreds := credentials.NewClientTLSFromCert(nil, "")
// note: you do not need to specify the tls creds for http, this happens automatically when https:// is used as the protocol scheme.
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithTLSCredentials(tlsCreds))
}

return nil
}
}

func WithHeaders(headers string) Option {
return func(o *transportOptions) error {
realHeaders := headersToMap(headers)
o.httpOptions = append(o.httpOptions, otlptracehttp.WithHeaders(realHeaders))
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithHeaders(realHeaders))
return nil
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The new option functions WithURL, WithInsecure, and WithHeaders look good!

Providing these option functions allows for clean and extensible configuration of the OTLP exporter. Exporting them enables flexibility in configuring the exporter from other packages.

However, static analysis tools have reported that these functions should have comments or be unexported.

Please add comments for the exported functions to improve code documentation and maintainability. For example:

// WithURL sets the endpoint URL for the OTLP exporter.
func WithURL(url string) Option {
	// ...
}

// WithInsecure enables or disables secure connection for the OTLP exporter.
func WithInsecure(isInsecure bool) Option {
	// ...
}

// WithHeaders sets the headers for the OTLP exporter.
func WithHeaders(headers string) Option {
	// ...
}
Tools
GitHub Check: Lint (core)

[failure] 196-196:
exported: exported function WithURL should have comment or be unexported (revive)


[failure] 205-205:
exported: exported function WithInsecure should have comment or be unexported (revive)


[failure] 220-220:
exported: exported function WithHeaders should have comment or be unexported (revive)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Outside diff range and nitpick comments (1)
core/metrics/otlp.go (1)

197-228: The new option functions look good, but add comments for exported functions.

The new option functions WithURL, WithInsecure, and WithHeaders provide a clean and extensible way to configure the OTLP exporter. They allow for flexible configuration of the exporter by appending the corresponding options for both HTTP and gRPC transports.

However, static analysis tools have reported that these exported functions should have comments to improve code documentation and maintainability.

Please add comments for the exported functions. For example:

// WithURL sets the endpoint URL for the OTLP exporter.
func WithURL(url string) Option {
	// ...
}

// WithInsecure enables or disables secure connection for the OTLP exporter.
func WithInsecure(isInsecure bool) Option {
	// ...
}

// WithHeaders sets the headers for the OTLP exporter.
func WithHeaders(headers string) Option {
	// ...
}
Tools
GitHub Check: Lint (core)

[failure] 197-197:
exported: exported function WithURL should have comment or be unexported (revive)


[failure] 206-206:
exported: exported function WithInsecure should have comment or be unexported (revive)


[failure] 221-221:
exported: exported function WithHeaders should have comment or be unexported (revive)

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 4db816f and a19300c.

Files selected for processing (1)
  • core/metrics/otlp.go (5 hunks)
Additional context used
GitHub Check: codecov/patch
core/metrics/otlp.go

[warning] 49-49: core/metrics/otlp.go#L49
Added line #L49 was not covered by tests


[warning] 54-54: core/metrics/otlp.go#L54
Added line #L54 was not covered by tests


[warning] 177-177: core/metrics/otlp.go#L177
Added line #L177 was not covered by tests


[warning] 181-182: core/metrics/otlp.go#L181-L182
Added lines #L181 - L182 were not covered by tests

GitHub Check: Lint (core)
core/metrics/otlp.go

[failure] 53-53:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


[failure] 197-197:
exported: exported function WithURL should have comment or be unexported (revive)


[failure] 206-206:
exported: exported function WithInsecure should have comment or be unexported (revive)


[failure] 221-221:
exported: exported function WithHeaders should have comment or be unexported (revive)

Additional comments not posted (4)
core/metrics/otlp.go (4)

32-70: Refactoring of the Start method looks good!

The changes to the Start method significantly improve the modularity and flexibility of the exporter setup. The creation of a primary exporter and the loop to add additional exporters based on environment variables enhances the configurability of the metrics handler. The use of a multiExporter simplifies the integration of multiple exporters into the baseHandler.

The addition of constants for maximum queue size and export batch size provides clearer configuration options for the exporter behavior.

Tools
GitHub Check: codecov/patch

[warning] 49-49: core/metrics/otlp.go#L49
Added line #L49 was not covered by tests


[warning] 54-54: core/metrics/otlp.go#L54
Added line #L54 was not covered by tests

GitHub Check: Lint (core)

[failure] 53-53:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


137-163: The new makeOTLPExporter function looks good!

The makeOTLPExporter function encapsulates the logic for creating an OTLP exporter based on the transport type and endpoint. It abstracts the details of creating the OTLP client by using the buildClientFromTransport function. The function handles errors appropriately and returns the created exporter.


165-184: The new buildClientFromTransport function looks good, but add more unit tests.

The buildClientFromTransport function provides a clean and extensible way to create OTLP clients based on the transport type and options. It applies the provided options to the transport-specific options, allowing for flexible configuration. The use of a switch statement makes it easy to add support for new transport types in the future.

However, static analysis tools have reported a lack of test coverage for certain lines in this function.

Please add unit tests to cover the following scenarios:

  • Creating an HTTP transport client (line 177)
  • Creating a gRPC transport client (line 179)
  • Handling an unknown transport type (lines 181-182)

This will ensure that all code paths in the function are properly tested.

Tools
GitHub Check: codecov/patch

[warning] 177-177: core/metrics/otlp.go#L177
Added line #L177 was not covered by tests


[warning] 181-182: core/metrics/otlp.go#L181-L182
Added lines #L181 - L182 were not covered by tests


49-49: Improve test coverage for the otlpHandler and buildClientFromTransport.

Static analysis tools have reported a lack of test coverage for the following lines:

  • Line 49: Breaking out of the loop when no more transports are available.
  • Line 54: Error handling when creating an exporter fails.
  • Line 177: Creating an HTTP transport client.
  • Lines 181-182: Handling an unknown transport type.

Please add unit tests to cover these scenarios and improve the overall test coverage of the file. This will ensure that all code paths are properly tested and increase confidence in the correctness of the implementation.

Also applies to: 54-54, 177-177, 181-182

Tools
GitHub Check: codecov/patch

[warning] 49-49: core/metrics/otlp.go#L49
Added line #L49 was not covered by tests


exporter, err := makeOTLPExporter(ctx, envSuffix)
if err != nil {
return fmt.Errorf("could not create exporter %d: %v", i, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Address the non-wrapping format verb for fmt.Errorf.

Static analysis tools have reported the following issue:

non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)

Please use %w to format errors for better error handling and wrapping. Apply this diff to fix the issue:

-return fmt.Errorf("could not create exporter %d: %v", i, err)
+return fmt.Errorf("could not create exporter %d: %w", i, err)
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return fmt.Errorf("could not create exporter %d: %v", i, err)
return fmt.Errorf("could not create exporter %d: %w", i, err)
Tools
GitHub Check: Lint (core)

[failure] 53-53:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between a19300c and ceeddbc.

Files selected for processing (2)
  • core/metrics/README.md (1 hunks)
  • core/metrics/otlp.go (5 hunks)
Additional context used
GitHub Check: codecov/patch
core/metrics/otlp.go

[warning] 50-50: core/metrics/otlp.go#L50
Added line #L50 was not covered by tests


[warning] 55-55: core/metrics/otlp.go#L55
Added line #L55 was not covered by tests


[warning] 58-58: core/metrics/otlp.go#L58
Added line #L58 was not covered by tests


[warning] 194-194: core/metrics/otlp.go#L194
Added line #L194 was not covered by tests


[warning] 198-199: core/metrics/otlp.go#L198-L199
Added lines #L198 - L199 were not covered by tests

GitHub Check: Lint (core)
core/metrics/otlp.go

[failure] 54-54:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


[failure] 230-230:
G402: TLS MinVersion too low. (gosec)


[failure] 214-214:
exported: exported function WithURL should have comment or be unexported (revive)


[failure] 223-223:
exported: exported function WithSecure should have comment or be unexported (revive)


[failure] 243-243:
exported: exported function WithHeaders should have comment or be unexported (revive)

Additional comments not posted (6)
core/metrics/README.md (2)

17-30: LGTM!

The table provides clear and useful information about the additional environment variables for configuring multiple OTLP exporters. The default values are appropriate placeholders.


33-33: LGTM!

The note provides clear and accurate information about configuring multiple OTLP exporters using incrementing numbers in the environment variable names. It aligns well with the details provided in the table above.

core/metrics/otlp.go (4)

33-71: Refactoring of the Start method looks good!

The changes to the Start method introduce support for multiple exporters, which improves the flexibility and modularity of the exporter setup. The creation of a primary exporter and the loop to add additional exporters based on environment variables is a clean and extensible approach.

The introduction of the multiExporter to combine all created exporters and integrate it into the baseHandler is a nice way to manage multiple export paths.

Overall, these changes enhance the robustness and configurability of the metrics handling.

Tools
GitHub Check: codecov/patch

[warning] 50-50: core/metrics/otlp.go#L50
Added line #L50 was not covered by tests


[warning] 55-55: core/metrics/otlp.go#L55
Added line #L55 was not covered by tests


[warning] 58-58: core/metrics/otlp.go#L58
Added line #L58 was not covered by tests

GitHub Check: Lint (core)

[failure] 54-54:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)


289-290: New constants for configuration look good!

The addition of the defaultMaxQueueSize and defaultMaxExportBatch constants is a good practice for maintainability and readability. Using these constants in the Start method when creating the baseHandler provides clearer configuration options for the exporter behavior.


141-180: New makeOTLPExporter function looks good!

The introduction of the makeOTLPExporter function is a good abstraction for creating OTLP exporters. It encapsulates the logic for creating exporters based on the transport type and URL, making the code more modular and reusable.

The use of the envSuffix parameter to support creating exporters with different configurations is a nice touch, as it allows for more flexible setup.

The function also uses the buildClientFromTransport function to create the OTLP client, which further improves the modularity of the code.

Overall, this function is a good addition to the codebase.


182-201: The new buildClientFromTransport function looks good!

Separating the client creation logic based on the transport type makes the code modular and extensible. The use of a switch statement makes it easy to add support for new transport types in the future, and applying the provided options allows for flexible configuration of the transport client.

However, static analysis tools have reported a lack of test coverage for certain lines in this function.

Please add unit tests to cover the following scenarios:

  • Creating an HTTP transport client (line 195)
  • Creating a gRPC transport client (line 197)
  • Handling an unknown transport type (lines 198-199)

This will ensure that all code paths in the function are properly tested.

Tools
GitHub Check: codecov/patch

[warning] 194-194: core/metrics/otlp.go#L194
Added line #L194 was not covered by tests


[warning] 198-199: core/metrics/otlp.go#L198-L199
Added lines #L198 - L199 were not covered by tests

Comment on lines 31 to 32
<!-- TODO: fully document before pr merged-->

Copy link
Contributor

Choose a reason for hiding this comment

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

Address the TODO comment.

Please ensure that the documentation is fully updated before merging the PR, as indicated by the TODO comment.

If you need any assistance with completing the documentation, feel free to let me know.

Comment on lines 214 to 221
func WithURL(url string) Option {
return func(o *transportOptions) error {
o.httpOptions = append(o.httpOptions, otlptracehttp.WithEndpointURL(url))
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithEndpointURL(url))

return nil
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The new option functions WithURL, WithInsecure, and WithHeaders look good!

Providing these option functions allows for clean and extensible configuration of the OTLP exporter. Exporting them enables flexibility in configuring the exporter from other packages.

The functions modify the transportOptions struct in a consistent way, appending the corresponding options for both HTTP and gRPC transports, which is a good practice for maintaining compatibility between the two transport types.

However, static analysis tools have reported that these functions should have comments or be unexported.

Please add comments for the exported functions to improve code documentation and maintainability. For example:

// WithURL sets the endpoint URL for the OTLP exporter.
func WithURL(url string) Option {
	// ...
}

// WithInsecure enables or disables secure connection for the OTLP exporter.
func WithInsecure(isInsecure bool) Option {
	// ...
}

// WithHeaders sets the headers for the OTLP exporter.
func WithHeaders(headers string) Option {
	// ...
}

Also applies to: 223-241, 243-250

Tools
GitHub Check: Lint (core)

[failure] 214-214:
exported: exported function WithURL should have comment or be unexported (revive)

Comment on lines 223 to 241
func WithSecure(secure bool) Option {
return func(o *transportOptions) error {
if secure {
tlsCreds := credentials.NewClientTLSFromCert(nil, "")
// note: you do not need to specify the tls creds for http, this happens automatically when https:// is used as the protocol scheme.
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithTLSCredentials(tlsCreds))

tlsConfig := &tls.Config{
// RootCAs is nil, which means the default system root CAs are used
}
o.httpOptions = append(o.httpOptions, otlptracehttp.WithTLSClientConfig(tlsConfig))
} else {
o.httpOptions = append(o.httpOptions, otlptracehttp.WithInsecure())
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithInsecure())
}

return nil
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve TLS configuration in the WithSecure function.

The WithSecure function is an important option for configuring secure connections for the OTLP exporter. It correctly sets the TLS credentials for gRPC transport and the TLS configuration for HTTP transport based on the secure parameter.

However, static analysis tools have reported that the TLS MinVersion is too low.

To ensure better security, consider setting a higher MinVersion for the TLS configuration. For example:

tlsConfig := &tls.Config{
	MinVersion: tls.VersionTLS12,
	// ...
}

Using tls.VersionTLS12 or higher is recommended for secure connections.

Tools
GitHub Check: Lint (core)

[failure] 230-230:
G402: TLS MinVersion too low. (gosec)


[failure] 223-223:
exported: exported function WithSecure should have comment or be unexported (revive)

Comment on lines 35 to 58
primaryExporter, err := makeOTLPExporter(ctx, "")
if err != nil {
return fmt.Errorf("failed to create otlp exporter: %w", err)
return fmt.Errorf("could not create default client: %w", err)
}
exporters = append(exporters, primaryExporter)

// Loop to create additional exporters
for i := 1; ; i++ {
envSuffix := fmt.Sprintf("_%d", i)
// if this is empty we can assume no config exists at all.
endpointEnv := otelEndpointEnv + envSuffix

// no more transports to add.
if !core.HasEnv(endpointEnv) {
break
}

n.baseHandler = newBaseHandler(n.buildInfo, tracesdk.WithBatcher(exporter, tracesdk.WithMaxQueueSize(1000000), tracesdk.WithMaxExportBatchSize(2000)), tracesdk.WithSampler(tracesdk.AlwaysSample()))
exporter, err := makeOTLPExporter(ctx, envSuffix)
if err != nil {
return fmt.Errorf("could not create exporter %d: %v", i, err)
}

exporters = append(exporters, exporter)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve error handling in the Start method and the buildClientFromTransport function.

The error handling in the Start method and the buildClientFromTransport function is generally good. The Start method correctly returns an error if the creation of any exporter fails, which is important for error propagation. The buildClientFromTransport function also handles the case of an unknown transport type by returning an error, which is crucial for catching invalid configurations.

However, static analysis tools have reported a non-wrapping format verb for fmt.Errorf in the Start method.

To improve error wrapping and preserve the original error context, use the %w verb for fmt.Errorf. Apply this diff to fix the issue:

-return fmt.Errorf("could not create exporter %d: %v", i, err)
+return fmt.Errorf("could not create exporter %d: %w", i, err)

Using %w ensures that the original error is wrapped properly and can be unwrapped later if needed.

Also applies to: 198-199

Tools
GitHub Check: codecov/patch

[warning] 50-50: core/metrics/otlp.go#L50
Added line #L50 was not covered by tests


[warning] 55-55: core/metrics/otlp.go#L55
Added line #L55 was not covered by tests


[warning] 58-58: core/metrics/otlp.go#L58
Added line #L58 was not covered by tests

GitHub Check: Lint (core)

[failure] 54-54:
non-wrapping format verb for fmt.Errorf. Use %w to format errors (errorlint)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

Outside diff range and nitpick comments (3)
core/metrics/multiexporter.go (1)

41-69: LGTM, but consider making the timeout configurable.

The doParallel function is well-implemented, ensuring proper concurrency safety and error handling. The use of WaitGroup and mutex is appropriate for managing the goroutines and collecting errors.

The timeout of 30 seconds is a reasonable default, but it might be worth considering making it configurable to allow users to adjust it based on their specific requirements.

To make the timeout configurable, you can add a timeout field to the multiExporter struct and use it in the doParallel function:

 type multiExporter struct {
     exporters []trace.SpanExporter
+    timeout   time.Duration
 }
 
-func NewMultiExporter(exporters ...trace.SpanExporter) MultiExporter {
+func NewMultiExporter(exporters []trace.SpanExporter, timeout time.Duration) MultiExporter {
     return &multiExporter{
         exporters: exporters,
+        timeout:   timeout,
     }
 }
 
 func (m *multiExporter) doParallel(parentCtx context.Context, fn func(context.Context, trace.SpanExporter) error) error {
-    ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout)
+    ctx, cancel := context.WithTimeout(parentCtx, m.timeout)
     defer cancel()
     // ...
 }
Tools
GitHub Check: codecov/patch

[warning] 50-50: core/metrics/multiexporter.go#L50
Added line #L50 was not covered by tests


[warning] 68-68: core/metrics/multiexporter.go#L68
Added line #L68 was not covered by tests

core/metrics/README.md (1)

15-16: Enhance the explanation of the multi-exporter functionality.

The section explaining the multi-exporter functionality could benefit from a more detailed explanation or example. The current text mentions that the multiexporter.go file "simply wraps multiple otel clients," but it does not explain how this is achieved or how users can configure or utilize this functionality effectively.

Consider adding a code snippet or a more detailed step-by-step guide on setting up multiple exporters using the provided environment variables.

core/metrics/otlp.go (1)

214-214: Add comments for the exported option functions.

Static analysis tools have reported that the exported functions withURL, withSecure, and withHeaders should have comments or be unexported.

To improve code documentation and maintainability, add comments for these functions. For example:

// withURL sets the endpoint URL for the OTLP exporter.
func withURL(url string) Option {
	// ...
}

// withSecure enables or disables secure connection for the OTLP exporter.
func withSecure(secure bool) Option {
	// ...
}

// withHeaders sets the headers for the OTLP exporter.
func withHeaders(headers string) Option {
	// ...
}

Also applies to: 223-223, 244-244

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between ceeddbc and 8a04f9e.

Files selected for processing (3)
  • core/metrics/README.md (1 hunks)
  • core/metrics/multiexporter.go (1 hunks)
  • core/metrics/otlp.go (5 hunks)
Additional context used
GitHub Check: codecov/patch
core/metrics/multiexporter.go

[warning] 50-50: core/metrics/multiexporter.go#L50
Added line #L50 was not covered by tests


[warning] 68-68: core/metrics/multiexporter.go#L68
Added line #L68 was not covered by tests

core/metrics/otlp.go

[warning] 50-50: core/metrics/otlp.go#L50
Added line #L50 was not covered by tests


[warning] 55-55: core/metrics/otlp.go#L55
Added line #L55 was not covered by tests


[warning] 58-58: core/metrics/otlp.go#L58
Added line #L58 was not covered by tests


[warning] 194-194: core/metrics/otlp.go#L194
Added line #L194 was not covered by tests


[warning] 198-199: core/metrics/otlp.go#L198-L199
Added lines #L198 - L199 were not covered by tests

Additional comments not posted (9)
core/metrics/multiexporter.go (3)

14-17: LGTM!

The MultiExporter interface is well-defined, extending the trace.SpanExporter interface and adding an AddExporter method to support multiple exporters.


26-30: LGTM!

The NewMultiExporter function correctly initializes a new multiExporter instance with the provided trace.SpanExporter instances.


79-81: LGTM!

The AddExporter method correctly adds the provided trace.SpanExporter to the list of registered exporters.

core/metrics/README.md (1)

17-30: LGTM!

The table clearly documents the additional environment variables for configuring multiple OTLP exporters. The naming convention and default values are well-defined.

core/metrics/otlp.go (5)

33-71: Refactoring of the Start method to support multiple exporters looks good!

The changes in the Start method to create a primary exporter and additional exporters based on environment variables are well-structured and modular. The usage of the makeOTLPExporter function abstracts the exporter creation logic, making the code more readable.

The creation of the multi-exporter and its integration into the baseHandler is a good approach to combine multiple exporters.

Tools
GitHub Check: codecov/patch

[warning] 50-50: core/metrics/otlp.go#L50
Added line #L50 was not covered by tests


[warning] 55-55: core/metrics/otlp.go#L55
Added line #L55 was not covered by tests


[warning] 58-58: core/metrics/otlp.go#L58
Added line #L58 was not covered by tests


140-180: The new makeOTLPExporter function is a good abstraction for creating OTLP exporters.

The makeOTLPExporter function encapsulates the logic for creating an OTLP exporter based on the transport type and URL, making the code more modular and reusable. It properly handles error scenarios and returns an error if the exporter creation fails, which is important for error propagation and handling in the calling code.

The usage of the buildClientFromTransport function to create the OTLP client is a good approach to separate the client creation logic based on the transport type.


182-201: The new buildClientFromTransport function is a good abstraction for creating OTLP clients.

The buildClientFromTransport function provides a clean and extensible way to create OTLP clients based on the transport type. It applies the provided options to configure the transport client, making it flexible and reusable.

The function correctly handles the case of an unknown transport type by returning an error, which is crucial for catching invalid configurations.

Tools
GitHub Check: codecov/patch

[warning] 194-194: core/metrics/otlp.go#L194
Added line #L194 was not covered by tests


[warning] 198-199: core/metrics/otlp.go#L198-L199
Added lines #L198 - L199 were not covered by tests


289-292: The new constants for maximum queue size and export batch size are a good addition.

The introduction of the defaultMaxQueueSize and defaultMaxExportBatch constants provides clear configuration options for the exporter behavior. It makes the code more readable and maintainable by giving meaningful names to the values used for queue size and batch size.

The constants are properly used in the Start method when creating the baseHandler, ensuring that the exporter is configured with the desired queue size and batch size.


214-221: The new option functions withURL, withSecure, and withHeaders are a good addition for configuring the OTLP exporter.

Providing these option functions allows for clean and extensible configuration of the OTLP exporter. Exporting them enables flexibility in configuring the exporter from other packages.

The functions modify the transportOptions struct in a consistent way, appending the corresponding options for both HTTP and gRPC transports, which is a good practice for maintaining compatibility between the two transport types.

Also applies to: 223-242, 244-251

Comment on lines +41 to +69
func (m *multiExporter) doParallel(parentCtx context.Context, fn func(context.Context, trace.SpanExporter) error) error {
ctx, cancel := context.WithTimeout(parentCtx, defaultTimeout)
defer cancel()

var wg sync.WaitGroup
var errors []error
var mu sync.Mutex

wg.Add(len(m.exporters))
for _, exporter := range m.exporters {
go func(exporter trace.SpanExporter) {
defer wg.Done()
err := fn(ctx, exporter)
if err != nil {
mu.Lock()
errors = append(errors, fmt.Errorf("error in doMultiple: %w", err))
mu.Unlock()
}
}(exporter)
}

wg.Wait()
if len(errors) > 0 {
// nolint: wrapcheck
return multierr.Combine(errors...)
}

return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ensure test coverage for the doParallel function.

The static analysis hints indicate that some lines in the doParallel function (lines 50 and 68) are not covered by tests.

Given that doParallel is a critical part of the multiExporter implementation, it is important to ensure that it is thoroughly tested, including the case when no errors occur.

Do you want me to generate the unit testing code or open a GitHub issue to track this task?

Tools
GitHub Check: codecov/patch

[warning] 50-50: core/metrics/multiexporter.go#L50
Added line #L50 was not covered by tests


[warning] 68-68: core/metrics/multiexporter.go#L68
Added line #L68 was not covered by tests

Comment on lines +35 to +39
func (m *multiExporter) ExportSpans(parentCtx context.Context, ss []trace.ReadOnlySpan) error {
return m.doParallel(parentCtx, func(ctx context.Context, exporter trace.SpanExporter) error {
return exporter.ExportSpans(ctx, ss)
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider collecting errors from all exporters before returning.

The current implementation returns immediately if an error occurs while exporting to one of the exporters. This means that if one exporter fails, the remaining exporters will not receive the spans.

It would be better to collect errors from all exporters and return a combined error. This way, all exporters will have a chance to receive the spans, and the caller will be informed of all the errors that occurred.

Apply this diff to collect errors from all exporters:

 func (m *multiExporter) ExportSpans(parentCtx context.Context, ss []trace.ReadOnlySpan) error {
-    return m.doParallel(parentCtx, func(ctx context.Context, exporter trace.SpanExporter) error {
-        return exporter.ExportSpans(ctx, ss)
-    })
+    var errs []error
+    m.doParallel(parentCtx, func(ctx context.Context, exporter trace.SpanExporter) error {
+        err := exporter.ExportSpans(ctx, ss)
+        if err != nil {
+            errs = append(errs, err)
+        }
+        return nil
+    })
+    if len(errs) > 0 {
+        return fmt.Errorf("could not export spans to some exporters: %v", errs)
+    }
+    return nil
 }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (m *multiExporter) ExportSpans(parentCtx context.Context, ss []trace.ReadOnlySpan) error {
return m.doParallel(parentCtx, func(ctx context.Context, exporter trace.SpanExporter) error {
return exporter.ExportSpans(ctx, ss)
})
}
func (m *multiExporter) ExportSpans(parentCtx context.Context, ss []trace.ReadOnlySpan) error {
var errs []error
m.doParallel(parentCtx, func(ctx context.Context, exporter trace.SpanExporter) error {
err := exporter.ExportSpans(ctx, ss)
if err != nil {
errs = append(errs, err)
}
return nil
})
if len(errs) > 0 {
return fmt.Errorf("could not export spans to some exporters: %v", errs)
}
return nil
}

Comment on lines +72 to +76
func (m *multiExporter) Shutdown(ctx context.Context) error {
return m.doParallel(ctx, func(ctx context.Context, exporter trace.SpanExporter) error {
return exporter.Shutdown(ctx)
})
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Consider collecting errors from all exporters before returning.

Similar to the ExportSpans method, the current implementation of Shutdown returns immediately if an error occurs while shutting down one of the exporters. This means that if one exporter fails to shut down, the remaining exporters will not be shut down.

As suggested earlier, it would be better to collect errors from all exporters and return a combined error. This way, all exporters will be attempted to be shut down, and the caller will be informed of all the errors that occurred.

Apply this diff to collect errors from all exporters:

 func (m *multiExporter) Shutdown(ctx context.Context) error {
-    return m.doParallel(ctx, func(ctx context.Context, exporter trace.SpanExporter) error {
-        return exporter.Shutdown(ctx)
-    })
+    var errs []error
+    m.doParallel(ctx, func(ctx context.Context, exporter trace.SpanExporter) error {
+        err := exporter.Shutdown(ctx)
+        if err != nil {
+            errs = append(errs, err)
+        }
+        return nil
+    })
+    if len(errs) > 0 {
+        return fmt.Errorf("could not stop some exporters: %v", errs)
+    }
+    return nil
 }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (m *multiExporter) Shutdown(ctx context.Context) error {
return m.doParallel(ctx, func(ctx context.Context, exporter trace.SpanExporter) error {
return exporter.Shutdown(ctx)
})
}
func (m *multiExporter) Shutdown(ctx context.Context) error {
var errs []error
m.doParallel(ctx, func(ctx context.Context, exporter trace.SpanExporter) error {
err := exporter.Shutdown(ctx)
if err != nil {
errs = append(errs, err)
}
return nil
})
if len(errs) > 0 {
return fmt.Errorf("could not stop some exporters: %v", errs)
}
return nil
}


You can do the same thing for `OTEL_EXPORTER_OTLP_SECURE_MODE` and `OTEL_EXPORTER_OTLP_HEADERS`

<!-- TODO: fully document these optins-->
Copy link
Contributor

Choose a reason for hiding this comment

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

Address the TODO comment.

Please ensure that the documentation is fully updated before merging the PR, as indicated by the TODO comment.

If you need any assistance with completing the documentation, feel free to let me know.

// note: you do not need to specify the tls creds for http, this happens automatically when https:// is used as the protocol scheme.
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithTLSCredentials(tlsCreds))

tlsConfig := &tls.Config{
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve TLS configuration in the withSecure function.

The withSecure function is an important option for configuring secure connections for the OTLP exporter. It correctly sets the TLS credentials for gRPC transport and the TLS configuration for HTTP transport based on the secure parameter.

However, static analysis tools have reported that the TLS MinVersion is too low.

To ensure better security, consider setting a higher MinVersion for the TLS configuration. For example:

tlsConfig := &tls.Config{
	MinVersion: tls.VersionTLS12,
	// ...
}

Using tls.VersionTLS12 or higher is recommended for secure connections.

n.baseHandler = newBaseHandler(n.buildInfo, tracesdk.WithBatcher(exporter, tracesdk.WithMaxQueueSize(1000000), tracesdk.WithMaxExportBatchSize(2000)), tracesdk.WithSampler(tracesdk.AlwaysSample()))
exporter, err := makeOTLPExporter(ctx, envSuffix)
if err != nil {
return fmt.Errorf("could not create exporter %d: %w", i, err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Improve error wrapping in the Start method.

The error handling in the Start method is generally good. It correctly returns an error if the creation of any exporter fails, which is important for error propagation.

However, static analysis tools have reported a non-wrapping format verb for fmt.Errorf in the Start method.

To improve error wrapping and preserve the original error context, use the %w verb for fmt.Errorf. Apply this diff to fix the issue:

-return fmt.Errorf("could not create exporter %d: %v", i, err)
+return fmt.Errorf("could not create exporter %d: %w", i, err)

Using %w ensures that the original error is wrapped properly and can be unwrapped later if needed.

Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return fmt.Errorf("could not create exporter %d: %w", i, err)
return fmt.Errorf("could not create exporter %d: %w", i, err)

// no more transports to add.
if !core.HasEnv(endpointEnv) {
break
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Tip

Codebase Verification

Add Tests for Error Handling in Multi-Exporter Logic

The current test TestMultiExporter verifies the successful operation of the multi-exporter logic but does not cover error handling scenarios such as exporter creation failure or breaking out of the loop when no exporters are configured. It is recommended to add unit tests to cover these scenarios to ensure robustness.

  • Consider adding tests for:
    • Breaking out of the loop when no more exporters are configured.
    • Error handling when exporter creation fails.
Analysis chain

Ensure test coverage for the multi-exporter logic and error handling scenarios.

Static analysis tools have reported that some lines in the Start method are not covered by tests:

  • Line 50: Breaking out of the loop when no more exporters are configured
  • Line 55: Error handling when exporter creation fails
  • Line 58: Appending the created exporter to the list of exporters

It's important to add unit tests to cover these scenarios and ensure the robustness of the multi-exporter logic and error handling.

Also applies to: 55-55, 58-58

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify test coverage for the multi-exporter logic and error handling scenarios.

# Test: Search for tests covering the multi-exporter logic. Expect: At least one test.
rg --type go $'func Test.*MultiExporter'

# Test: Search for tests covering the error handling scenarios. Expect: At least one test.
rg --type go $'func Test.*ExporterCreationError'

Length of output: 165


Script:

#!/bin/bash
# Search for the implementation of the TestMultiExporter function to check its coverage.
rg --type go -A 20 'func TestMultiExporter' core/metrics/multiexporter_test.go

# Search for other test functions in the same file to see if they cover error handling.
rg --type go 'func Test' core/metrics/multiexporter_test.go

Length of output: 825

Tools
GitHub Check: codecov/patch

[warning] 50-50: core/metrics/otlp.go#L50
Added line #L50 was not covered by tests

Comment on lines +198 to +199
default:
return nil, fmt.Errorf("unknown transport type: %s", transport.String())
Copy link
Contributor

Choose a reason for hiding this comment

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

Tip

Codebase Verification

Add unit tests for error handling in buildClientFromTransport.

The error handling for unknown transport types in the buildClientFromTransport function is not currently covered by any tests. It is important to add unit tests to cover this scenario and ensure the robustness of the function.

  • Consider adding a test case that provides an invalid transport type and verifies that an error is returned.
Analysis chain

Improve error handling in the buildClientFromTransport function.

Static analysis tools have reported that the error handling for unknown transport types (lines 198-199) is not covered by tests.

It's important to add unit tests to cover this scenario and ensure the robustness of the function. Consider adding a test case that provides an invalid transport type and verifies that an error is returned.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify test coverage for the error handling of unknown transport types.

# Test: Search for tests covering the error handling of unknown transport types. Expect: At least one test.
rg --type go $'func Test.*UnknownTransportType'

Length of output: 48


Script:

#!/bin/bash
# Description: Search for test cases involving the buildClientFromTransport function to check error handling coverage.

# Find the definition of the buildClientFromTransport function to understand its location and context.
ast-grep --lang go --pattern 'func buildClientFromTransport($_) $_ { $$$ }'

# Search for test functions that call buildClientFromTransport.
rg --type go 'buildClientFromTransport'

Length of output: 428


Script:

#!/bin/bash
# Description: Search for Go test files that might indirectly test the buildClientFromTransport function.

# List all Go test files in the repository.
fd --type f --extension go --exec basename {} \; | grep '_test.go'

# Search for any references to the buildClientFromTransport function in these test files.
rg --type go 'buildClientFromTransport' --glob '*_test.go'

Length of output: 5242

Tools
GitHub Check: codecov/patch

[warning] 198-199: core/metrics/otlp.go#L198-L199
Added lines #L198 - L199 were not covered by tests

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Outside diff range and nitpick comments (2)
core/metrics/otlp.go (2)

210-247: LGTM, but add comments for the exported functions.

The addition of the option functions is a good design choice. It allows for clean and extensible configuration of the OTLP exporter. Exporting these functions enables flexibility in configuring the exporter from other packages. The functions modify the transportOptions struct in a consistent way, appending the corresponding options for both HTTP and gRPC transports, which is a good practice for maintaining compatibility between the two transport types.

However, static analysis tools have reported that these functions should have comments or be unexported.

Please add comments for the exported functions to improve code documentation and maintainability. For example:

// WithURL sets the endpoint URL for the OTLP exporter.
func WithURL(url string) Option {
    // ...
}

// WithSecure enables or disables secure connection for the OTLP exporter.
func WithSecure(secure bool) Option {
    // ...
}

// WithHeaders sets the headers for the OTLP exporter.
func WithHeaders(headers string) Option {
    // ...
}

54-54: LGTM, but use %w for error wrapping.

The error handling in the Start method is generally good. It correctly returns an error if the creation of any exporter fails, which is important for error propagation.

However, static analysis tools have reported a non-wrapping format verb for fmt.Errorf.

To improve error wrapping and preserve the original error context, use the %w verb for fmt.Errorf. Apply this diff to fix the issue:

-return fmt.Errorf("could not create exporter %d: %v", i, err)
+return fmt.Errorf("could not create exporter %d: %w", i, err)

Using %w ensures that the original error is wrapped properly and can be unwrapped later if needed.

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 8a04f9e and 0e3a5ae.

Files selected for processing (1)
  • core/metrics/otlp.go (5 hunks)
Additional context used
GitHub Check: codecov/patch
core/metrics/otlp.go

[warning] 50-50: core/metrics/otlp.go#L50
Added line #L50 was not covered by tests


[warning] 55-55: core/metrics/otlp.go#L55
Added line #L55 was not covered by tests


[warning] 190-190: core/metrics/otlp.go#L190
Added line #L190 was not covered by tests


[warning] 194-195: core/metrics/otlp.go#L194-L195
Added lines #L194 - L195 were not covered by tests

Additional comments not posted (4)
core/metrics/otlp.go (4)

141-176: LGTM!

The makeOTLPExporter function is well-structured and handles errors appropriately. It uses the new buildClientFromTransport function to create the OTLP client based on the transport type and options, which is a good abstraction. The error handling is comprehensive, returning meaningful error messages.


179-197: LGTM, but consider adding a unit test for error handling.

The buildClientFromTransport function is well-structured and handles errors appropriately. It uses a switch statement to create the appropriate client based on the transport type, which is a clean and extensible approach.

However, static analysis indicates that the error handling for unknown transport types (lines 194-195) is not covered by tests.

Consider adding a unit test to cover this scenario and ensure the robustness of the function. For example:

func TestBuildClientFromTransport_UnknownTransport(t *testing.T) {
    _, err := buildClientFromTransport(otlpTransportType(0))
    assert.Error(t, err)
    assert.Contains(t, err.Error(), "unknown transport type")
}
Tools
GitHub Check: codecov/patch

[warning] 190-190: core/metrics/otlp.go#L190
Added line #L190 was not covered by tests


[warning] 194-195: core/metrics/otlp.go#L194-L195
Added lines #L194 - L195 were not covered by tests


33-71: Excellent refactoring, but ensure test coverage for the multi-exporter logic.

The refactoring of the Start function is a significant improvement. It allows for the creation of multiple exporters based on environment variables, which enhances flexibility. The use of the makeOTLPExporter function abstracts the exporter creation logic, making the code more readable and maintainable. The integration of the multi-exporter into the baseHandler is a good architectural decision.

However, static analysis indicates that some lines are not covered by tests:

  • Line 50: Breaking out of the loop when no more exporters are configured
  • Line 55: Error handling when exporter creation fails

Consider adding unit tests to cover these scenarios and ensure the robustness of the multi-exporter logic and error handling. For example:

func TestStart_MultipleExporters(t *testing.T) {
    // Set up environment variables for multiple exporters
    // ...

    handler := NewOTLPMetricsHandler(buildInfo)
    err := handler.Start(context.Background())
    assert.NoError(t, err)

    // Verify that multiple exporters were created
    // ...
}

func TestStart_ExporterCreationError(t *testing.T) {
    // Set up environment variables with invalid values
    // ...

    handler := NewOTLPMetricsHandler(buildInfo)
    err := handler.Start(context.Background())
    assert.Error(t, err)
    assert.Contains(t, err.Error(), "could not create exporter")
}
Tools
GitHub Check: codecov/patch

[warning] 50-50: core/metrics/otlp.go#L50
Added line #L50 was not covered by tests


[warning] 55-55: core/metrics/otlp.go#L55
Added line #L55 was not covered by tests


117-120: LGTM!

The addition of constants for maximum queue size and export batch size is a good practice. It provides clear configuration options for the exporter behavior. The modification of the constant names for the environment variables improves readability. The addition of the insecure mode environment variable constant is necessary for the new functionality.

Also applies to: 285-288

Comment on lines +219 to +238
func withSecure(secure bool) Option {
return func(o *transportOptions) error {
if secure {
tlsCreds := credentials.NewClientTLSFromCert(nil, "")
// note: you do not need to specify the tls creds for http, this happens automatically when https:// is used as the protocol scheme.
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithTLSCredentials(tlsCreds))

tlsConfig := &tls.Config{
MinVersion: tls.VersionTLS12,
// RootCAs is nil, which means the default system root CAs are used
}
o.httpOptions = append(o.httpOptions, otlptracehttp.WithTLSClientConfig(tlsConfig))
} else {
o.httpOptions = append(o.httpOptions, otlptracehttp.WithInsecure())
o.grpcOptions = append(o.grpcOptions, otlptracegrpc.WithInsecure())
}

return nil
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

LGTM, but consider improving the TLS configuration.

The withSecure function is an important option for configuring secure connections for the OTLP exporter. It correctly sets the TLS credentials for gRPC transport and the TLS configuration for HTTP transport based on the secure parameter.

However, static analysis tools have reported that the TLS MinVersion is too low.

To ensure better security, consider setting a higher MinVersion for the TLS configuration. For example:

tlsConfig := &tls.Config{
    MinVersion: tls.VersionTLS12,
    // ...
}

Using tls.VersionTLS12 or higher is recommended for secure connections.

@trajan0x trajan0x merged commit 2e517b3 into master Sep 11, 2024
50 of 52 checks passed
@trajan0x trajan0x deleted the multiple-exports branch September 11, 2024 12:49
@coderabbitai coderabbitai bot mentioned this pull request Sep 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
go Pull requests that update Go code size/m
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[SLT-141] otlp multiple exporter support
2 participants