Skip to content

Commit 82301cd

Browse files
authored
Refactor HTTP logging handler for efficiency (#1649)
Refactored SendAsync to skip logging when not enabled and moved logging logic to a dedicated method. Switched requestId generation to use a cryptographically secure hex string. Improved log level checks to reduce unnecessary work. Enhanced body logging to respect debug level and _logBody flag. Simplified JSON detection and body truncation logic, introducing a constant and StringBuilder for efficiency. Made minor code structure and formatting improvements.
1 parent ef1e6e8 commit 82301cd

1 file changed

Lines changed: 62 additions & 28 deletions

File tree

src/SIPSorcery/sys/HttpLoggingHandler.cs

Lines changed: 62 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
using System.Linq;
2424
using System.Net.Http;
2525
using System.Net.Http.Headers;
26+
using System.Security.Cryptography;
27+
using System.Text;
2628
using System.Threading;
2729
using System.Threading.Tasks;
2830
using Microsoft.Extensions.Logging;
@@ -51,53 +53,71 @@ public HttpLoggingHandler(ILogger<HttpLoggingHandler> logger, bool logBody = fal
5153
_logBody = logBody;
5254
}
5355

54-
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
56+
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
5557
{
56-
// Log request with better formatting
57-
var requestId = Guid.NewGuid().ToString("N").Substring(0, 8); // Short ID for correlation
58+
if (!_logger.IsEnabled(LogLevel.Information))
59+
{
60+
return base.SendAsync(request, cancellationToken);
61+
}
5862

59-
_logger.LogDebug("🚀 HTTP Request [{RequestId}]", requestId);
60-
_logger.LogDebug(" Method: {Method}", request.Method);
61-
_logger.LogDebug(" URL: {RequestUri}", request.RequestUri);
63+
return SendAsyncWithLogging(request, cancellationToken);
64+
}
6265

63-
// Log headers in a more readable format
64-
LogHeaders(" Request Headers:", request.Headers, SensitiveHeaders);
66+
private async Task<HttpResponseMessage> SendAsyncWithLogging(HttpRequestMessage request, CancellationToken cancellationToken)
67+
{
68+
var requestId = RandomNumberGenerator.GetHexString(8);
6569

66-
if (request.Content != null)
70+
var logDebugEnabled = _logger.IsEnabled(LogLevel.Debug);
71+
72+
if (logDebugEnabled)
6773
{
68-
LogHeaders(" Content Headers:", request.Content.Headers, null);
74+
// Log request with better formatting.
75+
_logger.LogDebug("🚀 HTTP Request [{RequestId}]", requestId);
76+
_logger.LogDebug(" Method: {Method}", request.Method);
77+
_logger.LogDebug(" URL: {RequestUri}", request.RequestUri);
78+
79+
// Log headers in a more readable format.
80+
LogHeaders(" Request Headers:", request.Headers, SensitiveHeaders);
6981

70-
if (_logBody)
82+
if (request.Content != null)
7183
{
72-
var requestBody = await request.Content.ReadAsStringAsync();
73-
if (!string.IsNullOrEmpty(requestBody))
84+
LogHeaders(" Content Headers:", request.Content.Headers, null);
85+
86+
if (_logBody)
7487
{
75-
_logger.LogDebug(" Request Body:\n{RequestBody}", FormatBody(requestBody));
88+
var requestBody = await request.Content.ReadAsStringAsync();
89+
if (!string.IsNullOrEmpty(requestBody))
90+
{
91+
_logger.LogDebug(" Request Body:\n{RequestBody}", FormatBody(requestBody));
92+
}
7693
}
7794
}
7895
}
7996

80-
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
97+
var startTimestamp = System.Diagnostics.Stopwatch.GetTimestamp();
8198
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
82-
stopwatch.Stop();
99+
var elapsedMilliseconds = System.Diagnostics.Stopwatch.GetElapsedTime(startTimestamp);
83100

84-
// Log response with better formatting
101+
// Log response with better formatting.
85102
var statusIcon = response.IsSuccessStatusCode ? "✅" : "❌";
86103
_logger.LogInformation("{StatusIcon} HTTP Response [{RequestId}] - {StatusCode} {ReasonPhrase} ({ElapsedMs}ms)",
87-
statusIcon, requestId, (int)response.StatusCode, response.ReasonPhrase, stopwatch.ElapsedMilliseconds);
104+
statusIcon, requestId, (int)response.StatusCode, response.ReasonPhrase, elapsedMilliseconds);
88105

89-
LogHeaders(" Response Headers:", response.Headers, null);
90-
91-
if (response.Content != null)
106+
if (logDebugEnabled)
92107
{
93-
LogHeaders(" Content Headers:", response.Content.Headers, SensitiveHeaders);
108+
LogHeaders(" Response Headers:", response.Headers, null);
94109

95-
if (_logBody)
110+
if (response.Content != null)
96111
{
97-
var responseBody = await response.Content.ReadAsStringAsync();
98-
if (!string.IsNullOrEmpty(responseBody))
112+
LogHeaders(" Content Headers:", response.Content.Headers, SensitiveHeaders);
113+
114+
if (_logBody)
99115
{
100-
_logger.LogDebug(" Response Body:\n{ResponseBody}", FormatBody(responseBody));
116+
var responseBody = await response.Content.ReadAsStringAsync();
117+
if (!string.IsNullOrEmpty(responseBody))
118+
{
119+
_logger.LogDebug(" Response Body:\n{ResponseBody}", FormatBody(responseBody));
120+
}
101121
}
102122
}
103123
}
@@ -135,7 +155,8 @@ private static string FormatBody(string body)
135155
// System.Text.Json that is not present for net462/netstandard2.0. TinyJson
136156
// returns null on parse failure rather than throwing, so an invalid body simply
137157
// falls through to the raw output below.
138-
if (body.TrimStart().StartsWith("{") || body.TrimStart().StartsWith("["))
158+
var trimmedBody = body.AsSpan().TrimStart();
159+
if (!trimmedBody.IsEmpty && trimmedBody[0] is '{' or '[')
139160
{
140161
var parsed = body.FromJson<object>();
141162
if (parsed != null)
@@ -144,7 +165,20 @@ private static string FormatBody(string body)
144165
}
145166
}
146167

168+
const int maxLoggedBodyChars = 500;
147169
// For non-JSON or if JSON parsing failed, add some basic formatting
148-
return body.Length > 500 ? $"{body.Substring(0, 500)}... (truncated, {body.Length} total chars)" : body;
170+
if (body.Length <= maxLoggedBodyChars)
171+
{
172+
return body;
173+
}
174+
175+
var truncatedBody = new StringBuilder(maxLoggedBodyChars + 64);
176+
truncatedBody
177+
.Append(body, 0, maxLoggedBodyChars)
178+
.Append("... (truncated, ")
179+
.Append(body.Length)
180+
.Append(" total chars)");
181+
182+
return truncatedBody.ToString();
149183
}
150184
}

0 commit comments

Comments
 (0)