- Added Diagnostics event Source and various events

- Custom Middleware for host projects to capture API metrics and global exception handlers
This commit is contained in:
ShekharGupta1988 2018-03-19 20:08:22 -07:00
Родитель 80dafaf912
Коммит c4cd9b8d77
14 изменённых файлов: 559 добавлений и 4 удалений

Просмотреть файл

@ -23,6 +23,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C0537983-2503-4D70-B831-27614DB29F81}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Diagnostics.Logger", "src\Diagnostics.Logger\Diagnostics.Logger.csproj", "{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -105,6 +107,18 @@ Global
{CFD34079-6A40-4938-8840-4325B504ACFA}.Release|x64.Build.0 = Release|Any CPU
{CFD34079-6A40-4938-8840-4325B504ACFA}.Release|x86.ActiveCfg = Release|Any CPU
{CFD34079-6A40-4938-8840-4325B504ACFA}.Release|x86.Build.0 = Release|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Debug|x64.ActiveCfg = Debug|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Debug|x64.Build.0 = Debug|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Debug|x86.ActiveCfg = Debug|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Debug|x86.Build.0 = Debug|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Release|Any CPU.Build.0 = Release|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Release|x64.ActiveCfg = Release|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Release|x64.Build.0 = Release|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Release|x86.ActiveCfg = Release|Any CPU
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -116,6 +130,7 @@ Global
{0080F318-4A06-40E3-BCA6-5C61978CBFE6} = {0F59F43E-D06C-4A0F-8D38-9538BA58FC6A}
{F23F3EDA-2CD2-4F7B-B605-13DF4A8A632F} = {0F59F43E-D06C-4A0F-8D38-9538BA58FC6A}
{CFD34079-6A40-4938-8840-4325B504ACFA} = {C0537983-2503-4D70-B831-27614DB29F81}
{9EBCA533-692C-4A6E-9831-AE84C0AFCEF2} = {0F59F43E-D06C-4A0F-8D38-9538BA58FC6A}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B4A19BFB-9A33-49E2-98AD-955E9472EFAD}

Просмотреть файл

@ -18,6 +18,7 @@
<ItemGroup>
<ProjectReference Include="..\Diagnostics.DataProviders\Diagnostics.DataProviders.csproj" />
<ProjectReference Include="..\Diagnostics.Logger\Diagnostics.Logger.csproj" />
<ProjectReference Include="..\Diagnostics.ModelsAndUtils\Diagnostics.ModelsAndUtils.csproj" />
<ProjectReference Include="..\Diagnostics.Scripts\Diagnostics.Scripts.csproj" />
</ItemGroup>

Просмотреть файл

@ -0,0 +1,96 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Diagnostics.Logger;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace Diagnostics.CompilerHost.Middleware
{
public class CompilerRequestMiddleware
{
private readonly RequestDelegate _next;
private readonly string _apiLoggerKey = "CompilerHost_ApiLogger";
public CompilerRequestMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
BeginRequest_Handle(httpContext);
try
{
await _next(httpContext);
}
catch (Exception ex)
{
if (!httpContext.Response.HasStarted)
{
httpContext.Response.Clear();
httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
LogException(httpContext, ex);
}
finally
{
EndRequest_Handle(httpContext);
}
return;
}
private void BeginRequest_Handle(HttpContext httpContext)
{
var logger = new ApiMetricsLogger(httpContext);
httpContext.Items.Add(_apiLoggerKey, logger);
}
private void EndRequest_Handle(HttpContext httpContext)
{
ApiMetricsLogger logger = (ApiMetricsLogger)httpContext.Items[_apiLoggerKey];
if (logger == null)
{
logger = new ApiMetricsLogger(httpContext);
}
logger.LogCompilerHostAPIMetrics(httpContext);
}
private void LogException(HttpContext context, Exception ex)
{
try
{
ApiMetricsLogger logger = (ApiMetricsLogger)context.Items[_apiLoggerKey];
if (logger == null)
{
logger = new ApiMetricsLogger(context);
}
logger.LogCompilerHostUnhandledException(context, ex);
}
catch (Exception logEx)
{
DiagnosticsETWProvider.Instance.LogCompilerHostUnhandledException(
string.Empty,
"LogException_CompilerRequestMiddleware",
logEx.GetType().ToString(),
logEx.ToString());
}
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class CompilerRequestMiddlewareExtensions
{
public static IApplicationBuilder UseCompilerRequestMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<CompilerRequestMiddleware>();
}
}
}

Просмотреть файл

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Diagnostics.CompilerHost.Middleware;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
@ -34,6 +35,7 @@ namespace Diagnostics.CompilerHost
app.UseDeveloperExceptionPage();
}
app.UseCompilerRequestMiddleware();
app.UseMvc();
}
}

