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

feat(apps): add otel exporter for graphql, service and web-api #1528

Merged
merged 37 commits into from
Dec 16, 2024

Conversation

arealmaas
Copy link
Collaborator

@arealmaas arealmaas commented Nov 25, 2024

Description

  • Sets up local OTEL setup that match the OTEL configuration in Azure Container Apps
  • Added fusioncache telemetry
  • Added Entity Framework telemetry
  • Missing liveMetrics if we want that. Needs to be considered. Other than that, the most relevant traces are pulled out from the AzureMonitor package.
  • Metrics are only visible locally for now. Turns out that the Azure Monitor Workspace has a Prometheus instance, but it does not allow for us sending metrics to it, as it does not have an OTEL endpoint 🙃 Solution here was adding the MetricsMonitor to send metrics directly to app insights for now..!
  • Will add logging in the next PR

To see your metrics, spin up the OTEL services by running docker-compose-otel.yml. The service should start sending to the OTLP collector automatically.

Example of a trace in the local Jaeger:

CleanShot 2024-12-09 at 17 52 11@2x

Related Issue(s)

Verification

  • Your code builds clean without any errors or warnings
  • Manual testing done (required)
  • Relevant automated test added (if you find this hard, leave it and we'll help out)

Documentation

  • Documentation is updated (either in docs-directory, Altinnpedia or a separate linked PR in altinn-studio-docs., if applicable)

Copy link
Contributor

coderabbitai bot commented Nov 25, 2024

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough
📝 Walkthrough

Walkthrough

This pull request introduces a comprehensive configuration for monitoring and observability using OpenTelemetry, Jaeger, Prometheus, and Grafana. It includes a new docker-compose-otel.yml file that defines multiple services for telemetry data collection and visualization. Existing services in docker-compose.yml were modified to integrate OpenTelemetry settings. Additionally, several new Grafana dashboard configurations and Prometheus settings were added, along with enhancements to telemetry configuration in various application components.

Changes

File Change Summary
docker-compose-otel.yml Added new services: otel-collector, jaeger, prometheus, grafana.
docker-compose.yml Included docker-compose-otel.yml and updated environment variables for dialogporten-webapi and dialogporten-graphql to integrate OpenTelemetry.
local-otel-configuration/dashboards/aspnet-core-metrics.json Created new Grafana dashboard configuration for ASP.NET Core metrics.
local-otel-configuration/dashboards/http-client-metrics.json Created new Grafana dashboard configuration for HTTP client metrics.
local-otel-configuration/dashboards/runtime-metrics.json Created new Grafana dashboard configuration for runtime metrics.
local-otel-configuration/grafana-dashboards.yml Added configuration for Grafana dashboard provisioning.
local-otel-configuration/grafana-datasources.yml Added Prometheus data source configuration for Grafana.
local-otel-configuration/otel-collector-config.yaml Introduced configuration for OpenTelemetry collector with receivers, processors, exporters, and service sections.
local-otel-configuration/prometheus.yml Created Prometheus configuration for scraping metrics from the OpenTelemetry collector.
src/Digdir.Domain.Dialogporten.GraphQL/Program.cs Enhanced telemetry configuration in BuildAndRun method.
src/Digdir.Domain.Dialogporten.Service/Program.cs Updated telemetry configuration in BuildAndRun method.
src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj Cosmetic changes to project file formatting.
src/Digdir.Domain.Dialogporten.WebApi/Program.cs Improved telemetry configuration in BuildAndRun method.
src/Digdir.Domain.Dialogporten.WebApi/appsettings.json Added newline at the end of the file.
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs Added TelemetrySettings class and updated ConfigureTelemetry method for enhanced telemetry configuration.
src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj Added package references for OpenTelemetry and FusionCache.

Possibly related PRs

Suggested reviewers

  • oskogstad

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>, please review it.
    • 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 gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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.

@arealmaas arealmaas marked this pull request as ready for review December 9, 2024 17:18
@arealmaas arealmaas requested a review from a team as a code owner December 9, 2024 17:18
@arealmaas arealmaas changed the title feat(web-api): add otel exporter feat(apps): add otel exporter for graphql, service and web-api Dec 9, 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: 16

🧹 Outside diff range and nitpick comments (10)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (3)

86-92: Validate and Parse Protocol Using Enum Parsing

Simplify protocol parsing by using Enum.TryParse for OtlpExportProtocol. This approach enhances readability and maintainability.

Consider applying this change:

- var otlpProtocol = settings.Protocol.ToLowerInvariant() switch
- {
-     "grpc" => OtlpExportProtocol.Grpc,
-     "http/protobuf" => OtlpExportProtocol.HttpProtobuf,
-     "http" => OtlpExportProtocol.HttpProtobuf,
-     _ => throw new ArgumentException($"Unsupported protocol: {settings.Protocol}")
- };
+ if (!Enum.TryParse<OtlpExportProtocol>(settings.Protocol, true, out var otlpProtocol))
+ {
+     throw new ArgumentException($"Unsupported protocol: {settings.Protocol}");
+ }

131-132: Use Logging Framework for Consistency

Replace Console.WriteLine with ILogger to maintain consistent logging practices throughout the application.

Apply this diff:

- Console.WriteLine("[OpenTelemetry] OTLP exporter not configured - skipping");
+ logger.LogWarning("OTLP exporter not configured - skipping");

154-163: Make TraceSources Configurable

Hardcoding trace source names can limit flexibility. Consider making TraceSources configurable via app settings to allow easier adjustments without code changes.

Example adjustment:

- public HashSet<string> TraceSources { get; set; } = new()
- {
-     AzureSource,
-     MassTransitSource
- };
+ public HashSet<string> TraceSources { get; set; }
+
+ public TelemetrySettings()
+ {
+     TraceSources = new HashSet<string>
+     {
+         AzureSource,
+         MassTransitSource
+     };
+ }

Then, allow injection of TraceSources via configuration.

local-otel-configuration/grafana-datasources.yml (1)

8-8: Remove Trailing Spaces and Add Newline at End of File

The file contains trailing spaces and is missing a newline at the end, which can cause issues with some parsers or tools.

Apply this diff to fix the formatting:

-    isDefault: true 
+    isDefault: true
+
🧰 Tools
🪛 yamllint (1.35.1)

[error] 8-8: no new line character at the end of file

(new-line-at-end-of-file)


[error] 8-8: trailing spaces

(trailing-spaces)

local-otel-configuration/otel-collector-config.yaml (2)

25-27: Reassess Debug Exporter's Sampling Configuration

The debug exporter with verbosity: detailed can generate a large volume of logs, impacting performance.

Consider adjusting the sampling rates or using it conditionally in development environments.


36-39: Set Appropriate Logging Levels for Production

Currently, the telemetry logs are set to debug level with development: true. In a production environment, this should be adjusted to reduce verbosity.

Adjust the logging configuration based on the environment:

telemetry:
  logs:
    level: info
    development: false
local-otel-configuration/dashboards/aspnet-core-metrics.json (1)

1-176: Enhance dashboard with additional critical metrics.

While the current metrics provide good basic monitoring, consider adding these essential metrics for comprehensive monitoring:

Add these panels to the dashboard:

 {
   "panels": [
     // ... existing panels ...
+    {
+      "title": "HTTP Response Status Codes",
+      "type": "timeseries",
+      "targets": [{
+        "expr": "sum(rate(dialogporten_http_server_requests_total[$__rate_interval])) by (status_code)",
+        "legendFormat": "{{status_code}}"
+      }]
+    },
+    {
+      "title": "Error Rate",
+      "type": "timeseries",
+      "targets": [{
+        "expr": "sum(rate(dialogporten_http_server_requests_total{status_code=~\"5.*\"}[$__rate_interval])) / sum(rate(dialogporten_http_server_requests_total[$__rate_interval])) * 100",
+        "legendFormat": "Error Rate %"
+      }]
+    }
   ]
 }
local-otel-configuration/dashboards/http-client-metrics.json (1)

1-296: Add failure monitoring metrics to the dashboard.

The dashboard provides good performance monitoring but lacks visibility into failures and timeouts.

Add these panels to track failures:

 {
   "panels": [
     // ... existing panels ...
+    {
+      "title": "Failed Requests by Reason",
+      "type": "timeseries",
+      "targets": [{
+        "expr": "sum(rate(dialogporten_http_client_requests_failed_total[$__rate_interval])) by (reason)",
+        "legendFormat": "{{reason}}"
+      }]
+    },
+    {
+      "title": "Request Timeouts",
+      "type": "timeseries",
+      "targets": [{
+        "expr": "sum(rate(dialogporten_http_client_requests_failed_total{reason=\"timeout\"}[$__rate_interval]))",
+        "legendFormat": "Timeouts"
+      }]
+    }
   ]
 }
local-otel-configuration/dashboards/runtime-metrics.json (1)

84-96: Enhance memory metrics panel with additional indicators.

Consider adding these essential memory metrics to provide a more comprehensive view:

Add these targets to the Memory Usage panel:

 "targets": [
     {
         "datasource": {
             "type": "prometheus",
             "uid": "Prometheus"
         },
         "expr": "dialogporten_process_runtime_dotnet_gc_heap_size_bytes",
         "legendFormat": "Heap Size",
         "refId": "A"
-    }
+    },
+    {
+        "datasource": {
+            "type": "prometheus",
+            "uid": "Prometheus"
+        },
+        "expr": "process_working_set_bytes",
+        "legendFormat": "Working Set",
+        "refId": "B"
+    }
 ]
local-otel-configuration/grafana-dashboards.yml (1)

1-11: Configuration looks good, minor YAML fixes needed.

The Grafana dashboard provisioning configuration is correctly structured. However, there are two minor YAML formatting issues to fix:

Apply this diff to fix the YAML formatting:

 apiVersion: 1

 providers:
   - name: 'Default'
     orgId: 1
     folder: ''
     type: file
     disableDeletion: false
     editable: true
     options:
-      path: /etc/grafana/provisioning/dashboards 
+      path: /etc/grafana/provisioning/dashboards
+
🧰 Tools
🪛 yamllint (1.35.1)

[error] 11-11: no new line character at the end of file

(new-line-at-end-of-file)


[error] 11-11: trailing spaces

(trailing-spaces)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 8aabb68 and d0cd2d7.

📒 Files selected for processing (16)
  • docker-compose-otel.yml (1 hunks)
  • docker-compose.yml (4 hunks)
  • local-otel-configuration/dashboards/aspnet-core-metrics.json (1 hunks)
  • local-otel-configuration/dashboards/http-client-metrics.json (1 hunks)
  • local-otel-configuration/dashboards/runtime-metrics.json (1 hunks)
  • local-otel-configuration/grafana-dashboards.yml (1 hunks)
  • local-otel-configuration/grafana-datasources.yml (1 hunks)
  • local-otel-configuration/otel-collector-config.yaml (1 hunks)
  • local-otel-configuration/prometheus.yml (1 hunks)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Service/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj (2 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/appsettings.json (1 hunks)
  • src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2 hunks)
  • src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/Digdir.Domain.Dialogporten.WebApi/appsettings.json
  • src/Digdir.Domain.Dialogporten.WebApi/Digdir.Domain.Dialogporten.WebApi.csproj
🧰 Additional context used
📓 Learnings (2)
src/Digdir.Domain.Dialogporten.GraphQL/Program.cs (1)
Learnt from: knuhau
PR: digdir/dialogporten#1349
File: src/Digdir.Domain.Dialogporten.GraphQL/Program.cs:70-74
Timestamp: 2024-11-12T05:32:45.312Z
Learning: In this project, the OpenTelemetry configurations are handled in the `.ConfigureTelemetry()` method.
src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1)
Learnt from: knuhau
PR: digdir/dialogporten#1349
File: src/Digdir.Domain.Dialogporten.GraphQL/Program.cs:70-74
Timestamp: 2024-11-12T05:32:45.312Z
Learning: In this project, the OpenTelemetry configurations are handled in the `.ConfigureTelemetry()` method.
🪛 yamllint (1.35.1)
local-otel-configuration/grafana-datasources.yml

[error] 8-8: no new line character at the end of file

(new-line-at-end-of-file)


[error] 8-8: trailing spaces

(trailing-spaces)

local-otel-configuration/grafana-dashboards.yml

[error] 11-11: no new line character at the end of file

(new-line-at-end-of-file)


[error] 11-11: trailing spaces

(trailing-spaces)

local-otel-configuration/prometheus.yml

[error] 8-8: no new line character at the end of file

(new-line-at-end-of-file)


[error] 8-8: trailing spaces

(trailing-spaces)

🔇 Additional comments (4)
local-otel-configuration/otel-collector-config.yaml (1)

17-19: Confirm Prometheus Exporter Endpoint and Namespace

Ensure that the Prometheus exporter endpoint and namespace align with your infrastructure and naming conventions.

Run the following script to verify that the endpoint is correctly set and not conflicting with other services:

✅ Verification successful

Let me gather more information about the Prometheus configuration in the codebase.


Prometheus configuration is correctly set and aligned with the infrastructure

The Prometheus exporter configuration is properly configured and integrated:

  • Port 8889 is consistently used across the OpenTelemetry setup:
    • Exposed in docker-compose-otel.yml
    • Configured as target in prometheus.yml
    • Set as endpoint in otel-collector-config.yaml
  • The "dialogporten" namespace is unique to this collector configuration
  • The configuration aligns with the overall monitoring setup including Grafana and Prometheus services
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if port 8889 is already in use and validate the namespace.

# Test: Check for services using port 8889
lsof -i :8889

# Expected Result: No other services should be using port 8889.

# Verify the namespace in existing Prometheus configurations
rg 'namespace: "dialogporten"' local-otel-configuration/

Length of output: 251


Script:

#!/bin/bash
# Search for any other Prometheus configurations or port definitions
rg -g '*.yaml' -g '*.yml' 'prometheus:' -A 5

# Search for any port 8889 usage across the codebase
rg '8889'

# Look for any other OpenTelemetry collector configurations
fd -e yaml -e yml | grep -i 'collector'

Length of output: 2560

src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj (1)

14-21: Ensure Compatibility of New Package References

The addition of OpenTelemetry.Exporter.OpenTelemetryProtocol and ZiggyCreatures.FusionCache.OpenTelemetry packages should be compatible with existing dependencies and the .NET target framework.

Run the following script to check for compatibility issues:

Do you want assistance in resolving any compatibility issues that may arise?

✅ Verification successful

OpenTelemetry Package Dependencies are Compatible

The OpenTelemetry packages are properly aligned and compatible:

  • Core OpenTelemetry package (1.10.0) matches the version of its exporter OpenTelemetry.Exporter.OpenTelemetryProtocol (1.10.0)
  • Azure.Monitor.OpenTelemetry.AspNetCore (1.2.0) is a stable version compatible with .NET packages
  • ZiggyCreatures.FusionCache.OpenTelemetry (1.4.1) is compatible with the core OpenTelemetry version
  • The project already has proper OpenTelemetry integration as evidenced by the usage in AspNetUtilitiesExtensions.cs
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify package compatibility.

# List all package references with versions
cat src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj | grep '<PackageReference'

# Check for any known conflicts or required updates
dotnet list package --outdated

Length of output: 1345


Script:

#!/bin/bash
# Let's check the target framework and other relevant information from the project file
cat src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj

# Also check if there are any OpenTelemetry related configurations or usages
rg "OpenTelemetry" -A 3 -B 3

Length of output: 10641

docker-compose.yml (1)

18-18: Verify scale reduction impact

Reducing scale from 2 to 1 for webapi could impact service availability and load handling capability.

✅ Verification successful

Let me search for more specific information about scaling configuration.


Let me check the deployment configuration in infrastructure code to understand if there are other scaling mechanisms in place.


Based on the findings from the CHANGELOG.md and docker-compose.yml, I can now provide a final response:

Scale reduction appears to be part of a broader infrastructure change

The scale reduction from 2 to 1 in docker-compose.yml is likely intentional as the application has been migrated to container apps with proper CPU and memory scalers:

The docker-compose.yml is likely used for local development, where high availability is not a primary concern.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for load balancing or high availability requirements
rg -A 5 "scale.*[2-9]|replicas.*[2-9]|high.availability|load.balancing" 

Length of output: 7541


Script:

#!/bin/bash
# Check for scale configuration in infrastructure code and deployment files
rg -A 5 "scale:|replicas:|minReplicas|maxReplicas" --type yaml

Length of output: 560


Script:

#!/bin/bash
# Check infrastructure code for container app scaling configuration
rg -A 10 "containerApp|scale" --type bicep --type terraform

Length of output: 91

local-otel-configuration/grafana-dashboards.yml (1)

11-11: Verify dashboard path configuration in Docker setup.

The dashboard path /etc/grafana/provisioning/dashboards needs to be properly mounted in the Grafana container.

✅ Verification successful

Dashboard path configuration is correctly set up in Docker environment

The verification confirms that:

  • The dashboard path is properly mounted in docker-compose-otel.yml with both the configuration and dashboard files
  • Dashboard JSON files exist in the expected location at local-otel-configuration/dashboards/
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the dashboard path is correctly configured in Docker Compose

# Check if the dashboard path is mounted in docker-compose-otel.yml
rg -A 5 "/etc/grafana/provisioning/dashboards" 

# Check if dashboard JSON files exist
fd -e json . local-otel-configuration/dashboards/

Length of output: 619

🧰 Tools
🪛 yamllint (1.35.1)

[error] 11-11: no new line character at the end of file

(new-line-at-end-of-file)


[error] 11-11: trailing spaces

(trailing-spaces)

src/Digdir.Domain.Dialogporten.Service/Program.cs Outdated Show resolved Hide resolved
src/Digdir.Domain.Dialogporten.GraphQL/Program.cs Outdated Show resolved Hide resolved
src/Digdir.Domain.Dialogporten.GraphQL/Program.cs Outdated Show resolved Hide resolved
src/Digdir.Domain.Dialogporten.WebApi/Program.cs Outdated Show resolved Hide resolved
local-otel-configuration/prometheus.yml Show resolved Hide resolved
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

🧹 Outside diff range and nitpick comments (2)
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)

56-58: Consider validating OTEL protocol format early.

The protocol value is used later in the configuration but isn't validated at the point of assignment. Early validation would provide clearer error messages.

