diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..3729ff0
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,25 @@
+**/.classpath
+**/.dockerignore
+**/.env
+**/.git
+**/.gitignore
+**/.project
+**/.settings
+**/.toolstarget
+**/.vs
+**/.vscode
+**/*.*proj.user
+**/*.dbmdl
+**/*.jfm
+**/azds.yaml
+**/bin
+**/charts
+**/docker-compose*
+**/Dockerfile*
+**/node_modules
+**/npm-debug.log
+**/obj
+**/secrets.dev.yaml
+**/values.dev.yaml
+LICENSE
+README.md
\ No newline at end of file
diff --git a/examples/AspNetCoreExample/AspNetCoreExample.csproj b/examples/AspNetCoreExample/AspNetCoreExample.csproj
index 9a2eecd..b37b8a0 100644
--- a/examples/AspNetCoreExample/AspNetCoreExample.csproj
+++ b/examples/AspNetCoreExample/AspNetCoreExample.csproj
@@ -2,6 +2,8 @@
net5.0
8
+ Linux
+ ..\..
@@ -9,6 +11,7 @@
+
diff --git a/examples/AspNetCoreExample/Dockerfile b/examples/AspNetCoreExample/Dockerfile
index 051cf9f..d8c412e 100644
--- a/examples/AspNetCoreExample/Dockerfile
+++ b/examples/AspNetCoreExample/Dockerfile
@@ -1,12 +1,22 @@
-#FROM mcr.microsoft.com/dotnet/core/sdk:3.1.406 AS build
+#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
+
+FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
+WORKDIR /app
+EXPOSE 80
+
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
+COPY ["examples/AspNetCoreExample/AspNetCoreExample.csproj", "examples/AspNetCoreExample/"]
+COPY ["src/prometheus-net.DotNetRuntime/prometheus-net.DotNetRuntime.csproj", "src/prometheus-net.DotNetRuntime/"]
+RUN dotnet restore "examples/AspNetCoreExample/AspNetCoreExample.csproj"
COPY . .
-RUN dotnet publish "examples/AspNetCoreExample" -c Release -o /app
+WORKDIR "/src/examples/AspNetCoreExample"
+RUN dotnet build "AspNetCoreExample.csproj" -c Release -o /app/build
+
+FROM build AS publish
+RUN dotnet publish "AspNetCoreExample.csproj" -c Release -o /app/publish
-#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.3 AS final
-#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.10 AS final
-FROM mcr.microsoft.com/dotnet/aspnet:5.0 as final
+FROM base AS final
WORKDIR /app
-COPY --from=build /app /app
-ENTRYPOINT ["dotnet", "AspNetCoreExample.dll"]
+COPY --from=publish /app/publish .
+ENTRYPOINT ["dotnet", "AspNetCoreExample.dll"]
\ No newline at end of file
diff --git a/examples/AspNetCoreExample/OriginalDockerfile b/examples/AspNetCoreExample/OriginalDockerfile
new file mode 100644
index 0000000..a500477
--- /dev/null
+++ b/examples/AspNetCoreExample/OriginalDockerfile
@@ -0,0 +1,13 @@
+# FROM mcr.microsoft.com/dotnet/core/sdk:3.1.201 AS build
+#FROM mcr.microsoft.com/dotnet/core/sdk:3.1.406 AS build
+FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
+WORKDIR /src
+COPY . .
+RUN dotnet publish "examples/AspNetCoreExample" -c Release -o /app
+
+#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.3 AS final
+#FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.10 AS final
+FROM mcr.microsoft.com/dotnet/aspnet:5.0 as final
+WORKDIR /app
+COPY --from=build /app /app
+ENTRYPOINT ["dotnet", "AspNetCoreExample.dll"]
diff --git a/examples/AspNetCoreExample/Properties/launchSettings.json b/examples/AspNetCoreExample/Properties/launchSettings.json
index 1277cb3..fc14095 100644
--- a/examples/AspNetCoreExample/Properties/launchSettings.json
+++ b/examples/AspNetCoreExample/Properties/launchSettings.json
@@ -1,16 +1,22 @@
-{
+{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
"AspNetCoreExample": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "metrics",
- "applicationUrl": "http://localhost:5000",
"environmentVariables": {
- "ASPNETCORE_ENVIRONMENT": "Development",
+ "Example__UseDefaultMetrics": "true",
"Example__UseDebuggingMetrics": "true",
- "Example__UseDefaultMetrics": "true"
- }
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "http://localhost:5000"
+ },
+ "Docker": {
+ "commandName": "Docker",
+ "launchBrowser": true,
+ "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/metrics",
+ "publishAllPorts": true
}
}
}
\ No newline at end of file
diff --git a/examples/AspNetCoreExample/Startup.cs b/examples/AspNetCoreExample/Startup.cs
index 350f73c..c75a80e 100644
--- a/examples/AspNetCoreExample/Startup.cs
+++ b/examples/AspNetCoreExample/Startup.cs
@@ -84,7 +84,8 @@ public static IDisposable CreateCollector()
.WithGcStats(CaptureLevel.Verbose)
.WithThreadPoolStats(CaptureLevel.Informational)
.WithExceptionStats(CaptureLevel.Errors)
- .WithJitStats();
+ .WithJitStats()
+ .WithKestrelStats(CaptureLevel.Informational);
}
builder
diff --git a/examples/AspNetCoreExample/dotnet-install.sh b/examples/AspNetCoreExample/dotnet-install.sh
new file mode 100644
index 0000000..8ffe169
--- /dev/null
+++ b/examples/AspNetCoreExample/dotnet-install.sh
@@ -0,0 +1,1222 @@
+#!/usr/bin/env bash
+# Copyright (c) .NET Foundation and contributors. All rights reserved.
+# Licensed under the MIT license. See LICENSE file in the project root for full license information.
+#
+
+# Stop script on NZEC
+set -e
+# Stop script if unbound variable found (use ${var:-} if intentional)
+set -u
+# By default cmd1 | cmd2 returns exit code of cmd2 regardless of cmd1 success
+# This is causing it to fail
+set -o pipefail
+
+# Use in the the functions: eval $invocation
+invocation='say_verbose "Calling: ${yellow:-}${FUNCNAME[0]} ${green:-}$*${normal:-}"'
+
+# standard output may be used as a return value in the functions
+# we need a way to write text on the screen in the functions so that
+# it won't interfere with the return value.
+# Exposing stream 3 as a pipe to standard output of the script itself
+exec 3>&1
+
+# Setup some colors to use. These need to work in fairly limited shells, like the Ubuntu Docker container where there are only 8 colors.
+# See if stdout is a terminal
+if [ -t 1 ] && command -v tput > /dev/null; then
+ # see if it supports colors
+ ncolors=$(tput colors)
+ if [ -n "$ncolors" ] && [ $ncolors -ge 8 ]; then
+ bold="$(tput bold || echo)"
+ normal="$(tput sgr0 || echo)"
+ black="$(tput setaf 0 || echo)"
+ red="$(tput setaf 1 || echo)"
+ green="$(tput setaf 2 || echo)"
+ yellow="$(tput setaf 3 || echo)"
+ blue="$(tput setaf 4 || echo)"
+ magenta="$(tput setaf 5 || echo)"
+ cyan="$(tput setaf 6 || echo)"
+ white="$(tput setaf 7 || echo)"
+ fi
+fi
+
+say_warning() {
+ printf "%b\n" "${yellow:-}dotnet_install: Warning: $1${normal:-}" >&3
+}
+
+say_err() {
+ printf "%b\n" "${red:-}dotnet_install: Error: $1${normal:-}" >&2
+}
+
+say() {
+ # using stream 3 (defined in the beginning) to not interfere with stdout of functions
+ # which may be used as return value
+ printf "%b\n" "${cyan:-}dotnet-install:${normal:-} $1" >&3
+}
+
+say_verbose() {
+ if [ "$verbose" = true ]; then
+ say "$1"
+ fi
+}
+
+# This platform list is finite - if the SDK/Runtime has supported Linux distribution-specific assets,
+# then and only then should the Linux distribution appear in this list.
+# Adding a Linux distribution to this list does not imply distribution-specific support.
+get_legacy_os_name_from_platform() {
+ eval $invocation
+
+ platform="$1"
+ case "$platform" in
+ "centos.7")
+ echo "centos"
+ return 0
+ ;;
+ "debian.8")
+ echo "debian"
+ return 0
+ ;;
+ "debian.9")
+ echo "debian.9"
+ return 0
+ ;;
+ "fedora.23")
+ echo "fedora.23"
+ return 0
+ ;;
+ "fedora.24")
+ echo "fedora.24"
+ return 0
+ ;;
+ "fedora.27")
+ echo "fedora.27"
+ return 0
+ ;;
+ "fedora.28")
+ echo "fedora.28"
+ return 0
+ ;;
+ "opensuse.13.2")
+ echo "opensuse.13.2"
+ return 0
+ ;;
+ "opensuse.42.1")
+ echo "opensuse.42.1"
+ return 0
+ ;;
+ "opensuse.42.3")
+ echo "opensuse.42.3"
+ return 0
+ ;;
+ "rhel.7"*)
+ echo "rhel"
+ return 0
+ ;;
+ "ubuntu.14.04")
+ echo "ubuntu"
+ return 0
+ ;;
+ "ubuntu.16.04")
+ echo "ubuntu.16.04"
+ return 0
+ ;;
+ "ubuntu.16.10")
+ echo "ubuntu.16.10"
+ return 0
+ ;;
+ "ubuntu.18.04")
+ echo "ubuntu.18.04"
+ return 0
+ ;;
+ "alpine.3.4.3")
+ echo "alpine"
+ return 0
+ ;;
+ esac
+ return 1
+}
+
+get_linux_platform_name() {
+ eval $invocation
+
+ if [ -n "$runtime_id" ]; then
+ echo "${runtime_id%-*}"
+ return 0
+ else
+ if [ -e /etc/os-release ]; then
+ . /etc/os-release
+ echo "$ID${VERSION_ID:+.${VERSION_ID}}"
+ return 0
+ elif [ -e /etc/redhat-release ]; then
+ local redhatRelease=$(&1 || true) | grep -q musl
+}
+
+get_current_os_name() {
+ eval $invocation
+
+ local uname=$(uname)
+ if [ "$uname" = "Darwin" ]; then
+ echo "osx"
+ return 0
+ elif [ "$uname" = "FreeBSD" ]; then
+ echo "freebsd"
+ return 0
+ elif [ "$uname" = "Linux" ]; then
+ local linux_platform_name
+ linux_platform_name="$(get_linux_platform_name)" || { echo "linux" && return 0 ; }
+
+ if [ "$linux_platform_name" = "rhel.6" ]; then
+ echo $linux_platform_name
+ return 0
+ elif is_musl_based_distro; then
+ echo "linux-musl"
+ return 0
+ elif [ "$linux_platform_name" = "linux-musl" ]; then
+ echo "linux-musl"
+ return 0
+ else
+ echo "linux"
+ return 0
+ fi
+ fi
+
+ say_err "OS name could not be detected: UName = $uname"
+ return 1
+}
+
+get_legacy_os_name() {
+ eval $invocation
+
+ local uname=$(uname)
+ if [ "$uname" = "Darwin" ]; then
+ echo "osx"
+ return 0
+ elif [ -n "$runtime_id" ]; then
+ echo $(get_legacy_os_name_from_platform "${runtime_id%-*}" || echo "${runtime_id%-*}")
+ return 0
+ else
+ if [ -e /etc/os-release ]; then
+ . /etc/os-release
+ os=$(get_legacy_os_name_from_platform "$ID${VERSION_ID:+.${VERSION_ID}}" || echo "")
+ if [ -n "$os" ]; then
+ echo "$os"
+ return 0
+ fi
+ fi
+ fi
+
+ say_verbose "Distribution specific OS name and version could not be detected: UName = $uname"
+ return 1
+}
+
+machine_has() {
+ eval $invocation
+
+ hash "$1" > /dev/null 2>&1
+ return $?
+}
+
+
+check_min_reqs() {
+ local hasMinimum=false
+ if machine_has "curl"; then
+ hasMinimum=true
+ elif machine_has "wget"; then
+ hasMinimum=true
+ fi
+
+ if [ "$hasMinimum" = "false" ]; then
+ say_err "curl (recommended) or wget are required to download dotnet. Install missing prerequisite to proceed."
+ return 1
+ fi
+ return 0
+}
+
+# args:
+# input - $1
+to_lowercase() {
+ #eval $invocation
+
+ echo "$1" | tr '[:upper:]' '[:lower:]'
+ return 0
+}
+
+# args:
+# input - $1
+remove_trailing_slash() {
+ #eval $invocation
+
+ local input="${1:-}"
+ echo "${input%/}"
+ return 0
+}
+
+# args:
+# input - $1
+remove_beginning_slash() {
+ #eval $invocation
+
+ local input="${1:-}"
+ echo "${input#/}"
+ return 0
+}
+
+# args:
+# root_path - $1
+# child_path - $2 - this parameter can be empty
+combine_paths() {
+ eval $invocation
+
+ # TODO: Consider making it work with any number of paths. For now:
+ if [ ! -z "${3:-}" ]; then
+ say_err "combine_paths: Function takes two parameters."
+ return 1
+ fi
+
+ local root_path="$(remove_trailing_slash "$1")"
+ local child_path="$(remove_beginning_slash "${2:-}")"
+ say_verbose "combine_paths: root_path=$root_path"
+ say_verbose "combine_paths: child_path=$child_path"
+ echo "$root_path/$child_path"
+ return 0
+}
+
+get_machine_architecture() {
+ eval $invocation
+
+ if command -v uname > /dev/null; then
+ CPUName=$(uname -m)
+ case $CPUName in
+ armv*l)
+ echo "arm"
+ return 0
+ ;;
+ aarch64|arm64)
+ echo "arm64"
+ return 0
+ ;;
+ esac
+ fi
+
+ # Always default to 'x64'
+ echo "x64"
+ return 0
+}
+
+# args:
+# architecture - $1
+get_normalized_architecture_from_architecture() {
+ eval $invocation
+
+ local architecture="$(to_lowercase "$1")"
+ case "$architecture" in
+ \)
+ echo "$(get_normalized_architecture_from_architecture "$(get_machine_architecture)")"
+ return 0
+ ;;
+ amd64|x64)
+ echo "x64"
+ return 0
+ ;;
+ arm)
+ echo "arm"
+ return 0
+ ;;
+ arm64)
+ echo "arm64"
+ return 0
+ ;;
+ esac
+
+ say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues"
+ return 1
+}
+
+# args:
+# user_defined_os - $1
+get_normalized_os() {
+ eval $invocation
+
+ local osname="$(to_lowercase "$1")"
+ if [ ! -z "$osname" ]; then
+ case "$osname" in
+ osx | freebsd | rhel.6 | linux-musl | linux)
+ echo "$osname"
+ return 0
+ ;;
+ *)
+ say_err "'$user_defined_os' is not a supported value for --os option, supported values are: osx, linux, linux-musl, freebsd, rhel.6. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues."
+ return 1
+ ;;
+ esac
+ else
+ osname="$(get_current_os_name)" || return 1
+ fi
+ echo "$osname"
+ return 0
+}
+
+# The version text returned from the feeds is a 1-line or 2-line string:
+# For the SDK and the dotnet runtime (2 lines):
+# Line 1: # commit_hash
+# Line 2: # 4-part version
+# For the aspnetcore runtime (1 line):
+# Line 1: # 4-part version
+
+# args:
+# version_text - stdin
+get_version_from_version_info() {
+ eval $invocation
+
+ cat | tail -n 1 | sed 's/\r$//'
+ return 0
+}
+
+# args:
+# install_root - $1
+# relative_path_to_package - $2
+# specific_version - $3
+is_dotnet_package_installed() {
+ eval $invocation
+
+ local install_root="$1"
+ local relative_path_to_package="$2"
+ local specific_version="${3//[$'\t\r\n']}"
+
+ local dotnet_package_path="$(combine_paths "$(combine_paths "$install_root" "$relative_path_to_package")" "$specific_version")"
+ say_verbose "is_dotnet_package_installed: dotnet_package_path=$dotnet_package_path"
+
+ if [ -d "$dotnet_package_path" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# args:
+# azure_feed - $1
+# channel - $2
+# normalized_architecture - $3
+get_latest_version_info() {
+ eval $invocation
+
+ local azure_feed="$1"
+ local channel="$2"
+ local normalized_architecture="$3"
+
+ local version_file_url=null
+ if [[ "$runtime" == "dotnet" ]]; then
+ version_file_url="$uncached_feed/Runtime/$channel/latest.version"
+ elif [[ "$runtime" == "aspnetcore" ]]; then
+ version_file_url="$uncached_feed/aspnetcore/Runtime/$channel/latest.version"
+ elif [ -z "$runtime" ]; then
+ version_file_url="$uncached_feed/Sdk/$channel/latest.version"
+ else
+ say_err "Invalid value for \$runtime"
+ return 1
+ fi
+ say_verbose "get_latest_version_info: latest url: $version_file_url"
+
+ download "$version_file_url"
+ return $?
+}
+
+# args:
+# json_file - $1
+parse_jsonfile_for_version() {
+ eval $invocation
+
+ local json_file="$1"
+ if [ ! -f "$json_file" ]; then
+ say_err "Unable to find \`$json_file\`"
+ return 1
+ fi
+
+ sdk_section=$(cat $json_file | awk '/"sdk"/,/}/')
+ if [ -z "$sdk_section" ]; then
+ say_err "Unable to parse the SDK node in \`$json_file\`"
+ return 1
+ fi
+
+ sdk_list=$(echo $sdk_section | awk -F"[{}]" '{print $2}')
+ sdk_list=${sdk_list//[\" ]/}
+ sdk_list=${sdk_list//,/$'\n'}
+
+ local version_info=""
+ while read -r line; do
+ IFS=:
+ while read -r key value; do
+ if [[ "$key" == "version" ]]; then
+ version_info=$value
+ fi
+ done <<< "$line"
+ done <<< "$sdk_list"
+ if [ -z "$version_info" ]; then
+ say_err "Unable to find the SDK:version node in \`$json_file\`"
+ return 1
+ fi
+
+ unset IFS;
+ echo "$version_info"
+ return 0
+}
+
+# args:
+# azure_feed - $1
+# channel - $2
+# normalized_architecture - $3
+# version - $4
+# json_file - $5
+get_specific_version_from_version() {
+ eval $invocation
+
+ local azure_feed="$1"
+ local channel="$2"
+ local normalized_architecture="$3"
+ local version="$(to_lowercase "$4")"
+ local json_file="$5"
+
+ if [ -z "$json_file" ]; then
+ if [[ "$version" == "latest" ]]; then
+ local version_info
+ version_info="$(get_latest_version_info "$azure_feed" "$channel" "$normalized_architecture" false)" || return 1
+ say_verbose "get_specific_version_from_version: version_info=$version_info"
+ echo "$version_info" | get_version_from_version_info
+ return 0
+ else
+ echo "$version"
+ return 0
+ fi
+ else
+ local version_info
+ version_info="$(parse_jsonfile_for_version "$json_file")" || return 1
+ echo "$version_info"
+ return 0
+ fi
+}
+
+# args:
+# azure_feed - $1
+# channel - $2
+# normalized_architecture - $3
+# specific_version - $4
+# normalized_os - $5
+construct_download_link() {
+ eval $invocation
+
+ local azure_feed="$1"
+ local channel="$2"
+ local normalized_architecture="$3"
+ local specific_version="${4//[$'\t\r\n']}"
+ local specific_product_version="$(get_specific_product_version "$1" "$4")"
+ local osname="$5"
+
+ local download_link=null
+ if [[ "$runtime" == "dotnet" ]]; then
+ download_link="$azure_feed/Runtime/$specific_version/dotnet-runtime-$specific_product_version-$osname-$normalized_architecture.tar.gz"
+ elif [[ "$runtime" == "aspnetcore" ]]; then
+ download_link="$azure_feed/aspnetcore/Runtime/$specific_version/aspnetcore-runtime-$specific_product_version-$osname-$normalized_architecture.tar.gz"
+ elif [ -z "$runtime" ]; then
+ download_link="$azure_feed/Sdk/$specific_version/dotnet-sdk-$specific_product_version-$osname-$normalized_architecture.tar.gz"
+ else
+ return 1
+ fi
+
+ echo "$download_link"
+ return 0
+}
+
+# args:
+# azure_feed - $1
+# specific_version - $2
+get_specific_product_version() {
+ # If we find a 'productVersion.txt' at the root of any folder, we'll use its contents
+ # to resolve the version of what's in the folder, superseding the specified version.
+ eval $invocation
+
+ local azure_feed="$1"
+ local specific_version="${2//[$'\t\r\n']}"
+ local specific_product_version=$specific_version
+
+ local download_link=null
+ if [[ "$runtime" == "dotnet" ]]; then
+ download_link="$azure_feed/Runtime/$specific_version/productVersion.txt${feed_credential}"
+ elif [[ "$runtime" == "aspnetcore" ]]; then
+ download_link="$azure_feed/aspnetcore/Runtime/$specific_version/productVersion.txt${feed_credential}"
+ elif [ -z "$runtime" ]; then
+ download_link="$azure_feed/Sdk/$specific_version/productVersion.txt${feed_credential}"
+ else
+ return 1
+ fi
+
+ if machine_has "curl"
+ then
+ specific_product_version=$(curl -s --fail "$download_link")
+ if [ $? -ne 0 ]
+ then
+ specific_product_version=$specific_version
+ fi
+ elif machine_has "wget"
+ then
+ specific_product_version=$(wget -qO- "$download_link")
+ if [ $? -ne 0 ]
+ then
+ specific_product_version=$specific_version
+ fi
+ fi
+ specific_product_version="${specific_product_version//[$'\t\r\n']}"
+
+ echo "$specific_product_version"
+ return 0
+}
+
+# args:
+# azure_feed - $1
+# channel - $2
+# normalized_architecture - $3
+# specific_version - $4
+construct_legacy_download_link() {
+ eval $invocation
+
+ local azure_feed="$1"
+ local channel="$2"
+ local normalized_architecture="$3"
+ local specific_version="${4//[$'\t\r\n']}"
+
+ local distro_specific_osname
+ distro_specific_osname="$(get_legacy_os_name)" || return 1
+
+ local legacy_download_link=null
+ if [[ "$runtime" == "dotnet" ]]; then
+ legacy_download_link="$azure_feed/Runtime/$specific_version/dotnet-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz"
+ elif [ -z "$runtime" ]; then
+ legacy_download_link="$azure_feed/Sdk/$specific_version/dotnet-dev-$distro_specific_osname-$normalized_architecture.$specific_version.tar.gz"
+ else
+ return 1
+ fi
+
+ echo "$legacy_download_link"
+ return 0
+}
+
+get_user_install_path() {
+ eval $invocation
+
+ if [ ! -z "${DOTNET_INSTALL_DIR:-}" ]; then
+ echo "$DOTNET_INSTALL_DIR"
+ else
+ echo "$HOME/.dotnet"
+ fi
+ return 0
+}
+
+# args:
+# install_dir - $1
+resolve_installation_path() {
+ eval $invocation
+
+ local install_dir=$1
+ if [ "$install_dir" = "" ]; then
+ local user_install_path="$(get_user_install_path)"
+ say_verbose "resolve_installation_path: user_install_path=$user_install_path"
+ echo "$user_install_path"
+ return 0
+ fi
+
+ echo "$install_dir"
+ return 0
+}
+
+# args:
+# relative_or_absolute_path - $1
+get_absolute_path() {
+ eval $invocation
+
+ local relative_or_absolute_path=$1
+ echo "$(cd "$(dirname "$1")" && pwd -P)/$(basename "$1")"
+ return 0
+}
+
+# args:
+# input_files - stdin
+# root_path - $1
+# out_path - $2
+# override - $3
+copy_files_or_dirs_from_list() {
+ eval $invocation
+
+ local root_path="$(remove_trailing_slash "$1")"
+ local out_path="$(remove_trailing_slash "$2")"
+ local override="$3"
+ local osname="$(get_current_os_name)"
+ local override_switch=$(
+ if [ "$override" = false ]; then
+ if [ "$osname" = "linux-musl" ]; then
+ printf -- "-u";
+ else
+ printf -- "-n";
+ fi
+ fi)
+
+ cat | uniq | while read -r file_path; do
+ local path="$(remove_beginning_slash "${file_path#$root_path}")"
+ local target="$out_path/$path"
+ if [ "$override" = true ] || (! ([ -d "$target" ] || [ -e "$target" ])); then
+ mkdir -p "$out_path/$(dirname "$path")"
+ if [ -d "$target" ]; then
+ rm -rf "$target"
+ fi
+ cp -R $override_switch "$root_path/$path" "$target"
+ fi
+ done
+}
+
+# args:
+# zip_path - $1
+# out_path - $2
+extract_dotnet_package() {
+ eval $invocation
+
+ local zip_path="$1"
+ local out_path="$2"
+
+ local temp_out_path="$(mktemp -d "$temporary_file_template")"
+
+ local failed=false
+ tar -xzf "$zip_path" -C "$temp_out_path" > /dev/null || failed=true
+
+ local folders_with_version_regex='^.*/[0-9]+\.[0-9]+[^/]+/'
+ find "$temp_out_path" -type f | grep -Eo "$folders_with_version_regex" | sort | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" false
+ find "$temp_out_path" -type f | grep -Ev "$folders_with_version_regex" | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" "$override_non_versioned_files"
+
+ rm -rf "$temp_out_path"
+ rm -f "$zip_path" && say_verbose "Temporary zip file $zip_path was removed"
+
+ if [ "$failed" = true ]; then
+ say_err "Extraction failed"
+ return 1
+ fi
+ return 0
+}
+
+get_http_header_curl() {
+ eval $invocation
+ local remote_path="$1"
+ remote_path_with_credential="${remote_path}${feed_credential}"
+ curl_options="-I -sSL --retry 5 --retry-delay 2 --connect-timeout 15 "
+ curl $curl_options "$remote_path_with_credential" || return 1
+ return 0
+}
+
+get_http_header_wget() {
+ eval $invocation
+ local remote_path="$1"
+ remote_path_with_credential="${remote_path}${feed_credential}"
+ wget_options="-q -S --spider --tries 5 --waitretry 2 --connect-timeout 15 "
+ wget $wget_options "$remote_path_with_credential" 2>&1 || return 1
+ return 0
+}
+
+# args:
+# remote_path - $1
+# [out_path] - $2 - stdout if not provided
+download() {
+ eval $invocation
+
+ local remote_path="$1"
+ local out_path="${2:-}"
+
+ if [[ "$remote_path" != "http"* ]]; then
+ cp "$remote_path" "$out_path"
+ return $?
+ fi
+
+ local failed=false
+ local attempts=0
+ while [ $attempts -lt 3 ]; do
+ attempts=$((attempts+1))
+ failed=false
+ if machine_has "curl"; then
+ downloadcurl "$remote_path" "$out_path" || failed=true
+ elif machine_has "wget"; then
+ downloadwget "$remote_path" "$out_path" || failed=true
+ else
+ say_err "Missing dependency: neither curl nor wget was found."
+ exit 1
+ fi
+
+ if [ "$failed" = false ] || [ $attempts -ge 3 ] || { [ ! -z $http_code ] && [ $http_code = "404" ]; }; then
+ break
+ fi
+
+ say "Download attempt #$attempts has failed: $http_code $download_error_msg"
+ say "Attempt #$((attempts+1)) will start in $((attempts*10)) seconds."
+ sleep $((attempts*20))
+ done
+
+
+
+ if [ "$failed" = true ]; then
+ say_verbose "Download failed: $remote_path"
+ return 1
+ fi
+ return 0
+}
+
+# Updates global variables $http_code and $download_error_msg
+downloadcurl() {
+ eval $invocation
+ unset http_code
+ unset download_error_msg
+ local remote_path="$1"
+ local out_path="${2:-}"
+ # Append feed_credential as late as possible before calling curl to avoid logging feed_credential
+ local remote_path_with_credential="${remote_path}${feed_credential}"
+ local curl_options="--retry 20 --retry-delay 2 --connect-timeout 15 -sSL -f --create-dirs "
+ local failed=false
+ if [ -z "$out_path" ]; then
+ curl $curl_options "$remote_path_with_credential" || failed=true
+ else
+ curl $curl_options -o "$out_path" "$remote_path_with_credential" || failed=true
+ fi
+ if [ "$failed" = true ]; then
+ local response=$(get_http_header_curl $remote_path_with_credential)
+ http_code=$( echo "$response" | awk '/^HTTP/{print $2}' | tail -1 )
+ download_error_msg="Unable to download $remote_path."
+ if [[ $http_code != 2* ]]; then
+ download_error_msg+=" Returned HTTP status code: $http_code."
+ fi
+ say_verbose "$download_error_msg"
+ return 1
+ fi
+ return 0
+}
+
+
+# Updates global variables $http_code and $download_error_msg
+downloadwget() {
+ eval $invocation
+ unset http_code
+ unset download_error_msg
+ local remote_path="$1"
+ local out_path="${2:-}"
+ # Append feed_credential as late as possible before calling wget to avoid logging feed_credential
+ local remote_path_with_credential="${remote_path}${feed_credential}"
+ local wget_options="--tries 20 --waitretry 2 --connect-timeout 15 "
+ local failed=false
+ if [ -z "$out_path" ]; then
+ wget -q $wget_options -O - "$remote_path_with_credential" || failed=true
+ else
+ wget $wget_options -O "$out_path" "$remote_path_with_credential" || failed=true
+ fi
+ if [ "$failed" = true ]; then
+ local response=$(get_http_header_wget $remote_path_with_credential)
+ http_code=$( echo "$response" | awk '/^ HTTP/{print $2}' | tail -1 )
+ download_error_msg="Unable to download $remote_path."
+ if [[ $http_code != 2* ]]; then
+ download_error_msg+=" Returned HTTP status code: $http_code."
+ fi
+ say_verbose "$download_error_msg"
+ return 1
+ fi
+ return 0
+}
+
+calculate_vars() {
+ eval $invocation
+ valid_legacy_download_link=true
+
+ normalized_architecture="$(get_normalized_architecture_from_architecture "$architecture")"
+ say_verbose "normalized_architecture=$normalized_architecture"
+
+ normalized_os="$(get_normalized_os "$user_defined_os")"
+ say_verbose "normalized_os=$normalized_os"
+
+ specific_version="$(get_specific_version_from_version "$azure_feed" "$channel" "$normalized_architecture" "$version" "$json_file")"
+ specific_product_version="$(get_specific_product_version "$azure_feed" "$specific_version")"
+ say_verbose "specific_version=$specific_version"
+ if [ -z "$specific_version" ]; then
+ say_err "Could not resolve version information."
+ return 1
+ fi
+
+ download_link="$(construct_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version" "$normalized_os")"
+ say_verbose "Constructed primary named payload URL: $download_link"
+
+ legacy_download_link="$(construct_legacy_download_link "$azure_feed" "$channel" "$normalized_architecture" "$specific_version")" || valid_legacy_download_link=false
+
+ if [ "$valid_legacy_download_link" = true ]; then
+ say_verbose "Constructed legacy named payload URL: $legacy_download_link"
+ else
+ say_verbose "Cound not construct a legacy_download_link; omitting..."
+ fi
+
+ install_root="$(resolve_installation_path "$install_dir")"
+ say_verbose "InstallRoot: $install_root"
+}
+
+install_dotnet() {
+ eval $invocation
+ local download_failed=false
+ local asset_name=''
+ local asset_relative_path=''
+
+ if [[ "$runtime" == "dotnet" ]]; then
+ asset_relative_path="shared/Microsoft.NETCore.App"
+ asset_name=".NET Core Runtime"
+ elif [[ "$runtime" == "aspnetcore" ]]; then
+ asset_relative_path="shared/Microsoft.AspNetCore.App"
+ asset_name="ASP.NET Core Runtime"
+ elif [ -z "$runtime" ]; then
+ asset_relative_path="sdk"
+ asset_name=".NET Core SDK"
+ else
+ say_err "Invalid value for \$runtime"
+ return 1
+ fi
+
+ # Check if the SDK version is already installed.
+ if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_version"; then
+ say "$asset_name version $specific_version is already installed."
+ return 0
+ fi
+
+ mkdir -p "$install_root"
+ zip_path="$(mktemp "$temporary_file_template")"
+ say_verbose "Zip path: $zip_path"
+
+
+ # Failures are normal in the non-legacy case for ultimately legacy downloads.
+ # Do not output to stderr, since output to stderr is considered an error.
+ say "Downloading primary link $download_link"
+
+ # The download function will set variables $http_code and $download_error_msg in case of failure.
+ download "$download_link" "$zip_path" 2>&1 || download_failed=true
+
+ # if the download fails, download the legacy_download_link
+ if [ "$download_failed" = true ]; then
+ primary_path_http_code="$http_code"; primary_path_download_error_msg="$download_error_msg"
+ case $primary_path_http_code in
+ 404)
+ say "The resource at $download_link is not available."
+ ;;
+ *)
+ say "$primary_path_download_error_msg"
+ ;;
+ esac
+ rm -f "$zip_path" 2>&1 && say_verbose "Temporary zip file $zip_path was removed"
+ if [ "$valid_legacy_download_link" = true ]; then
+ download_failed=false
+ download_link="$legacy_download_link"
+ zip_path="$(mktemp "$temporary_file_template")"
+ say_verbose "Legacy zip path: $zip_path"
+
+ say "Downloading legacy link $download_link"
+
+ # The download function will set variables $http_code and $download_error_msg in case of failure.
+ download "$download_link" "$zip_path" 2>&1 || download_failed=true
+
+ if [ "$download_failed" = true ]; then
+ legacy_path_http_code="$http_code"; legacy_path_download_error_msg="$download_error_msg"
+ case $legacy_path_http_code in
+ 404)
+ say "The resource at $download_link is not available."
+ ;;
+ *)
+ say "$legacy_path_download_error_msg"
+ ;;
+ esac
+ rm -f "$zip_path" 2>&1 && say_verbose "Temporary zip file $zip_path was removed"
+ fi
+ fi
+ fi
+
+ if [ "$download_failed" = true ]; then
+ if [[ "$primary_path_http_code" = "404" && ( "$valid_legacy_download_link" = false || "$legacy_path_http_code" = "404") ]]; then
+ say_err "Could not find \`$asset_name\` with version = $specific_version"
+ say_err "Refer to: https://aka.ms/dotnet-os-lifecycle for information on .NET Core support"
+ else
+ say_err "Could not download: \`$asset_name\` with version = $specific_version"
+ # 404-NotFound is an expected response if it goes from only one of the links, do not show that error.
+ # If primary path is available (not 404-NotFound) then show the primary error else show the legacy error.
+ if [ "$primary_path_http_code" != "404" ]; then
+ say_err "$primary_path_download_error_msg"
+ return 1
+ fi
+ if [[ "$valid_legacy_download_link" = true && "$legacy_path_http_code" != "404" ]]; then
+ say_err "$legacy_path_download_error_msg"
+ return 1
+ fi
+ fi
+ return 1
+ fi
+
+ say "Extracting zip from $download_link"
+ extract_dotnet_package "$zip_path" "$install_root" || return 1
+
+ # Check if the SDK version is installed; if not, fail the installation.
+ # if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed.
+ if [[ $specific_version == *"rtm"* || $specific_version == *"servicing"* ]]; then
+ IFS='-'
+ read -ra verArr <<< "$specific_version"
+ release_version="${verArr[0]}"
+ unset IFS;
+ say_verbose "Checking installation: version = $release_version"
+ if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$release_version"; then
+ return 0
+ fi
+ fi
+
+ # Check if the standard SDK version is installed.
+ say_verbose "Checking installation: version = $specific_product_version"
+ if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$specific_product_version"; then
+ return 0
+ fi
+
+ # Version verification failed. More likely something is wrong either with the downloaded content or with the verification algorithm.
+ say_err "Failed to verify the version of installed \`$asset_name\`.\nInstallation source: $download_link.\nInstallation location: $install_root.\nReport the bug at https://github.com/dotnet/install-scripts/issues."
+ say_err "\`$asset_name\` with version = $specific_product_version failed to install with an unknown error."
+ return 1
+}
+
+args=("$@")
+
+local_version_file_relative_path="/.version"
+bin_folder_relative_path=""
+temporary_file_template="${TMPDIR:-/tmp}/dotnet.XXXXXXXXX"
+
+channel="LTS"
+version="Latest"
+json_file=""
+install_dir=""
+architecture=""
+dry_run=false
+no_path=false
+no_cdn=false
+azure_feed="https://dotnetcli.azureedge.net/dotnet"
+uncached_feed="https://dotnetcli.blob.core.windows.net/dotnet"
+feed_credential=""
+verbose=false
+runtime=""
+runtime_id=""
+override_non_versioned_files=true
+non_dynamic_parameters=""
+user_defined_os=""
+
+while [ $# -ne 0 ]
+do
+ name="$1"
+ case "$name" in
+ -c|--channel|-[Cc]hannel)
+ shift
+ channel="$1"
+ ;;
+ -v|--version|-[Vv]ersion)
+ shift
+ version="$1"
+ ;;
+ -i|--install-dir|-[Ii]nstall[Dd]ir)
+ shift
+ install_dir="$1"
+ ;;
+ --arch|--architecture|-[Aa]rch|-[Aa]rchitecture)
+ shift
+ architecture="$1"
+ ;;
+ --os|-[Oo][SS])
+ shift
+ user_defined_os="$1"
+ ;;
+ --shared-runtime|-[Ss]hared[Rr]untime)
+ say_warning "The --shared-runtime flag is obsolete and may be removed in a future version of this script. The recommended usage is to specify '--runtime dotnet'."
+ if [ -z "$runtime" ]; then
+ runtime="dotnet"
+ fi
+ ;;
+ --runtime|-[Rr]untime)
+ shift
+ runtime="$1"
+ if [[ "$runtime" != "dotnet" ]] && [[ "$runtime" != "aspnetcore" ]]; then
+ say_err "Unsupported value for --runtime: '$1'. Valid values are 'dotnet' and 'aspnetcore'."
+ if [[ "$runtime" == "windowsdesktop" ]]; then
+ say_err "WindowsDesktop archives are manufactured for Windows platforms only."
+ fi
+ exit 1
+ fi
+ ;;
+ --dry-run|-[Dd]ry[Rr]un)
+ dry_run=true
+ ;;
+ --no-path|-[Nn]o[Pp]ath)
+ no_path=true
+ non_dynamic_parameters+=" $name"
+ ;;
+ --verbose|-[Vv]erbose)
+ verbose=true
+ non_dynamic_parameters+=" $name"
+ ;;
+ --no-cdn|-[Nn]o[Cc]dn)
+ no_cdn=true
+ non_dynamic_parameters+=" $name"
+ ;;
+ --azure-feed|-[Aa]zure[Ff]eed)
+ shift
+ azure_feed="$1"
+ non_dynamic_parameters+=" $name "\""$1"\"""
+ ;;
+ --uncached-feed|-[Uu]ncached[Ff]eed)
+ shift
+ uncached_feed="$1"
+ non_dynamic_parameters+=" $name "\""$1"\"""
+ ;;
+ --feed-credential|-[Ff]eed[Cc]redential)
+ shift
+ feed_credential="$1"
+ non_dynamic_parameters+=" $name "\""$1"\"""
+ ;;
+ --runtime-id|-[Rr]untime[Ii]d)
+ shift
+ runtime_id="$1"
+ non_dynamic_parameters+=" $name "\""$1"\"""
+ say_warning "Use of --runtime-id is obsolete and should be limited to the versions below 2.1. To override architecture, use --architecture option instead. To override OS, use --os option instead."
+ ;;
+ --jsonfile|-[Jj][Ss]on[Ff]ile)
+ shift
+ json_file="$1"
+ ;;
+ --skip-non-versioned-files|-[Ss]kip[Nn]on[Vv]ersioned[Ff]iles)
+ override_non_versioned_files=false
+ non_dynamic_parameters+=" $name"
+ ;;
+ -?|--?|-h|--help|-[Hh]elp)
+ script_name="$(basename "$0")"
+ echo ".NET Tools Installer"
+ echo "Usage: $script_name [-c|--channel ] [-v|--version ] [-p|--prefix ]"
+ echo " $script_name -h|-?|--help"
+ echo ""
+ echo "$script_name is a simple command line interface for obtaining dotnet cli."
+ echo ""
+ echo "Options:"
+ echo " -c,--channel Download from the channel specified, Defaults to \`$channel\`."
+ echo " -Channel"
+ echo " Possible values:"
+ echo " - Current - most current release"
+ echo " - LTS - most current supported release"
+ echo " - 2-part version in a format A.B - represents a specific release"
+ echo " examples: 2.0; 1.0"
+ echo " - Branch name"
+ echo " examples: release/2.0.0; Master"
+ echo " Note: The version parameter overrides the channel parameter."
+ echo " -v,--version Use specific VERSION, Defaults to \`$version\`."
+ echo " -Version"
+ echo " Possible values:"
+ echo " - latest - most latest build on specific channel"
+ echo " - 3-part version in a format A.B.C - represents specific version of build"
+ echo " examples: 2.0.0-preview2-006120; 1.1.0"
+ echo " -i,--install-dir Install under specified location (see Install Location below)"
+ echo " -InstallDir"
+ echo " --architecture Architecture of dotnet binaries to be installed, Defaults to \`$architecture\`."
+ echo " --arch,-Architecture,-Arch"
+ echo " Possible values: x64, arm, and arm64"
+ echo " --os Specifies operating system to be used when selecting the installer."
+ echo " Overrides the OS determination approach used by the script. Supported values: osx, linux, linux-musl, freebsd, rhel.6."
+ echo " In case any other value is provided, the platform will be determined by the script based on machine configuration."
+ echo " Not supported for legacy links. Use --runtime-id to specify platform for legacy links."
+ echo " Refer to: https://aka.ms/dotnet-os-lifecycle for more information."
+ echo " --runtime Installs a shared runtime only, without the SDK."
+ echo " -Runtime"
+ echo " Possible values:"
+ echo " - dotnet - the Microsoft.NETCore.App shared runtime"
+ echo " - aspnetcore - the Microsoft.AspNetCore.App shared runtime"
+ echo " --dry-run,-DryRun Do not perform installation. Display download link."
+ echo " --no-path, -NoPath Do not set PATH for the current process."
+ echo " --verbose,-Verbose Display diagnostics information."
+ echo " --azure-feed,-AzureFeed Azure feed location. Defaults to $azure_feed, This parameter typically is not changed by the user."
+ echo " --uncached-feed,-UncachedFeed Uncached feed location. This parameter typically is not changed by the user."
+ echo " --feed-credential,-FeedCredential Azure feed shared access token. This parameter typically is not specified."
+ echo " --skip-non-versioned-files Skips non-versioned files if they already exist, such as the dotnet executable."
+ echo " -SkipNonVersionedFiles"
+ echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly."
+ echo " --jsonfile Determines the SDK version from a user specified global.json file."
+ echo " Note: global.json must have a value for 'SDK:Version'"
+ echo " -?,--?,-h,--help,-Help Shows this help message"
+ echo ""
+ echo "Obsolete parameters:"
+ echo " --shared-runtime The recommended alternative is '--runtime dotnet'."
+ echo " This parameter is obsolete and may be removed in a future version of this script."
+ echo " Installs just the shared runtime bits, not the entire SDK."
+ echo " --runtime-id Installs the .NET Tools for the given platform (use linux-x64 for portable linux)."
+ echo " -RuntimeId" The parameter is obsolete and may be removed in a future version of this script. Should be used only for versions below 2.1.
+ echo " For primary links to override OS or/and architecture, use --os and --architecture option instead."
+ echo ""
+ echo "Install Location:"
+ echo " Location is chosen in following order:"
+ echo " - --install-dir option"
+ echo " - Environmental variable DOTNET_INSTALL_DIR"
+ echo " - $HOME/.dotnet"
+ exit 0
+ ;;
+ *)
+ say_err "Unknown argument \`$name\`"
+ exit 1
+ ;;
+ esac
+
+ shift
+done
+
+if [ "$no_cdn" = true ]; then
+ azure_feed="$uncached_feed"
+fi
+
+say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:"
+say "- The SDK needs to be installed without user interaction and without admin rights."
+say "- The SDK installation doesn't need to persist across multiple CI runs."
+say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.\n"
+
+check_min_reqs
+calculate_vars
+script_name=$(basename "$0")
+
+if [ "$dry_run" = true ]; then
+ say "Payload URLs:"
+ say "Primary named payload URL: $download_link"
+ if [ "$valid_legacy_download_link" = true ]; then
+ say "Legacy named payload URL: $legacy_download_link"
+ fi
+ repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\"""
+ if [[ "$runtime" == "dotnet" ]]; then
+ repeatable_command+=" --runtime "\""dotnet"\"""
+ elif [[ "$runtime" == "aspnetcore" ]]; then
+ repeatable_command+=" --runtime "\""aspnetcore"\"""
+ fi
+ repeatable_command+="$non_dynamic_parameters"
+ say "Repeatable invocation: $repeatable_command"
+ exit 0
+fi
+
+install_dotnet
+
+bin_path="$(get_absolute_path "$(combine_paths "$install_root" "$bin_folder_relative_path")")"
+if [ "$no_path" = false ]; then
+ say "Adding to current process PATH: \`$bin_path\`. Note: This change will be visible only when sourcing script."
+ export PATH="$bin_path":"$PATH"
+else
+ say "Binaries of dotnet can be found in $bin_path"
+fi
+
+say "Note that the script does not resolve dependencies during installation."
+say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section."
+say "Installation finished successfully."
diff --git a/prometheus-net.DotNetRuntime.sln b/prometheus-net.DotNetRuntime.sln
index cc4796a..9ee729e 100644
--- a/prometheus-net.DotNetRuntime.sln
+++ b/prometheus-net.DotNetRuntime.sln
@@ -1,48 +1,62 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "prometheus-net.DotNetRuntime", "src\prometheus-net.DotNetRuntime\prometheus-net.DotNetRuntime.csproj", "{A40AD08A-53CB-40F3-A6D8-6FFCEC024289}"
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31205.134
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "prometheus-net.DotNetRuntime", "src\prometheus-net.DotNetRuntime\prometheus-net.DotNetRuntime.csproj", "{A40AD08A-53CB-40F3-A6D8-6FFCEC024289}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "prometheus-net.DotNetRuntime.Tests", "src\prometheus-net.DotNetRuntime.Tests\prometheus-net.DotNetRuntime.Tests.csproj", "{7F4E2E72-5745-4312-B238-CD7B731957B0}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "prometheus-net.DotNetRuntime.Tests", "src\prometheus-net.DotNetRuntime.Tests\prometheus-net.DotNetRuntime.Tests.csproj", "{7F4E2E72-5745-4312-B238-CD7B731957B0}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{31AD912F-A1DC-434A-8C8D-049F4BBD67D4}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCoreExample", "examples\AspNetCoreExample\AspNetCoreExample.csproj", "{D01E9ED3-E35C-4F44-A5AD-5350E43AA636}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCoreExample", "examples\AspNetCoreExample\AspNetCoreExample.csproj", "{D01E9ED3-E35C-4F44-A5AD-5350E43AA636}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "src\Benchmarks\Benchmarks.csproj", "{DD607E45-45AD-4F9D-9102-82BD99E49BEC}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Benchmarks", "src\Benchmarks\Benchmarks.csproj", "{DD607E45-45AD-4F9D-9102-82BD99E49BEC}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{D8594A14-5AC8-40F3-B346-38A266B235E0}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocsGenerator", "tools\DocsGenerator\DocsGenerator.csproj", "{193B461A-49E4-4178-B88C-BA0EF6B4FC55}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DocsGenerator", "tools\DocsGenerator\DocsGenerator.csproj", "{193B461A-49E4-4178-B88C-BA0EF6B4FC55}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1A25FB96-03CD-4D50-B46A-8E3A44E7520E}"
+ ProjectSection(SolutionItems) = preProject
+ examples\docker-compose.yml = examples\docker-compose.yml
+ EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Release|Any CPU = Release|Any CPU
Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Release|Any CPU.Build.0 = Release|Any CPU
- {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Release|Any CPU.Build.0 = Release|Any CPU
- {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Release|Any CPU.Build.0 = Release|Any CPU
{A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7F4E2E72-5745-4312-B238-CD7B731957B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7F4E2E72-5745-4312-B238-CD7B731957B0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A40AD08A-53CB-40F3-A6D8-6FFCEC024289}.Release|Any CPU.Build.0 = Release|Any CPU
{7F4E2E72-5745-4312-B238-CD7B731957B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7F4E2E72-5745-4312-B238-CD7B731957B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7F4E2E72-5745-4312-B238-CD7B731957B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7F4E2E72-5745-4312-B238-CD7B731957B0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D01E9ED3-E35C-4F44-A5AD-5350E43AA636}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DD607E45-45AD-4F9D-9102-82BD99E49BEC}.Release|Any CPU.Build.0 = Release|Any CPU
{193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {193B461A-49E4-4178-B88C-BA0EF6B4FC55}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{D01E9ED3-E35C-4F44-A5AD-5350E43AA636} = {31AD912F-A1DC-434A-8C8D-049F4BBD67D4}
{193B461A-49E4-4178-B88C-BA0EF6B4FC55} = {D8594A14-5AC8-40F3-B346-38A266B235E0}
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {542EF13B-659B-4462-9961-99DCC8AF669F}
+ EndGlobalSection
EndGlobal
diff --git a/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/KestrelTests.cs b/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/KestrelTests.cs
new file mode 100644
index 0000000..c32d08c
--- /dev/null
+++ b/src/prometheus-net.DotNetRuntime.Tests/IntegrationTests/KestrelTests.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using NUnit.Framework;
+using Prometheus.DotNetRuntime.EventListening.Parsers;
+using Prometheus.DotNetRuntime.Metrics.Producers;
+
+namespace Prometheus.DotNetRuntime.Tests.IntegrationTests
+{
+ //[TestFixture]
+ //internal class Given_Contention_Events_Are_Enabled_For_Kestrel_Stats
+ //{
+ // [Test]
+ // public void Will_measure_no_contention_on_an_uncontested_lock()
+ // {
+ // var kestrelInfo = new EventConsumer();
+ // var blah = new KestrelMetricsProducer(kestrelInfo, new EventConsumer());
+
+ // kestrelInfo.Events.ConnectionStart =>
+
+ // // assert
+ // Assert.That(MetricProducer.ConnectionTotal.Value, Is.EqualTo(1));
+ // Assert.That(MetricProducer.CurrentConnectionCount.Value, Is.EqualTo(0));
+ // }
+ //}
+}
\ No newline at end of file
diff --git a/src/prometheus-net.DotNetRuntime/DotNetRuntimeStatsBuilder.cs b/src/prometheus-net.DotNetRuntime/DotNetRuntimeStatsBuilder.cs
index 76c8f4b..e34dd2b 100644
--- a/src/prometheus-net.DotNetRuntime/DotNetRuntimeStatsBuilder.cs
+++ b/src/prometheus-net.DotNetRuntime/DotNetRuntimeStatsBuilder.cs
@@ -28,6 +28,7 @@ public static Builder Default()
.WithContentionStats()
.WithThreadPoolStats()
.WithGcStats()
+ .WithKestrelStats()
.WithJitStats()
.WithExceptionStats();
}
@@ -103,6 +104,31 @@ public Builder WithThreadPoolStats(CaptureLevel level = CaptureLevel.Counters, T
return this;
}
+ ///
+ /// Include metrics around the kestrel stats
+ ///
+ ///
+ ///
+ /// The sampling rate for contention events (defaults to 100%). A lower sampling rate reduces memory use
+ /// but reduces the accuracy of metrics produced (as a percentage of events are discarded).
+ ///
+ public Builder WithKestrelStats(CaptureLevel level = CaptureLevel.Counters, SampleEvery sampleRate = SampleEvery.TwoEvents)
+ {
+ try
+ {
+ if (level != CaptureLevel.Counters)
+ ListenerRegistrations.AddOrReplace(ListenerRegistration.Create(CaptureLevel.Informational, sp => new KestrelEventParser(sampleRate)));
+ }
+ catch (UnsupportedEventParserLevelException ex)
+ {
+ throw UnsupportedCaptureLevelException.CreateWithCounterSupport(ex);
+ }
+
+ _services.TryAddSingletonEnumerable();
+
+ return this;
+ }
+
///
/// Include metrics around volume of locks contended.
///
diff --git a/src/prometheus-net.DotNetRuntime/EventListening/Parsers/ContentionEventParser.cs b/src/prometheus-net.DotNetRuntime/EventListening/Parsers/ContentionEventParser.cs
index c9e710c..dfdb9c5 100644
--- a/src/prometheus-net.DotNetRuntime/EventListening/Parsers/ContentionEventParser.cs
+++ b/src/prometheus-net.DotNetRuntime/EventListening/Parsers/ContentionEventParser.cs
@@ -53,8 +53,6 @@ public interface Info : IInfoEvents
event Action ContentionEnd;
}
-
-
public class ContentionStartEvent
{
public static readonly ContentionStartEvent Instance = new();
diff --git a/src/prometheus-net.DotNetRuntime/EventListening/Parsers/KestrelEventParser.cs b/src/prometheus-net.DotNetRuntime/EventListening/Parsers/KestrelEventParser.cs
new file mode 100644
index 0000000..def7b5d
--- /dev/null
+++ b/src/prometheus-net.DotNetRuntime/EventListening/Parsers/KestrelEventParser.cs
@@ -0,0 +1,220 @@
+using System;
+using System.Diagnostics.Tracing;
+using Prometheus.DotNetRuntime.EventListening.EventSources;
+using Prometheus.DotNetRuntime.EventListening.Parsers.Util;
+
+namespace Prometheus.DotNetRuntime.EventListening.Parsers
+{
+ public class KestrelEventParser : IEventParser, KestrelEventParser.Events.Info
+ {
+ private readonly SamplingRate _samplingRate;
+ private const int
+ EventIdConnectionStart = 1,
+ EventIdConnectionStop = 2,
+ EventIdRequestStart = 3,
+ EventIdRequestStop = 4,
+ EventIdConnectionRejected = 5,
+ EventIdTlsHandshakeStart = 8,
+ EventIdTlsHandshakeStop = 9,
+ EventIdTlsHandshakeFailed = 10;
+ // TODO: add eventPairTimer for Tls events
+ private readonly EventPairTimer _eventPairTimerConnections;
+ private readonly EventPairTimer _eventPairTimerRequests;
+
+ public event Action ConnectionStart;
+ public event Action ConnectionStop;
+ public event Action RequestStart;
+ public event Action RequestStop;
+ public event Action ConnectionRejected;
+
+ public Guid EventSourceGuid => KestrelEventSource.Id;
+ public EventKeywords Keywords => EventKeywords.None;
+
+ public KestrelEventParser(SamplingRate samplingRate)
+ {
+ _samplingRate = samplingRate;
+ _eventPairTimerConnections = new EventPairTimer(
+ EventIdConnectionStart,
+ EventIdConnectionStop,
+ x => x.OSThreadId,
+ samplingRate
+ );
+ _eventPairTimerRequests = new EventPairTimer(
+ EventIdRequestStart,
+ EventIdRequestStop,
+ x => x.OSThreadId,
+ samplingRate
+ );
+ }
+
+ public void ProcessEvent(EventWrittenEventArgs e)
+ {
+ switch (_eventPairTimerConnections.TryGetDuration(e, out var duration1))
+ {
+ case DurationResult.Start:
+ ConnectionStart?.Invoke(Events.ConnectionStartEvent.Instance);
+ break;
+
+ case DurationResult.FinalWithDuration:
+ ConnectionStop?.InvokeManyTimes(_samplingRate.SampleEvery, Events.ConnectionStopEvent.GetFrom(duration1));
+ break;
+
+ default:
+ break;
+ }
+
+ switch (_eventPairTimerRequests.TryGetDuration(e, out var duration2))
+ {
+ case DurationResult.Start:
+ ConnectionStart?.Invoke(Events.ConnectionStartEvent.Instance);
+ break;
+
+ case DurationResult.FinalWithDuration:
+ ConnectionStop?.InvokeManyTimes(_samplingRate.SampleEvery, Events.ConnectionStopEvent.GetFrom(duration2));
+ break;
+
+ default:
+ break;
+ }
+
+ if (e.EventId == EventIdConnectionStart)
+ {
+ ConnectionStart?.Invoke(Events.ConnectionStartEvent.ParseFrom(e));
+ return;
+ }
+
+ if (e.EventId == EventIdConnectionStop)
+ {
+ ConnectionStop?.Invoke(Events.ConnectionStopEvent.ParseFrom(e));
+ return;
+ }
+
+ if (e.EventId == EventIdRequestStart)
+ {
+ RequestStart?.Invoke(Events.RequestStartEvent.ParseFrom(e));
+ return;
+ }
+
+ if (e.EventId == EventIdRequestStop)
+ {
+ RequestStop?.Invoke(Events.RequestStopEvent.ParseFrom(e));
+ return;
+ }
+
+ if (e.EventId == EventIdConnectionRejected)
+ {
+ ConnectionRejected?.Invoke(Events.ConnectionRejectedEvent.ParseFrom(e));
+ return;
+ }
+ }
+
+ public static class Events
+ {
+ public interface Info : IInfoEvents
+ {
+ event Action ConnectionStart;
+ event Action ConnectionStop;
+ event Action RequestStart;
+ event Action RequestStop;
+ event Action ConnectionRejected;
+ }
+
+ public class ConnectionStartEvent
+ {
+ public static readonly ConnectionStartEvent Instance = new();
+ private ConnectionStartEvent() { }
+
+ //public string ConnectionId { get; private set; }
+ //public string LocalEndPoint { get; private set; }
+ //public string RemoteEndPoint { get; private set; }
+
+ public static ConnectionStartEvent ParseFrom(EventWrittenEventArgs e)
+ {
+ //Instance.ConnectionId = (string)e.Payload[0];
+ //Instance.LocalEndPoint = (string)e.Payload[1];
+ //Instance.RemoteEndPoint = (string)e.Payload[2];
+ return Instance;
+ }
+
+ }
+
+ public class ConnectionStopEvent
+ {
+ private static readonly ConnectionStopEvent Instance = new();
+
+ private ConnectionStopEvent()
+ {
+ }
+
+ //public string ConnectionId { get; private set; }
+ //public string LocalEndPoint { get; private set; }
+ //public string RemoteEndPoint { get; private set; }
+
+ //public static ConnectionStopEvent ParseFrom(EventWrittenEventArgs e)
+ //{
+ // Instance.ConnectionId = (string)e.Payload[0];
+ // Instance.LocalEndPoint = (string)e.Payload[1];
+ // Instance.RemoteEndPoint = (string)e.Payload[2];
+ // return Instance;
+ //}
+
+ public TimeSpan ConnectionDuration { get; private set; }
+
+ public static ConnectionStopEvent GetFrom(TimeSpan connectionDuration)
+ {
+ Instance.ConnectionDuration = connectionDuration;
+ return Instance;
+ }
+
+ public static ConnectionStopEvent ParseFrom(EventWrittenEventArgs e)
+ {
+ //Instance.ConnectionId = (string)e.Payload[0];
+ //Instance.LocalEndPoint = (string)e.Payload[1];
+ //Instance.RemoteEndPoint = (string)e.Payload[2];
+ return Instance;
+ }
+ }
+
+ public class RequestStartEvent
+ {
+ private static readonly RequestStartEvent Instance = new();
+ private RequestStartEvent() { }
+
+ public static RequestStartEvent ParseFrom(EventWrittenEventArgs e)
+ {
+ return Instance;
+ }
+ }
+
+ public class RequestStopEvent
+ {
+ private static readonly RequestStopEvent Instance = new();
+ private RequestStopEvent() { }
+
+ public TimeSpan RequestDuration { get; private set; }
+
+ public static RequestStopEvent GetFrom(TimeSpan requestDuration)
+ {
+ Instance.RequestDuration = requestDuration;
+ return Instance;
+ }
+
+ public static RequestStopEvent ParseFrom(EventWrittenEventArgs e)
+ {
+ return Instance;
+ }
+ }
+
+ public class ConnectionRejectedEvent
+ {
+ private static readonly ConnectionRejectedEvent Instance = new();
+ private ConnectionRejectedEvent() { }
+
+ public static ConnectionRejectedEvent ParseFrom(EventWrittenEventArgs e)
+ {
+ return Instance;
+ }
+ }
+ }
+ }
+}
diff --git a/src/prometheus-net.DotNetRuntime/EventListening/Parsers/RuntimeEventParser.cs b/src/prometheus-net.DotNetRuntime/EventListening/Parsers/RuntimeEventParser.cs
index 0e89a4f..5006e0e 100644
--- a/src/prometheus-net.DotNetRuntime/EventListening/Parsers/RuntimeEventParser.cs
+++ b/src/prometheus-net.DotNetRuntime/EventListening/Parsers/RuntimeEventParser.cs
@@ -12,13 +12,13 @@ public class RuntimeEventParser : EventCounterParserBase,
{
#pragma warning disable CS0067
[CounterName("threadpool-thread-count")]
- public event Action? ThreadPoolThreadCount;
+ public event Action? ConnectionCount;
[CounterName("threadpool-queue-length")]
public event Action? ThreadPoolQueueLength;
[CounterName("threadpool-completed-items-count")]
- public event Action? ThreadPoolCompletedItemsCount;
+ public event Action? ConnectionCompletedItemsCount;
[CounterName("monitor-lock-contention-count")]
public event Action? MonitorLockContentionCount;
@@ -69,9 +69,9 @@ public static class Events
{
public interface CountersV3_0 : ICounterEvents
{
- event Action ThreadPoolThreadCount;
+ event Action ConnectionCount;
event Action ThreadPoolQueueLength;
- event Action ThreadPoolCompletedItemsCount;
+ event Action ConnectionCompletedItemsCount;
event Action MonitorLockContentionCount;
event Action ActiveTimerCount;
event Action ExceptionCount;
diff --git a/src/prometheus-net.DotNetRuntime/EventListening/Sources/KestrelEventSource.cs b/src/prometheus-net.DotNetRuntime/EventListening/Sources/KestrelEventSource.cs
new file mode 100644
index 0000000..b33ebce
--- /dev/null
+++ b/src/prometheus-net.DotNetRuntime/EventListening/Sources/KestrelEventSource.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Prometheus.DotNetRuntime.EventListening.EventSources
+{
+ ///
+ /// Provider name: Microsoft-AspNetCore-Server-Kestrel. Provides events generated by the .NET runtime (unmanaged code).
+ ///
+ public class KestrelEventSource
+ {
+ public static readonly Guid Id = Guid.Parse("bdeb4676-a36e-5442-db99-4764e2326c7d");
+ }
+}
\ No newline at end of file
diff --git a/src/prometheus-net.DotNetRuntime/Metrics/Producers/KestrelMetricsProducer.cs b/src/prometheus-net.DotNetRuntime/Metrics/Producers/KestrelMetricsProducer.cs
new file mode 100644
index 0000000..4aa659c
--- /dev/null
+++ b/src/prometheus-net.DotNetRuntime/Metrics/Producers/KestrelMetricsProducer.cs
@@ -0,0 +1,61 @@
+using Prometheus.DotNetRuntime.EventListening.EventSources;
+using Prometheus.DotNetRuntime.EventListening.Parsers;
+using Prometheus.DotNetRuntime.Metrics.Producers.Util;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace Prometheus.DotNetRuntime.Metrics.Producers
+{
+ public class KestrelMetricsProducer : IMetricProducer
+ {
+ private readonly Consumes _kestrelInfo;
+ private readonly Consumes _runtimeCounters;
+
+ public KestrelMetricsProducer(Consumes kestrelInfo, Consumes runtimeCounters)
+ {
+ _kestrelInfo = kestrelInfo;
+ _runtimeCounters = runtimeCounters;
+ }
+
+ internal Counter ConnectionSecondsTotal { get; private set; }
+ internal Counter ConnectionTotal { get; private set; }
+ internal Gauge CurrentConnectionCount { get; private set; }
+ internal Counter RequestDurationSeconds { get; private set; }
+ internal Counter RequestTotal { get; private set; }
+ internal Gauge CurrentRequestCount { get; private set; }
+ internal Counter RequestRejectedTotal { get; private set; }
+ // TODO turn some of the totals into a histogram instead?
+ internal Histogram QueueLength { get; private set; }
+
+ public void RegisterMetrics(MetricFactory metrics)
+ {
+ if (!_kestrelInfo.Enabled && !_runtimeCounters.Enabled)
+ return;
+
+ ConnectionTotal = metrics.CreateCounter("dotnet_kestrel_total_connections", "The total number of kestrel connections");
+ CurrentConnectionCount = metrics.CreateGauge("dotnet_kestrel_current_connections",
+ "The current number of kestrel connections");
+ _kestrelInfo.Events.ConnectionStart += e => ConnectionTotal.Inc();
+ _kestrelInfo.Events.ConnectionStart += e => CurrentConnectionCount.Inc();
+
+ ConnectionSecondsTotal = metrics.CreateCounter("dotnet_kestrel_connection_seconds_total", "The total amount of time spent on connections");
+ _kestrelInfo.Events.ConnectionStop += e => ConnectionSecondsTotal.Inc(e.ConnectionDuration.TotalSeconds);
+ _kestrelInfo.Events.ConnectionStop += e => CurrentConnectionCount.Dec();
+
+ RequestTotal = metrics.CreateCounter("dotnet_kestrel_total_requests", "The total number of kestrel requests");
+ CurrentRequestCount = metrics.CreateGauge("dotnet_kestrel_current_requests",
+ "The current number of kestrel requests");
+ _kestrelInfo.Events.RequestStart += e => RequestTotal.Inc();
+ _kestrelInfo.Events.RequestStart += e => CurrentRequestCount.Inc();
+
+ RequestDurationSeconds = metrics.CreateCounter("dotnet_kestrel_request_seconds_total", "The total amount of time spent connected for requests");
+ _kestrelInfo.Events.RequestStop += e => RequestDurationSeconds.Inc(e.RequestDuration.TotalSeconds);
+ _kestrelInfo.Events.RequestStop += e => CurrentRequestCount.Dec();
+
+ RequestRejectedTotal = metrics.CreateCounter("dotnet_kestrel_requests_rejected_total", "The total amount of requests rejected");
+ _kestrelInfo.Events.ConnectionRejected += e => RequestRejectedTotal.Inc();
+ }
+
+ public void UpdateMetrics() { }
+ }
+}
\ No newline at end of file
diff --git a/src/prometheus-net.DotNetRuntime/Metrics/Producers/ThreadPoolMetricsProducer.cs b/src/prometheus-net.DotNetRuntime/Metrics/Producers/ThreadPoolMetricsProducer.cs
index bbc3e91..4b99f27 100644
--- a/src/prometheus-net.DotNetRuntime/Metrics/Producers/ThreadPoolMetricsProducer.cs
+++ b/src/prometheus-net.DotNetRuntime/Metrics/Producers/ThreadPoolMetricsProducer.cs
@@ -34,10 +34,10 @@ public void RegisterMetrics(MetricFactory metrics)
return;
NumThreads = metrics.CreateGauge("dotnet_threadpool_num_threads", "The number of active threads in the thread pool");
- _runtimeCounters.Events.ThreadPoolThreadCount += e => NumThreads.Set(e.Mean);
+ _runtimeCounters.Events.ConnectionCount += e => NumThreads.Set(e.Mean);
Throughput = metrics.CreateCounter("dotnet_threadpool_throughput_total", "The total number of work items that have finished execution in the thread pool");
- _runtimeCounters.Events.ThreadPoolCompletedItemsCount += e => Throughput.Inc(e.IncrementedBy);
+ _runtimeCounters.Events.ConnectionCompletedItemsCount += e => Throughput.Inc(e.IncrementedBy);
QueueLength = metrics.CreateHistogram("dotnet_threadpool_queue_length",
"Measures the queue length of the thread pool. Values greater than 0 indicate a backlog of work for the threadpool to process.",