diff --git a/.github/actions/generate-release-notes/index.js b/.github/actions/generate-release-notes/index.js index 8eb4b7f860d..29f237e3c68 100644 --- a/.github/actions/generate-release-notes/index.js +++ b/.github/actions/generate-release-notes/index.js @@ -79,6 +79,7 @@ async function getPrsToMention(octokit, branch, repoOwner, repoName, minMergeDat // Patch the origin PR information to have the backport PR number and URL // so that the release notes links to the backport, but grabs the rest of // the information from the origin PR. + originPr.originNumber = originPr.number; originPr.number = pr.number; originPr.html_url = pr.html_url; @@ -99,6 +100,9 @@ async function getPrsToMention(octokit, branch, repoOwner, repoName, minMergeDat commitHashesInRelease.add(commit.sha); } + // Keep track of all of the prs we mention to avoid duplicates from resolved backports. + let mentionedOriginNumbers = new Set(); + let prs = []; for (const pr of candidatePrs) { // Get a fully-qualified version of the pr that has all of the relevant information, @@ -109,8 +113,10 @@ async function getPrsToMention(octokit, branch, repoOwner, repoName, minMergeDat pull_number: pr.number }))?.data; - if (commitHashesInRelease.has(fqPr.merge_commit_sha)) { - console.log(`Including: #${fqPr.number}`); + let originNumber = pr.originNumber ?? pr.number; + if (commitHashesInRelease.has(fqPr.merge_commit_sha) && !mentionedOriginNumbers.has(originNumber)) { + console.log(`Including: #${fqPr.number} -- origin:#${originNumber}`); + mentionedOriginNumbers.add(originNumber); prs.push(pr); } else { console.log(`Skipping: #${fqPr.number} --- ${fqPr.merge_commit_sha}`); @@ -143,7 +149,7 @@ async function generateChangelog(octokit, branch, repoOwner, repoName, minMergeD entry += ` ${significantLabels[index].moniker}`; } - const changelogRegex=/^###### Release Notes Entry\r?\n(?.*)/m + const changelogRegex=/^###### Release Notes Entry\s+(?.*)/m const userDefinedChangelogEntry = pr.body?.match(changelogRegex)?.groups?.releaseNotesEntry?.trim(); if (userDefinedChangelogEntry !== undefined && userDefinedChangelogEntry.length !== 0) { entry += ` ${userDefinedChangelogEntry}` diff --git a/.github/dependabot.template.yml b/.github/dependabot.template.yml index 3e42072a3e9..005619b4121 100644 --- a/.github/dependabot.template.yml +++ b/.github/dependabot.template.yml @@ -10,6 +10,7 @@ updates: interval: "daily" target-branch: "main" #@ for branch in ["main", "release/8.x", "release/7.x", "release/7.0", "release/6.x"]: +#@ commit_prefix = "[" + branch + "] " - package-ecosystem: "nuget" directory: "/eng/dependabot" schedule: @@ -18,11 +19,15 @@ updates: ignore: - dependency-name: "Microsoft.Extensions.*" update-types: [ "version-update:semver-major" ] + commit-message: + prefix: #@ commit_prefix - package-ecosystem: "nuget" directory: "/eng/dependabot/nuget.org" schedule: interval: "daily" target-branch: #@ branch + commit-message: + prefix: #@ commit_prefix #@ for tfm in ["net7.0", "net6.0", "netcoreapp3.1"]: - package-ecosystem: "nuget" directory: #@ "/eng/dependabot/" + tfm @@ -32,5 +37,7 @@ updates: ignore: - dependency-name: "*" update-types: [ "version-update:semver-major" ] + commit-message: + prefix: #@ commit_prefix #@ end #@ end diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f935336482f..4b11693815c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -14,11 +14,15 @@ updates: - dependency-name: Microsoft.Extensions.* update-types: - version-update:semver-major + commit-message: + prefix: '[main] ' - package-ecosystem: nuget directory: /eng/dependabot/nuget.org schedule: interval: daily target-branch: main + commit-message: + prefix: '[main] ' - package-ecosystem: nuget directory: /eng/dependabot/net7.0 schedule: @@ -28,6 +32,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[main] ' - package-ecosystem: nuget directory: /eng/dependabot/net6.0 schedule: @@ -37,6 +43,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[main] ' - package-ecosystem: nuget directory: /eng/dependabot/netcoreapp3.1 schedule: @@ -46,6 +54,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[main] ' - package-ecosystem: nuget directory: /eng/dependabot schedule: @@ -55,11 +65,15 @@ updates: - dependency-name: Microsoft.Extensions.* update-types: - version-update:semver-major + commit-message: + prefix: '[release/8.x] ' - package-ecosystem: nuget directory: /eng/dependabot/nuget.org schedule: interval: daily target-branch: release/8.x + commit-message: + prefix: '[release/8.x] ' - package-ecosystem: nuget directory: /eng/dependabot/net7.0 schedule: @@ -69,6 +83,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/8.x] ' - package-ecosystem: nuget directory: /eng/dependabot/net6.0 schedule: @@ -78,6 +94,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/8.x] ' - package-ecosystem: nuget directory: /eng/dependabot/netcoreapp3.1 schedule: @@ -87,6 +105,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/8.x] ' - package-ecosystem: nuget directory: /eng/dependabot schedule: @@ -96,11 +116,15 @@ updates: - dependency-name: Microsoft.Extensions.* update-types: - version-update:semver-major + commit-message: + prefix: '[release/7.x] ' - package-ecosystem: nuget directory: /eng/dependabot/nuget.org schedule: interval: daily target-branch: release/7.x + commit-message: + prefix: '[release/7.x] ' - package-ecosystem: nuget directory: /eng/dependabot/net7.0 schedule: @@ -110,6 +134,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/7.x] ' - package-ecosystem: nuget directory: /eng/dependabot/net6.0 schedule: @@ -119,6 +145,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/7.x] ' - package-ecosystem: nuget directory: /eng/dependabot/netcoreapp3.1 schedule: @@ -128,6 +156,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/7.x] ' - package-ecosystem: nuget directory: /eng/dependabot schedule: @@ -137,11 +167,15 @@ updates: - dependency-name: Microsoft.Extensions.* update-types: - version-update:semver-major + commit-message: + prefix: '[release/7.0] ' - package-ecosystem: nuget directory: /eng/dependabot/nuget.org schedule: interval: daily target-branch: release/7.0 + commit-message: + prefix: '[release/7.0] ' - package-ecosystem: nuget directory: /eng/dependabot/net7.0 schedule: @@ -151,6 +185,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/7.0] ' - package-ecosystem: nuget directory: /eng/dependabot/net6.0 schedule: @@ -160,6 +196,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/7.0] ' - package-ecosystem: nuget directory: /eng/dependabot/netcoreapp3.1 schedule: @@ -169,6 +207,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/7.0] ' - package-ecosystem: nuget directory: /eng/dependabot schedule: @@ -178,11 +218,15 @@ updates: - dependency-name: Microsoft.Extensions.* update-types: - version-update:semver-major + commit-message: + prefix: '[release/6.x] ' - package-ecosystem: nuget directory: /eng/dependabot/nuget.org schedule: interval: daily target-branch: release/6.x + commit-message: + prefix: '[release/6.x] ' - package-ecosystem: nuget directory: /eng/dependabot/net7.0 schedule: @@ -192,6 +236,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/6.x] ' - package-ecosystem: nuget directory: /eng/dependabot/net6.0 schedule: @@ -201,6 +247,8 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/6.x] ' - package-ecosystem: nuget directory: /eng/dependabot/netcoreapp3.1 schedule: @@ -210,3 +258,5 @@ updates: - dependency-name: '*' update-types: - version-update:semver-major + commit-message: + prefix: '[release/6.x] ' diff --git a/.github/workflows/scan-for-to-do-comments.yml b/.github/workflows/scan-for-to-do-comments.yml new file mode 100644 index 00000000000..4d959576e7e --- /dev/null +++ b/.github/workflows/scan-for-to-do-comments.yml @@ -0,0 +1,27 @@ +name: Scan For To Do Comments +on: + pull_request_review_comment: + types: [created] + issue_comment: + types: [created] + +jobs: + scan-for-todo-issue: + runs-on: ubuntu-latest + if: startsWith(github.event.comment.body, '/TODO') + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Generate artifacts + run: | + trimmed_comment=$(echo "${{ github.event.comment.body }}" | sed 's|/TODO ||I') + mkdir -p ./issue + echo -n $trimmed_comment > ./issue/issue-title + echo -n "${{ github.event.comment.html_url }}" > ./issue/issue-url + echo -n "${{ github.event.comment.user.login }}" > ./issue/issue-user + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: issue-todo + path: issue/ diff --git a/.github/workflows/submit-to-do-issue.yml b/.github/workflows/submit-to-do-issue.yml new file mode 100644 index 00000000000..5d323a4744b --- /dev/null +++ b/.github/workflows/submit-to-do-issue.yml @@ -0,0 +1,45 @@ +name: 'Submit To Do Issue' + +on: + workflow_run: + workflows: ["Scan For To Do Comments"] + types: + - completed + +permissions: + issues: write + +jobs: + submit-todo-issue: + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion == 'success' + steps: + - uses: actions/github-script@v6.4.0 + id: check-user + with: + script: | + await github.rest.repos.checkCollaborator({ + owner: context.repo.owner, + repo: context.repo.repo, + username: context.payload.workflow_run.triggering_actor.login + }); + - name: Checkout + uses: actions/checkout@v3 + # Download the artifact from the workflow that kicked off this one. + # The default artifact download action doesn't support cross-workflow + # artifacts, so use a 3rd party one. + - name: 'Download linting results' + uses: dawidd6/action-download-artifact@v2 + with: + workflow: ${{env.workflow_name}} + run_id: ${{github.event.workflow_run.id }} + name: issue-todo + path: ./issue-todo + - name: Submit Issue + run: | + title=$(cat ./issue-todo/issue-title) + user=$(cat ./issue-todo/issue-user) + url=$(cat ./issue-todo/issue-url) + gh issue create --label "todo" --assignee "$user" --title "$title" --body "$url" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/track-shipping-version.yml b/.github/workflows/track-shipping-version.yml new file mode 100644 index 00000000000..6f1e92fdf3c --- /dev/null +++ b/.github/workflows/track-shipping-version.yml @@ -0,0 +1,35 @@ +name: Track shipped versions +on: + release: + types: [released] + +permissions: + contents: write + +env: + shipping_branch_prefix: 'shipped' + +jobs: + update-shipping-branch: + if: github.repository == 'dotnet/dotnet-monitor' + name: '[${{ github.ref_name }}] Update shipping branch' + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Update shipping branch + run: | + release_version=${GITHUB_REF_NAME%-*} + if [[ ! "$release_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Unexpected release tag: $release_version." + exit 1 + fi + + major_minor_version=${release_version%.*} + shipping_branch_name="${{ env.shipping_branch_prefix }}/$major_minor_version" + + # This is a shallow clone so we will always create a new local branch even if it already exists on the remote + git checkout -b "$shipping_branch_name" + git push --force --set-upstream origin "HEAD:$shipping_branch_name" diff --git a/documentation/api/definitions.md b/documentation/api/definitions.md index 8e24027de40..c5d35b743b3 100644 --- a/documentation/api/definitions.md +++ b/documentation/api/definitions.md @@ -244,6 +244,16 @@ Object describing a metric from the application. | `counterType` | string | The type of metric. This is typically `Rate` or `Metric`. | | `value` | double | The value of the metric. | +## MetricType (8.0+) + +Enumeration that describes the type of metrics a provider consumes. + +| Name | +|---| +| `EventCounter` | +| `Meter` | +| `All` | + ## OperationError | Name | Type | Description | diff --git a/documentation/api/livemetrics-custom.md b/documentation/api/livemetrics-custom.md index ecba82ccf90..f2a5705be2c 100644 --- a/documentation/api/livemetrics-custom.md +++ b/documentation/api/livemetrics-custom.md @@ -115,7 +115,7 @@ Location: localhost:52323/operations/67f07e40-5cca-4709-9062-26302c484f18 } ``` -### System.Diagnostics.Metrics +### System.Diagnostics.Metrics (8.0+) #### Sample Request @@ -151,11 +151,28 @@ Location: localhost:52323/operations/67f07e40-5cca-4709-9062-26302c484f18 "displayName": "myHistogram", "unit": null, "counterType": "Metric", - "value": { - "0.5": 2892, - "0.95": 4848, - "0.99": 4984 - } + "tags": "Percentile=50", + "value": 2292 +} +{ + "timestamp": "2021-08-31T16:58:39.7514031+00:00", + "provider": "CustomProvider", + "name": "myHistogram", + "displayName": "myHistogram", + "unit": null, + "counterType": "Metric", + "tags": "Percentile=95", + "value": 4616 +} +{ + "timestamp": "2021-08-31T16:58:39.7514031+00:00", + "provider": "CustomProvider", + "name": "myHistogram", + "displayName": "myHistogram", + "unit": null, + "counterType": "Metric", + "tags": "Percentile=99", + "value": 4960 } ``` diff --git a/documentation/authentication.md b/documentation/authentication.md index 6fa02fba561..bb9c69132cd 100644 --- a/documentation/authentication.md +++ b/documentation/authentication.md @@ -25,7 +25,7 @@ An API Key is the recommended authentication mechanism for `dotnet monitor`. API or -- Use the `--temp-api-key` command line option to generate a one-time API key for that instantiation of dotnet-monitor. The API key will be reported back as part of log output during the startup of the process. +- Use the `--temp-apikey` command line option to generate a one-time API key for that instantiation of dotnet-monitor. The API key will be reported back as part of log output during the startup of the process. > **Note**: API Key Authentication should only be used when TLS is enabled to protect the key while in transit. `dotnet monitor` will emit a warning if authentication is enabled over an insecure transport medium. diff --git a/documentation/collectionrules/collectionruleexamples.md b/documentation/collectionrules/collectionruleexamples.md index 0bb3b90ed19..09108ea0b82 100644 --- a/documentation/collectionrules/collectionruleexamples.md +++ b/documentation/collectionrules/collectionruleexamples.md @@ -394,7 +394,7 @@ This rule, named "BadResponseStatus", will trigger when 5 4xx status codes are e ### Explanation -This rule, named "HighRequestCount", will trigger when a process with a `ProcessId` of 12345 has 10 requests within a 1 minute sliding window. If the rule is triggered, information level logs will be collected for one minute and egressed to the specified `Egress` provider (in this case, `artifacts` has been configured to save the logs to the local filesystem). There is a limit that states that this may only be triggered for one hour (to prevent an excessive number of logs from being collected), and there is a default `ActionCount` limit stating that this rule may only be triggered 5 times. +This rule, named "HighRequestCount", will trigger when a process with a `ProcessId` of 12345 has 10 requests within a 1 minute sliding window. If the rule is triggered, error level logs will be collected for one minute and egressed to the specified `Egress` provider (in this case, `artifacts` has been configured to save the logs to the local filesystem). There is a limit that states that this may only be triggered for one hour (to prevent an excessive number of logs from being collected), and there is a default `ActionCount` limit stating that this rule may only be triggered 5 times. ## Collect Trace - Too Many Long Requests (`AspNetRequestDuration` Trigger) diff --git a/documentation/configuration/metrics-configuration.md b/documentation/configuration/metrics-configuration.md index 8c81287c060..396a71bb830 100644 --- a/documentation/configuration/metrics-configuration.md +++ b/documentation/configuration/metrics-configuration.md @@ -8,6 +8,8 @@ Due to limitations in event counters, `dotnet monitor` supports only **one** refresh interval when collecting metrics. This interval is used for Prometheus metrics, livemetrics, triggers, traces, and trigger actions that collect traces. The default interval is 5 seconds, but can be changed in configuration. +[7.1+] For EventCounter providers, is possible to specify a different interval for each provider. See [Per provider intervals](#per-provider-intervals-71). +
JSON @@ -37,6 +39,48 @@ Prometheus metrics, livemetrics, triggers, traces, and trigger actions that coll ```
+## Per provider intervals (7.1+) + +It is possible to override the global interval on a per provider basis. Note this forces all scenarios (triggers, live metrics, prometheus metrics, traces) that use a particular provider to use that interval. Metrics that are `System.Diagnostics.Metrics` based always use global interval. + +
+ JSON + + ```json + { + "GlobalCounter": { + "IntervalSeconds": 5, + "Providers": { + "System.Runtime": { + "IntervalSeconds": 10 + } + } + } + } + ``` +
+ +
+ Kubernetes ConfigMap + + ```yaml + GlobalCounter__IntervalSeconds: "5" + GlobalCounter__Providers__System.Runtime__IntervalSeconds: "10" + ``` +
+ +
+ Kubernetes Environment Variables + + ```yaml + - name: DotnetMonitor_GlobalCounter__IntervalSeconds + value: "5" + - name: DotnetMonitor_GlobalCounter__Providers__System.Runtime__IntervalSeconds + value: "10" + + ``` +
+ ## Metrics Urls In addition to the ordinary diagnostics urls that `dotnet monitor` binds to, it also binds to metric urls that only expose the `/metrics` endpoint. Unlike the other endpoints, the metrics urls do not require authentication. Unless you enable collection of custom providers that may contain sensitive business logic, it is generally considered safe to expose metrics endpoints. @@ -166,7 +210,63 @@ Additional metrics providers and counter names to return from this route can be When `CounterNames` are not specified, all the counters associated with the `ProviderName` are collected. -[7.1+] Custom metrics support labels for metadata. Metadata cannot include commas (`,`); the inclusion of a comma in metadata will result in all metadata being removed from the custom metric. +[8.0+] Custom metrics support labels for metadata. Metadata cannot include commas (`,`); the inclusion of a comma in metadata will result in all metadata being removed from the custom metric. + +[8.0+] `System.Diagnostics.Metrics` is now supported in a limited capacity for custom metrics. At this time, there are several known limitations: + * `System.Diagnostics.Metrics` cannot have multiple sessions collecting metrics concurrently (i.e. `/metrics` and `/livemetrics` cannot both be looking for `System.Diagnostics.Metrics` at the same time). + * There is currently no trigger for `System.Diagnostics.Metrics` for collection rule scenarios. + * `dotnet monitor` may fail to collect `System.Diagnostics.Metrics` if it begins collecting the metric before the target app creates the Meter ([note that this is fixed for .NET 8+ apps](https://github.com/dotnet/runtime/pull/76965)). + +### Set [`MetricType`](../api/definitions.md#metrictype-80) + +By default, `dotnet monitor` is unable to determine whether a custom provider is an `EventCounter` or `Meter`, and will attempt to collect both kinds of metrics for the specified provider. To explicitly specify whether a custom provider is an `EventCounter` or `Meter`, set the appropriate `MetricType`: + +
+ JSON + + ```json + { + "Metrics": { + "Providers": [ + { + "ProviderName": "MyCustomEventCounterProvider", + "MetricType": "EventCounter" + }, + { + "ProviderName": "MyCustomSDMProvider", + "MetricType": "Meter" + } + ] + } + } + ``` +
+ +
+ Kubernetes ConfigMap + + ```yaml + Metrics__Providers__0__ProviderName: "MyCustomEventCounterProvider" + Metrics__Providers__0__MetricType: "EventCounter" + Metrics__Providers__1__ProviderName: "MyCustomSDMProvider" + Metrics__Providers__1__MetricType: "Meter" + ``` +
+ +
+ Kubernetes Environment Variables + + ```yaml + - name: DotnetMonitor_Metrics__Providers__0__ProviderName + value: "MyCustomEventCounterProvider" + - name: DotnetMonitor_Metrics__Providers__0__MetricType + value: "EventCounter" + - name: DotnetMonitor_Metrics__Providers__1__ProviderName + value: "MyCustomSDMProvider" + - name: DotnetMonitor_Metrics__Providers__1__MetricType + value: "Meter" + ``` +
## Limit How Many Histograms To Track (8.0+) diff --git a/documentation/openapi.json b/documentation/openapi.json index 189a55a4cc9..69746ef80d6 100644 --- a/documentation/openapi.json +++ b/documentation/openapi.json @@ -1903,19 +1903,6 @@ } } } - }, - "securitySchemes": { - "ApiKeyAuth": { - "type": "apiKey", - "description": "JWT Authorization header using bearer token authentication. Put the Authorization header value (\"Bearer\" prefix and the JWT value) in the textbox when prompted.", - "name": "Authorization", - "in": "header" - } - } - }, - "security": [ - { - "ApiKeyAuth": [ ] } - ] + } } \ No newline at end of file diff --git a/documentation/release-process.md b/documentation/release-process.md index 8eef2e17e63..810141c4ed9 100644 --- a/documentation/release-process.md +++ b/documentation/release-process.md @@ -19,6 +19,8 @@ 1. Be sure to call [darc authenticate](https://github.com/dotnet/arcade/blob/main/Documentation/Darc.md#authenticate). You will need to create the requested tokens. 1. You will need to add the branch to a channel. E.g. `darc add-default-channel --channel ".NET Core Tooling Release" --branch release/8.x --repo https://github.com/dotnet/dotnet-monitor` +1. Ensure that `UseMicrosoftDiagnosticsMonitoringShippedVersion` is set appropriately. See [Updating dependencies](#updating-dependencies). +1. Ensure that dependabot configuration is updated at [../.github/dependabot.template.yml](../.github/dependabot.template.yml). - It can be helpful to create test release branches (e.g. release/test/8.x). Note these branches will trigger warnings because they are considered unprotected release branches and should be deleted as soon as possible. - If you created a build from a newly created release branch without a channel, you will get the message 'target build already exists on all channels'. To use this build you need to add it to a channel: `darc add-build-to-channel --id --channel "General Testing"`. diff --git a/documentation/releaseNotes/releaseNotes.v6.3.2.md b/documentation/releaseNotes/releaseNotes.v6.3.2.md new file mode 100644 index 00000000000..a0049beb2ec --- /dev/null +++ b/documentation/releaseNotes/releaseNotes.v6.3.2.md @@ -0,0 +1,3 @@ +Today we are releasing the 6.3.2 build of the `dotnet monitor` tool. This release includes: + +- Produce RID-specific, TFM-specific, framework dependent archives ([#3521](https://github.com/dotnet/dotnet-monitor/pull/3521)) \ No newline at end of file diff --git a/documentation/releaseNotes/releaseNotes.v7.0.2.md b/documentation/releaseNotes/releaseNotes.v7.0.2.md new file mode 100644 index 00000000000..3f922d11390 --- /dev/null +++ b/documentation/releaseNotes/releaseNotes.v7.0.2.md @@ -0,0 +1,3 @@ +Today we are releasing the 7.0.2 build of the `dotnet monitor` tool. This release includes: + +- Produce RID-specific, TFM-specific, framework dependent archives ([#3520](https://github.com/dotnet/dotnet-monitor/pull/3520)) \ No newline at end of file diff --git a/documentation/schema.json b/documentation/schema.json index 302a60af3da..a810cbc27f9 100644 --- a/documentation/schema.json +++ b/documentation/schema.json @@ -899,6 +899,31 @@ "default": 1000, "maximum": 2147483647.0, "minimum": 1.0 + }, + "Providers": { + "type": [ + "null", + "object" + ], + "description": "Dictionary of provider names and their global configuration.", + "additionalProperties": { + "$ref": "#/definitions/GlobalProviderOptions" + } + } + } + }, + "GlobalProviderOptions": { + "type": "object", + "additionalProperties": false, + "properties": { + "IntervalSeconds": { + "type": [ + "null", + "number" + ], + "format": "float", + "maximum": 86400.0, + "minimum": 1.0 } } }, diff --git a/dotnet-monitor.yml b/dotnet-monitor.yml index 8f0a895217d..b0d9331ac72 100644 --- a/dotnet-monitor.yml +++ b/dotnet-monitor.yml @@ -43,13 +43,11 @@ parameters: default: false variables: +- template: /eng/common/templates/variables/pool-providers.yml - name: _TeamName value: DotNetCore - name: _TPNFile value: THIRD-PARTY-NOTICES.TXT -# Scheduled builds of main branch will be marked for update by dotnet-docker if this is true -- name: NightlyUpdateDockerFromMain - value: true - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: # DotNet-Diagnostics-SDL-Params provides Tsa* variables for SDL checks. @@ -105,7 +103,7 @@ stages: - Pack_Sign publishUsingPipelines: true pool: - name: NetCore1ESPool-Internal + name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-windows-2019 # These are the stages that perform validation of several SDL requirements and publish the bits required to the designated feed. - template: /eng/common/templates/post-build/post-build.yml @@ -141,7 +139,8 @@ stages: - template: /eng/pipelines/stages/preparerelease.yml parameters: ${{ if eq(parameters.updateDocker, 'true') }}: - updateDocker: true + updateDockerCondition: true ${{ else }}: # If scheduled build from main and nightly update from main enabled - updateDocker: ${{ and(eq(variables['Build.SourceBranch'], 'refs/heads/main'), eq(variables['Build.Reason'], 'Schedule'), eq(variables['NightlyUpdateDockerFromMain'], 'true')) }} + updateDockerCondition: and(eq(variables['Build.SourceBranch'], 'refs/heads/main'), eq(variables['Build.Reason'], 'Schedule'), eq(variables['NightlyUpdateDockerFromMain'], 'true')) + diff --git a/eng/AfterSolutionBuild.targets b/eng/AfterSolutionBuild.targets index d3557d4db3f..5d7de6e6b91 100644 --- a/eng/AfterSolutionBuild.targets +++ b/eng/AfterSolutionBuild.targets @@ -1,7 +1,7 @@ + - - + + + diff --git a/eng/Signing.props b/eng/Signing.props index a00e9db071a..699b16dfc98 100644 --- a/eng/Signing.props +++ b/eng/Signing.props @@ -4,6 +4,8 @@ + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index a7edde2bf00..681f8d88bb9 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,58 +1,62 @@ - + + https://github.com/dotnet/aspnetcore + cec7fbf64e63af88a9165cbbf14aa4ab6f8cc76d + + https://github.com/dotnet/diagnostics - 9d61ee4c5831e6492f27bfb67ead2f3fe1c18338 + ae326a3c6fe3ed64718a6c77e263dac59cabc41b - + https://github.com/dotnet/diagnostics - 9d61ee4c5831e6492f27bfb67ead2f3fe1c18338 + ae326a3c6fe3ed64718a6c77e263dac59cabc41b - + https://github.com/dotnet/command-line-api - 6524142f1398ed78eadfb9b802ba6492c6af63c4 + 1bc03fed713b0b10455942d9f8eeab371d519cba - - https://github.com/dotnet/aspnetcore - 6f1752a798a9460b8a039750e30b827578528c90 - - + https://github.com/dotnet/roslyn-analyzers - a25bf5393c439af5da9ef30800b7d554a401d673 + 5cd64de1a3319d7f15380b843352a2b963e59b57 - + https://github.com/dotnet/arcade - c4d6ac086bdb9764f72c5ede705d3ef12e1b4c74 + 96d8be74c39a4765ec919ff9bebf9e0c875fc195 - + https://github.com/dotnet/arcade - c4d6ac086bdb9764f72c5ede705d3ef12e1b4c74 + 96d8be74c39a4765ec919ff9bebf9e0c875fc195 - + https://github.com/dotnet/arcade - c4d6ac086bdb9764f72c5ede705d3ef12e1b4c74 + 96d8be74c39a4765ec919ff9bebf9e0c875fc195 + + + https://github.com/dotnet/installer + 0004325188fe0c920870901208d774c8e9ff6753 - + https://github.com/dotnet/arcade - c4d6ac086bdb9764f72c5ede705d3ef12e1b4c74 + 96d8be74c39a4765ec919ff9bebf9e0c875fc195 - + https://github.com/dotnet/symstore - 117c711598f1cc144e0c9d82c4e9f78638e0315d + c6ecba72647d4277bfe0f85f5ed0405fdfa3a445 - + https://github.com/dotnet/runtime - 78f5a9839ac7d49a3cfaa6f4015a90b1cc846fa1 + e71a4fb10d7ea6b502dd5efe7a8fcefa2b9c1550 - + https://github.com/dotnet/aspnetcore - 6f1752a798a9460b8a039750e30b827578528c90 + cec7fbf64e63af88a9165cbbf14aa4ab6f8cc76d - + https://github.com/dotnet/runtime - 78f5a9839ac7d49a3cfaa6f4015a90b1cc846fa1 + e71a4fb10d7ea6b502dd5efe7a8fcefa2b9c1550 diff --git a/eng/Versions.props b/eng/Versions.props index fcb6202cbfa..b41c5b7f87e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -58,24 +58,24 @@ --> - 8.0.0-beta.23101.1 - 8.0.0-beta.23101.1 - 8.0.0-beta.23101.1 + 8.0.0-beta.23110.3 + 8.0.0-beta.23110.3 + 8.0.0-beta.23110.3 - 8.0.0-alpha.1.23066.6 - 8.0.0-alpha.1.23066.6 + 8.0.0-preview.2.23107.2 + 8.0.0-preview.2.23107.2 - 2.0.0-beta4.23073.1 + 2.0.0-beta4.23103.1 - 7.0.0-preview.23101.2 - 7.0.0-preview.23101.2 + 7.0.0-preview.23110.1 + 7.0.0-preview.23110.1 - 8.0.0-preview1.23081.3 + 8.0.0-preview1.23111.1 - 8.0.0-alpha.1.23059.14 - 8.0.0-alpha.1.23059.14 + 8.0.0-preview.2.23107.1 + 8.0.0-preview.2.23107.1 - 1.0.408101 + 1.0.410601 $(MicrosoftNETCoreApp31Version) diff --git a/eng/common/generate-locproject.ps1 b/eng/common/generate-locproject.ps1 index 69e65eeae7d..bcb579e37a9 100644 --- a/eng/common/generate-locproject.ps1 +++ b/eng/common/generate-locproject.ps1 @@ -137,6 +137,7 @@ $locJson = @{ @{ LanguageSet = $LanguageSet CloneLanguageSet = "VS_macOS_CloneLanguages" + LssFiles = @( ".\eng\common\loc\P22DotNetHtmlLocalization.lss" ) LocItems = @( $macosHtmlFiles | ForEach-Object { $outputPath = "$($_.Directory.FullName | Resolve-Path -Relative)\" diff --git a/eng/common/init-tools-native.ps1 b/eng/common/init-tools-native.ps1 index fbc67effc36..27ccdb9ecc9 100644 --- a/eng/common/init-tools-native.ps1 +++ b/eng/common/init-tools-native.ps1 @@ -83,7 +83,8 @@ try { Select-Object -Expand 'native-tools' -ErrorAction SilentlyContinue if ($NativeTools) { if ($PathPromotion -eq $True) { - if ($env:SYSTEM_TEAMPROJECT) { # check to see if we're in an Azure pipelines build + $ArcadeToolsDirectory = "$env:SYSTEMDRIVE\arcade-tools" + if (Test-Path $ArcadeToolsDirectory) { # if this directory exists, we should use native tools on machine $NativeTools.PSObject.Properties | ForEach-Object { $ToolName = $_.Name $ToolVersion = $_.Value @@ -93,11 +94,6 @@ try { if ($ToolVersion -eq "latest") { $ToolVersion = "" } - $ArcadeToolsDirectory = "C:\arcade-tools" - if (-not (Test-Path $ArcadeToolsDirectory)) { - Write-Error "Arcade tools directory '$ArcadeToolsDirectory' was not found; artifacts were not properly installed." - exit 1 - } $ToolDirectories = (Get-ChildItem -Path "$ArcadeToolsDirectory" -Filter "$ToolName-$ToolVersion*" | Sort-Object -Descending) if ($ToolDirectories -eq $null) { Write-Error "Unable to find directory for $ToolName $ToolVersion; please make sure the tool is installed on this image." @@ -125,6 +121,7 @@ try { if ((Get-Command "$ToolName" -ErrorAction SilentlyContinue) -eq $null) { Write-PipelineTelemetryError -Category 'NativeToolsBootstrap' -Message "$ToolName not found on path. Please install $ToolName $ToolVersion before proceeding." + Write-PipelineTelemetryError -Category 'NativeToolsBootstrap' -Message "If this is running on a build machine, the arcade-tools directory was not found, which means there's an error with the image." } } exit 0 diff --git a/eng/common/loc/P22DotNetHtmlLocalization.lss b/eng/common/loc/P22DotNetHtmlLocalization.lss new file mode 100644 index 00000000000..6661fed566e Binary files /dev/null and b/eng/common/loc/P22DotNetHtmlLocalization.lss differ diff --git a/eng/common/native/init-compiler.sh b/eng/common/native/init-compiler.sh index c670cb79689..7aee4213e1b 100644 --- a/eng/common/native/init-compiler.sh +++ b/eng/common/native/init-compiler.sh @@ -63,7 +63,7 @@ if [ -z "$CLR_CC" ]; then # Set default versions if [ -z "$majorVersion" ]; then # note: gcc (all versions) and clang versions higher than 6 do not have minor version in file name, if it is zero. - if [ "$compiler" = "clang" ]; then versions="15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5" + if [ "$compiler" = "clang" ]; then versions="16 15 14 13 12 11 10 9 8 7 6.0 5.0 4.0 3.9 3.8 3.7 3.6 3.5" elif [ "$compiler" = "gcc" ]; then versions="12 11 10 9 8 7 6 5 4.9"; fi for version in $versions; do diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index f0af425d9f6..61914a1fbcd 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -134,7 +134,7 @@ jobs: - ${{ if and(eq(parameters.runAsPublic, 'false'), eq(variables['System.TeamProject'], 'internal')) }}: - task: NuGetAuthenticate@0 - - ${{ if or(eq(parameters.artifacts.download, 'true'), ne(parameters.artifacts.download, '')) }}: + - ${{ if and(ne(parameters.artifacts.download, 'false'), ne(parameters.artifacts.download, '')) }}: - task: DownloadPipelineArtifact@2 inputs: buildType: current @@ -171,7 +171,7 @@ jobs: TeamName: $(_TeamName) - ${{ if ne(parameters.artifacts.publish, '') }}: - - ${{ if or(eq(parameters.artifacts.publish.artifacts, 'true'), ne(parameters.artifacts.publish.artifacts, '')) }}: + - ${{ if and(ne(parameters.artifacts.publish.artifacts, 'false'), ne(parameters.artifacts.publish.artifacts, '')) }}: - task: CopyFiles@2 displayName: Gather binaries for publish to artifacts inputs: @@ -192,7 +192,7 @@ jobs: ArtifactName: ${{ coalesce(parameters.artifacts.publish.artifacts.name , 'Artifacts_$(Agent.Os)_$(_BuildConfig)') }} continueOnError: true condition: always() - - ${{ if or(eq(parameters.artifacts.publish.logs, 'true'), ne(parameters.artifacts.publish.logs, '')) }}: + - ${{ if and(ne(parameters.artifacts.publish.logs, 'false'), ne(parameters.artifacts.publish.logs, '')) }}: - publish: artifacts/log artifact: ${{ coalesce(parameters.artifacts.publish.logs.name, 'Logs_Build_$(Agent.Os)_$(_BuildConfig)') }} displayName: Publish logs diff --git a/eng/common/templates/job/source-build.yml b/eng/common/templates/job/source-build.yml index e40bf35203b..8a3deef2b72 100644 --- a/eng/common/templates/job/source-build.yml +++ b/eng/common/templates/job/source-build.yml @@ -46,20 +46,12 @@ jobs: # source-build builds run in Docker, including the default managed platform. # /eng/common/templates/variables/pool-providers.yml can't be used here (some customers declare variables already), so duplicate its logic pool: - # Main environments - ${{ if and(eq(variables['System.TeamProject'], 'public'), ne(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), true)) }}: - name: NetCore-Public + ${{ if eq(variables['System.TeamProject'], 'public') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore-Svc-Public' ), False, 'NetCore-Public')] demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open - ${{ if and(eq(variables['System.TeamProject'], 'internal'), ne(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), true)) }}: - name: NetCore1ESPool-Internal - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64 - # Servicing build environments - ${{ if and(eq(variables['System.TeamProject'], 'public'), contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release')) }}: - name: NetCore-Svc-Public - demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open - ${{ if and(eq(variables['System.TeamProject'], 'internal'), contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release')) }}: - name: NetCore1ESPool-Svc-Internal + ${{ if eq(variables['System.TeamProject'], 'internal') }}: + name: $[replace(replace(eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), True, 'NetCore1ESPool-Svc-Internal'), False, 'NetCore1ESPool-Internal')] demands: ImageOverride -equals Build.Ubuntu.1804.Amd64 ${{ if ne(parameters.platform.pool, '') }}: diff --git a/eng/common/templates/variables/pool-providers.yml b/eng/common/templates/variables/pool-providers.yml index 1b820b41605..9cc5c550d3b 100644 --- a/eng/common/templates/variables/pool-providers.yml +++ b/eng/common/templates/variables/pool-providers.yml @@ -26,23 +26,32 @@ # demands: ImageOverride -equals windows.vs2019.amd64 variables: -# Coalesce the target and source branches so we know when a PR targets a release branch -# If these variables are somehow missing, fall back to main (tends to have more capacity) -- name: BranchNameForPoolSelection - value: ${{ coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main') }} + # Coalesce the target and source branches so we know when a PR targets a release branch + # If these variables are somehow missing, fall back to main (tends to have more capacity) -# Any new -Svc alternative pools should have variables added here to allow for splitting work - -# Main branch pools -- ${{ if ne(contains(variables['BranchNameForPoolSelection'], 'release'), true) }}: + # Any new -Svc alternative pools should have variables added here to allow for splitting work - name: DncEngPublicBuildPool - value: NetCore-Public - - name: DncEngInternalBuildPool - value: NetCore1ESPool-Internal + value: $[ + replace( + replace( + eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), + True, + 'NetCore-Svc-Public' + ), + False, + 'NetCore-Public' + ) + ] -# Release branch pools -- ${{ if contains(variables['BranchNameForPoolSelection'], 'release') }}: - - name: DncEngPublicBuildPool - value: NetCore-Svc-Public - name: DncEngInternalBuildPool - value: NetCore1ESPool-Svc-Internal + value: $[ + replace( + replace( + eq(contains(coalesce(variables['System.PullRequest.TargetBranch'], variables['Build.SourceBranch'], 'refs/heads/main'), 'release'), 'true'), + True, + 'NetCore1ESPool-Svc-Internal' + ), + False, + 'NetCore1ESPool-Internal' + ) + ] \ No newline at end of file diff --git a/eng/dependabot/Versions.props b/eng/dependabot/Versions.props index ee702ccf5e2..d4d721d5928 100644 --- a/eng/dependabot/Versions.props +++ b/eng/dependabot/Versions.props @@ -2,7 +2,7 @@ - 1.8.1 + 1.8.2 12.14.1 12.12.0 7.0.0 @@ -10,8 +10,8 @@ 7.0.0 7.0.0 7.0.0 - 6.26.0 - 1.6.0 + 6.26.1 + 1.6.1 4.3.2 5.0.0 diff --git a/eng/pipelines/dotnet-monitor-codeql.yml b/eng/pipelines/dotnet-monitor-codeql.yml index 4677bcb60b5..71675e3b95e 100644 --- a/eng/pipelines/dotnet-monitor-codeql.yml +++ b/eng/pipelines/dotnet-monitor-codeql.yml @@ -1,4 +1,5 @@ variables: +- template: /eng/common/templates/variables/pool-providers.yml - name: _TeamName value: DotNetCore diff --git a/eng/pipelines/dotnet-monitor-release.yml b/eng/pipelines/dotnet-monitor-release.yml index 7cf7dabe080..3111c12e3c5 100644 --- a/eng/pipelines/dotnet-monitor-release.yml +++ b/eng/pipelines/dotnet-monitor-release.yml @@ -10,6 +10,7 @@ parameters: default: true variables: +- template: /eng/common/templates/variables/pool-providers.yml - name: _TeamName value: DotNetCore readonly: true @@ -30,7 +31,7 @@ stages: - stage: Validation pool: - name: NetCore1ESPool-Internal + name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-windows-2019 jobs: @@ -94,7 +95,7 @@ stages: - Validation pool: - name: NetCore1ESPool-Internal + name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-windows-2019 jobs: diff --git a/eng/pipelines/jobs/build.yml b/eng/pipelines/jobs/build.yml index bbe191ad7d9..68332526719 100644 --- a/eng/pipelines/jobs/build.yml +++ b/eng/pipelines/jobs/build.yml @@ -47,12 +47,12 @@ jobs: # Public Linux Build Pool ${{ if in(parameters.osGroup, 'Linux', 'Linux_Musl') }}: ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: NetCore-Public + name: $(DncEngPublicBuildPool) demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open # Official Build Linux Pool ${{ if ne(variables['System.TeamProject'], 'public') }}: - name: NetCore1ESPool-Internal + name: $(DncEngInternalBuildPool) demands: ImageOverride -equals Build.Ubuntu.1804.Amd64 # Build OSX Pool @@ -62,11 +62,11 @@ jobs: # Public Windows Build Pool ${{ if eq(parameters.osGroup, 'Windows') }}: ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: NetCore-Public + name: $(DncEngPublicBuildPool) demands: ImageOverride -equals windows.vs2019.amd64.open ${{ if ne(variables['System.TeamProject'], 'public') }}: - name: NetCore1ESPool-Internal + name: $(DncEngInternalBuildPool) demands: ImageOverride -equals windows.vs2019.amd64 ${{ if eq(parameters.osGroup, 'Linux') }}: diff --git a/eng/pipelines/jobs/sign-binaries.yml b/eng/pipelines/jobs/sign-binaries.yml index 6587fceff97..6f9d5f077f6 100644 --- a/eng/pipelines/jobs/sign-binaries.yml +++ b/eng/pipelines/jobs/sign-binaries.yml @@ -8,7 +8,7 @@ jobs: name: Sign_Binaries displayName: Sign Binaries pool: - name: NetCore1ESPool-Internal + name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-windows-2019 enableMicrobuild: true artifacts: diff --git a/eng/pipelines/jobs/tpn.yml b/eng/pipelines/jobs/tpn.yml index 2d365d2d3c9..576d7ff31d2 100644 --- a/eng/pipelines/jobs/tpn.yml +++ b/eng/pipelines/jobs/tpn.yml @@ -6,7 +6,7 @@ jobs: disableComponentGovernance: true enableSbom: false pool: - name: NetCore1ESPool-Internal + name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-windows-2019 steps: # Only restore the dotnet-monitor project so only packages we ship get included in the below CG scan diff --git a/eng/pipelines/stages/preparerelease.yml b/eng/pipelines/stages/preparerelease.yml index 204de4c683f..bf8284a010a 100644 --- a/eng/pipelines/stages/preparerelease.yml +++ b/eng/pipelines/stages/preparerelease.yml @@ -1,7 +1,6 @@ parameters: -- name: updateDocker - displayName: 'Update dotnet-docker? (Only for release branches)' - type: boolean +- name: updateDockerCondition + type: string default: false stages: @@ -15,10 +14,10 @@ stages: displayName: Prepare release with Darc pool: ${{ if eq(variables['System.TeamProject'], 'public') }}: - name: NetCore-Public + name: $(DncEngPublicBuildPool) demands: ImageOverride -equals 1es-windows-2019-open ${{ if ne(variables['System.TeamProject'], 'public') }}: - name: NetCore1ESPool-Internal + name: $(DncEngInternalBuildPool) demands: ImageOverride -equals 1es-windows-2019 variables: - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), or(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/test/release/'))) }}: @@ -88,9 +87,10 @@ stages: inputs: type: 'Build' tags: 'MonitorRelease' - - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest'), eq(parameters.updateDocker, true)) }}: + - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - task: tagBuildOrRelease@0 displayName: 'Tag Build with update-docker' + condition: ${{ parameters.updateDockerCondition }} inputs: type: 'Build' tags: 'update-docker' diff --git a/eng/release/Scripts/GetBuildVersion.ps1 b/eng/release/Scripts/GetBuildVersion.ps1 index e0886f31295..2fa5941a61c 100644 --- a/eng/release/Scripts/GetBuildVersion.ps1 +++ b/eng/release/Scripts/GetBuildVersion.ps1 @@ -19,7 +19,7 @@ Write-Verbose 'ReleaseData:' $releaseDataJson = $releaseData | ConvertTo-Json Write-Verbose $releaseDataJson -[array]$matchingData = $releaseData | Where-Object { $_.name -match '.nupkg.buildversion$' } +[array]$matchingData = $releaseData | Where-Object { $_.name -match 'MergedManifest.xml$' -and $_.nonShipping -ieq 'true' } if ($matchingData.Length -ne 1) { Write-Error 'Unable to obtain build version.' diff --git a/global.json b/global.json index d48d9a6ccb2..41eea54716a 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "tools": { - "dotnet": "8.0.100-alpha.1.23061.8", + "dotnet": "8.0.100-preview.2.23102.11", "runtimes": { "aspnetcore": [ "$(MicrosoftAspNetCoreApp60Version)", @@ -26,6 +26,6 @@ }, "msbuild-sdks": { "Microsoft.Build.NoTargets": "3.7.0", - "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23101.1" + "Microsoft.DotNet.Arcade.Sdk": "8.0.0-beta.23110.3" } } diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/GlobalCounterOptions.cs b/src/Microsoft.Diagnostics.Monitoring.Options/GlobalCounterOptions.cs index 54e601769c1..ccb83017e09 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/GlobalCounterOptions.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/GlobalCounterOptions.cs @@ -2,12 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Linq; namespace Microsoft.Diagnostics.Monitoring.WebApi { - public class GlobalCounterOptions + public partial class GlobalCounterOptions { public const float IntervalMinSeconds = 1; public const float IntervalMaxSeconds = 60 * 60 * 24; // One day @@ -32,6 +35,38 @@ public class GlobalCounterOptions [DefaultValue(GlobalCounterOptionsDefaults.MaxTimeSeries)] [Range(1, int.MaxValue)] public int? MaxTimeSeries { get; set; } + + [Display( + ResourceType = typeof(OptionsDisplayStrings), + Description = nameof(OptionsDisplayStrings.DisplayAttributeDescription_GlobalCounterOptions_Providers))] + public System.Collections.Generic.IDictionary Providers { get; set; } = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + public class GlobalProviderOptions + { + [Range(GlobalCounterOptions.IntervalMinSeconds, GlobalCounterOptions.IntervalMaxSeconds)] + public float? IntervalSeconds { get; set; } + } + + partial class GlobalCounterOptions : IValidatableObject + { + public IEnumerable Validate(ValidationContext validationContext) + { + var results = new List(); + var providerResults = new List(); + foreach ((string provider, GlobalProviderOptions options) in Providers) + { + providerResults.Clear(); + if (!Validator.TryValidateObject(options, new ValidationContext(options), providerResults, true)) + { + // We prefix the validation error with the provider. + results.AddRange(providerResults.Select(r => new ValidationResult( + string.Format(CultureInfo.CurrentCulture, OptionsDisplayStrings.ErrorMessage_NestedProviderValidationError, provider, r.ErrorMessage)))); + } + } + + return results; + } } internal static class GlobalCounterOptionsExtensions @@ -44,5 +79,8 @@ public static int GetMaxHistograms(this GlobalCounterOptions options) => public static int GetMaxTimeSeries(this GlobalCounterOptions options) => options.MaxTimeSeries.GetValueOrDefault(GlobalCounterOptionsDefaults.MaxTimeSeries); + + public static float GetProviderSpecificInterval(this GlobalCounterOptions options, string providerName) => + options.Providers.TryGetValue(providerName, out GlobalProviderOptions providerOptions) ? providerOptions.IntervalSeconds ?? options.GetIntervalSeconds() : options.GetIntervalSeconds(); } } diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs index 23a8c506174..c75f319596c 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.Designer.cs @@ -1031,6 +1031,15 @@ public static string DisplayAttributeDescription_GlobalCounterOptions_IntervalSe } } + /// + /// Looks up a localized string similar to Dictionary of provider names and their global configuration.. + /// + public static string DisplayAttributeDescription_GlobalCounterOptions_Providers { + get { + return ResourceManager.GetString("DisplayAttributeDescription_GlobalCounterOptions_Providers", resourceCulture); + } + } + /// /// Looks up a localized string similar to Allows features that require diagnostic components to be loaded into target processes to be enabled. These features may have minimal performance impact on target processes.. /// @@ -1580,6 +1589,15 @@ public static string ErrorMessage_FilterValueMissing { } } + /// + /// Looks up a localized string similar to Provider '{0}' validation error: '{1}'. + /// + public static string ErrorMessage_NestedProviderValidationError { + get { + return ResourceManager.GetString("ErrorMessage_NestedProviderValidationError", resourceCulture); + } + } + /// /// Looks up a localized string similar to An egress provider must be specified if there is no default egress provider.. /// diff --git a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx index aa8de3d4e18..63496c18201 100644 --- a/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx +++ b/src/Microsoft.Diagnostics.Monitoring.Options/OptionsDisplayStrings.resx @@ -807,4 +807,10 @@ The type of metrics this provider consumes The description provided for the MetricType parameter on MetricProvider. + + Dictionary of provider names and their global configuration. + + + Provider '{0}' validation error: '{1}' + \ No newline at end of file diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs index 5b17611c6a6..1276751232d 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/DiagController.cs @@ -337,7 +337,7 @@ public Task CaptureTrace( { TimeSpan duration = Utilities.ConvertSecondsToTimeSpan(durationSeconds); - var aggregateConfiguration = TraceUtilities.GetTraceConfiguration(profile, _counterOptions.CurrentValue.GetIntervalSeconds()); + var aggregateConfiguration = TraceUtilities.GetTraceConfiguration(profile, _counterOptions.CurrentValue); return StartTrace(processInfo, aggregateConfiguration, duration, egressProvider, tags); }, processKey, Utilities.ArtifactType_Trace); diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/SwaggerFilesController.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/SwaggerFilesController.cs deleted file mode 100644 index ad500ba1e3b..00000000000 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Controllers/SwaggerFilesController.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System; -using System.Reflection; - - -namespace Microsoft.Diagnostics.Monitoring.WebApi.Controllers -{ - [Route("/swagger/v1/")] - [ApiController] - [ApiExplorerSettings(IgnoreApi = true)] - public class SwaggerFilesController : ControllerBase - { - private readonly ILogger _logger; - - public SwaggerFilesController(ILogger logger, - IServiceProvider serviceProvider) - { - _logger = logger; - } - - /// - /// Get the swagger files embedded into the assembly - /// - [HttpGet("{filename}")] - [ProducesResponseType(typeof(string), StatusCodes.Status200OK)] - public FileStreamResult GetFile(string filename) - { - if (filename != "swagger.json") { throw new Exception(string.Format(Strings.ErrorMessage_ResourceNotFound, filename)); } - var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.Diagnostics.Monitoring.WebApi.swagger.v1." + filename); - string mimetype = "text/json"; - - return new FileStreamResult(stream, mimetype) - { - FileDownloadName = filename - }; - } - } -} diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/IExperimentalFlags.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/IExperimentalFlags.cs index 6e615f41313..aa39493c2fc 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/IExperimentalFlags.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/IExperimentalFlags.cs @@ -10,5 +10,7 @@ namespace Microsoft.Diagnostics.Monitoring.WebApi internal interface IExperimentalFlags { bool IsCallStacksEnabled { get; } + + bool IsExceptionsEnabled { get; } } } diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/IInProcessFeatures.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/IInProcessFeatures.cs index 6f8f910a23b..ff9c450e69d 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/IInProcessFeatures.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/IInProcessFeatures.cs @@ -6,5 +6,7 @@ namespace Microsoft.Diagnostics.Monitoring.WebApi public interface IInProcessFeatures { bool IsCallStacksEnabled { get; } + + bool IsExceptionsEnabled { get; } } } diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Metrics/MetricsSettingsFactory.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Metrics/MetricsSettingsFactory.cs index 4385a11487d..0bc17e7e9a1 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Metrics/MetricsSettingsFactory.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Metrics/MetricsSettingsFactory.cs @@ -4,6 +4,7 @@ using Microsoft.Diagnostics.Monitoring.EventPipe; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading; @@ -20,6 +21,7 @@ public static MetricsPipelineSettings CreateSettings(GlobalCounterOptions counte return CreateSettings(includeDefaults, durationSeconds, counterOptions.GetIntervalSeconds(), + counterOptions.Providers, counterOptions.GetMaxHistograms(), counterOptions.GetMaxTimeSeries(), () => new List(0)); @@ -29,6 +31,7 @@ public static MetricsPipelineSettings CreateSettings(GlobalCounterOptions counte { return CreateSettings(options.IncludeDefaultProviders.GetValueOrDefault(MetricsOptionsDefaults.IncludeDefaultProviders), Timeout.Infinite, counterOptions.GetIntervalSeconds(), + counterOptions.Providers, counterOptions.GetMaxHistograms(), counterOptions.GetMaxTimeSeries(), () => ConvertCounterGroups(options.Providers)); @@ -40,6 +43,7 @@ public static MetricsPipelineSettings CreateSettings(GlobalCounterOptions counte return CreateSettings(configuration.IncludeDefaultProviders, durationSeconds, counterOptions.GetIntervalSeconds(), + counterOptions.Providers, counterOptions.GetMaxHistograms(), counterOptions.GetMaxTimeSeries(), () => ConvertCounterGroups(configuration.Providers)); @@ -48,6 +52,7 @@ public static MetricsPipelineSettings CreateSettings(GlobalCounterOptions counte private static MetricsPipelineSettings CreateSettings(bool includeDefaults, int durationSeconds, float counterInterval, + IDictionary intervalMap, int maxHistograms, int maxTimeSeries, Func> createCounterGroups) @@ -61,6 +66,15 @@ private static MetricsPipelineSettings CreateSettings(bool includeDefaults, eventPipeCounterGroups.Add(new EventPipeCounterGroup { ProviderName = MonitoringSourceConfiguration.GrpcAspNetCoreServer, Type = CounterGroupType.EventCounter }); } + foreach (EventPipeCounterGroup counterGroup in eventPipeCounterGroups) + { + if (intervalMap.TryGetValue(counterGroup.ProviderName, out GlobalProviderOptions providerInterval)) + { + Debug.Assert(counterGroup.IntervalSeconds == null, "Unexpected value for provider interval"); + counterGroup.IntervalSeconds = providerInterval.IntervalSeconds; + } + } + return new MetricsPipelineSettings { CounterGroups = eventPipeCounterGroups.ToArray(), diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Metrics/MetricsStore.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Metrics/MetricsStore.cs index 00953d8fa05..1ebfdbef892 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Metrics/MetricsStore.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Metrics/MetricsStore.cs @@ -5,6 +5,7 @@ using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Threading; @@ -103,6 +104,12 @@ public void AddMetric(ICounterPayload metric) { metrics.Dequeue(); } + + // CONSIDER We only keep 1 histogram representation per snapshot. Is it meaningful for Prometheus to see previous histograms? These are not timestamped. + if ((metrics.Count > 1) && (metric is PercentilePayload)) + { + metrics.Dequeue(); + } } } @@ -133,7 +140,8 @@ public async Task SnapshotMetrics(Stream outputStream, CancellationToken token) { if (metric is PercentilePayload percentilePayload) { - foreach (Quantile quantile in percentilePayload.Quantiles) + // Summary quantiles must appear from smallest to largest + foreach (Quantile quantile in percentilePayload.Quantiles.OrderBy(q => q.Percentage)) { string metricValue = PrometheusDataModel.GetPrometheusNormalizedValue(metric.Unit, quantile.Value); string metricLabels = GetMetricLabels(metric, quantile.Percentage); @@ -153,14 +161,17 @@ public async Task SnapshotMetrics(Stream outputStream, CancellationToken token) private static string GetMetricLabels(ICounterPayload metric, double? quantile) { string metadata = metric.Metadata; + + char separator = IsMeter(metric) ? '=' : ':'; + var metadataValues = CounterUtilities.GetMetadata(metadata, separator); if (quantile.HasValue) { - metadata = CounterUtilities.AppendPercentile(metadata, quantile.Value); + metadataValues.Add("quantile", quantile.Value.ToString(CultureInfo.InvariantCulture)); } - char separator = IsMeter(metric) ? '=' : ':'; - var keyValuePairs = from pair in CounterUtilities.GetMetadata(metadata, separator) - select pair.Key + "=" + "\"" + pair.Value + "\""; + var keyValuePairs = from pair in metadataValues + select PrometheusDataModel.GetPrometheusNormalizedLabel(pair.Key, pair.Value); + string metricLabels = string.Join(", ", keyValuePairs); return metricLabels; diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Microsoft.Diagnostics.Monitoring.WebApi.csproj b/src/Microsoft.Diagnostics.Monitoring.WebApi/Microsoft.Diagnostics.Monitoring.WebApi.csproj index 8e005ca8ba0..3b49c225ac1 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Microsoft.Diagnostics.Monitoring.WebApi.csproj +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Microsoft.Diagnostics.Monitoring.WebApi.csproj @@ -41,10 +41,6 @@ - - - - True @@ -56,4 +52,4 @@ Strings.Designer.cs - \ No newline at end of file + diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Strings.Designer.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Strings.Designer.cs index 1aeb9e1bf36..6a73c585fe8 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Strings.Designer.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Strings.Designer.cs @@ -106,7 +106,7 @@ internal static string ErrorMessage_InvalidMetricCount { } /// - /// Looks up a localized string similar to Custom trace metric provider '{0}' must use the global counter interval '{1}'. + /// Looks up a localized string similar to Custom trace metric provider '{0}' must use the expected counter interval '{1}'.. /// internal static string ErrorMessage_InvalidMetricInterval { get { diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Strings.resx b/src/Microsoft.Diagnostics.Monitoring.WebApi/Strings.resx index aef63084a5d..c855827f271 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Strings.resx +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Strings.resx @@ -136,7 +136,7 @@ Gets a string similar to "Invalid metric count.". - Custom trace metric provider '{0}' must use the global counter interval '{1}' + Custom trace metric provider '{0}' must use the expected counter interval '{1}'. Metrics was not enabled. diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/TraceUtilities.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/TraceUtilities.cs index 33ecebe9f2f..ee4a949fe13 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/TraceUtilities.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Utilities/TraceUtilities.cs @@ -13,7 +13,7 @@ namespace Microsoft.Diagnostics.Monitoring.WebApi { internal static class TraceUtilities { - public static MonitoringSourceConfiguration GetTraceConfiguration(Models.TraceProfile profile, float metricsIntervalSeconds) + public static MonitoringSourceConfiguration GetTraceConfiguration(Models.TraceProfile profile, GlobalCounterOptions options) { var configurations = new List(); if (profile.HasFlag(Models.TraceProfile.Cpu)) @@ -34,7 +34,14 @@ public static MonitoringSourceConfiguration GetTraceConfiguration(Models.TracePr } if (profile.HasFlag(Models.TraceProfile.Metrics)) { - configurations.Add(new MetricSourceConfiguration(metricsIntervalSeconds, Enumerable.Empty())); + IEnumerable defaultProviders = MonitoringSourceConfiguration.DefaultMetricProviders.Select(provider => new MetricEventPipeProvider + { + Provider = provider, + IntervalSeconds = options.GetProviderSpecificInterval(provider), + Type = MetricType.EventCounter + }); + + configurations.Add(new MetricSourceConfiguration(options.GetIntervalSeconds(), defaultProviders)); } return new AggregateSourceConfiguration(configurations.ToArray()); diff --git a/src/Microsoft.Diagnostics.Monitoring.WebApi/Validation/CounterValidator.cs b/src/Microsoft.Diagnostics.Monitoring.WebApi/Validation/CounterValidator.cs index a1f6ad99d65..bae063dd3c3 100644 --- a/src/Microsoft.Diagnostics.Monitoring.WebApi/Validation/CounterValidator.cs +++ b/src/Microsoft.Diagnostics.Monitoring.WebApi/Validation/CounterValidator.cs @@ -17,12 +17,12 @@ public static bool ValidateProvider(GlobalCounterOptions counterOptions, if (provider.Arguments?.TryGetValue("EventCounterIntervalSec", out string intervalValue) == true) { if (float.TryParse(intervalValue, out float intervalSeconds) && - intervalSeconds != counterOptions.GetIntervalSeconds()) + intervalSeconds != counterOptions.GetProviderSpecificInterval(provider.Name)) { errorMessage = string.Format(CultureInfo.CurrentCulture, Strings.ErrorMessage_InvalidMetricInterval, provider.Name, - counterOptions.GetIntervalSeconds()); + counterOptions.GetProviderSpecificInterval(provider.Name)); return false; } } diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/Microsoft.Diagnostics.Monitoring.OpenApiGen.csproj b/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/Microsoft.Diagnostics.Monitoring.OpenApiGen.csproj index c571e2ae38a..9737afbb262 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/Microsoft.Diagnostics.Monitoring.OpenApiGen.csproj +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/Microsoft.Diagnostics.Monitoring.OpenApiGen.csproj @@ -9,8 +9,4 @@ - - - - diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/Program.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/Program.cs index c6ba3b42e35..6c861c8f153 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/Program.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/Program.cs @@ -1,12 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.Diagnostics.Monitoring.WebApi.Controllers; using Microsoft.Diagnostics.Tools.Monitor; +using Microsoft.Diagnostics.Tools.Monitor.Swagger; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using Microsoft.Net.Http.Headers; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Writers; using Swashbuckle.AspNetCore.Swagger; @@ -18,18 +16,6 @@ namespace Microsoft.Diagnostics.Monitoring.OpenApiGen { internal sealed class Program { - private const string ApiKeySecurityDefinitionName = "ApiKeyAuth"; - - private static readonly OpenApiSchema ProcessKey_Int32Schema = - new OpenApiSchema() { Type = "integer", Format = "int32", Description = "The ID of the process." }; - private static readonly OpenApiSchema ProcessKey_GuidSchema = - new OpenApiSchema() { Type = "string", Format = "uuid", Description = "The runtime instance cookie of the runtime." }; - private static readonly OpenApiSchema ProcessKey_StringSchema = - new OpenApiSchema() { Type = "string", Description = "The name of the process." }; - - private static Func CreateProcessKeySchema => - () => new OpenApiSchema() { OneOf = { ProcessKey_Int32Schema, ProcessKey_GuidSchema, ProcessKey_StringSchema } }; - public static void Main(string[] args) { if (args.Length != 1) @@ -56,42 +42,7 @@ public static void Main(string[] args) .CreateHostBuilder(settings) .ConfigureServices(services => { - services.AddSwaggerGen(options => - { - options.AddSecurityDefinition(ApiKeySecurityDefinitionName, new OpenApiSecurityScheme - { - Name = HeaderNames.Authorization, - Type = SecuritySchemeType.ApiKey, - Scheme = JwtBearerDefaults.AuthenticationScheme, - BearerFormat = "JWT", - In = ParameterLocation.Header, - Description = Strings.HelpDescription_SecurityDefinitionDescription_ApiKey - }); - - options.AddSecurityRequirement(new OpenApiSecurityRequirement - { - { - new OpenApiSecurityScheme - { - Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = ApiKeySecurityDefinitionName } - }, - Array.Empty() - } - }); - - options.DocumentFilter(); - options.DocumentFilter(); - options.DocumentFilter(); - - options.OperationFilter(); - options.OperationFilter(); - options.OperationFilter(); - options.OperationFilter(); - - var documentationFile = $"{typeof(DiagController).Assembly.GetName().Name}.xml"; - var documentationPath = Path.Combine(AppContext.BaseDirectory, documentationFile); - options.IncludeXmlComments(documentationPath); - }); + services.AddSwaggerGen(options => options.ConfigureMonitorSwaggerGen()); }) .Build(); diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.TestCommon/CommonTestTimeouts.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.TestCommon/CommonTestTimeouts.cs index 7c1e02e069d..0299cd68b63 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.TestCommon/CommonTestTimeouts.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.TestCommon/CommonTestTimeouts.cs @@ -72,6 +72,11 @@ public static class CommonTestTimeouts /// public static readonly TimeSpan LogsDuration = TimeSpan.FromSeconds(10); + /// + /// Default live metrics collection duration. + /// + public static readonly int LiveMetricsDurationSeconds = 10; + /// /// Default timeout for environment variable manipulation. /// diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/LiveMetricsTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/LiveMetricsTests.cs index 9c0e884de6e..a6a54293e0e 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/LiveMetricsTests.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/LiveMetricsTests.cs @@ -45,7 +45,7 @@ public Task TestDefaultMetrics() async (appRunner, apiClient) => { using ResponseStreamHolder holder = await apiClient.CaptureMetricsAsync(await appRunner.ProcessIdTask, - durationSeconds: 2); + durationSeconds: CommonTestTimeouts.LiveMetricsDurationSeconds); var metrics = LiveMetricsTestUtilities.GetAllMetrics(holder.Stream); await LiveMetricsTestUtilities.ValidateMetrics(new[] { EventPipe.MonitoringSourceConfiguration.SystemRuntimeEventSourceName }, @@ -85,7 +85,7 @@ public Task TestCustomMetrics() var counterNames = new[] { "cpu-usage", "working-set" }; using ResponseStreamHolder holder = await apiClient.CaptureMetricsAsync(await appRunner.ProcessIdTask, - durationSeconds: 2, + durationSeconds: CommonTestTimeouts.LiveMetricsDurationSeconds, metricsConfiguration: new EventMetricsConfiguration { IncludeDefaultProviders = false, @@ -134,7 +134,7 @@ public Task TestCustomMetrics_MetricProviderType(MetricProviderType metricType, var counterNames = new[] { "cpu-usage", "working-set" }; using ResponseStreamHolder holder = await apiClient.CaptureMetricsAsync(await appRunner.ProcessIdTask, - durationSeconds: 2, + durationSeconds: CommonTestTimeouts.LiveMetricsDurationSeconds, metricsConfiguration: new EventMetricsConfiguration { IncludeDefaultProviders = false, @@ -214,7 +214,7 @@ await ScenarioRunner.SingleTarget( appValidate: async (runner, client) => { using ResponseStreamHolder holder = await client.CaptureMetricsAsync(await runner.ProcessIdTask, - durationSeconds: 2, + durationSeconds: CommonTestTimeouts.LiveMetricsDurationSeconds, metricsConfiguration: new EventMetricsConfiguration { IncludeDefaultProviders = false, @@ -312,7 +312,7 @@ await ScenarioRunner.SingleTarget( appValidate: async (runner, client) => { using ResponseStreamHolder holder = await client.CaptureMetricsAsync(await runner.ProcessIdTask, - durationSeconds: 2, + durationSeconds: CommonTestTimeouts.LiveMetricsDurationSeconds, metricsConfiguration: new EventMetricsConfiguration { IncludeDefaultProviders = false, @@ -392,7 +392,7 @@ await ScenarioRunner.SingleTarget( appValidate: async (runner, client) => { using ResponseStreamHolder holder = await client.CaptureMetricsAsync(await runner.ProcessIdTask, - durationSeconds: 2, + durationSeconds: CommonTestTimeouts.LiveMetricsDurationSeconds, metricsConfiguration: new EventMetricsConfiguration { IncludeDefaultProviders = false, @@ -463,7 +463,7 @@ await ScenarioRunner.SingleTarget( appValidate: async (runner, client) => { using ResponseStreamHolder holder = await client.CaptureMetricsAsync(await runner.ProcessIdTask, - durationSeconds: 2, + durationSeconds: CommonTestTimeouts.LiveMetricsDurationSeconds, metricsConfiguration: new EventMetricsConfiguration { IncludeDefaultProviders = false, diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/Options/OptionsExtensions.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/Options/OptionsExtensions.cs index b40c9a80433..d953295bcd1 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/Options/OptionsExtensions.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.FunctionalTests/Options/OptionsExtensions.cs @@ -13,6 +13,7 @@ using System.Security.Cryptography; using System.Text.Json; using System.Text.Json.Serialization; +using Xunit; namespace Microsoft.Diagnostics.Monitoring.TestCommon.Options { @@ -46,6 +47,15 @@ public static RootOptions AddGlobalCounter(this RootOptions options, int interva return options; } + public static RootOptions AddProviderInterval(this RootOptions options, string name, int intervalSeconds) + { + Assert.NotNull(options.GlobalCounter); + + options.GlobalCounter.Providers.Add(name, new GlobalProviderOptions { IntervalSeconds = (float)intervalSeconds }); + + return options; + } + public static CollectionRuleOptions CreateCollectionRule(this RootOptions rootOptions, string name) { CollectionRuleOptions options = new(); diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs index 5b2d4ba92f7..55f88a787ef 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/CollectionRuleOptionsTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using Microsoft.Diagnostics.Monitoring.EventPipe; using Microsoft.Diagnostics.Monitoring.TestCommon; using Microsoft.Diagnostics.Monitoring.TestCommon.Options; using Microsoft.Diagnostics.Monitoring.WebApi.Models; @@ -19,6 +20,7 @@ using System.Diagnostics.Tracing; using System.Globalization; using System.Linq; +using System.Reflection; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -1055,6 +1057,65 @@ public Task CollectionRuleOptions_CollectTraceAction_PropertyValidation() }); } + [Fact] + public Task CollectionRuleOptions_CollectTraceAction_ValidateProviderIntervals() + { + const string ExpectedEgressProvider = "TmpEgressProvider"; + const int ExpectedInterval = 7; + + return ValidateFailure( + rootOptions => + { + rootOptions.AddGlobalCounter(5); + rootOptions.AddProviderInterval(MonitoringSourceConfiguration.SystemRuntimeEventSourceName, ExpectedInterval); + + rootOptions.AddFileSystemEgress(ExpectedEgressProvider, "/tmp"); + + rootOptions.CreateCollectionRule(DefaultRuleName) + .SetStartupTrigger() + .AddCollectTraceAction(new EventPipeProvider[] { new EventPipeProvider + { + Name = MonitoringSourceConfiguration.SystemRuntimeEventSourceName, + Arguments = new Dictionary{ { "EventCounterIntervalSec", "5" } }, + }}, + ExpectedEgressProvider, null); + }, + ex => + { + string failure = Assert.Single(ex.Failures); + VerifyProviderIntervalMessage(failure, MonitoringSourceConfiguration.SystemRuntimeEventSourceName, ExpectedInterval); + }); + } + + [Fact] + public Task CollectionRuleOptions_CollectTraceAction_InvalidProviderInterval() + { + const string ExpectedEgressProvider = "TmpEgressProvider"; + + return ValidateFailure( + rootOptions => + { + rootOptions.AddGlobalCounter(5); + rootOptions.AddProviderInterval(MonitoringSourceConfiguration.SystemRuntimeEventSourceName, -2); + + rootOptions.AddFileSystemEgress(ExpectedEgressProvider, "/tmp"); + + rootOptions.CreateCollectionRule(DefaultRuleName) + .SetStartupTrigger() + .AddCollectTraceAction(new EventPipeProvider[] { new EventPipeProvider + { + Name = MonitoringSourceConfiguration.SystemRuntimeEventSourceName, + Arguments = new Dictionary{ { "EventCounterIntervalSec", "5" } }, + }}, + ExpectedEgressProvider, null); + }, + ex => + { + string failure = Assert.Single(ex.Failures); + VerifyNestedGlobalInterval(failure, MonitoringSourceConfiguration.SystemRuntimeEventSourceName); + }); + } + [Fact] public Task CollectionRuleOptions_CollectTraceAction_NoProfileOrProviders() { @@ -1869,5 +1930,23 @@ private static void VerifyMissingStoppingEventProviderMessage(string[] failures, Assert.Equal(message, failures[index]); } + + private static void VerifyProviderIntervalMessage(string failure, string provider, int expectedInterval) + { + string message = string.Format(CultureInfo.CurrentCulture, WebApi.Strings.ErrorMessage_InvalidMetricInterval, provider, expectedInterval); + + Assert.Equal(message, failure); + } + + private static void VerifyNestedGlobalInterval(string failure, string provider) + { + string rangeValidationMessage = typeof(WebApi.GlobalProviderOptions) + .GetProperty(nameof(WebApi.GlobalProviderOptions.IntervalSeconds)) + .GetCustomAttribute() + .FormatErrorMessage(nameof(WebApi.GlobalProviderOptions.IntervalSeconds)); + + string message = string.Format(CultureInfo.CurrentCulture, WebApi.OptionsDisplayStrings.ErrorMessage_NestedProviderValidationError, provider, rangeValidationMessage); + Assert.Equal(message, failure); + } } } diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/MetricsFormattingTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/MetricsFormattingTests.cs index 2a7ddac41fc..22fa08055e2 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/MetricsFormattingTests.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/MetricsFormattingTests.cs @@ -42,8 +42,7 @@ public async Task HistogramFormat_Test() { List payload = new(); - string tags1 = "Percentile=50"; - payload.Add(new PercentilePayload(MeterName, InstrumentName, "DisplayName", string.Empty, tags1, + payload.Add(new PercentilePayload(MeterName, InstrumentName, "DisplayName", string.Empty, string.Empty, new Quantile[] { new(0.5, Value1), new(0.95, Value2), new(0.99, Value3) }, Timestamp)); @@ -54,16 +53,16 @@ public async Task HistogramFormat_Test() // should we call this method, or should this also be implicitly testing its behavior by having this hard-coded? string metricName = $"{MeterName.ToLowerInvariant()}_{payload[0].Name}"; - const string percentile_50 = "{Percentile=\"50\"}"; - const string percentile_95 = "{Percentile=\"95\"}"; - const string percentile_99 = "{Percentile=\"99\"}"; + const string quantile_50 = "{quantile=\"0.5\"}"; + const string quantile_95 = "{quantile=\"0.95\"}"; + const string quantile_99 = "{quantile=\"0.99\"}"; Assert.Equal(5, lines.Count); Assert.Equal(FormattableString.Invariant($"# HELP {metricName}{payload[0].Unit} {payload[0].DisplayName}"), lines[0]); Assert.Equal(FormattableString.Invariant($"# TYPE {metricName} summary"), lines[1]); - Assert.Equal(FormattableString.Invariant($"{metricName}{percentile_50} {Value1}"), lines[2]); - Assert.Equal(FormattableString.Invariant($"{metricName}{percentile_95} {Value2}"), lines[3]); - Assert.Equal(FormattableString.Invariant($"{metricName}{percentile_99} {Value3}"), lines[4]); + Assert.Equal(FormattableString.Invariant($"{metricName}{quantile_50} {Value1}"), lines[2]); + Assert.Equal(FormattableString.Invariant($"{metricName}{quantile_95} {Value2}"), lines[3]); + Assert.Equal(FormattableString.Invariant($"{metricName}{quantile_99} {Value3}"), lines[4]); } [Fact] diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/MetricsSettingsTests.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/MetricsSettingsTests.cs new file mode 100644 index 00000000000..f6a1a9a5a7b --- /dev/null +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/MetricsSettingsTests.cs @@ -0,0 +1,126 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.Diagnostics.Monitoring.EventPipe; +using Microsoft.Diagnostics.Monitoring.TestCommon; +using Microsoft.Diagnostics.Monitoring.TestCommon.Options; +using Microsoft.Diagnostics.Monitoring.WebApi; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; +using System.Collections.Generic; +using System.Linq; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests +{ + public class MetricsSettingsTests + { + private readonly ITestOutputHelper _outputHelper; + + public MetricsSettingsTests(ITestOutputHelper outputHelper) + { + _outputHelper = outputHelper; + } + + [Fact] + public void ValidateDefaultMetricSettings() + { + const int ExpectedGlobalInterval = 5; + int customInterval = ExpectedGlobalInterval + 1; + int[] expectedIntervals = MonitoringSourceConfiguration.DefaultMetricProviders.Select((_, index) => index + ExpectedGlobalInterval + 1).ToArray(); + + using IHost host = TestHostHelper.CreateHost(_outputHelper, (rootOptions) => + { + rootOptions.AddGlobalCounter(ExpectedGlobalInterval); + foreach (string provider in MonitoringSourceConfiguration.DefaultMetricProviders) + { + rootOptions.AddProviderInterval(provider, customInterval++); + } + }, + servicesCallback: null, + loggingCallback: null, + overrideSource: null); + + var options = host.Services.GetRequiredService>(); + + var settings = MetricsSettingsFactory.CreateSettings(options.CurrentValue, includeDefaults: true, durationSeconds: 30); + + Assert.Equal(ExpectedGlobalInterval, settings.CounterIntervalSeconds); + + customInterval = ExpectedGlobalInterval + 1; + foreach (string provider in MonitoringSourceConfiguration.DefaultMetricProviders) + { + Assert.Equal(customInterval++, GetInterval(settings, provider)); + } + } + + [Fact] + public void ValidateApiMetricsSettings() + { + const int ExpectedGlobalInterval = 5; + const int CustomInterval = 6; + const string CustomProvider1 = nameof(CustomProvider1); + const string CustomProvider2 = nameof(CustomProvider2); + + using IHost host = TestHostHelper.CreateHost(_outputHelper, (rootOptions) => + { + rootOptions.AddGlobalCounter(ExpectedGlobalInterval) + .AddProviderInterval(CustomProvider1, CustomInterval); + }, + servicesCallback: null, + loggingCallback: null, + overrideSource: null); + + var options = host.Services.GetRequiredService>(); + + var settings = MetricsSettingsFactory.CreateSettings(options.CurrentValue, 30, new WebApi.Models.EventMetricsConfiguration + { + IncludeDefaultProviders = false, + Providers = new[] { new WebApi.Models.EventMetricsProvider { ProviderName = CustomProvider1 }, new WebApi.Models.EventMetricsProvider { ProviderName = CustomProvider2 } } + }); + + Assert.Equal(ExpectedGlobalInterval, settings.CounterIntervalSeconds); + Assert.Equal(CustomInterval, GetInterval(settings, CustomProvider1)); + Assert.Null(GetInterval(settings, CustomProvider2)); + } + + [Fact] + public void ValidateMetricStoreSettings() + { + const int ExpectedGlobalInterval = 5; + const int CustomInterval = 6; + const string CustomProvider1 = nameof(CustomProvider1); + const string CustomProvider2 = nameof(CustomProvider2); + + using IHost host = TestHostHelper.CreateHost(_outputHelper, (rootOptions) => + { + rootOptions.AddGlobalCounter(ExpectedGlobalInterval) + .AddProviderInterval(CustomProvider1, CustomInterval); + }, + servicesCallback: null, + loggingCallback: null, + overrideSource: null); + + var options = host.Services.GetRequiredService>(); + + var settings = MetricsSettingsFactory.CreateSettings(options.CurrentValue, new MetricsOptions + { + IncludeDefaultProviders = false, + Providers = new List { new MetricProvider { ProviderName = CustomProvider1 }, new MetricProvider { ProviderName = CustomProvider2 } } + }); + + Assert.Equal(ExpectedGlobalInterval, settings.CounterIntervalSeconds); + Assert.Equal(CustomInterval, GetInterval(settings, CustomProvider1)); + Assert.Null(GetInterval(settings, CustomProvider2)); + } + + private static float? GetInterval(MetricsPipelineSettings settings, string provider) + { + EventPipeCounterGroup counterGroup = settings.CounterGroups.FirstOrDefault(g => g.ProviderName == provider); + Assert.NotNull(counterGroup); + return counterGroup.IntervalSeconds; + } + } +} diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/TestExperimentalFlags.cs b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/TestExperimentalFlags.cs index 7bdfc5f3514..aecd2ea46db 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/TestExperimentalFlags.cs +++ b/src/Tests/Microsoft.Diagnostics.Monitoring.Tool.UnitTests/TestExperimentalFlags.cs @@ -6,5 +6,7 @@ namespace Microsoft.Diagnostics.Monitoring.Tool.UnitTests internal sealed class TestExperimentalFlags : Microsoft.Diagnostics.Monitoring.WebApi.IExperimentalFlags { public bool IsCallStacksEnabled { get; set; } + + public bool IsExceptionsEnabled { get; set; } } } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs index cfe51e83b74..e0f7f83c097 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Actions/CollectTraceAction.cs @@ -70,9 +70,7 @@ protected override async Task ExecuteCoreAsync( if (Options.Profile.HasValue) { TraceProfile profile = Options.Profile.Value; - float metricsIntervalSeconds = _counterOptions.CurrentValue.GetIntervalSeconds(); - - configuration = TraceUtilities.GetTraceConfiguration(profile, metricsIntervalSeconds); + configuration = TraceUtilities.GetTraceConfiguration(profile, _counterOptions.CurrentValue); } else { diff --git a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.Validate.cs b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.Validate.cs index 182973fd250..6ab69e32461 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.Validate.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Options/Actions/CollectTraceOptions.Validate.cs @@ -55,6 +55,18 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali } else if (hasProviders) { + GlobalCounterOptions counterOptions = null; + + try + { + // Nested validations have to be handled by catching the exception and converting it to a ValidationResult. + counterOptions = validationContext.GetRequiredService>().CurrentValue; + } + catch (OptionsValidationException e) + { + results.AddRange(e.Failures.Select((string failure) => new ValidationResult(e.Message))); + } + // Validate that each provider is valid. int index = 0; foreach (EventPipeProvider provider in Providers) @@ -64,9 +76,7 @@ IEnumerable IValidatableObject.Validate(ValidationContext vali Validator.TryValidateObject(provider, providerContext, results, validateAllProperties: true); - IOptionsMonitor counterOptions = validationContext.GetRequiredService>(); - if (!CounterValidator.ValidateProvider(counterOptions.CurrentValue, - provider, out string errorMessage)) + if (counterOptions != null && !CounterValidator.ValidateProvider(counterOptions, provider, out string errorMessage)) { results.Add(new ValidationResult(errorMessage, new[] { nameof(EventPipeProvider.Arguments) })); } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Triggers/AspNetRequestDurationTriggerFactory.cs b/src/Tools/dotnet-monitor/CollectionRules/Triggers/AspNetRequestDurationTriggerFactory.cs index 6091b366e95..616862cf5ed 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Triggers/AspNetRequestDurationTriggerFactory.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Triggers/AspNetRequestDurationTriggerFactory.cs @@ -44,7 +44,8 @@ public ICollectionRuleTrigger Create(IEndpointInfo endpointInfo, Action callback SlidingWindowDuration = options.SlidingWindowDuration ?? TimeSpan.Parse(AspNetRequestDurationOptionsDefaults.SlidingWindowDuration, CultureInfo.InvariantCulture), }; - var aspnetTriggerSourceConfiguration = new AspNetTriggerSourceConfiguration(_counterOptions.CurrentValue.GetIntervalSeconds()); + //HACK we get the provider specific interval for the configuration + var aspnetTriggerSourceConfiguration = new AspNetTriggerSourceConfiguration(_counterOptions.CurrentValue.GetProviderSpecificInterval(MonitoringSourceConfiguration.MicrosoftAspNetCoreHostingEventSourceName)); return EventPipeTriggerFactory.Create(endpointInfo, aspnetTriggerSourceConfiguration, _traceEventTriggerFactory, settings, callback); } diff --git a/src/Tools/dotnet-monitor/CollectionRules/Triggers/EventCounterTriggerFactory.cs b/src/Tools/dotnet-monitor/CollectionRules/Triggers/EventCounterTriggerFactory.cs index 690bed92cce..309bfa90933 100644 --- a/src/Tools/dotnet-monitor/CollectionRules/Triggers/EventCounterTriggerFactory.cs +++ b/src/Tools/dotnet-monitor/CollectionRules/Triggers/EventCounterTriggerFactory.cs @@ -40,7 +40,7 @@ public ICollectionRuleTrigger Create(IEndpointInfo endpointInfo, Action callback EventCounterTriggerSettings settings = new() { ProviderName = options.ProviderName, - CounterIntervalSeconds = _counterOptions.CurrentValue.GetIntervalSeconds(), + CounterIntervalSeconds = _counterOptions.CurrentValue.GetProviderSpecificInterval(options.ProviderName), CounterName = options.CounterName, GreaterThan = options.GreaterThan, LessThan = options.LessThan, diff --git a/src/Tools/dotnet-monitor/Commands/CollectCommandHandler.cs b/src/Tools/dotnet-monitor/Commands/CollectCommandHandler.cs index da124204590..7e127ff9cd8 100644 --- a/src/Tools/dotnet-monitor/Commands/CollectCommandHandler.cs +++ b/src/Tools/dotnet-monitor/Commands/CollectCommandHandler.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Diagnostics.Monitoring; using Microsoft.Diagnostics.Monitoring.WebApi; +using Microsoft.Diagnostics.Tools.Monitor.Swagger; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -137,6 +138,12 @@ private static IHostBuilder Configure(this IHostBuilder builder, AuthConfigurati services.AddSingleton(); } + services.AddSwaggerGen(options => + { + options.ConfigureMonitorSwaggerGen(); + options.ConfigureMonitorSwaggerGenSecurity(); + }); + services.ConfigureDiagnosticPort(context.Configuration); services.AddSingleton(); diff --git a/src/Tools/dotnet-monitor/Experimental/ExperimentalFlags.cs b/src/Tools/dotnet-monitor/Experimental/ExperimentalFlags.cs index 3b7012a9737..68cb342e074 100644 --- a/src/Tools/dotnet-monitor/Experimental/ExperimentalFlags.cs +++ b/src/Tools/dotnet-monitor/Experimental/ExperimentalFlags.cs @@ -17,6 +17,7 @@ internal class ExperimentalFlags : IExperimentalFlags // Feature flags public const string Feature_CallStacks = ExperimentalPrefix + nameof(Feature_CallStacks); + public const string Feature_Exceptions = ExperimentalPrefix + nameof(Feature_Exceptions); // Behaviors private const string EnabledTrueValue = "True"; @@ -24,6 +25,8 @@ internal class ExperimentalFlags : IExperimentalFlags private readonly Lazy _isCallStacksEnabledLazy = new Lazy(() => IsFeatureEnabled(Feature_CallStacks)); + private readonly Lazy _isExceptionsEnabledLazy = new Lazy(() => IsFeatureEnabled(Feature_Exceptions)); + private static bool IsFeatureEnabled(string environmentVariable) { string value = Environment.GetEnvironmentVariable(environmentVariable); @@ -33,5 +36,7 @@ private static bool IsFeatureEnabled(string environmentVariable) } public bool IsCallStacksEnabled => _isCallStacksEnabledLazy.Value; + + public bool IsExceptionsEnabled => _isExceptionsEnabledLazy.Value; } } diff --git a/src/Tools/dotnet-monitor/Experimental/ExperimentalStartupLogger.cs b/src/Tools/dotnet-monitor/Experimental/ExperimentalStartupLogger.cs index 82b61176954..76eabb81559 100644 --- a/src/Tools/dotnet-monitor/Experimental/ExperimentalStartupLogger.cs +++ b/src/Tools/dotnet-monitor/Experimental/ExperimentalStartupLogger.cs @@ -24,6 +24,10 @@ public void Log() { _logger.ExperimentalFeatureEnabled(Strings.FeatureName_CallStacks); } + if (_experimentalFlags.IsExceptionsEnabled) + { + _logger.ExperimentalFeatureEnabled(Strings.FeatureName_Exceptions); + } } } } diff --git a/src/Tools/dotnet-monitor/InProcessFeatures/InProcessFeatures.cs b/src/Tools/dotnet-monitor/InProcessFeatures/InProcessFeatures.cs index 2782ab90afb..12829fc8544 100644 --- a/src/Tools/dotnet-monitor/InProcessFeatures/InProcessFeatures.cs +++ b/src/Tools/dotnet-monitor/InProcessFeatures/InProcessFeatures.cs @@ -19,5 +19,7 @@ public InProcessFeatures(IOptions options, IExperiment } public bool IsCallStacksEnabled => _options.GetEnabled() && _experimentalFlags.IsCallStacksEnabled; + + public bool IsExceptionsEnabled => _options.GetEnabled() && _experimentalFlags.IsExceptionsEnabled; } } diff --git a/src/Tools/dotnet-monitor/ServiceCollectionExtensions.cs b/src/Tools/dotnet-monitor/ServiceCollectionExtensions.cs index 0826692ea4a..9e1b8d74980 100644 --- a/src/Tools/dotnet-monitor/ServiceCollectionExtensions.cs +++ b/src/Tools/dotnet-monitor/ServiceCollectionExtensions.cs @@ -43,7 +43,9 @@ public static IServiceCollection ConfigureCors(this IServiceCollection services, public static IServiceCollection ConfigureGlobalCounter(this IServiceCollection services, IConfiguration configuration) { - return ConfigureOptions(services, configuration, ConfigurationKeys.GlobalCounter); + return ConfigureOptions(services, configuration, ConfigurationKeys.GlobalCounter) + .AddSingleton, DataAnnotationValidateOptions>(); + } public static IServiceCollection ConfigureCollectionRuleDefaults(this IServiceCollection services, IConfiguration configuration) diff --git a/src/Tools/dotnet-monitor/Startup.cs b/src/Tools/dotnet-monitor/Startup.cs index 61456611574..9f7b82e3dae 100644 --- a/src/Tools/dotnet-monitor/Startup.cs +++ b/src/Tools/dotnet-monitor/Startup.cs @@ -81,6 +81,7 @@ public static void Configure(IApplicationBuilder app, IWebHostEnvironment env, I app.UseHsts(); } + app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "dotnet-monitor v1.0"); diff --git a/src/Tools/dotnet-monitor/Strings.Designer.cs b/src/Tools/dotnet-monitor/Strings.Designer.cs index a78203af400..91f3959c93c 100644 --- a/src/Tools/dotnet-monitor/Strings.Designer.cs +++ b/src/Tools/dotnet-monitor/Strings.Designer.cs @@ -528,6 +528,15 @@ internal static string FeatureName_CallStacks { } } + /// + /// Looks up a localized string similar to Exceptions. + /// + internal static string FeatureName_Exceptions { + get { + return ResourceManager.GetString("FeatureName_Exceptions", resourceCulture); + } + } + /// /// Looks up a localized string similar to Monitor logs and metrics in a .NET application send the results to a chosen destination.. /// diff --git a/src/Tools/dotnet-monitor/Strings.resx b/src/Tools/dotnet-monitor/Strings.resx index 5e87690ef55..16301be4d5f 100644 --- a/src/Tools/dotnet-monitor/Strings.resx +++ b/src/Tools/dotnet-monitor/Strings.resx @@ -372,6 +372,9 @@ Call Stacks + + Exceptions + Monitor logs and metrics in a .NET application send the results to a chosen destination. Gets the string to display in help that explains what the 'collect' command does. diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/BadRequestResponseDocumentFilter.cs b/src/Tools/dotnet-monitor/Swagger/Filters/BadRequestResponseDocumentFilter.cs similarity index 95% rename from src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/BadRequestResponseDocumentFilter.cs rename to src/Tools/dotnet-monitor/Swagger/Filters/BadRequestResponseDocumentFilter.cs index d4948d002fd..6039d889ef2 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/BadRequestResponseDocumentFilter.cs +++ b/src/Tools/dotnet-monitor/Swagger/Filters/BadRequestResponseDocumentFilter.cs @@ -6,7 +6,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Microsoft.Diagnostics.Monitoring.OpenApiGen +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger.Filters { /// /// Adds an BadRequestResponse response component to the document. diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/BadRequestResponseOperationFilter.cs b/src/Tools/dotnet-monitor/Swagger/Filters/BadRequestResponseOperationFilter.cs similarity index 94% rename from src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/BadRequestResponseOperationFilter.cs rename to src/Tools/dotnet-monitor/Swagger/Filters/BadRequestResponseOperationFilter.cs index 334d65600ed..1c21b56e58c 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/BadRequestResponseOperationFilter.cs +++ b/src/Tools/dotnet-monitor/Swagger/Filters/BadRequestResponseOperationFilter.cs @@ -4,7 +4,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Microsoft.Diagnostics.Monitoring.OpenApiGen +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger.Filters { /// /// Clears all content of the 400 response and adds a reference to the diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/RemoveFailureContentTypesOperationFilter.cs b/src/Tools/dotnet-monitor/Swagger/Filters/RemoveFailureContentTypesOperationFilter.cs similarity index 93% rename from src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/RemoveFailureContentTypesOperationFilter.cs rename to src/Tools/dotnet-monitor/Swagger/Filters/RemoveFailureContentTypesOperationFilter.cs index 45dd06d5164..ff8a00784e8 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/RemoveFailureContentTypesOperationFilter.cs +++ b/src/Tools/dotnet-monitor/Swagger/Filters/RemoveFailureContentTypesOperationFilter.cs @@ -6,7 +6,7 @@ using Swashbuckle.AspNetCore.SwaggerGen; using System.Collections.Generic; -namespace Microsoft.Diagnostics.Monitoring.OpenApiGen +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger.Filters { /// /// Removes failure content types (e.g. application/problem+json) from success operations. diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/TooManyRequestsResponseDocumentFilter.cs b/src/Tools/dotnet-monitor/Swagger/Filters/TooManyRequestsResponseDocumentFilter.cs similarity index 95% rename from src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/TooManyRequestsResponseDocumentFilter.cs rename to src/Tools/dotnet-monitor/Swagger/Filters/TooManyRequestsResponseDocumentFilter.cs index 3786991f0bb..6fcefacf981 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/TooManyRequestsResponseDocumentFilter.cs +++ b/src/Tools/dotnet-monitor/Swagger/Filters/TooManyRequestsResponseDocumentFilter.cs @@ -6,7 +6,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Microsoft.Diagnostics.Monitoring.OpenApiGen +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger.Filters { internal sealed class TooManyRequestsResponseDocumentFilter : IDocumentFilter { diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/TooManyRequestsResponseOperationFilter.cs b/src/Tools/dotnet-monitor/Swagger/Filters/TooManyRequestsResponseOperationFilter.cs similarity index 93% rename from src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/TooManyRequestsResponseOperationFilter.cs rename to src/Tools/dotnet-monitor/Swagger/Filters/TooManyRequestsResponseOperationFilter.cs index 342994728d3..eb57e961d6d 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/TooManyRequestsResponseOperationFilter.cs +++ b/src/Tools/dotnet-monitor/Swagger/Filters/TooManyRequestsResponseOperationFilter.cs @@ -4,7 +4,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Microsoft.Diagnostics.Monitoring.OpenApiGen +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger.Filters { internal sealed class TooManyRequestsResponseOperationFilter : IOperationFilter { diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/UnauthorizedResponseDocumentFilter.cs b/src/Tools/dotnet-monitor/Swagger/Filters/UnauthorizedResponseDocumentFilter.cs similarity index 94% rename from src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/UnauthorizedResponseDocumentFilter.cs rename to src/Tools/dotnet-monitor/Swagger/Filters/UnauthorizedResponseDocumentFilter.cs index dc6b25e3a65..58267696f22 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/UnauthorizedResponseDocumentFilter.cs +++ b/src/Tools/dotnet-monitor/Swagger/Filters/UnauthorizedResponseDocumentFilter.cs @@ -4,7 +4,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Microsoft.Diagnostics.Monitoring.OpenApiGen +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger.Filters { /// /// Adds an UnauthorizedResponse response component to the document. diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/UnauthorizedResponseOperationFilter.cs b/src/Tools/dotnet-monitor/Swagger/Filters/UnauthorizedResponseOperationFilter.cs similarity index 94% rename from src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/UnauthorizedResponseOperationFilter.cs rename to src/Tools/dotnet-monitor/Swagger/Filters/UnauthorizedResponseOperationFilter.cs index 171af2bad3e..c9542c3a87c 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/UnauthorizedResponseOperationFilter.cs +++ b/src/Tools/dotnet-monitor/Swagger/Filters/UnauthorizedResponseOperationFilter.cs @@ -4,7 +4,7 @@ using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; -namespace Microsoft.Diagnostics.Monitoring.OpenApiGen +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger.Filters { /// /// Clears all content of the 401 response and adds a reference to the diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/ResponseNames.cs b/src/Tools/dotnet-monitor/Swagger/ResponseNames.cs similarity index 89% rename from src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/ResponseNames.cs rename to src/Tools/dotnet-monitor/Swagger/ResponseNames.cs index 92847a5f2eb..8dfa5710382 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/ResponseNames.cs +++ b/src/Tools/dotnet-monitor/Swagger/ResponseNames.cs @@ -1,7 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -namespace Microsoft.Diagnostics.Monitoring.OpenApiGen +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger { internal static class ResponseNames { diff --git a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/StatusCodeStrings.cs b/src/Tools/dotnet-monitor/Swagger/StatusCodeStrings.cs similarity index 92% rename from src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/StatusCodeStrings.cs rename to src/Tools/dotnet-monitor/Swagger/StatusCodeStrings.cs index 1d923515d7c..3ccc27573d5 100644 --- a/src/Tests/Microsoft.Diagnostics.Monitoring.OpenApiGen/StatusCodeStrings.cs +++ b/src/Tools/dotnet-monitor/Swagger/StatusCodeStrings.cs @@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Http; using System.Globalization; -namespace Microsoft.Diagnostics.Monitoring.OpenApiGen +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger { internal static class StatusCodeStrings { diff --git a/src/Tools/dotnet-monitor/Swagger/SwaggerGenOptionsExtensions.cs b/src/Tools/dotnet-monitor/Swagger/SwaggerGenOptionsExtensions.cs new file mode 100644 index 00000000000..4bc6d430205 --- /dev/null +++ b/src/Tools/dotnet-monitor/Swagger/SwaggerGenOptionsExtensions.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.Diagnostics.Monitoring.WebApi.Controllers; +using Microsoft.Diagnostics.Tools.Monitor.Swagger.Filters; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; +using System; +using System.IO; + +namespace Microsoft.Diagnostics.Tools.Monitor.Swagger +{ + internal static class SwaggerGenOptionsExtensions + { + public static void ConfigureMonitorSwaggerGen(this SwaggerGenOptions options) + { + options.DocumentFilter(); + options.DocumentFilter(); + options.DocumentFilter(); + + options.OperationFilter(); + options.OperationFilter(); + options.OperationFilter(); + options.OperationFilter(); + + string documentationFile = $"{typeof(DiagController).Assembly.GetName().Name}.xml"; + string documentationPath = Path.Combine(AppContext.BaseDirectory, documentationFile); + options.IncludeXmlComments(documentationPath); + } + + public static void ConfigureMonitorSwaggerGenSecurity(this SwaggerGenOptions options) + { + const string ApiKeySecurityDefinitionName = "ApiKeyAuth"; + + options.AddSecurityDefinition(ApiKeySecurityDefinitionName, new OpenApiSecurityScheme + { + Name = HeaderNames.Authorization, + Type = SecuritySchemeType.ApiKey, + Scheme = JwtBearerDefaults.AuthenticationScheme, + BearerFormat = "JWT", + In = ParameterLocation.Header, + Description = Strings.HelpDescription_SecurityDefinitionDescription_ApiKey + }); + + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = ApiKeySecurityDefinitionName } + }, + Array.Empty() + } + }); + } + } +} diff --git a/src/Tools/dotnet-monitor/dotnet-monitor.csproj b/src/Tools/dotnet-monitor/dotnet-monitor.csproj index 23413c41719..874417704e0 100644 --- a/src/Tools/dotnet-monitor/dotnet-monitor.csproj +++ b/src/Tools/dotnet-monitor/dotnet-monitor.csproj @@ -9,7 +9,7 @@ false false - + @@ -19,6 +19,7 @@ + @@ -44,7 +45,7 @@ - + True diff --git a/src/archives/Directory.Build.props b/src/archives/Directory.Build.props index 6749e52a6ee..6e6549b128c 100644 --- a/src/archives/Directory.Build.props +++ b/src/archives/Directory.Build.props @@ -2,5 +2,11 @@ $(MSBuildThisFileDirectory) + + $(ToolTargetFrameworks) + $(DefaultRuntimeIdentifiers) diff --git a/src/archives/PublishedProjectArchive.targets b/src/archives/PublishedProjectPackage.targets similarity index 100% rename from src/archives/PublishedProjectArchive.targets rename to src/archives/PublishedProjectPackage.targets diff --git a/src/archives/dotnet-monitor.props b/src/archives/dotnet-monitor/Package.props similarity index 89% rename from src/archives/dotnet-monitor.props rename to src/archives/dotnet-monitor/Package.props index 79d12aa8454..436c4024548 100644 --- a/src/archives/dotnet-monitor.props +++ b/src/archives/dotnet-monitor/Package.props @@ -1,7 +1,9 @@ - - + + + dotnet-monitor + dotnet-monitor $(DotnetMonitorPublishPath) @@ -36,6 +38,4 @@ - - diff --git a/src/archives/dotnet-monitor/ProjectsToBuild.props b/src/archives/dotnet-monitor/ProjectsToBuild.props new file mode 100644 index 00000000000..a2b69dcc3de --- /dev/null +++ b/src/archives/dotnet-monitor/ProjectsToBuild.props @@ -0,0 +1,10 @@ + + + + TargetFramework=$(LatestTargetFramework);RuntimeIdentifier=$(PackageRid) + + + TargetFramework=$(LatestTargetFramework);RuntimeIdentifier=$(PackageRid) + + + \ No newline at end of file diff --git a/eng/DotnetMonitorProjectToPublish.props b/src/archives/dotnet-monitor/ProjectsToPublish.props similarity index 100% rename from eng/DotnetMonitorProjectToPublish.props rename to src/archives/dotnet-monitor/ProjectsToPublish.props diff --git a/src/archives/dotnet-monitor/dotnet-monitor-archive.proj b/src/archives/dotnet-monitor/dotnet-monitor-archive.proj new file mode 100644 index 00000000000..16533c4261b --- /dev/null +++ b/src/archives/dotnet-monitor/dotnet-monitor-archive.proj @@ -0,0 +1,4 @@ + + + + diff --git a/src/archives/dotnet-monitor/dotnet-monitor-symbols.proj b/src/archives/dotnet-monitor/dotnet-monitor-symbols.proj new file mode 100644 index 00000000000..c6b8d6cc668 --- /dev/null +++ b/src/archives/dotnet-monitor/dotnet-monitor-symbols.proj @@ -0,0 +1,4 @@ + + + + diff --git a/src/archives/pkgs/Directory.Build.props b/src/archives/pkgs/Common.props similarity index 80% rename from src/archives/pkgs/Directory.Build.props rename to src/archives/pkgs/Common.props index 10875c413c6..d756f8815b8 100644 --- a/src/archives/pkgs/Directory.Build.props +++ b/src/archives/pkgs/Common.props @@ -1,10 +1,8 @@ - zip tar.gz - - false + true true true diff --git a/src/archives/pkgs/Directory.Build.targets b/src/archives/pkgs/Common.targets similarity index 89% rename from src/archives/pkgs/Directory.Build.targets rename to src/archives/pkgs/Common.targets index 2cef2ca20b4..2d3c58992d4 100644 --- a/src/archives/pkgs/Directory.Build.targets +++ b/src/archives/pkgs/Common.targets @@ -1,12 +1,10 @@ - - + DependsOnTargets="$(PublishToDiskDependsOn);MarkEntrypointAsExecutable"> <_FileToArchive Remove="@(_FileToArchive)" /> @@ -76,4 +74,14 @@ + + + + + + true + + + diff --git a/src/archives/pkgs/PublishedProject.targets b/src/archives/pkgs/PublishedProject.targets new file mode 100644 index 00000000000..8a4305a7297 --- /dev/null +++ b/src/archives/pkgs/PublishedProject.targets @@ -0,0 +1,5 @@ + + + + + diff --git a/src/archives/pkgs/dotnet-monitor/dotnet-monitor-archive.proj b/src/archives/pkgs/dotnet-monitor/dotnet-monitor-archive.proj deleted file mode 100644 index 20f69d0d786..00000000000 --- a/src/archives/pkgs/dotnet-monitor/dotnet-monitor-archive.proj +++ /dev/null @@ -1,21 +0,0 @@ - - - - dotnet-monitor - $(ToolTargetFrameworks) - $(DefaultRuntimeIdentifiers) - true - - - - $(PublishToDiskDependsOn);UpdateFilesToArchive - - - - - - true - - - - diff --git a/src/archives/symbols/Directory.Build.props b/src/archives/symbols/Common.props similarity index 87% rename from src/archives/symbols/Directory.Build.props rename to src/archives/symbols/Common.props index f8d330e4e22..7f5915c5b58 100644 --- a/src/archives/symbols/Directory.Build.props +++ b/src/archives/symbols/Common.props @@ -1,6 +1,7 @@ - + $(ArchiveName)-archive.$(RuntimeIdentifier) + Symbols for the $(ArchiveName) $(RuntimeIdentifier) archive. true true diff --git a/src/archives/symbols/Directory.Build.targets b/src/archives/symbols/Common.targets similarity index 92% rename from src/archives/symbols/Directory.Build.targets rename to src/archives/symbols/Common.targets index 3cded050aaa..fc27c685b49 100644 --- a/src/archives/symbols/Directory.Build.targets +++ b/src/archives/symbols/Common.targets @@ -1,6 +1,4 @@ - - diff --git a/src/archives/symbols/PublishedProject.targets b/src/archives/symbols/PublishedProject.targets new file mode 100644 index 00000000000..8a4305a7297 --- /dev/null +++ b/src/archives/symbols/PublishedProject.targets @@ -0,0 +1,5 @@ + + + + + diff --git a/src/archives/symbols/dotnet-monitor/dotnet-monitor-symbols.proj b/src/archives/symbols/dotnet-monitor/dotnet-monitor-symbols.proj deleted file mode 100644 index 511ae70d71a..00000000000 --- a/src/archives/symbols/dotnet-monitor/dotnet-monitor-symbols.proj +++ /dev/null @@ -1,14 +0,0 @@ - - - - dotnet-monitor-archive.$(RuntimeIdentifier) - - $(ToolTargetFrameworks) - $(DefaultRuntimeIdentifiers) - Symbols for the dotnet-monitor $(RuntimeIdentifier) archive. - - -