Skip to content

v1.0.19#51

Merged
yilmaztayfun merged 3 commits into
release-v1.0from
master
Mar 12, 2026
Merged

v1.0.19#51
yilmaztayfun merged 3 commits into
release-v1.0from
master

Conversation

@yilmaztayfun
Copy link
Copy Markdown
Contributor

@yilmaztayfun yilmaztayfun commented Mar 12, 2026

Summary by Sourcery

Add configurable HTTP header-based telemetry enrichment and optional HTTP body logging, and improve background job lifecycle handling.

New Features:

  • Introduce configurable trace header tags to attach selected HTTP request headers as span attributes.
  • Add global logging enrichers that apply custom attributes and selected headers to all log records for consistent querying.
  • Provide optional HTTP request/response body logging middleware with redaction, size limits, and path/content-type filtering.

Bug Fixes:

  • Ensure background jobs that are already processed or have no handler are removed from the external scheduler to prevent repeated triggering.
  • Record CreatedAt and ModifiedAt timestamps correctly for background job entities when saving or updating status.

Enhancements:

  • Refine telemetry options and wiring to separate tracing header configuration from logging enrichers and to plug in a dedicated log enricher processor.
  • Expand telemetry documentation with configuration examples and guidance for log enrichment, HTTP body logging, and path exclusion behavior.

Documentation:

  • Document telemetry configuration keys for tracing headers, logging enrichers, and HTTP body logging, including examples and middleware usage guidance.

… job improvements

Telemetry:
- Add configurable HTTP request/response body logging middleware (UseHttpBodyLogging)
  with Body options, path exclusion, and sensitive field/header redaction
- Introduce HttpBodyLoggingOptions, LoggingEnricherOptions in TelemetryOptions
- Replace AetherLogEnricherProcessor with EnricherLogProcessor; document
  Tracing.Headers as explicit list (no wildcard)
- Update telemetry README with Logging.Body and Enrichers configuration

Background jobs:
- Set CreatedAt in BackgroundJobInfo constructor, ModifiedAt in EfCoreJobStore
  on update and status change
- Integrate IJobScheduler.DeleteAsync in JobDispatcher on completion, failure,
  and cancellation; refactor idempotency check to use single job fetch

Made-with: Cursor
Initialize a cached, case-insensitive _allowedContentTypes HashSet from options in the constructor and use it in IsAllowedContentType to avoid allocating a new set on each call (retains the +json suffix handling). Rewrite TruncateUtf8 to check UTF-8 byte count before truncating and use an Encoding.UTF8 Decoder to avoid splitting multi-byte characters, appending "…[TRUNCATED]" when truncated.
…-wildcard-headers-and-requestresponse-logging-configuration

feat(telemetry): HTTP body logging, enricher refactor, and background job improvements
@yilmaztayfun yilmaztayfun self-assigned this Mar 12, 2026
@yilmaztayfun yilmaztayfun requested review from a team and ukaratas March 12, 2026 22:27
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Mar 12, 2026

Reviewer's Guide

Adds configurable HTTP body logging and header-based enrichment to telemetry, exposes tracing/logging header configuration in options and docs, and improves background job handling by syncing scheduler state and tracking Created/Modified timestamps.

Sequence diagram for HTTP request logging with body middleware and enricher processor

