Last active
November 8, 2025 10:26
-
-
Save christianarg/1f01d574fe480ede43c26ac706f9bdd2 to your computer and use it in GitHub Desktop.
Debug any LLM HTTP Calls in c# / dotnet. Actually Debug any library HTTP calls
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System.Diagnostics; | |
| /// <summary> | |
| /// Sets up a DiagnosticListener to log HTTP requests made by HttpClient. | |
| /// Created it with the help of ChatGPT some time ago. | |
| /// I wouldn't enable in production at all, but it's useful for debugging purposes. | |
| /// | |
| /// Used to debug AI Frameworks HTTP calls since of them don't show exactly what is being sent. | |
| /// | |
| /// Usage: | |
| /// // no filter (any request): | |
| /// HttpDiagnosticListener.Initialize(); | |
| /// | |
| /// // filter by specific URI content: | |
| /// HttpDiagnosticListener.Initialize(filterByUriContent: "generativelanguage.googleapis.com"); | |
| /// </summary> | |
| public static class HttpDiagnosticListener | |
| { | |
| public static void Initialize(string filterByUriContent = "") | |
| { | |
| var listener = new HttpClientLoggingListener(filterByUriContent); | |
| DiagnosticListener.AllListeners.Subscribe(new DiagnosticListenerObserver(listener)); | |
| } | |
| } | |
| public class DiagnosticListenerObserver : IObserver<DiagnosticListener> | |
| { | |
| private readonly IObserver<KeyValuePair<string, object>> _innerObserver; | |
| public DiagnosticListenerObserver(IObserver<KeyValuePair<string, object>> innerObserver) | |
| { | |
| _innerObserver = innerObserver; | |
| } | |
| public void OnNext(DiagnosticListener diagnosticListener) | |
| { | |
| if (diagnosticListener.Name == "HttpHandlerDiagnosticListener") | |
| { | |
| diagnosticListener.Subscribe(_innerObserver); | |
| } | |
| } | |
| public void OnError(Exception error) | |
| { | |
| // Handle errors here, if needed | |
| } | |
| public void OnCompleted() | |
| { | |
| // Handle completion, if needed | |
| } | |
| } | |
| public class HttpClientLoggingListener : IObserver<KeyValuePair<string, object>> | |
| { | |
| private readonly string _filterByUriContent; | |
| public HttpClientLoggingListener() | |
| { | |
| } | |
| public HttpClientLoggingListener(string filterByUriContent) | |
| { | |
| _filterByUriContent = filterByUriContent; | |
| } | |
| public void OnNext(KeyValuePair<string, object> value) | |
| { | |
| if (value.Key == "System.Net.Http.HttpRequestOut.Start") | |
| { | |
| var request = (HttpRequestMessage)value.Value.GetType().GetProperty("Request")?.GetValue(value.Value); | |
| //Console.WriteLine($"Request: {request?.Method} {request?.RequestUri}"); | |
| // && request.RequestUri.ToString().Contains("openai.azure.com") | |
| if (request.Method == HttpMethod.Post) | |
| { | |
| if (!string.IsNullOrEmpty(_filterByUriContent) && !request.RequestUri.ToString().Contains(_filterByUriContent)) | |
| { | |
| return; | |
| } | |
| Console.WriteLine($"POST Request to {request.RequestUri}"); | |
| if (request.Content != null) | |
| { | |
| // Read the body as a string | |
| var contentTask = request.Content.ReadAsStringAsync(); | |
| string requestBody = contentTask.Result; // WARNING! sync-over-async | |
| Console.WriteLine("Request Body:"); | |
| Console.WriteLine(requestBody); | |
| } | |
| else | |
| { | |
| Console.WriteLine("Request Body: <No Content>"); | |
| } | |
| } | |
| } | |
| } | |
| public void OnError(Exception error) { } | |
| public void OnCompleted() { } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment