Skip to content

Conversation

JamesNK
Copy link
Member

@JamesNK JamesNK commented Aug 25, 2024

Fixes regression from #5357

There is code in the Aspire host that listens to dashboard logs. It expects JSON logs from the dashboard, which are parsed and possibly written to the host. That allows for dashboard errors to be visible in the host console.

The previous PR changes Aspire to add a timestamp to executable log content, including the dashboard. Parsing timestamp prefixed messages as JSON fails.

Unfortunatly there were no unit tests here and there is no indication when running the host that there are errors when parsing the logs.

Customer Impact

Customer won't see errors from the dashboard in the Aspire host console.

Testing

Unit tests and manual testing.

Before (no error):

image

After (error in console):

image

Risk

Low. Product change is to trim a timestamp (if present) from content before deserializing it.

Regression?

Yes

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No
  • Does the change require an update in our Aspire docs?
    • Yes
      • Link to aspire-docs issue:
    • No
Microsoft Reviewers: Open in CodeFlow

@JamesNK JamesNK added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Aug 25, 2024
@JamesNK JamesNK requested a review from mitchdenny as a code owner August 25, 2024 07:07

namespace Aspire.Dashboard.ConsoleLogs;

internal static partial class TimestampParser
Copy link
Member Author

Choose a reason for hiding this comment

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

Note that this is mostly a copy and paste from what is in the dashboard. There will be duplicate copies after this PR but this will be cleaned up by #5398. I did this to minimize changes.

@JamesNK JamesNK requested a review from joperezr August 25, 2024 07:29
@JamesNK
Copy link
Member Author

JamesNK commented Aug 25, 2024

/backport to release/8.2

Copy link
Contributor

Started backporting to release/8.2: https://github.com/dotnet/aspire/actions/runs/10545263397

@davidfowl
Copy link
Member

Do we need to handle 8.2 apphost with 8.1 dashboard?

@joperezr
Copy link
Member

Do we need to handle 8.2 apphost with 8.1 dashboard?

Not really, unless someone is really going out of their way to force a downgrade of the dashboard, but beginning with 8.2, the dashboard version will always match the reference to the apphost package. ☺️

@JamesNK
Copy link
Member Author

JamesNK commented Aug 25, 2024

The timestamps in dashboard logs are added by DCP. And DCP adding the timestamp is configured by the apphost. And reading the logs is in the apphost.

all the changes are in the apphost so there is no concern about mixing versions of apphost, DCP or dashboard.


internal static partial class TimestampParser
{
private static readonly Regex s_rfc3339RegEx = GenerateRfc3339RegEx();
Copy link
Member

Choose a reason for hiding this comment

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

From https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expression-source-generators?pivots=dotnet-8-0#source-generation:

The generated implementation of GenerateRfc3339RegEx() similarly caches a singleton Regex instance, so no additional caching is needed to consume code.

Copy link
Member Author

Choose a reason for hiding this comment

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

I want to minimize changes here and for 8.2 backport. I'll make regex changes in #5398

if (match.Success)
{
var span = text.AsSpan();
var timestamp = span[match.Index..(match.Index + match.Length)];
Copy link
Member

Choose a reason for hiding this comment

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

I think this can just be:

Suggested change
var timestamp = span[match.Index..(match.Index + match.Length)];
var timestamp = match.ValueSpan;

// Z - Literal, same as +00:00
// (?:[Z+-](?:[01][0-9]|2[0-3]):(?:[0-5][0-9])) - Time Zone offset, in the form ZHH:MM or +HH:MM or -HH:MM
//
// Note: (?:) is a non-capturing group, since we don't care about the values, we are just interested in whether or not there is a match
Copy link
Member

Choose a reason for hiding this comment

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

Using RegexOptions.ExplicitCapture instead of ?: simplifies the pattern, and ensures that no groups are accidentally captured.

Consider also adding RegexOptions.CultureInvariant.

Comment on lines +57 to +75
// Explanation:
// ^ - Starts the string
// (?:\\d{4}) - Four digits for the year
// - - Separator for the date
// (?:0[1-9]|1[0-2]) - Two digits for the month, restricted to 01-12
// - - Separator for the date
// (?:0[1-9]|[12][0-9]|3[01]) - Two digits for the day, restricted to 01-31
// [T ] - Literal, separator between date and time, either a T or a space
// (?:[01][0-9]|2[0-3]) - Two digits for the hour, restricted to 00-23
// : - Separator for the time
// (?:[0-5][0-9]) - Two digits for the minutes, restricted to 00-59
// : - Separator for the time
// (?:[0-5][0-9]) - Two digits for the seconds, restricted to 00-59
// (?:\\.\\d{1,9}) - A period and up to nine digits for the partial seconds
// Z - Literal, same as +00:00
// (?:[Z+-](?:[01][0-9]|2[0-3]):(?:[0-5][0-9])) - Time Zone offset, in the form ZHH:MM or +HH:MM or -HH:MM
//
// Note: (?:) is a non-capturing group, since we don't care about the values, we are just interested in whether or not there is a match
[GeneratedRegex("^(?:\\d{4})-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12][0-9]|3[01])T(?:[01][0-9]|2[0-3]):(?:[0-5][0-9]):(?:[0-5][0-9])(?:\\.\\d{1,9})?(?:Z|(?:[Z+-](?:[01][0-9]|2[0-3]):(?:[0-5][0-9])))?")]
Copy link
Member

@drewnoakes drewnoakes Aug 26, 2024

Choose a reason for hiding this comment

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

The duplication here can be avoided by using RegexOptions.IgnorePatternWhitespace and including inline comments in the regex pattern. This means they can never get out of sync.

Something like:

    [GeneratedRegex("""
        ^                                          # Starts the string
        \d{4}                                      # Four digits for the year
        -                                          # Separator for the date
        (0[1-9]|1[0-2])                            # Two digits for the month, restricted to 01-12
        -                                          # Separator for the date
        (0[1-9]|[12][0-9]|3[01])                   # Two digits for the day, restricted to 01-31
        [T ]                                       # Literal, separator between date and time, either a T or a space
        ([01][0-9]|2[0-3])                         # Two digits for the hour, restricted to 00-23
        :                                          # Separator for the time
        ([0-5][0-9])                               # Two digits for the minutes, restricted to 00-59
        :                                          # Separator for the time
        [0-5][0-9]                                 # Two digits for the seconds, restricted to 00-59
        \.\d{1,9}                                  # A period and up to nine digits for the partial seconds
        Z                                          # Literal, same as +00:00
        [Z+-]([01][0-9]|2[0-3]):[0-5][0-9]         # Time Zone offset, in the form ZHH:MM or +HH:MM or -HH:MM
        """,
        RegexOptions.ExplicitCapture | RegexOptions.InvariantCulture | RegexOptions.IgnorePatternWhitespace)]

@JamesNK
Copy link
Member Author

JamesNK commented Aug 26, 2024

/azp run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@JamesNK
Copy link
Member Author

JamesNK commented Aug 26, 2024

/azp run

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@JamesNK JamesNK merged commit 70105d0 into main Aug 26, 2024
11 checks passed
@JamesNK JamesNK deleted the jamesnk/dashboard-lifecyclehook-logging branch August 26, 2024 06:57
@github-actions github-actions bot locked and limited conversation to collaborators Sep 26, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants