Skip to content

Commit ce4a48d

Browse files
Set up Application Insights scopes & telemetry (#36)
1 parent 41dea3f commit ce4a48d

12 files changed

+407
-790
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System.Net;
2+
using ByteSync.Functions.Constants;
3+
using Microsoft.ApplicationInsights;
4+
using Microsoft.Azure.Functions.Worker;
5+
using Microsoft.Azure.Functions.Worker.Http;
6+
using Microsoft.Azure.Functions.Worker.Middleware;
7+
using Microsoft.Extensions.Logging;
8+
9+
namespace ByteSync.Functions.Helpers.Middlewares;
10+
11+
public class ErrorHandlingMiddleware : IFunctionsWorkerMiddleware
12+
{
13+
private readonly TelemetryClient _telemetryClient;
14+
private readonly ILogger<ErrorHandlingMiddleware> _logger;
15+
16+
public ErrorHandlingMiddleware(TelemetryClient telemetryClient, ILogger<ErrorHandlingMiddleware> logger)
17+
{
18+
_telemetryClient = telemetryClient;
19+
_logger = logger;
20+
}
21+
22+
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
23+
{
24+
try
25+
{
26+
await next(context);
27+
}
28+
catch (Exception ex)
29+
{
30+
_telemetryClient.TrackException(ex);
31+
_logger.LogError(ex, "An error occurred in function {FunctionName}", context.FunctionDefinition.Name);
32+
33+
var httpRequest = await context.GetHttpRequestDataAsync();
34+
if (httpRequest != null)
35+
{
36+
var response = httpRequest.CreateResponse();
37+
response.StatusCode = HttpStatusCode.InternalServerError;
38+
await response.WriteAsJsonAsync(new { error = ErrorConstants.INTERNAL_SERVER_ERROR }, HttpStatusCode.InternalServerError);
39+
40+
context.GetInvocationResult().Value = response;
41+
return;
42+
}
43+
throw;
44+
}
45+
}
46+
}

src/ByteSync.Functions/Helpers/JwtMiddleware.cs renamed to src/ByteSync.Functions/Helpers/Middlewares/JwtMiddleware.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
using Microsoft.Extensions.Options;
1313
using Microsoft.IdentityModel.Tokens;
1414

15-
namespace ByteSync.Functions.Helpers;
15+
namespace ByteSync.Functions.Helpers.Middlewares;
1616

1717
public class JwtMiddleware : IFunctionsWorkerMiddleware
1818
{
@@ -55,6 +55,8 @@ public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next
5555
var tokenHandler = new JwtSecurityTokenHandler();
5656
var key = Encoding.ASCII.GetBytes(_secret);
5757

58+
bool isTokenChecked = false;
59+
string? clientInstanceId = null;
5860
try
5961
{
6062
var claims = tokenHandler.ValidateToken(token, new TokenValidationParameters
@@ -75,7 +77,7 @@ public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next
7577

7678
if (claims != null)
7779
{
78-
var clientInstanceId = claims.Claims.FirstOrDefault(c => c.Type.Equals(AuthConstants.CLAIM_CLIENT_INSTANCE_ID))?.Value;
80+
clientInstanceId = claims.Claims.FirstOrDefault(c => c.Type.Equals(AuthConstants.CLAIM_CLIENT_INSTANCE_ID))?.Value;
7981
if (clientInstanceId == null)
8082
{
8183
throw new SecurityTokenExpiredException("clientInstanceId is null");
@@ -90,14 +92,27 @@ public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next
9092
context.Items.Add(AuthConstants.FUNCTION_CONTEXT_CLIENT, client);
9193
}
9294

93-
await next(context);
95+
isTokenChecked = true;
9496
}
9597
catch (Exception ex)
9698
{
9799
_logger.LogError(ex, "Error validating token");
98100

99101
await HandleTokenError(context, "Invalid token");
100102
}
103+
104+
if (isTokenChecked)
105+
{
106+
var scopeProperties = new Dictionary<string, object>
107+
{
108+
["ClientInstanceId"] = clientInstanceId!
109+
};
110+
111+
using (_logger.BeginScope(scopeProperties))
112+
{
113+
await next(context);
114+
}
115+
}
101116
}
102117
else
103118
{
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Microsoft.ApplicationInsights;
2+
using Microsoft.ApplicationInsights.DataContracts;
3+
using Microsoft.Azure.Functions.Worker;
4+
using Microsoft.Azure.Functions.Worker.Middleware;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace ByteSync.Functions.Helpers.Middlewares;
8+
9+
public class TelemetryAndLoggingMiddleware : IFunctionsWorkerMiddleware
10+
{
11+
private readonly TelemetryClient _telemetryClient;
12+
private readonly ILogger<TelemetryAndLoggingMiddleware> _logger;
13+
14+
public TelemetryAndLoggingMiddleware(TelemetryClient telemetryClient, ILogger<TelemetryAndLoggingMiddleware> logger)
15+
{
16+
_telemetryClient = telemetryClient;
17+
_logger = logger;
18+
}
19+
20+
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
21+
{
22+
using var operation = _telemetryClient.StartOperation<RequestTelemetry>(context.FunctionDefinition.Name);
23+
24+
var scopeProperties = new Dictionary<string, object>
25+
{
26+
["OperationId"] = operation.Telemetry.Context.Operation.Id,
27+
["FunctionName"] = context.FunctionDefinition.Name
28+
};
29+
30+
if (context.BindingContext.BindingData.TryGetValue("sessionId", out var sessionId))
31+
{
32+
scopeProperties["SessionId"] = sessionId!;
33+
}
34+
35+
if (context.BindingContext.BindingData.TryGetValue("cloudSessionProfileId", out var cloudSessionProfileId))
36+
{
37+
scopeProperties["CloudSessionProfileId"] = cloudSessionProfileId!;
38+
}
39+
40+
using (_logger.BeginScope(scopeProperties))
41+
{
42+
await next(context);
43+
}
44+
}
45+
}

src/ByteSync.Functions/Http/AuthFunction.cs

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,39 @@
11
using System.Net;
22
using ByteSync.Common.Business.Auth;
3-
using ByteSync.Functions.Constants;
43
using ByteSync.Functions.Helpers;
54
using ByteSync.ServerCommon.Interfaces.Services;
65
using Microsoft.AspNetCore.Authorization;
76
using Microsoft.Azure.Functions.Worker;
87
using Microsoft.Azure.Functions.Worker.Http;
9-
using Microsoft.Extensions.Logging;
108

119
namespace ByteSync.Functions.Http;
1210

1311
public class AuthFunction
1412
{
1513
private readonly IAuthService _authService;
16-
private readonly ILogger<AuthFunction> _logger;
1714

18-
public AuthFunction(IAuthService authService, ILoggerFactory loggerFactory)
15+
public AuthFunction(IAuthService authService)
1916
{
2017
_authService = authService;
21-
_logger = loggerFactory.CreateLogger<AuthFunction>();
2218
}
2319

2420
[AllowAnonymous]
2521
[Function("Login")]
2622
public async Task<HttpResponseData> Login([HttpTrigger(AuthorizationLevel.Anonymous, "post", "get", Route = "auth/login")] HttpRequestData req,
2723
FunctionContext executionContext)
2824
{
29-
var response = req.CreateResponse();
30-
try
31-
{
32-
var loginData = await FunctionHelper.DeserializeRequestBody<LoginData>(req);
25+
var loginData = await FunctionHelper.DeserializeRequestBody<LoginData>(req);
3326

34-
var authResult = await _authService.Authenticate(loginData, GetIpAddress(req));
27+
var authResult = await _authService.Authenticate(loginData, ExtractIpAddress(req));
3528

36-
if (authResult.IsSuccess)
37-
{
38-
await response.WriteAsJsonAsync(authResult, HttpStatusCode.OK);
39-
}
40-
else
41-
{
42-
await response.WriteAsJsonAsync(authResult, HttpStatusCode.Unauthorized);
43-
}
29+
var response = req.CreateResponse();
30+
if (authResult.IsSuccess)
31+
{
32+
await response.WriteAsJsonAsync(authResult, HttpStatusCode.OK);
4433
}
45-
catch (Exception ex)
34+
else
4635
{
47-
_logger.LogError(ex, "Error while logging in");
48-
await response.WriteAsJsonAsync(new { error = ErrorConstants.INTERNAL_SERVER_ERROR }, HttpStatusCode.InternalServerError);
36+
await response.WriteAsJsonAsync(authResult, HttpStatusCode.Unauthorized);
4937
}
5038

5139
return response;
@@ -55,41 +43,31 @@ public async Task<HttpResponseData> Login([HttpTrigger(AuthorizationLevel.Anonym
5543
public async Task<HttpResponseData> RefreshTokens([HttpTrigger(AuthorizationLevel.Anonymous, "post", "get", Route = "auth/refreshTokens")] HttpRequestData req,
5644
FunctionContext executionContext)
5745
{
58-
var response = req.CreateResponse();
59-
try
60-
{
61-
var refreshTokensData = await FunctionHelper.DeserializeRequestBody<RefreshTokensData>(req);
46+
var refreshTokensData = await FunctionHelper.DeserializeRequestBody<RefreshTokensData>(req);
6247

63-
var authResult = await _authService.RefreshTokens(refreshTokensData, GetIpAddress(req));
48+
var authResult = await _authService.RefreshTokens(refreshTokensData, ExtractIpAddress(req));
6449

65-
if (authResult.IsSuccess)
66-
{
67-
await response.WriteAsJsonAsync(authResult, HttpStatusCode.OK);
68-
}
69-
else
70-
{
71-
await response.WriteAsJsonAsync(authResult, HttpStatusCode.Unauthorized);
72-
}
50+
var response = req.CreateResponse();
51+
if (authResult.IsSuccess)
52+
{
53+
await response.WriteAsJsonAsync(authResult, HttpStatusCode.OK);
7354
}
74-
catch (Exception ex)
55+
else
7556
{
76-
_logger.LogError(ex, "Error while refreshing tokens");
77-
await response.WriteAsJsonAsync(new { error = ErrorConstants.INTERNAL_SERVER_ERROR }, HttpStatusCode.InternalServerError);
57+
await response.WriteAsJsonAsync(authResult, HttpStatusCode.Unauthorized);
7858
}
7959

8060
return response;
8161
}
8262

83-
private string GetIpAddress(HttpRequestData req)
63+
private string ExtractIpAddress(HttpRequestData req)
8464
{
8565
var headerDictionary = req.Headers.ToDictionary(x => x.Key.ToLower(), x => x.Value, StringComparer.Ordinal);
8666
var key = "x-forwarded-for";
87-
if (headerDictionary.ContainsKey(key))
67+
if (headerDictionary.TryGetValue(key, out var headerValues))
8868
{
89-
IPAddress? ipAddress = null;
90-
var headerValues = headerDictionary[key];
91-
var ipn = headerValues?.FirstOrDefault()?.Split(new char[] { ',' }).FirstOrDefault()?.Split(new char[] { ':' }).FirstOrDefault();
92-
if (IPAddress.TryParse(ipn, out ipAddress))
69+
var ipn = headerValues.FirstOrDefault()?.Split([',']).FirstOrDefault()?.Split([':']).FirstOrDefault();
70+
if (IPAddress.TryParse(ipn, out var ipAddress))
9371
{
9472
var ipAddressString = ipAddress.ToString();
9573
return ipAddressString;

0 commit comments

Comments
 (0)