sequenceDiagram
    actor User
    participant Browser
    participant AspNetCoreApp as AspNetCore_App
    participant HttpBodyMiddleware as HttpBodyLoggingMiddleware
    participant NextMiddleware
    participant Controller
    participant Logger as ILogger
    participant EnricherProcessor as EnricherLogProcessor
    participant OtelExporter as OpenTelemetry_Exporter

    User->>Browser: Make HTTP request
    Browser->>AspNetCoreApp: HTTP request
    AspNetCoreApp->>HttpBodyMiddleware: Invoke(HttpContext)
    HttpBodyMiddleware->>HttpBodyMiddleware: ShouldProcess(path) and compile regex
    alt Request body capture enabled
        HttpBodyMiddleware->>HttpBodyMiddleware: CaptureRequestBodyAsync
    end
    HttpBodyMiddleware->>NextMiddleware: Invoke(HttpContext)
    NextMiddleware->>Controller: Execute action
    Controller-->>NextMiddleware: Response
    NextMiddleware-->>HttpBodyMiddleware: Response

    alt Response body capture enabled
        HttpBodyMiddleware->>HttpBodyMiddleware: CaptureResponseBodyAsync
    end

    HttpBodyMiddleware->>Logger: LogInformation("HTTP request/response logged", scope with Method, RequestPath, StatusCode, RequestHeaders, ResponseHeaders, RequestBody*, ResponseBody*, Enrichers CustomAttributes + Headers)
    Logger-->>AspNetCoreApp: LogRecord created

    note over Logger,EnricherProcessor: OpenTelemetry logging pipeline processes LogRecord

    Logger->>EnricherProcessor: OnEnd(LogRecord)
    EnricherProcessor->>EnricherProcessor: Add CustomAttributes + header properties when not already enriched by middleware log
    EnricherProcessor-->>Logger: Enriched LogRecord
    Logger->>OtelExporter: Export enriched log to OTLP backend
    OtelExporter-->>Logger: Ack

    HttpBodyMiddleware-->>AspNetCoreApp: Response body restored
    AspNetCoreApp-->>Browser: HTTP response
    Browser-->>User: Show result
Loading

Sequence diagram for background job dispatch with scheduler deletion

sequenceDiagram
    participant Scheduler as ExternalScheduler
    participant Dispatcher as JobDispatcher
    participant Scope as DI_Scope
    participant UowMgr as IUnitOfWorkManager
    participant JobStore as IJobStore
    participant SchedulerSvc as IJobScheduler
    participant Handler as JobHandler
    participant Db as Database

    Scheduler->>Dispatcher: DispatchAsync(jobId, handlerName)
    Dispatcher->>Scope: CreateScope()
    Scope->>UowMgr: BeginRequiresNew()
    Scope->>JobStore: GetAsync(jobId)
    JobStore-->>Scope: BackgroundJobInfo jobInfo

    alt jobInfo is null
        Scope-->>Dispatcher: return
        Dispatcher-->>Scheduler: exit
    else jobInfo exists
        Dispatcher->>Dispatcher: IsJobAlreadyProcessed(jobInfo)
        alt already Completed or Cancelled
            Scope->>SchedulerSvc: TryDeleteFromSchedulerAsync(handlerName, jobInfo.JobName)
            SchedulerSvc->>Db: Delete scheduled job trigger
            Db-->>SchedulerSvc: ok or error (logged, not thrown)
            Scope-->>Dispatcher: Commit and return
            Dispatcher-->>Scheduler: exit
        else Pending
            Scope->>JobStore: UpdateStatusAsync(jobId, Running)
            Scope->>Db: Save changes
            Scope-->>Dispatcher: Commit

            Dispatcher->>Handler: Execute job handler
            alt handler succeeds
                Dispatcher->>UowMgr: BeginRequiresNew()
                UowMgr->>JobStore: UpdateStatusAsync(jobId, Completed, handledTime)
                UowMgr->>Db: Save changes
                UowMgr-->>Dispatcher: Commit
                Dispatcher->>SchedulerSvc: TryDeleteFromSchedulerAsync(handlerName, jobInfo.JobName)
            else handler throws or cancelled
                Dispatcher->>UowMgr: MarkJobStatusAsync(jobId, Failed or Cancelled)
                UowMgr->>Db: Save changes
                UowMgr-->>Dispatcher: Commit
                Dispatcher->>SchedulerSvc: TryDeleteFromSchedulerAsync(handlerName, jobInfo.JobName)
            end
        end
    end

    SchedulerSvc->>Db: DeleteAsync(handlerName, jobName)
    Db-->>SchedulerSvc: success or failure
    SchedulerSvc-->>Dispatcher: no exception on failure (warning logged)