-        settings.Protocol = configuration["OTEL_EXPORTER_OTLP_PROTOCOL"];
+        var protocol = configuration["OTEL_EXPORTER_OTLP_PROTOCOL"];
+        if (!string.IsNullOrEmpty(protocol) && 
+            !new[] { "grpc", "http/protobuf", "http" }.Contains(protocol.ToLowerInvariant()))
+        {
+            throw new ArgumentException($"Unsupported protocol: {protocol}. " +
+                "Supported values are: grpc, http/protobuf, http");
+        }
+        settings.Protocol = protocol;
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (1)

115-127: Simplify HTTP client instrumentation filter.

The current implementation can be made more concise while maintaining readability.

         .AddHttpClientInstrumentation(o =>
         {
             o.RecordException = true;
-            o.FilterHttpRequestMessage = (_) =>
-            {
-                var parentActivity = Activity.Current?.Parent;
-                if (parentActivity != null && parentActivity.Source.Name.Equals("Azure.Core.Http", StringComparison.Ordinal))
-                {
-                    return false;
-                }
-                return true;
-            };
+            o.FilterHttpRequestMessage = _ => 
+                Activity.Current?.Parent?.Source.Name != "Azure.Core.Http";
         })
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d0cd2d7 and 59b8c77.

📒 Files selected for processing (4)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Service/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1 hunks)
  • src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
🧰 Additional context used
📓 Learnings (1)
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)
Learnt from: arealmaas
PR: digdir/dialogporten#1528
File: src/Digdir.Domain.Dialogporten.Service/Program.cs:56-58
Timestamp: 2024-12-10T10:54:37.635Z
Learning: In `src/Digdir.Domain.Dialogporten.Service/Program.cs`, when running locally without Docker, OTEL settings (`OTEL_SERVICE_NAME`, `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_PROTOCOL`) are optional and may not be provided, so validation for these settings is not required.
🔇 Additional comments (4)
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)

61-84: LGTM! Robust attribute parsing implementation.

The implementation uses HttpUtility.ParseQueryString for parsing resource attributes, which is more resilient than simple string splitting. Good error handling with clear error messages.

src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (1)

152-167: LGTM! Well-structured telemetry settings class.

The TelemetrySettings class provides a clean interface for configuring telemetry with sensible defaults for trace sources.

src/Digdir.Domain.Dialogporten.WebApi/Program.cs (2)

81-112: LGTM! Consistent telemetry configuration.

The implementation maintains consistency with other services while properly handling null values and attribute parsing.


81-86: Verify OpenTelemetry configuration in docker-compose files.

Since this is for local OpenTelemetry setup, ensure that the environment variables are properly configured in docker-compose files.

✅ Verification successful

OpenTelemetry configuration is properly set up in docker-compose.yml

The verification confirms that the OpenTelemetry environment variables are correctly configured in docker-compose.yml for both the WebAPI and GraphQL services:

  • OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318
  • OTEL_EXPORTER_OTLP_PROTOCOL=http
  • OTEL_SERVICE_NAME is set appropriately for each service
  • Additional attributes and debug logging are also configured
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if OTEL environment variables are configured in docker-compose files

# Look for docker-compose files
echo "Checking docker-compose files for OTEL configuration:"
fd --type f "docker-compose.*\.ya?ml$" | while read -r file; do
    echo "\nChecking $file:"
    # Check for OTEL environment variables
    rg -A 5 "OTEL_(SERVICE_NAME|EXPORTER_OTLP_ENDPOINT|EXPORTER_OTLP_PROTOCOL)" "$file"
done

Length of output: 1830

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)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2)

115-127: Improve HTTP client instrumentation configuration structure

The current nested configuration structure could be improved for better readability and maintainability.

Consider this restructuring:

-                        .AddHttpClientInstrumentation(o =>
-                        {
-                            o.RecordException = true;
-                            o.FilterHttpRequestMessage = (_) =>
-                            {
-                                var parentActivity = Activity.Current?.Parent;
-                                if (parentActivity != null && parentActivity.Source.Name.Equals("Azure.Core.Http", StringComparison.Ordinal))
-                                {
-                                    return false;
-                                }
-                                return true;
-                            };
-                        })
+                        .AddHttpClientInstrumentation(o =>
+                        {
+                            o.RecordException = true;
+                            o.FilterHttpRequestMessage = ShouldTraceRequest;
+                        })

Add this helper method:

private static bool ShouldTraceRequest(HttpRequestMessage _)
{
    var parentActivity = Activity.Current?.Parent;
    return parentActivity?.Source.Name != "Azure.Core.Http";
}

152-167: Add XML documentation to TelemetrySettings class

The TelemetrySettings class would benefit from XML documentation to explain its purpose and properties.

Consider adding documentation:

+/// <summary>
+/// Configuration settings for OpenTelemetry integration.
+/// </summary>
 public class TelemetrySettings
 {
     private const string MassTransitSource = "MassTransit";
     private const string AzureSource = "Azure.*";

+    /// <summary>
+    /// Gets or sets the service name for telemetry identification.
+    /// </summary>
     public string? ServiceName { get; set; }
+    /// <summary>
+    /// Gets or sets the OTLP endpoint URL.
+    /// </summary>
     public string? Endpoint { get; set; }
     // Add documentation for remaining properties
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d0cd2d7 and 59b8c77.

📒 Files selected for processing (4)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Service/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1 hunks)
  • src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
🧰 Additional context used
📓 Learnings (1)
src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1)
Learnt from: knuhau
PR: digdir/dialogporten#1349
File: src/Digdir.Domain.Dialogporten.GraphQL/Program.cs:70-74
Timestamp: 2024-11-12T05:32:45.312Z
Learning: In this project, the OpenTelemetry configurations are handled in the `.ConfigureTelemetry()` method.
🔇 Additional comments (2)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (1)

64-68: 🛠️ Refactor suggestion

Replace Console.WriteLine with structured logging

The use of Console.WriteLine for logging configuration details is not recommended for production code. Consider using a bootstrap logger or environment-specific logging configuration.

Consider this approach:

-Console.WriteLine($"[OpenTelemetry] Configuring telemetry for service: {settings.ServiceName}");
-foreach (var attr in settings.ResourceAttributes)
-{
-    Console.WriteLine($"[OpenTelemetry] Resource attribute: {attr.Key}={attr.Value}");
-}
+if (builder.Environment.IsDevelopment())
+{
+    Console.WriteLine($"[OpenTelemetry] Configuring telemetry for service: {settings.ServiceName}");
+    foreach (var attr in settings.ResourceAttributes)
+    {
+        Console.WriteLine($"[OpenTelemetry] Resource attribute: {attr.Key}={attr.Value}");
+    }
+}

Likely invalid or redundant comment.

src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1)

81-112: Same telemetry configuration improvements apply

This code segment has the same duplication issue as identified in the Service/Program.cs file.

src/Digdir.Domain.Dialogporten.Service/Program.cs Outdated Show resolved Hide resolved
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

🧹 Outside diff range and nitpick comments (3)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (3)

57-68: Consider using structured logging instead of Console.WriteLine

Previous attempts to use ILogger failed due to logger initialization issues. Consider creating a dedicated logging abstraction for telemetry configuration that can handle the bootstrap phase.

+    private static class TelemetryLogger
+    {
+        public static void Log(string message, params object[] args) =>
+            Console.WriteLine($"[OpenTelemetry] {string.Format(message, args)}");
+    }

     public static WebApplicationBuilder ConfigureTelemetry(
         this WebApplicationBuilder builder,
         Action<TelemetrySettings, IConfiguration>? configure = null)
     {
         var settings = new TelemetrySettings();
         configure?.Invoke(settings, builder.Configuration);

-        Console.WriteLine($"[OpenTelemetry] Configuring telemetry for service: {settings.ServiceName}");
+        TelemetryLogger.Log("Configuring telemetry for service: {0}", settings.ServiceName);
         foreach (var attr in settings.ResourceAttributes)
         {
-            Console.WriteLine($"[OpenTelemetry] Resource attribute: {attr.Key}={attr.Value}");
+            TelemetryLogger.Log("Resource attribute: {0}={1}", attr.Key, attr.Value);
         }

81-94: Improve resource attributes parsing robustness

The current implementation uses HttpUtility.ParseQueryString which is clever but might not be the most maintainable approach. Consider a more explicit parsing strategy.

-            var attributes = System.Web.HttpUtility.ParseQueryString(
-                resourceAttributes.Replace(',', '&')
-            );
+            var attributes = resourceAttributes
+                .Split(',')
+                .Select(pair => pair.Split('=', 2))
+                .Where(parts => parts.Length == 2)
+                .ToDictionary(
+                    parts => parts[0].Trim(),
+                    parts => parts[1].Trim()
+                );

96-145: Add XML documentation for telemetry configuration options

The telemetry configuration is comprehensive but would benefit from XML documentation explaining the available options and their impact.

Consider adding documentation:

+    /// <summary>
+    /// Configures OpenTelemetry instrumentation with support for both OTLP and Azure Monitor exporters.
+    /// </summary>
+    /// <param name="builder">The web application builder.</param>
+    /// <param name="configure">Optional action to configure telemetry settings.</param>
+    /// <remarks>
+    /// Supported environment variables:
+    /// - OTEL_SERVICE_NAME: Name of the service
+    /// - OTEL_EXPORTER_OTLP_ENDPOINT: OTLP endpoint URL
+    /// - OTEL_EXPORTER_OTLP_PROTOCOL: Protocol (grpc, http/protobuf)
+    /// - OTEL_RESOURCE_ATTRIBUTES: Comma-separated key=value pairs
+    /// - APPLICATIONINSIGHTS_CONNECTION_STRING: Azure Application Insights connection string
+    /// </remarks>
     public static WebApplicationBuilder ConfigureTelemetry(
         this WebApplicationBuilder builder,
         Action<TelemetrySettings, IConfiguration>? configure = null)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d0cd2d7 and 59b8c77.

📒 Files selected for processing (4)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Service/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1 hunks)
  • src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
🧰 Additional context used
📓 Learnings (1)
src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1)
Learnt from: knuhau
PR: digdir/dialogporten#1349
File: src/Digdir.Domain.Dialogporten.GraphQL/Program.cs:70-74
Timestamp: 2024-11-12T05:32:45.312Z
Learning: In this project, the OpenTelemetry configurations are handled in the `.ConfigureTelemetry()` method.
🔇 Additional comments (3)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (1)

152-167: LGTM! Well-structured telemetry settings class.

The TelemetrySettings class provides a clean encapsulation of telemetry configuration with appropriate defaults for trace sources.

src/Digdir.Domain.Dialogporten.Service/Program.cs (1)

54-85: LGTM! Consistent telemetry configuration

The telemetry configuration follows the same pattern as other services, ensuring consistency across the application.

src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1)

81-112: LGTM! Well-integrated telemetry configuration

The telemetry configuration is properly integrated with the existing logging and monitoring infrastructure.

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

🧹 Outside diff range and nitpick comments (4)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2)

109-130: Improve HTTP instrumentation configuration.

The current setup of HTTP client instrumentation could be optimized:

  1. The filter logic is nested inside FilterHttpRequestMessage
  2. RecordException setting could be shared

Consider this refactoring:

 .AddHttpClientInstrumentation(o =>
 {
     o.RecordException = true;
-    o.FilterHttpRequestMessage = (_) =>
+    o.FilterHttpRequestMessage = (request) =>
     {
         var parentActivity = Activity.Current?.Parent;
-        if (parentActivity != null && parentActivity.Source.Name.Equals("Azure.Core.Http", StringComparison.Ordinal))
+        return parentActivity?.Source.Name != "Azure.Core.Http";
-        {
-            return false;
-        }
-        return true;
     };
 })

152-167: Consider making TelemetrySettings immutable.

The TelemetrySettings class could benefit from immutability to prevent accidental modifications after initialization.

Consider this pattern:

- public class TelemetrySettings
+ public sealed class TelemetrySettings
 {
     private const string MassTransitSource = "MassTransit";
     private const string AzureSource = "Azure.*";
 
-    public string? ServiceName { get; set; }
-    public string? Endpoint { get; set; }
-    public string? Protocol { get; set; }
-    public string? AppInsightsConnectionString { get; set; }
+    public string? ServiceName { get; init; }
+    public string? Endpoint { get; init; }
+    public string? Protocol { get; init; }
+    public string? AppInsightsConnectionString { get; init; }
src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1)

