/// <summary>
        /// Constructor
        /// </summary>
        /// <param name="storageConnectionString">Storage account connection string</param>
        /// <param name="azureTablePrefix">The repository stores data in two tables called httprequestbycorrelationid and httprequestbydatedescending, if this parameter is not null and not whitespace then it is used as a prefix for those table names.</param>
        /// <param name="granularity">The level of granularity for data in the partition. On a low traffic site hourly or even daily can be useful, whereas busy sites minute or second are more useful.</param>
        public AzureHttpLoggerRepository(string storageConnectionString, string azureTablePrefix, LogByDateGranularityEnum granularity)
        {
            if (string.IsNullOrWhiteSpace(storageConnectionString)) throw new ArgumentNullException(nameof(storageConnectionString));
            if (azureTablePrefix == null)
            {
                azureTablePrefix = "";
            }
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString);
            CloudTableClient client = storageAccount.CreateCloudTableClient();
            _byCorrelationIdTable = client.GetTableReference(string.Concat(azureTablePrefix, RequestByCorrelationIdTableName));
            _byDateTimeDescendingTable = client.GetTableReference(string.Concat(azureTablePrefix, RequestByDateTimeDescendingTableName));

            _byCorrelationIdTable.CreateIfNotExists();
            _byDateTimeDescendingTable.CreateIfNotExists();

            switch (granularity)
            {
                case LogByDateGranularityEnum.Hour:
                    _granularPartitionKeyFormat = "yyyy-MM-dd hh";
                    break;

                case LogByDateGranularityEnum.Day:
                    _granularPartitionKeyFormat = "yyyy-MM-dd";
                    break;

                case LogByDateGranularityEnum.Minute:
                    _granularPartitionKeyFormat = "yyyy-MM-dd hh:mm";
                    break;

                case LogByDateGranularityEnum.Second:
                    _granularPartitionKeyFormat = "yyyy-MM-dd hh:mm:ss";
                    break;
            }
        }
 /// <summary>
 /// In order to capture the full set of data during a HTTP request this OWIN plugin must be the first plugin in the chain.
 ///
 /// Configure OWIN to trace HTTP requests and responses. Note that capturing request query parameters and request and response data could cause
 /// data security issues - potentially sensitive data will be logged to Azure table storage.
 ///
 /// Therefore by default none of this is captured.
 ///
 /// The HTTP traces are stored in these tables:
 ///
 ///     httprequestbydatedescending
 ///     httprequestbycorrelationid
 ///
 /// If this causes you any conflicts then the table names can be given an optional prefix using the azurePrefix parameter.
 ///
 /// Request and response data is stored in blob containers named httprequestdata and httpresponsedata respectively and the azurePrefix
 /// parameter is also applied to these container names. Blob names are linked to the log item ID held within the tables.
 /// </summary>
 /// <param name="appBuilder">The app builder extended</param>
 /// <param name="storageConnectionString">The storage string for </param>
 /// <param name="captureRequestParams">True if you wish to capture query parameters, false if not.</param>
 /// <param name="captureRequestData">True if you wish to capture request data, false if not.</param>
 /// <param name="captureResponseData">True if you wish to capture response data, falise if not.</param>
 /// <param name="captureRequestHeaders">To capture all request headers set a single array element of "*" otherwise specify the headers you wish to capture.</param>
 /// <param name="captureResponseHeaders">To capture all response headers set a single array element of "*" otherwise specify the headers you wish to capture.</param>
 /// <param name="httpCorrelationHeaderKey">
 /// It can be helpful when calling across http boundaries to be able to tie together the flow of events with a correlation ID and by default the logger
 /// looks for a correlation ID in the header correlation-id. If the header is missing then no correlation ID is used but the logger will work. If you
 /// wish to disable this behaviour then set this to null or if you want to use a different header then set the header name here.
 ///
 /// The HttpCorrelator middleware also in this assembly can be used to add a correlation ID if none is present and should be placed before the logger middleware
 /// in the pipeline.
 /// </param>
 /// <param name="azurePrefix">If you need to avoid table name conflicts</param>
 /// <param name="granularity">Sets the level of granularity on the partition key of the httprequestbydatedescending table. Busier your site likely the more granular you want
 /// this to be. Defaults to hourly</param>
 /// <returns></returns>
 public static IAppBuilder UseAzureHttpLogger(this IAppBuilder appBuilder,
                                              string storageConnectionString,
                                              bool captureRequestParams       = false,
                                              bool captureRequestData         = false,
                                              bool captureResponseData        = false,
                                              string[] captureRequestHeaders  = null,
                                              string[] captureResponseHeaders = null,
                                              string httpCorrelationHeaderKey = "correlation-id",
                                              string azurePrefix = "",
                                              LogByDateGranularityEnum granularity = LogByDateGranularityEnum.Hour)
 {
     appBuilder.Use(typeof(HttpLogger),
                    new AzureHttpLoggerRepository(storageConnectionString, azurePrefix, granularity),
                    captureRequestParams,
                    captureRequestData,
                    captureResponseData,
                    captureRequestHeaders,
                    captureResponseHeaders,
                    httpCorrelationHeaderKey);
     return(appBuilder);
 }
 /// <summary>
 /// In order to capture the full set of data during a HTTP request this OWIN plugin must be the first plugin in the chain.
 /// 
 /// Configure OWIN to trace HTTP requests and responses. Note that capturing request query parameters and request and response data could cause
 /// data security issues - potentially sensitive data will be logged to Azure table storage.
 /// 
 /// Therefore by default none of this is captured.
 /// 
 /// The HTTP traces are stored in these tables:
 /// 
 ///     httprequestbydatedescending
 ///     httprequestbycorrelationid
 /// 
 /// If this causes you any conflicts then the table names can be given an optional prefix using the azurePrefix parameter.
 /// 
 /// Request and response data is stored in blob containers named httprequestdata and httpresponsedata respectively and the azurePrefix
 /// parameter is also applied to these container names. Blob names are linked to the log item ID held within the tables.
 /// </summary>
 /// <param name="appBuilder">The app builder extended</param>
 /// <param name="storageConnectionString">The storage string for </param>
 /// <param name="captureRequestParams">True if you wish to capture query parameters, false if not.</param>
 /// <param name="captureRequestData">True if you wish to capture request data, false if not.</param>
 /// <param name="captureResponseData">True if you wish to capture response data, falise if not.</param>
 /// <param name="captureRequestHeaders">To capture all request headers set a single array element of "*" otherwise specify the headers you wish to capture.</param>
 /// <param name="captureResponseHeaders">To capture all response headers set a single array element of "*" otherwise specify the headers you wish to capture.</param>
 /// <param name="httpCorrelationHeaderKey">
 /// It can be helpful when calling across http boundaries to be able to tie together the flow of events with a correlation ID and by default the logger
 /// looks for a correlation ID in the header correlation-id. If the header is missing then no correlation ID is used but the logger will work. If you
 /// wish to disable this behaviour then set this to null or if you want to use a different header then set the header name here.
 /// 
 /// The HttpCorrelator middleware also in this assembly can be used to add a correlation ID if none is present and should be placed before the logger middleware
 /// in the pipeline.
 /// </param>
 /// <param name="azurePrefix">If you need to avoid table name conflicts</param>
 /// <param name="granularity">Sets the level of granularity on the partition key of the httprequestbydatedescending table. Busier your site likely the more granular you want
 /// this to be. Defaults to hourly</param>
 /// <returns></returns>
 public static IAppBuilder UseAzureHttpLogger(this IAppBuilder appBuilder,
     string storageConnectionString,
     bool captureRequestParams = false,
     bool captureRequestData = false,
     bool captureResponseData = false,
     string[] captureRequestHeaders = null,
     string[] captureResponseHeaders = null,
     string httpCorrelationHeaderKey = "correlation-id",
     string azurePrefix = "",
     LogByDateGranularityEnum granularity = LogByDateGranularityEnum.Hour)
 {
     appBuilder.Use(typeof(HttpLogger),
         new AzureHttpLoggerRepository(storageConnectionString, azurePrefix, granularity),
         captureRequestParams,
         captureRequestData,
         captureResponseData,
         captureRequestHeaders,
         captureResponseHeaders,
         httpCorrelationHeaderKey);
     return appBuilder;
 }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="storageConnectionString">Storage account connection string</param>
        /// <param name="azureTablePrefix">The repository stores data in two tables called httprequestbycorrelationid and httprequestbydatedescending, if this parameter is not null and not whitespace then it is used as a prefix for those table names.</param>
        /// <param name="granularity">The level of granularity for data in the partition. On a low traffic site hourly or even daily can be useful, whereas busy sites minute or second are more useful.</param>
        public AzureHttpLoggerRepository(string storageConnectionString, string azureTablePrefix, LogByDateGranularityEnum granularity)
        {
            if (string.IsNullOrWhiteSpace(storageConnectionString))
            {
                throw new ArgumentNullException(nameof(storageConnectionString));
            }
            if (azureTablePrefix == null)
            {
                azureTablePrefix = "";
            }
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString);
            CloudTableClient    client         = storageAccount.CreateCloudTableClient();

            _byCorrelationIdTable      = client.GetTableReference(string.Concat(azureTablePrefix, RequestByCorrelationIdTableName));
            _byDateTimeDescendingTable = client.GetTableReference(string.Concat(azureTablePrefix, RequestByDateTimeDescendingTableName));

            _byCorrelationIdTable.CreateIfNotExists();
            _byDateTimeDescendingTable.CreateIfNotExists();

            switch (granularity)
            {
            case LogByDateGranularityEnum.Hour:
                _granularPartitionKeyFormat = "yyyy-MM-dd hh";
                break;

            case LogByDateGranularityEnum.Day:
                _granularPartitionKeyFormat = "yyyy-MM-dd";
                break;

            case LogByDateGranularityEnum.Minute:
                _granularPartitionKeyFormat = "yyyy-MM-dd hh:mm";
                break;

            case LogByDateGranularityEnum.Second:
                _granularPartitionKeyFormat = "yyyy-MM-dd hh:mm:ss";
                break;
            }
        }