From b0e1c1a841d577c83150b82289814a016f33e376 Mon Sep 17 00:00:00 2001 From: ogail Date: Wed, 25 Sep 2013 11:08:03 -0700 Subject: [PATCH] Incorporate HTTP logging in the PowerShell --- .../CloudService/CloudServiceClient.cs | 12 ++- .../Commands.Utilities.csproj | 1 + .../Common/CloudBaseCmdlet.cs | 19 +---- .../Commands.Utilities/Common/CmdletBase.cs | 1 + .../src/Commands.Utilities/Common/General.cs | 83 ++++++++++++++----- .../Common/HttpClientExtensions.cs | 4 +- .../Common/HttpRestCallLogger.cs | 46 ++++++++++ .../Websites/WebsitesClient.cs | 3 +- 8 files changed, 129 insertions(+), 40 deletions(-) create mode 100644 WindowsAzurePowershell/src/Commands.Utilities/Common/HttpRestCallLogger.cs diff --git a/WindowsAzurePowershell/src/Commands.Utilities/CloudService/CloudServiceClient.cs b/WindowsAzurePowershell/src/Commands.Utilities/CloudService/CloudServiceClient.cs index d9fb42d12..44884a99a 100644 --- a/WindowsAzurePowershell/src/Commands.Utilities/CloudService/CloudServiceClient.cs +++ b/WindowsAzurePowershell/src/Commands.Utilities/CloudService/CloudServiceClient.cs @@ -527,15 +527,21 @@ out serviceName ManagementClient = CloudContext.Clients.CreateManagementClient( new CertificateCloudCredentials(subscription.SubscriptionId, subscription.Certificate), - new Uri(subscription.ServiceEndpoint)).WithHandler(new StandardHeadersHandler()); + new Uri(subscription.ServiceEndpoint)) + .WithHandler(new StandardHeadersHandler()) + .WithHandler(new HttpRestCallLogger()); StorageClient = CloudContext.Clients.CreateStorageManagementClient( new CertificateCloudCredentials(subscription.SubscriptionId, subscription.Certificate), - new Uri(subscription.ServiceEndpoint)).WithHandler(new StandardHeadersHandler()); + new Uri(subscription.ServiceEndpoint)) + .WithHandler(new StandardHeadersHandler()) + .WithHandler(new HttpRestCallLogger()); ComputeClient = CloudContext.Clients.CreateComputeManagementClient( new CertificateCloudCredentials(subscription.SubscriptionId, subscription.Certificate), - new Uri(subscription.ServiceEndpoint)).WithHandler(new StandardHeadersHandler()); + new Uri(subscription.ServiceEndpoint)) + .WithHandler(new StandardHeadersHandler()) + .WithHandler(new HttpRestCallLogger()); } internal CloudServiceClient( diff --git a/WindowsAzurePowershell/src/Commands.Utilities/Commands.Utilities.csproj b/WindowsAzurePowershell/src/Commands.Utilities/Commands.Utilities.csproj index fa6bf55cb..3ddb786fd 100644 --- a/WindowsAzurePowershell/src/Commands.Utilities/Commands.Utilities.csproj +++ b/WindowsAzurePowershell/src/Commands.Utilities/Commands.Utilities.csproj @@ -259,6 +259,7 @@ + diff --git a/WindowsAzurePowershell/src/Commands.Utilities/Common/CloudBaseCmdlet.cs b/WindowsAzurePowershell/src/Commands.Utilities/Common/CloudBaseCmdlet.cs index 9536a39c8..9f158b88d 100644 --- a/WindowsAzurePowershell/src/Commands.Utilities/Common/CloudBaseCmdlet.cs +++ b/WindowsAzurePowershell/src/Commands.Utilities/Common/CloudBaseCmdlet.cs @@ -147,21 +147,10 @@ protected virtual void OnProcessRecord() protected override void ProcessRecord() { - try - { - Validate.ValidateInternetConnection(); - InitChannelCurrentSubscription(); - ExecuteCmdlet(); - OnProcessRecord(); - } - catch (CommunicationException ex) - { - WriteErrorDetails(ex); - } - catch (Exception ex) - { - WriteExceptionError(ex); - } + Validate.ValidateInternetConnection(); + InitChannelCurrentSubscription(); + base.ProcessRecord(); + OnProcessRecord(); } /// diff --git a/WindowsAzurePowershell/src/Commands.Utilities/Common/CmdletBase.cs b/WindowsAzurePowershell/src/Commands.Utilities/Common/CmdletBase.cs index 91a84c0b1..88605a472 100644 --- a/WindowsAzurePowershell/src/Commands.Utilities/Common/CmdletBase.cs +++ b/WindowsAzurePowershell/src/Commands.Utilities/Common/CmdletBase.cs @@ -98,6 +98,7 @@ protected override void ProcessRecord() { WriteExceptionError(ex); } + finally { WriteDebug(HttpRestCallLogger.Flush()); } } /// diff --git a/WindowsAzurePowershell/src/Commands.Utilities/Common/General.cs b/WindowsAzurePowershell/src/Commands.Utilities/Common/General.cs index 469dad9b3..d6b4c80ec 100644 --- a/WindowsAzurePowershell/src/Commands.Utilities/Common/General.cs +++ b/WindowsAzurePowershell/src/Commands.Utilities/Common/General.cs @@ -36,6 +36,7 @@ namespace Microsoft.WindowsAzure.Commands.Utilities.Common using System.Xml.Serialization; using XmlSchema.ServiceConfigurationSchema; using JsonFormatting = Newtonsoft.Json.Formatting; + using System.Net.Http; public static class General { @@ -777,6 +778,43 @@ public static string GetHttpRequestLog(string method, string requestUri, HttpHea return GetHttpRequestLog(method, requestUri, ConvertHttpHeadersToWebHeaderCollection(headers), body); } + public static string GetLog(HttpResponseMessage response) + { + string body = response.Content == null ? string.Empty : FormatString(response.Content.ReadAsStringAsync().Result); + + return GetHttpResponseLog( + response.StatusCode.ToString(), + response.Headers, + body); + } + + public static string GetLog(HttpRequestMessage request) + { + string body = request.Content == null ? string.Empty : FormatString(request.Content.ReadAsStringAsync().Result); + + return GetHttpRequestLog( + request.Method.ToString(), + request.RequestUri.ToString(), + (HttpHeaders)request.Headers, + body); + } + + public static string FormatString(string content) + { + if (IsXml(content)) + { + return TryFormatXml(content); + } + else if (IsJson(content)) + { + return General.TryFormatJson(content); + } + else + { + return content; + } + } + private static WebHeaderCollection ConvertHttpHeadersToWebHeaderCollection(HttpHeaders headers) { WebHeaderCollection webHeaders = new WebHeaderCollection(); @@ -804,21 +842,6 @@ private static string MessageHeadersToString(WebHeaderCollection headers) return result.ToString(); } - [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")] - public static string TryFormatJson(string str) - { - try - { - object parsedJson = JsonConvert.DeserializeObject(str); - return JsonConvert.SerializeObject(parsedJson, JsonFormatting.Indented); - } - catch - { - // can't parse JSON, return the original string - return str; - } - } - public static Encoding GetFileEncoding(string path) { Encoding encoding; @@ -857,18 +880,18 @@ public static Uri CreateHttpsEndpoint(string endpointUri) /// /// Formats the given XML into indented way. /// - /// The input xml string + /// The input xml string /// The formatted xml string - public static string FormatXml(string xml) + public static string TryFormatXml(string content) { try { - XDocument doc = XDocument.Parse(xml); + XDocument doc = XDocument.Parse(content); return doc.ToString(); } catch (Exception) { - return xml; + return content; } } @@ -890,6 +913,28 @@ public static bool IsXml(string content) } } + [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Handling the failure by returning the original string.")] + public static string TryFormatJson(string str) + { + try + { + object parsedJson = JsonConvert.DeserializeObject(str); + return JsonConvert.SerializeObject(parsedJson, JsonFormatting.Indented); + } + catch + { + // can't parse JSON, return the original string + return str; + } + } + + public static bool IsJson(string content) + { + content = content.Trim(); + return content.StartsWith("{") && content.EndsWith("}") + || content.StartsWith("[") && content.EndsWith("]"); + } + public static string GetNonEmptyValue(string oldValue, string newValue) { return string.IsNullOrEmpty(newValue) ? oldValue : newValue; diff --git a/WindowsAzurePowershell/src/Commands.Utilities/Common/HttpClientExtensions.cs b/WindowsAzurePowershell/src/Commands.Utilities/Common/HttpClientExtensions.cs index 2c14f181b..270060d76 100644 --- a/WindowsAzurePowershell/src/Commands.Utilities/Common/HttpClientExtensions.cs +++ b/WindowsAzurePowershell/src/Commands.Utilities/Common/HttpClientExtensions.cs @@ -153,14 +153,14 @@ public static T GetJson(this HttpClient client, string requestUri, Action logger) { client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); - return GetRawBody(client, requestUri, logger, General.FormatXml); + return GetRawBody(client, requestUri, logger, General.TryFormatXml); } public static T GetXml(this HttpClient client, string requestUri, Action logger) where T: class, new() { client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml")); - return GetFormat(client, requestUri, logger, General.FormatXml, General.DeserializeXmlString); + return GetFormat(client, requestUri, logger, General.TryFormatXml, General.DeserializeXmlString); } public static T PostJson( diff --git a/WindowsAzurePowershell/src/Commands.Utilities/Common/HttpRestCallLogger.cs b/WindowsAzurePowershell/src/Commands.Utilities/Common/HttpRestCallLogger.cs new file mode 100644 index 000000000..18711f04c --- /dev/null +++ b/WindowsAzurePowershell/src/Commands.Utilities/Common/HttpRestCallLogger.cs @@ -0,0 +1,46 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Utilities.Common +{ + using System.Net.Http; + using System.Text; + using System.Threading; + + public class HttpRestCallLogger : MessageProcessingHandler + { + private static StringBuilder HttpLog = new StringBuilder(); + + public static string Flush() + { + string logs = HttpLog.ToString(); + HttpLog.Clear(); + + return logs; + } + + protected override HttpResponseMessage ProcessResponse(HttpResponseMessage response, CancellationToken cancellationToken) + { + HttpLog.Append(General.GetLog(response)); + return response; + } + + protected override HttpRequestMessage ProcessRequest(HttpRequestMessage request, CancellationToken cancellationToken) + { + HttpLog.Append(General.GetLog(request)); + return request; + } + } + +} diff --git a/WindowsAzurePowershell/src/Commands.Utilities/Websites/WebsitesClient.cs b/WindowsAzurePowershell/src/Commands.Utilities/Websites/WebsitesClient.cs index e8443a52e..ece78b377 100644 --- a/WindowsAzurePowershell/src/Commands.Utilities/Websites/WebsitesClient.cs +++ b/WindowsAzurePowershell/src/Commands.Utilities/Websites/WebsitesClient.cs @@ -61,7 +61,8 @@ public WebsitesClient(SubscriptionData subscription, Action logger) WebsiteManagementClient = CloudContext.Clients.CreateWebSiteManagementClient(new CertificateCloudCredentials( subscriptionId, Subscription.Certificate), new Uri(Subscription.ServiceEndpoint)) - .WithHandler(new StandardHeadersHandler()); + .WithHandler(new StandardHeadersHandler()) + .WithHandler(new HttpRestCallLogger()); } ///