81-112: Improve resource attributes parsing and validation.

The current implementation of resource attributes parsing could be enhanced:

  1. The error message could be more descriptive
  2. The empty value handling could be more explicit

Consider these improvements:

 try
 {
     var attributes = System.Web.HttpUtility.ParseQueryString(
         resourceAttributes.Replace(',', '&')
     );
     foreach (string key in attributes.Keys)
     {
-        if (!string.IsNullOrEmpty(key))
+        if (string.IsNullOrEmpty(key))
+        {
+            continue;
+        }
+        var value = attributes[key];
+        if (value == null)
         {
-            settings.ResourceAttributes[key] = attributes[key] ?? string.Empty;
+            Log.Warning("Empty value for resource attribute key: {Key}", key);
+            continue;
         }
+        settings.ResourceAttributes[key] = value;
     }
 }
 catch (Exception ex)
 {
     throw new InvalidOperationException(
-        "Failed to parse OTEL_RESOURCE_ATTRIBUTES. Expected format: key1=value1,key2=value2",
+        "Failed to parse OTEL_RESOURCE_ATTRIBUTES. Expected format: key1=value1,key2=value2. " +
+        "Values should not be empty and keys should be unique.",
         ex
     );
 }
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)

61-84: Consider enhancing resource attributes parsing robustness.

While the current implementation is functional, consider these improvements:

  1. Add validation for attribute values to ensure they are valid OTEL attributes
  2. Add debug logging for successful parsing and warnings for skipped entries
  3. Consider using a more direct parsing approach instead of HttpUtility.ParseQueryString

Here's a suggested implementation:

         var resourceAttributes = configuration["OTEL_RESOURCE_ATTRIBUTES"];
         if (!string.IsNullOrEmpty(resourceAttributes))
         {
             try
             {
-                var attributes = System.Web.HttpUtility.ParseQueryString(
-                    resourceAttributes.Replace(',', '&')
-                );
-                foreach (string key in attributes.Keys)
+                foreach (var pair in resourceAttributes.Split(',', StringSplitOptions.RemoveEmptyEntries))
                 {
-                    if (!string.IsNullOrEmpty(key))
+                    var parts = pair.Split('=', 2);
+                    if (parts.Length != 2)
                     {
-                        settings.ResourceAttributes[key] = attributes[key] ?? string.Empty;
+                        Log.Warning("Skipping invalid attribute pair: {Pair}", pair);
+                        continue;
                     }
+                    
+                    var key = parts[0].Trim();
+                    var value = parts[1].Trim();
+                    
+                    if (string.IsNullOrEmpty(key))
+                    {
+                        Log.Warning("Skipping empty key in pair: {Pair}", pair);
+                        continue;
+                    }
+                    
+                    settings.ResourceAttributes[key] = value;
+                    Log.Debug("Added resource attribute: {Key}={Value}", key, value);
                 }
             }
             catch (Exception ex)
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d0cd2d7 and 59b8c77.

📒 Files selected for processing (4)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Service/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1 hunks)
  • src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
🧰 Additional context used
📓 Learnings (1)
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)
Learnt from: arealmaas
PR: digdir/dialogporten#1528
File: src/Digdir.Domain.Dialogporten.Service/Program.cs:56-58
Timestamp: 2024-12-10T10:54:37.635Z
Learning: In `src/Digdir.Domain.Dialogporten.Service/Program.cs`, when running locally without Docker, OTEL settings (`OTEL_SERVICE_NAME`, `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_PROTOCOL`) are optional and may not be provided, so validation for these settings is not required.
🔇 Additional comments (2)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (1)

64-68: 🛠️ Refactor suggestion

Replace Console.WriteLine with proper logging.

Using Console.WriteLine for logging bypasses the logging framework's configuration and features.

Create a temporary logger factory for startup logging:

+ using var loggerFactory = LoggerFactory.Create(builder =>
+ {
+     builder.AddConsole();
+ });
+ var logger = loggerFactory.CreateLogger("TelemetryConfiguration");
- Console.WriteLine($"[OpenTelemetry] Configuring telemetry for service: {settings.ServiceName}");
+ logger.LogInformation("Configuring telemetry for service: {ServiceName}", settings.ServiceName);

Likely invalid or redundant comment.

src/Digdir.Domain.Dialogporten.Service/Program.cs (1)

54-59: LGTM! Configuration aligns with local development requirements.

The telemetry configuration correctly implements optional OTEL settings with appropriate fallbacks, which is suitable for both local development and containerized environments.

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

🧹 Outside diff range and nitpick comments (2)
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)

61-84: Consider adding logging for successful attribute parsing.

While error handling is good, adding debug-level logging for successful attribute parsing would help with observability.

 try
 {
     var attributes = System.Web.HttpUtility.ParseQueryString(
         resourceAttributes.Replace(',', '&')
     );
+    Log.Debug("Successfully parsed {Count} resource attributes", attributes.Count);
     foreach (string key in attributes.Keys)
     {
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (1)

109-130: Consider adding metrics for filtered requests.

While the filtering logic is correct, adding metrics to track filtered requests would help monitor the filtering effectiveness.

 .AddHttpClientInstrumentation(o =>
 {
     o.RecordException = true;
     o.FilterHttpRequestMessage = (_) =>
     {
         var parentActivity = Activity.Current?.Parent;
         if (parentActivity != null && parentActivity.Source.Name.Equals("Azure.Core.Http", StringComparison.Ordinal))
         {
+            Metrics.CreateCounter<long>("filtered_requests").Add(1, new("source", "azure_core"));
             return false;
         }
         return true;
     };
 })
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between d0cd2d7 and 59b8c77.

📒 Files selected for processing (4)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Service/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1 hunks)
  • src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
🧰 Additional context used
📓 Learnings (1)
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)
Learnt from: arealmaas
PR: digdir/dialogporten#1528
File: src/Digdir.Domain.Dialogporten.Service/Program.cs:56-58
Timestamp: 2024-12-10T10:54:37.635Z
Learning: In `src/Digdir.Domain.Dialogporten.Service/Program.cs`, when running locally without Docker, OTEL settings (`OTEL_SERVICE_NAME`, `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_PROTOCOL`) are optional and may not be provided, so validation for these settings is not required.
🔇 Additional comments (5)
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)

54-58: LGTM! Telemetry configuration setup is well-structured.

The configuration follows best practices by making the settings optional for local development, as confirmed by previous learnings.

src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (3)

64-68: Replace Console.WriteLine with proper logging.

Using Console.WriteLine for logging bypasses the logging framework. However, based on past review comments, there's a known issue with logger initialization at this stage.


86-92: LGTM! Protocol handling is robust.

The protocol handling is well-implemented with:

  • Case-insensitive comparison
  • Support for both 'http' and 'http/protobuf'
  • Clear error message for unsupported protocols

152-167: LGTM! Well-structured settings class.

The TelemetrySettings class is well-designed with:

  • Clear separation of concerns
  • Sensible defaults for trace sources
  • Immutable constants for source names
src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1)

81-112: LGTM! Configuration is consistent with other services.

The telemetry configuration follows the established pattern and integrates well with the WebApi's architecture.

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 (6)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (4)

169-184: LGTM! Well-structured telemetry settings class.

The TelemetrySettings class provides a clean interface for configuring telemetry with appropriate nullable annotations and default trace sources for MassTransit and Azure services.

Consider adding XML documentation for the properties to improve maintainability and IDE support.


71-94: Enhance resource attributes parsing with structured logging.

The resource attributes parsing logic is robust, but consider these improvements:

  1. Replace Console.WriteLine with structured logging
  2. Add validation for attribute values

Apply this diff:

-Console.WriteLine($"[OpenTelemetry] Resource attribute: {key}={attributes[key]}");
+var logger = builder.Logging.Services.BuildServiceProvider().GetRequiredService<ILogger<TelemetrySettings>>();
+logger.LogInformation("Adding resource attribute: {Key}={Value}", key, attributes[key]);

+if (string.IsNullOrWhiteSpace(attributes[key]))
+{
+    logger.LogWarning("Empty value for resource attribute: {Key}", key);
+    continue;
+}

103-109: Improve protocol validation error message.

The protocol validation is correct, but the error message could be more helpful by listing supported protocols.

Apply this diff:

-                _ => throw new ArgumentException($"Unsupported protocol: {settings.Protocol}")
+                _ => throw new ArgumentException(
+                    $"Unsupported protocol: {settings.Protocol}. " +
+                    "Supported protocols are: 'grpc', 'http/protobuf', 'http'")

126-147: Extract filter logic for better maintainability.

The instrumentation configuration is comprehensive, but consider extracting the filter logic into named methods for better readability and maintainability.

Apply this diff:

+private static bool ShouldTraceHttpContext(HttpContext context) => 
+    !context.Request.Path.StartsWithSegments("/health");

+private static bool ShouldTraceHttpRequest(HttpRequestMessage _)
+{
+    var parentActivity = Activity.Current?.Parent;
+    return parentActivity == null || 
+           !parentActivity.Source.Name.Equals("Azure.Core.Http", StringComparison.Ordinal);
+}

 tracing
     .AddAspNetCoreInstrumentation(opts =>
     {
         opts.RecordException = true;
-        opts.Filter = httpContext => !httpContext.Request.Path.StartsWithSegments("/health");
+        opts.Filter = ShouldTraceHttpContext;
     })
     .AddHttpClientInstrumentation(o =>
     {
         o.RecordException = true;
-        o.FilterHttpRequestMessage = (_) =>
-        {
-            var parentActivity = Activity.Current?.Parent;
-            if (parentActivity != null && parentActivity.Source.Name.Equals("Azure.Core.Http", StringComparison.Ordinal))
-            {
-                return false;
-            }
-            return true;
-        };
+        o.FilterHttpRequestMessage = ShouldTraceHttpRequest;
     })
src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1)

81-88: Add validation for Application Insights connection string.

The telemetry configuration is good, but consider validating the Application Insights connection string format to prevent runtime issues.

Add validation before setting the connection string:

+        var appInsightsConnString = configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"];
+        if (!string.IsNullOrEmpty(appInsightsConnString) && !appInsightsConnString.Contains("InstrumentationKey="))
+        {
+            throw new ArgumentException("Invalid Application Insights connection string format");
+        }
-        settings.AppInsightsConnectionString = configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"];
+        settings.AppInsightsConnectionString = appInsightsConnString;
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)

54-61: Consider centralizing telemetry configuration

The telemetry configuration logic might be duplicated across different Program.cs files (Service, GraphQL, WebApi). Consider moving this configuration to a shared extension method to maintain consistency and reduce duplication.

Example approach:

// In a shared location (e.g., Digdir.Library.Utils.AspNet)
public static class TelemetryConfigurationExtensions
{
    public static void ConfigureDialogportenTelemetry(
        this TelemetrySettings settings,
        IConfiguration configuration,
        string defaultServiceName)
    {
        settings.ServiceName = configuration["OTEL_SERVICE_NAME"] ?? defaultServiceName;
        settings.Endpoint = configuration["OTEL_EXPORTER_OTLP_ENDPOINT"];
        settings.Protocol = configuration["OTEL_EXPORTER_OTLP_PROTOCOL"];
        settings.AppInsightsConnectionString = configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"];
        // Include the resource attributes parsing logic here
    }
}

// Usage in Program.cs
builder.ConfigureTelemetry((settings, configuration) =>
{
    settings.ConfigureDialogportenTelemetry(
        configuration,
        builder.Environment.ApplicationName);
});
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 59b8c77 and ad6852e.

📒 Files selected for processing (4)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.Service/Program.cs (1 hunks)
  • src/Digdir.Domain.Dialogporten.WebApi/Program.cs (1 hunks)
  • src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Digdir.Domain.Dialogporten.GraphQL/Program.cs
🧰 Additional context used
📓 Learnings (1)
src/Digdir.Domain.Dialogporten.Service/Program.cs (1)
Learnt from: arealmaas
PR: digdir/dialogporten#1528
File: src/Digdir.Domain.Dialogporten.Service/Program.cs:56-58
Timestamp: 2024-12-10T10:54:37.635Z
Learning: In `src/Digdir.Domain.Dialogporten.Service/Program.cs`, when running locally without Docker, OTEL settings (`OTEL_SERVICE_NAME`, `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_PROTOCOL`) are optional and may not be provided, so validation for these settings is not required.

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 (6)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesSettings.cs (3)