Loading

Class diagram for updated telemetry logging options and middleware

classDiagram
    class AetherTelemetryOptions {
        bool TracingEnabled
        bool MetricsEnabled
        bool LoggingEnabled
        AetherLoggingOptions Logging
        AetherTracingOptions Tracing
    }

    class AetherLoggingOptions {
        List~string~ ExcludedPaths
        LoggingEnricherOptions Enrichers
        HttpBodyLoggingOptions Body
        bool EnableOtlpExporter
        bool EnableConsoleExporter
        bool IncludeFormattedMessage
        bool IncludeScopes
        bool ParseStateValues
    }

    class HttpBodyLoggingOptions {
        <<sealed>>
        +static IReadOnlyList~string~ DefaultSensitiveJsonFields
        +static IReadOnlyList~string~ DefaultSensitiveHeaderNames
        bool EnableRequestBody
        bool EnableResponseBody
        int MaxBodyLengthToCapture
        List~string~ AllowedContentTypes
        List~string~ AdditionalSensitiveJsonFields
        List~string~ AdditionalSensitiveHeaderNames
    }

    class LoggingEnricherOptions {
        <<sealed>>
        Dictionary~string,string~ CustomAttributes
        List~string~ Headers
    }

    class AetherTracingOptions {
        <<sealed>>
        bool EnableAspNetCore
        bool EnableHttpClient
        List~string~ ExcludedPaths
        List~string~ AdditionalSources
        List~string~ Headers
    }

    class HttpBodyLoggingMiddleware {
        <<sealed>>
        -RequestDelegate _next
        -ILogger~HttpBodyLoggingMiddleware~ _logger
        -HttpBodyLoggingOptions _bodyOptions
        -LoggingEnricherOptions _enricherOptions
        -List~Regex~ _excludedPatterns
        -HashSet~string~ _sensitiveJsonFields
        -HashSet~string~ _sensitiveHeaderNames
        -HashSet~string~ _allowedContentTypes
        +HttpBodyLoggingMiddleware(RequestDelegate next, ILogger logger, IOptions~AetherTelemetryOptions~ options)
        +Task Invoke(HttpContext context)
        -bool ShouldProcess(HttpContext context)
        -bool CanReadRequestBody(HttpRequest request)
        -bool CanReadResponseBody(HttpResponse response)
        -bool IsAllowedContentType(string contentType)
        -Task<(string Body,int Size,bool Truncated,bool Captured)> CaptureRequestBodyAsync(HttpRequest request)
        -Task<(string Body,int Size,bool Truncated,bool Captured)> CaptureResponseBodyAsync(HttpResponse response, MemoryStream buffer)
        -void LogBodies(HttpContext context, string requestBody, string responseBody, int requestBodySize, int responseBodySize, bool requestTruncated, bool responseTruncated, bool requestCaptured, bool responseCaptured, string requestHeadersJson, string responseHeadersJson)
        -void AddEnricherProperties(List~KeyValuePair~string,object~~ scope, HttpContext context, string requestHeadersJson, string responseHeadersJson)
        -static string HeadersToJson(IHeaderDictionary headers, HashSet~string~ sensitiveHeaderNames)
        -static List~Regex~ CompileRegex(IEnumerable~string~ patterns)
        -static string TruncateUtf8(string value, int maxBytes)
        -static string RedactIfPossible(string text, string contentType, HashSet~string~ sensitiveJsonFields)
        -static string RedactJson(string json, HashSet~string~ sensitiveJsonFields)
        -static object RedactElement(JsonElement element, HashSet~string~ sensitiveJsonFields)
    }

    class EnricherLogProcessor {
        <<sealed>>
        -HashSet~string~ _sensitiveHeaderNames
        +EnricherLogProcessor(AetherTelemetryOptions options, IHttpContextAccessor httpContextAccessor)
        +void OnEnd(LogRecord record)
        -static HashSet~string~ BuildSensitiveHeaderNames(AetherTelemetryOptions options)
        -static bool HasBodyOrHeaderEnrichment(LogRecord record)
        -static string NormalizeHeaderKey(string key)
    }

    class HttpBodyLoggingApplicationBuilderExtensions {
        <<static>>
        +IApplicationBuilder UseHttpBodyLogging(IApplicationBuilder app)
    }

    AetherTelemetryOptions --> AetherLoggingOptions : uses
    AetherTelemetryOptions --> AetherTracingOptions : uses
    AetherLoggingOptions --> LoggingEnricherOptions : has
    AetherLoggingOptions --> HttpBodyLoggingOptions : has
    HttpBodyLoggingMiddleware --> HttpBodyLoggingOptions : uses
    HttpBodyLoggingMiddleware --> LoggingEnricherOptions : uses
    EnricherLogProcessor --> AetherTelemetryOptions : uses
    EnricherLogProcessor --> HttpBodyLoggingOptions : uses defaults
    HttpBodyLoggingApplicationBuilderExtensions --> HttpBodyLoggingMiddleware : adds middleware
Loading

Class diagram for updated background job processing and scheduler integration

classDiagram
    class BackgroundJobInfo {
        Guid Id
        string HandlerName
        string JobName
        BackgroundJobStatus Status
        DateTime CreatedAt
        DateTime? ModifiedAt
        DateTime? HandledTime
        ExtraPropertyDictionary ExtraProperties
        byte[] Payload
        BackgroundJobInfo(Guid id, string handlerName, string jobName)
    }

    class BackgroundJobStatus {
        <<enum>>
        Pending
        Running
        Completed
        Cancelled
        Failed
    }

    class IJobStore {
        <<interface>>
        Task SaveAsync(BackgroundJobInfo jobInfo, CancellationToken cancellationToken)
        Task<BackgroundJobInfo> GetAsync(Guid id, CancellationToken cancellationToken)
        Task UpdateStatusAsync(Guid id, BackgroundJobStatus status, DateTime? handledTime, string error, CancellationToken cancellationToken)
    }

    class EfCoreJobStore {
        +Task SaveAsync(BackgroundJobInfo jobInfo, CancellationToken cancellationToken)
        +Task UpdateStatusAsync(Guid id, BackgroundJobStatus status, DateTime? handledTime, string error, CancellationToken cancellationToken)
        -DbContext _dbContext
    }

    class IJobScheduler {
        <<interface>>
        Task DeleteAsync(string handlerName, string jobName, CancellationToken cancellationToken)
    }

    class IUnitOfWorkManager {
        <<interface>>
        Task<IUnitOfWork> BeginRequiresNew(CancellationToken cancellationToken)
    }

    class IUnitOfWork {
        <<interface>>
        Task CommitAsync(CancellationToken cancellationToken)
        ValueTask DisposeAsync()
    }

    class JobDispatcher {
        +Task DispatchAsync(Guid jobId, string handlerName, CancellationToken cancellationToken)
        -bool IsJobAlreadyProcessed(BackgroundJobInfo jobInfo, Guid jobId, string handlerName)
        -Task TryDeleteFromSchedulerAsync(IJobScheduler jobScheduler, string handlerName, string jobName, CancellationToken cancellationToken)
        -Task MarkJobStatusAsync(IUnitOfWorkManager uowManager, IJobStore jobStore, Guid jobId, BackgroundJobStatus status, string reason, CancellationToken cancellationToken)
        -ILogger logger
    }

    IJobStore <|.. EfCoreJobStore
    EfCoreJobStore --> BackgroundJobInfo : persists
    JobDispatcher --> IJobStore : uses
    JobDispatcher --> IJobScheduler : uses
    JobDispatcher --> IUnitOfWorkManager : uses
    BackgroundJobInfo --> BackgroundJobStatus : has
Loading

File-Level Changes

Change Details Files
Extend telemetry configuration to support trace header tags, log enrichers, and HTTP body logging options, and document their usage.
  • Update telemetry README to describe Trace header tags, log enrichers, and HTTP body logging features.
  • Change example appsettings.json to use root Telemetry section, add Tracing.Headers, enriched Logging.Enrichers configuration, and new Logging.Body options.
  • Document how Enrichers apply to all logs, how HTTP body logging middleware works (ordering, exclusions, sensitivity redaction), and how Logging.ExcludedPaths interacts with body logging.
framework/docs/telemetry/README.md
Introduce structured options for HTTP body logging and tracing/logging header configuration.
  • Add HttpBodyLoggingOptions with defaults for enabling request/response body capture, max body length, allowed content types, and configurable sensitive JSON fields and headers.
  • Extend AetherLoggingOptions with a Body property and adjust LoggingEnricherOptions semantics/docs to focus on log/body enrichment (CustomAttributes and Headers, with redaction).
  • Extend AetherTracingOptions with a Headers list for request headers to be attached as activity tags on spans.
framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/TelemetryOptions.cs
Add HTTP body logging middleware and app builder extension that logs request/response bodies and headers with redaction and enrichment.
  • Implement HttpBodyLoggingMiddleware that conditionally captures request/response bodies based on content type and Logging.Body flags, applies redaction for sensitive headers/JSON fields, and logs a single structured event per request.
  • Use Logging.ExcludedPaths as regex patterns to skip logging for noisy paths, with safe regex compilation and path matching.
  • Attach CustomAttributes and configured headers as scoped properties (RequestHeader.* / ResponseHeader.*) on the body log event, redacting sensitive headers and tracking capture/truncation metadata.
  • Provide UseHttpBodyLogging extension to register the middleware and document its recommended ordering and configuration keys.
framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/HttpBodyLoggingMiddleware.cs
framework/src/BBT.Aether.AspNetCore/Microsoft/AspNetCore/Builder/HttpBodyLoggingApplicationBuilderExtensions.cs
Refine telemetry wiring so tracing headers come from Tracing.Headers and log enrichment is handled by a new log processor that runs on all logs.
  • Change ASP.NET Core tracing configuration to use opts.Tracing.Headers instead of Logging.Enrichers.Headers when adding request headers as span tags.
  • Replace AetherLogEnricherProcessor with EnricherLogProcessor, which globally appends CustomAttributes and configured header values to all log records, skipping records that already contain HTTP body/header payloads to avoid duplication.
  • Wire EnricherLogProcessor into AddAetherTelemetry logging pipeline and keep resource attributes populated from Logging.Enrichers.CustomAttributes.
framework/src/BBT.Aether.AspNetCore/Microsoft/Extensions/DependencyInjection/AetherTelemetryServiceCollectionExtensions.cs
framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/EnricherLogProcessor.cs
framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/AetherLogEnricherProcessor.cs
Improve background job dispatching to keep scheduler in sync and ensure job timestamps are maintained.
  • Modify JobDispatcher to fetch BackgroundJobInfo once, cache jobName, and perform idempotency checks in-memory via IsJobAlreadyProcessed.
  • Inject IJobScheduler into JobDispatcher and add TryDeleteFromSchedulerAsync to attempt deletion of the job from the external scheduler after completion, cancellation, duplicate detection, or no-handler cases, logging a warning on failure without affecting DB consistency.
  • Ensure BackgroundJobInfo.CreatedAt is initialized in the constructor and that EfCoreJobStore updates ModifiedAt on save and status updates.
framework/src/BBT.Aether.Infrastructure/BBT/Aether/BackgroundJob/JobDispatcher.cs
framework/src/BBT.Aether.Infrastructure/BBT/Aether/BackgroundJob/EfCoreJobStore.cs
framework/src/BBT.Aether.Domain/BBT/Aether/Domain/Entities/BackgroundJobInfo.cs

Tips and commands

Interacting with Sourcery

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

Customizing Your Experience

Access your dashboard to:

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

Getting Help

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 12, 2026

Warning

Rate limit exceeded

@yilmaztayfun has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 14 minutes and 30 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9864d306-6602-423f-a279-0b386c58b6ad

📥 Commits

Reviewing files that changed from the base of the PR and between b98ef85 and 787cace.

📒 Files selected for processing (10)
  • framework/docs/telemetry/README.md
  • framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/AetherLogEnricherProcessor.cs
  • framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/EnricherLogProcessor.cs
  • framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/HttpBodyLoggingMiddleware.cs
  • framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/TelemetryOptions.cs
  • framework/src/BBT.Aether.AspNetCore/Microsoft/AspNetCore/Builder/HttpBodyLoggingApplicationBuilderExtensions.cs
  • framework/src/BBT.Aether.AspNetCore/Microsoft/Extensions/DependencyInjection/AetherTelemetryServiceCollectionExtensions.cs
  • framework/src/BBT.Aether.Domain/BBT/Aether/Domain/Entities/BackgroundJobInfo.cs
  • framework/src/BBT.Aether.Infrastructure/BBT/Aether/BackgroundJob/EfCoreJobStore.cs
  • framework/src/BBT.Aether.Infrastructure/BBT/Aether/BackgroundJob/JobDispatcher.cs

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'review'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch master
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@yilmaztayfun yilmaztayfun merged commit 11359c3 into release-v1.0 Mar 12, 2026
4 of 5 checks passed
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces significant enhancements to the application's telemetry and background job processing capabilities. The telemetry system now offers advanced HTTP request/response body and header logging with sensitive data redaction, alongside a more robust and centralized log enrichment mechanism. Furthermore, improvements to background job management ensure better tracking of job lifecycle events and more reliable cleanup from job schedulers. These changes collectively aim to provide richer diagnostic data and more resilient background task execution.

Highlights

  • Enhanced Telemetry Logging: Introduced a new HttpBodyLoggingMiddleware to capture and log HTTP request/response bodies and headers, with configurable redaction for sensitive data and path exclusion.
  • Centralized Log Enrichment: Replaced the AetherLogEnricherProcessor with EnricherLogProcessor to consistently add custom attributes and request/response headers to all log records, improving observability.
  • Tracing Header Tags: Added support for configuring specific request headers to be included as activity tags on OpenTelemetry spans, enhancing trace context propagation.
  • Background Job Management Improvements: Implemented automatic CreatedAt and ModifiedAt timestamps for background jobs and refined the JobDispatcher to include idempotency checks and a mechanism to delete completed/failed jobs from the scheduler.
  • Updated Documentation: The telemetry README was extensively updated to reflect the new logging and tracing features, including configuration examples.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • framework/docs/telemetry/README.md
    • Updated the list of Aether features to include trace header tags, log enrichers, and optional HTTP body logging.
    • Revised the appsettings.json configuration example to reflect new Tracing.Headers and Logging.Body sections.
    • Added detailed explanations for Tracing.Headers, Logging.Enrichers, and Logging.Body configuration options.
    • Introduced new sections on "Enrichers on All Logs" and "HTTP Request/Response Body and Header Logging" with usage and configuration details.
    • Updated the "Exclude Health Checks" section to "Exclude Health Checks and Noisy Paths" and added Logging.ExcludedPaths configuration.
  • framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/AetherLogEnricherProcessor.cs
    • Removed the AetherLogEnricherProcessor file.
  • framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/EnricherLogProcessor.cs
    • Added a new EnricherLogProcessor to enrich log records with custom attributes and request/response headers, handling sensitive data redaction.
  • framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/HttpBodyLoggingMiddleware.cs
    • Added a new HttpBodyLoggingMiddleware to capture and log HTTP request/response bodies and headers, including content type filtering, truncation, and sensitive data redaction.
  • framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/TelemetryOptions.cs
    • Added HttpBodyLoggingOptions to AetherLoggingOptions for configuring HTTP body logging.
    • Defined HttpBodyLoggingOptions with properties for enabling request/response body capture, max length, allowed content types, and sensitive fields/headers.
    • Updated LoggingEnricherOptions with more descriptive comments for CustomAttributes and Headers.
    • Added Headers property to AetherTracingOptions to specify request headers for activity tags.
  • framework/src/BBT.Aether.AspNetCore/Microsoft/AspNetCore/Builder/HttpBodyLoggingApplicationBuilderExtensions.cs
    • Added UseHttpBodyLogging extension method to IApplicationBuilder for integrating the new middleware.
  • framework/src/BBT.Aether.AspNetCore/Microsoft/Extensions/DependencyInjection/AetherTelemetryServiceCollectionExtensions.cs
    • Modified tracing configuration to use opts.Tracing.Headers for adding request headers to spans.
    • Replaced the registration of AetherLogEnricherProcessor with the new EnricherLogProcessor in the logging configuration.
  • framework/src/BBT.Aether.Domain/BBT/Aether/Domain/Entities/BackgroundJobInfo.cs
    • Initialized CreatedAt with DateTime.UtcNow in the constructor.
  • framework/src/BBT.Aether.Infrastructure/BBT/Aether/BackgroundJob/EfCoreJobStore.cs
    • Updated ModifiedAt timestamp when updating an existing job.
    • Updated ModifiedAt timestamp when changing a job's status.
  • framework/src/BBT.Aether.Infrastructure/BBT/Aether/BackgroundJob/JobDispatcher.cs
    • Refactored IsJobAlreadyProcessedAsync to IsJobAlreadyProcessed and now takes BackgroundJobInfo directly.
    • Introduced TryDeleteFromSchedulerAsync to attempt deleting jobs from the scheduler upon completion, cancellation, or failure.
    • Integrated TryDeleteFromSchedulerAsync calls into the job dispatching logic.
Activity
  • No human activity has been recorded for this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • In HttpBodyLoggingMiddleware, CompileRegex will throw during startup if any Logging.ExcludedPaths entry is an invalid regex; consider handling per-pattern compilation failures (e.g. try/catch with a warning and skip) so a bad config value doesn’t take the whole app down.
  • AddEnricherProperties in HttpBodyLoggingMiddleware takes requestHeadersJson and responseHeadersJson but never uses them; you can drop these unused parameters (and corresponding call-site arguments) to simplify the method signature.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `HttpBodyLoggingMiddleware`, `CompileRegex` will throw during startup if any `Logging.ExcludedPaths` entry is an invalid regex; consider handling per-pattern compilation failures (e.g. try/catch with a warning and skip) so a bad config value doesn’t take the whole app down.
- `AddEnricherProperties` in `HttpBodyLoggingMiddleware` takes `requestHeadersJson` and `responseHeadersJson` but never uses them; you can drop these unused parameters (and corresponding call-site arguments) to simplify the method signature.

## Individual Comments

### Comment 1
<location path="framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/HttpBodyLoggingMiddleware.cs" line_range="432-436" />
<code_context>
+        }
+    }
+
+    private static object? RedactElement(JsonElement element, HashSet<string> sensitiveJsonFields)
+    {
+        return element.ValueKind switch
+        {
+            JsonValueKind.Object => element.EnumerateObject()
+                .ToDictionary(
+                    p => p.Name,
</code_context>
<issue_to_address>
**issue (bug_risk):** Avoid potential exceptions when redacting JSON objects with duplicate property names.

`EnumerateObject().ToDictionary(...)` will throw if the JSON has duplicate property names, which JSON technically allows. That exception would trigger the `RedactJson` catch and skip redaction entirely. To avoid this, iterate and populate the dictionary manually (e.g., assigning keys so later properties overwrite earlier ones) instead of using `ToDictionary`, so duplicates don’t break redaction.
</issue_to_address>

### Comment 2
<location path="framework/src/BBT.Aether.AspNetCore/BBT/Aether/AspNetCore/Telemetry/EnricherLogProcessor.cs" line_range="62-63" />
<code_context>
+        if (enricherAttrs.Count == 0)
+            return;
+
+        var existing = record.Attributes ?? Array.Empty<KeyValuePair<string, object?>>();
+        var merged = new List<KeyValuePair<string, object?>>(existing.Count + enricherAttrs.Count);
+        foreach (var kv in existing)
+            merged.Add(kv);
</code_context>
<issue_to_address>
**suggestion (performance):** Use array Length instead of LINQ Count for existing attributes to avoid unnecessary overhead.

Since `existing` is always an array (`record.Attributes ?? Array.Empty<...>()`), `existing.Count` calls the LINQ extension and walks the array. Use `existing.Length` instead to avoid the extra allocation and O(n) overhead in this per-log-record path.

```suggestion
        var existing = record.Attributes ?? Array.Empty<KeyValuePair<string, object?>>();
        var merged = new List<KeyValuePair<string, object?>>(existing.Length + enricherAttrs.Count);
```
</issue_to_address>

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

Comment on lines +432 to +436
private static object? RedactElement(JsonElement element, HashSet<string> sensitiveJsonFields)
{
return element.ValueKind switch
{
JsonValueKind.Object => element.EnumerateObject()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Avoid potential exceptions when redacting JSON objects with duplicate property names.

EnumerateObject().ToDictionary(...) will throw if the JSON has duplicate property names, which JSON technically allows. That exception would trigger the RedactJson catch and skip redaction entirely. To avoid this, iterate and populate the dictionary manually (e.g., assigning keys so later properties overwrite earlier ones) instead of using ToDictionary, so duplicates don’t break redaction.

Comment on lines +62 to +63
var existing = record.Attributes ?? Array.Empty<KeyValuePair<string, object?>>();
var merged = new List<KeyValuePair<string, object?>>(existing.Count + enricherAttrs.Count);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (performance): Use array Length instead of LINQ Count for existing attributes to avoid unnecessary overhead.

Since existing is always an array (record.Attributes ?? Array.Empty<...>()), existing.Count calls the LINQ extension and walks the array. Use existing.Length instead to avoid the extra allocation and O(n) overhead in this per-log-record path.

Suggested change
var existing = record.Attributes ?? Array.Empty<KeyValuePair<string, object?>>();
var merged = new List<KeyValuePair<string, object?>>(existing.Count + enricherAttrs.Count);
var existing = record.Attributes ?? Array.Empty<KeyValuePair<string, object?>>();
var merged = new List<KeyValuePair<string, object?>>(existing.Length + enricherAttrs.Count);

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant enhancements to the telemetry and background job handling systems. The new configurable HTTP header enrichment and optional body logging are well-documented and the implementation appears robust, especially with features like content redaction and stream handling. The improvements to the background job dispatcher, which now ensures jobs are removed from the scheduler upon completion or failure, are a great step towards a more resilient system. I've found one area for improvement regarding potential duplication in logging enrichment configuration.

Comment on lines 238 to 241
logging.SetResourceBuilder(ResourceBuilder.CreateDefault()
.AddService(opts.ServiceName!, opts.ServiceNamespace ?? "aether", opts.ServiceVersion ?? "1.0.0", false, Environment.MachineName)
.AddAttributes(opts.Logging.Enrichers.CustomAttributes
.ToDictionary(x => x.Key, x => (object)x.Value)));
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The CustomAttributes from Logging.Enrichers are being added to the logging ResourceBuilder here, while they are also added to each log record by the EnricherLogProcessor (registered on line 247). This results in duplicating these attributes on every log record.

Since EnricherLogProcessor is designed to enrich all log records with these attributes, adding them to the resource builder is redundant. To avoid this duplication, the .AddAttributes(...) call should be removed.

                logging.SetResourceBuilder(ResourceBuilder.CreateDefault()
                    .AddService(opts.ServiceName!, opts.ServiceNamespace ?? "aether", opts.ServiceVersion ?? "1.0.0", false, Environment.MachineName));

@sonarqubecloud
Copy link
Copy Markdown

❌ The last analysis has failed.

See analysis details on SonarQube Cloud

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.

1 participant