Просмотреть файл

@ -0,0 +1,183 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Text;
namespace Diagnostics.Logger
{
public class ApiMetricsLogger
{
private string _requestId;
private string _address;
private string _verb;
private DateTime _startTime;
private DateTime _endTime;
private long _latencyInMs;
private string _subscriptionId;
private string _resourceGroupName;
private string _resourceName;
public ApiMetricsLogger(HttpContext context)
{
StartMetricCapture(context);
}
private void StartMetricCapture(HttpContext context)
{
if (context.Request.Headers.TryGetValue(HeaderConstants.RequestIdHeaderName, out StringValues values))
{
_requestId = values.ToString();
}
_startTime = DateTime.UtcNow;
_verb = context.Request.Method;
_address = context.Request.Path.ToString();
ParseFromAddress(context);
}
private void ParseFromAddress(HttpContext httpContext)
{
try
{
if (String.IsNullOrWhiteSpace(httpContext.Request.Path.ToString()))
{
return;
}
var parts = httpContext.Request.Path.ToString().ToLowerInvariant().Split('?')[0].Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
var cursor = ((IEnumerable<string>)parts).GetEnumerator();
while (cursor.MoveNext())
{
if (String.Equals(cursor.Current, "subscriptions", StringComparison.OrdinalIgnoreCase))
{
break;
}
}
if (!cursor.MoveNext())
{
return;
}
_subscriptionId = cursor.Current;
if (!cursor.MoveNext())
{
return;
}
if (String.Equals(cursor.Current, "resourceGroups", StringComparison.OrdinalIgnoreCase))
{
if (!cursor.MoveNext())
{
return;
}
_resourceGroupName = cursor.Current;
if (!cursor.MoveNext() || !String.Equals(cursor.Current, "providers", StringComparison.OrdinalIgnoreCase))
{
return;
}
if (!cursor.MoveNext() || !String.Equals(cursor.Current, "Microsoft.Web", StringComparison.OrdinalIgnoreCase))
{
return;
}
}
if (cursor.MoveNext() && String.Equals(cursor.Current, "sites", StringComparison.OrdinalIgnoreCase) || String.Equals(cursor.Current, "hostingEnvironments", StringComparison.OrdinalIgnoreCase))
{
if (!cursor.MoveNext())
{
return;
}
_resourceName = cursor.Current;
}
else
{
return;
}
}
catch
{
// ignore error
}
}
public void LogRuntimeHostAPIMetrics(HttpContext context)
{
if (context == null) return;
_endTime = DateTime.UtcNow;
_latencyInMs = Convert.ToInt64((_endTime - _startTime).TotalMilliseconds);
DiagnosticsETWProvider.Instance.LogRuntimeHostAPISummary(
_requestId ?? string.Empty,
_subscriptionId ?? string.Empty,
_resourceGroupName ?? string.Empty,
_resourceName ?? string.Empty,
_address ?? string.Empty,
_verb ?? string.Empty,
string.Empty,
context.Response.StatusCode,
_latencyInMs,
_startTime.ToString("HH:mm:ss.fff"),
_endTime.ToString("HH:mm:ss.fff")
);
}
public void LogCompilerHostAPIMetrics(HttpContext context)
{
if (context == null) return;
_endTime = DateTime.UtcNow;
_latencyInMs = Convert.ToInt64((_endTime - _startTime).TotalMilliseconds);
DiagnosticsETWProvider.Instance.LogCompilerHostAPISummary(
_requestId ?? string.Empty,
_address ?? string.Empty,
_verb ?? string.Empty,
context.Response.StatusCode,
_latencyInMs,
_startTime.ToString("HH:mm:ss.fff"),
_endTime.ToString("HH:mm:ss.fff")
);
}
public void LogRuntimeHostUnhandledException(HttpContext context, Exception ex)
{
if (context == null)
{
return;
}
DiagnosticsETWProvider.Instance.LogRuntimeHostUnhandledException(
_requestId ?? string.Empty,
_address ?? string.Empty,
_subscriptionId ?? string.Empty,
_resourceGroupName ?? string.Empty,
_resourceName ?? string.Empty,
ex != null ? ex.GetType().ToString() : string.Empty,
ex != null ? ex.ToString() : string.Empty
);
}
public void LogCompilerHostUnhandledException(HttpContext context, Exception ex)
{
if (context == null)
{
return;
}
DiagnosticsETWProvider.Instance.LogCompilerHostUnhandledException(
_requestId ?? string.Empty,
_address ?? string.Empty,
ex != null ? ex.GetType().ToString() : string.Empty,
ex != null ? ex.ToString() : string.Empty
);
}
}
}

Просмотреть файл

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Diagnostics.Logger
{
public class HeaderConstants
{
public const string RequestIdHeaderName = "x-ms-request-id";
public const string EtagHeaderName = "ETag";
public const string IfMatchHeaderName = "If-Match";
public const string IfNoneMatchHeaderName = "If-None-Match";
public const string UserAgentHeaderName = "User-Agent";
}
}

Просмотреть файл

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Class1.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Features" Version="2.0.1" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.AspNetCore.Http.Abstractions">
<HintPath>C:\Program Files\dotnet\sdk\NuGetFallbackFolder\microsoft.aspnetcore.http.abstractions\2.0.1\lib\netstandard2.0\Microsoft.AspNetCore.Http.Abstractions.dll</HintPath>
</Reference>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Text;
namespace Diagnostics.Logger
{
[EventSource(Name = "Microsoft-Azure-AppService-Diagnostics")]
public sealed class DiagnosticsETWProvider : DiagnosticsEventSourceBase
{
public static DiagnosticsETWProvider Instance = new DiagnosticsETWProvider();
#region Compile Host Events (ID Range : 1000 - 1999)
[Event(1000, Level = EventLevel.Informational, Channel = EventChannel.Admin)]
public void LogCompilerHostStartup(string Message)
{
WriteDiagnosticsEvent(1000, Message);
}
[Event(1001, Level = EventLevel.Error, Channel = EventChannel.Admin)]
public void LogCompilerHostUnhandledException(string RequestId, string Source, string ExceptionType, string ExceptionDetails)
{
WriteDiagnosticsEvent(1001,
RequestId,
Source,
ExceptionType,
ExceptionDetails);
}
[Event(1002, Level = EventLevel.Informational, Channel = EventChannel.Admin)]
public void LogCompilerHostAPISummary(string RequestId, string Address, string Verb, int StatusCode, long LatencyInMilliseconds, string StartTime, string EndTime)
{
WriteDiagnosticsEvent(1002,
RequestId,
Address,
Verb,
StatusCode,
LatencyInMilliseconds,
StartTime,
EndTime);
}
#endregion
#region Runtime Host Events (ID Range : 2000 - 2999)
[Event(2000, Level = EventLevel.Informational, Channel = EventChannel.Admin)]
public void LogRuntimeHostStartup(string Message)
{
WriteDiagnosticsEvent(2000, Message);
}
[Event(2001, Level = EventLevel.Error, Channel = EventChannel.Admin)]
public void LogRuntimeHostUnhandledException(string RequestId, string Source, string SubscriptionId, string ResourceGroup, string Resource, string ExceptionType, string ExceptionDetails)
{
WriteDiagnosticsEvent(2001,
RequestId,
Source,
SubscriptionId,
ResourceGroup,
Resource,
ExceptionType,
ExceptionDetails);
}
[Event(2002, Level = EventLevel.Informational, Channel = EventChannel.Admin)]
public void LogRuntimeHostAPISummary(string RequestId, string SubscriptionId, string ResourceGroup, string Resource, string Address, string Verb, string OperationName, int StatusCode, long LatencyInMilliseconds, string StartTime, string EndTime)
{
WriteDiagnosticsEvent(2002,
RequestId,
SubscriptionId,
ResourceGroup,
Resource,
Address,
Verb,
OperationName,
StatusCode,
LatencyInMilliseconds,
StartTime,
EndTime);
}
#endregion
#region Data Provider Events (ID Range : 3000 - 3999)
#endregion
}
}

Просмотреть файл

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using System.Text;
namespace Diagnostics.Logger
{
public abstract class DiagnosticsEventSourceBase : EventSource
{
[NonEvent]
protected void WriteDiagnosticsEvent(int eventId, params object[] args)
{
WriteEvent(eventId, args);
}
}
}

Просмотреть файл

@ -23,6 +23,7 @@
<ItemGroup>
<ProjectReference Include="..\Diagnostics.DataProviders\Diagnostics.DataProviders.csproj" />
<ProjectReference Include="..\Diagnostics.Logger\Diagnostics.Logger.csproj" />
<ProjectReference Include="..\Diagnostics.ModelsAndUtils\Diagnostics.ModelsAndUtils.csproj" />
<ProjectReference Include="..\Diagnostics.Scripts\Diagnostics.Scripts.csproj" />
</ItemGroup>

Просмотреть файл

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Diagnostics.Logger;
using Diagnostics.RuntimeHost.Utilities;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
namespace Diagnostics.RuntimeHost.Middleware
{
public class DiagnosticsRequestMiddleware
{
private readonly RequestDelegate _next;
public DiagnosticsRequestMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
GenerateMissingRequestHeaders(ref httpContext);
BeginRequest_Handle(httpContext);
try
{
await _next(httpContext);
}
catch (Exception ex)
{
if (!httpContext.Response.HasStarted)
{
httpContext.Response.Clear();
httpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
LogException(httpContext, ex);
}
finally
{
EndRequest_Handle(httpContext);
}
return;
}
private void BeginRequest_Handle(HttpContext httpContext)
{
var logger = new ApiMetricsLogger(httpContext);
httpContext.Items.Add(HostConstants.ApiLoggerKey, logger);
}
private void EndRequest_Handle(HttpContext httpContext)
{
ApiMetricsLogger logger = (ApiMetricsLogger)httpContext.Items[HostConstants.ApiLoggerKey];
if (logger == null)
{
logger = new ApiMetricsLogger(httpContext);
}
logger.LogRuntimeHostAPIMetrics(httpContext);
}
private void LogException(HttpContext context, Exception ex)
{
try
{
ApiMetricsLogger logger = (ApiMetricsLogger)context.Items[HostConstants.ApiLoggerKey];
if (logger == null)
{
logger = new ApiMetricsLogger(context);
}
logger.LogRuntimeHostUnhandledException(context, ex);
}
catch (Exception logEx)
{
DiagnosticsETWProvider.Instance.LogRuntimeHostUnhandledException(
string.Empty,
"LogException_DiagnosticsRequestMiddleware",
string.Empty,
string.Empty,
string.Empty,
logEx.GetType().ToString(),
logEx.ToString());
}
}
private void GenerateMissingRequestHeaders(ref HttpContext httpContext)
{
if (!httpContext.Request.Headers.TryGetValue(HeaderConstants.RequestIdHeaderName, out StringValues values)
|| values == default(StringValues) || !values.Any() || string.IsNullOrWhiteSpace(values.First()))
{
httpContext.Request.Headers[HeaderConstants.RequestIdHeaderName] = Guid.NewGuid().ToString();
}
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class DiagnosticsRequestMiddlewareExtensions
{
public static IApplicationBuilder UseDiagnosticsRequestMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<DiagnosticsRequestMiddleware>();
}
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using Diagnostics.RuntimeHost.Utilities;
using Diagnostics.Logger;
using Diagnostics.RuntimeHost.Utilities;
using Diagnostics.Scripts;
using Diagnostics.Scripts.Models;
using Microsoft.AspNetCore.Hosting;
@ -26,7 +27,6 @@ namespace Diagnostics.RuntimeHost.Services.SourceWatcher
private string _lastModifiedMarkerName;
private string _deleteMarkerName;
private string _cacheIdFileName;
private string _etagHeaderName;
private string _destinationCsxPath;
private int _pollingIntervalInSeconds;
@ -41,7 +41,6 @@ namespace Diagnostics.RuntimeHost.Services.SourceWatcher
_lastModifiedMarkerName = "_lastModified.marker";
_deleteMarkerName = "_delete.marker";
_cacheIdFileName = "cacheId.txt";
_etagHeaderName = "ETag";
LoadConfigurations();
Start();
}
@ -84,7 +83,7 @@ namespace Diagnostics.RuntimeHost.Services.SourceWatcher
return;
}
string githubRootContentETag = GetHeaderValue(response, _etagHeaderName).Replace("W/", string.Empty);
string githubRootContentETag = GetHeaderValue(response, HeaderConstants.EtagHeaderName).Replace("W/", string.Empty);
GithubEntry[] githubDirectories = await response.Content.ReadAsAsyncCustom<GithubEntry[]>();
foreach (GithubEntry gitHubDir in githubDirectories)

Просмотреть файл

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Diagnostics.DataProviders;
using Diagnostics.RuntimeHost.Middleware;
using Diagnostics.RuntimeHost.Services;
using Diagnostics.RuntimeHost.Services.SourceWatcher;
using Diagnostics.Scripts;
@ -54,6 +55,7 @@ namespace Diagnostics.RuntimeHost
app.UseDeveloperExceptionPage();
}
app.UseDiagnosticsRequestMiddleware();
app.UseMvc();
}
}

Просмотреть файл

@ -8,6 +8,7 @@ namespace Diagnostics.RuntimeHost.Utilities
internal class HostConstants
{
internal const int WatcherDefaultPollingIntervalInSeconds = 5 * 60;
internal const string ApiLoggerKey = "API_LOGGER";
// These Configurations probably should be in Data Providers