13-14: Add XML documentation for the class and its properties

The TelemetrySettings class and its properties would benefit from XML documentation to explain:

  • The purpose of each property
  • Expected formats and valid values
  • Whether certain properties are required for specific scenarios

Example documentation:

+ /// <summary>
+ /// Configuration settings for OpenTelemetry integration.
+ /// </summary>
 public sealed class TelemetrySettings
 {
     // ... constants ...

+    /// <summary>
+    /// The name of the service to be used in telemetry data.
+    /// </summary>
     public string? ServiceName { get; set; }

+    /// <summary>
+    /// The OpenTelemetry collector endpoint URL.
+    /// </summary>
     public string? Endpoint { get; set; }

Also applies to: 18-22


15-16: Consider refining the Azure trace source pattern

The Azure.* pattern is quite broad and might capture more traces than intended. Consider:

  1. Being more specific about which Azure services you want to trace
  2. Documenting the impact of these patterns
  3. Making the patterns configurable rather than constant

Example implementation:

-    private const string AzureSource = "Azure.*";
+    private const string AzureStorageSource = "Azure.Storage.*";
+    private const string AzureIdentitySource = "Azure.Identity.*";
     public HashSet<string> TraceSources { get; set; } = new()
     {
-        AzureSource,
+        AzureStorageSource,
+        AzureIdentitySource,
         MassTransitSource
     };

Also applies to: 23-27


18-22: Consider adding required field validation

Some properties might be required depending on the configuration scenario. Consider adding validation logic or implementing IValidatable interface.

This could be implemented through:

  1. Constructor validation
  2. IValidatable interface
  3. Fluent validation
  4. Guard clauses in the configuration extension method

Would you like me to provide an example implementation of any of these approaches?

src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (3)

98-111: Add validation for endpoint and protocol settings

While the protocol handling is good, consider adding more robust validation:

  1. Move protocol validation to TelemetrySettings:
public class TelemetrySettings
{
    private string? protocol;
    public string? Protocol
    {
        get => protocol;
        set => protocol = ValidateProtocol(value);
    }

    private static string? ValidateProtocol(string? value)
    {
        if (string.IsNullOrEmpty(value)) return value;
        return value.ToLowerInvariant() switch
        {
            "grpc" or "http/protobuf" or "http" => value,
            _ => throw new ArgumentException($"Unsupported protocol: {value}")
        };
    }
}
  1. Add URI validation:
-telemetryBuilder.UseOtlpExporter(otlpProtocol, new Uri(settings.Endpoint));
+if (!Uri.TryCreate(settings.Endpoint, UriKind.Absolute, out var endpoint))
+{
+    throw new ArgumentException("Invalid endpoint URI", nameof(settings.Endpoint));
+}
+telemetryBuilder.UseOtlpExporter(otlpProtocol, endpoint);

127-131: Enhance health check endpoint filtering

The current health check filter could be more robust.

Consider using a constant or configuration for health check paths:

-opts.Filter = httpContext => !httpContext.Request.Path.StartsWithSegments("/health");
+private static readonly string[] HealthCheckPaths = new[] { "/health", "/health/startup", "/health/liveness", "/health/readiness", "/health/deep" };
+opts.Filter = httpContext => !HealthCheckPaths.Any(path => httpContext.Request.Path.StartsWithSegments(path, StringComparison.OrdinalIgnoreCase));

157-163: Consider adding more metrics instrumentation

The current metrics configuration is good but could be enhanced.

Consider adding these additional instrumentations for better observability:

 metrics.AddRuntimeInstrumentation()
     .AddAspNetCoreInstrumentation()
     .AddHttpClientInstrumentation()
+    .AddEventCountersInstrumentation(c => {
+        c.AddEventSources("Microsoft.AspNetCore.Hosting");
+        c.AddEventSources("Microsoft-AspNetCore-Server-Kestrel");
+    })
     .AddAzureMonitorMetricExporter();
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between ad6852e and df8825f.

📒 Files selected for processing (3)
  • src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (2 hunks)
  • src/Digdir.Library.Utils.AspNet/AspNetUtilitiesSettings.cs (1 hunks)
  • src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Digdir.Library.Utils.AspNet/Digdir.Library.Utils.AspNet.csproj
🔇 Additional comments (4)
src/Digdir.Library.Utils.AspNet/AspNetUtilitiesSettings.cs (1)

22-22: Add validation for ResourceAttributes format

The ResourceAttributes property should follow the W3C trace context format. Consider adding validation or documentation about the expected format (e.g., key1=value1,key2=value2).

Let's check if there are any existing validation patterns in the codebase:

src/Digdir.Library.Utils.AspNet/AspNetUtilitiesExtensions.cs (3)

6-6: LGTM: Imports are well-organized and necessary

The added imports provide all required dependencies for OpenTelemetry integration.

Also applies to: 10-10, 15-19


57-65: Replace Console.WriteLine with structured logging

Using Console.WriteLine for logging is not recommended in production code. Use ILogger instead for better configurability and to respect logging levels.


132-144: Improve HTTP client instrumentation configuration

The current implementation of FilterHttpRequestMessage could be improved.

@arealmaas
Copy link
Collaborator Author

@coderabbitai pause

Copy link
Contributor

coderabbitai bot commented Dec 10, 2024

✅ Actions performed

Reviews paused.

oskogstad
oskogstad previously approved these changes Dec 13, 2024
@arealmaas arealmaas force-pushed the feat/add-otel-exporter branch from d8b2e3d to 998fc9d Compare December 16, 2024 14:10
Copy link

@arealmaas arealmaas merged commit cb9238e into main Dec 16, 2024
25 checks passed
@arealmaas arealmaas deleted the feat/add-otel-exporter branch December 16, 2024 15:38
arealmaas pushed a commit that referenced this pull request Dec 17, 2024
🤖 I have created a release *beep* *boop*
---


##
[1.42.0](v1.41.3...v1.42.0)
(2024-12-16)


### Features

* **apps:** add otel exporter for graphql, service and web-api
([#1528](#1528))
([cb9238e](cb9238e))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants