1
0
mirror of https://github.com/JKorf/CryptoExchange.Net synced 2025-06-09 17:06:19 +00:00

Changed requestId to int, added it to more logging related to requests. Added some comments

This commit is contained in:
JKorf 2020-08-24 12:01:49 +02:00
parent c8a6a2ec12
commit 664cbee8dd
8 changed files with 93 additions and 211 deletions

View File

@ -37,7 +37,7 @@ namespace CryptoExchange.Net
/// </summary> /// </summary>
protected internal AuthenticationProvider? authProvider; protected internal AuthenticationProvider? authProvider;
/// <summary> /// <summary>
/// Should check received objects /// Should check objects for missing properties based on the model and the received JSON
/// </summary> /// </summary>
public bool ShouldCheckObjects { get; set; } public bool ShouldCheckObjects { get; set; }
@ -132,8 +132,9 @@ namespace CryptoExchange.Net
/// <param name="data">The data to deserialize</param> /// <param name="data">The data to deserialize</param>
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param> /// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
/// <param name="serializer">A specific serializer to use</param> /// <param name="serializer">A specific serializer to use</param>
/// <param name="requestId">Id of the request</param>
/// <returns></returns> /// <returns></returns>
protected CallResult<T> Deserialize<T>(string data, bool? checkObject = null, JsonSerializer? serializer = null) protected CallResult<T> Deserialize<T>(string data, bool? checkObject = null, JsonSerializer? serializer = null, int? requestId = null)
{ {
var tokenResult = ValidateJson(data); var tokenResult = ValidateJson(data);
if (!tokenResult) if (!tokenResult)
@ -142,7 +143,7 @@ namespace CryptoExchange.Net
return new CallResult<T>(default, tokenResult.Error); return new CallResult<T>(default, tokenResult.Error);
} }
return Deserialize<T>(tokenResult.Data, checkObject, serializer); return Deserialize<T>(tokenResult.Data, checkObject, serializer, requestId);
} }
/// <summary> /// <summary>
@ -153,7 +154,7 @@ namespace CryptoExchange.Net
/// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param> /// <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
/// <param name="serializer">A specific serializer to use</param> /// <param name="serializer">A specific serializer to use</param>
/// <returns></returns> /// <returns></returns>
protected CallResult<T> Deserialize<T>(JToken obj, bool? checkObject = null, JsonSerializer? serializer = null) protected CallResult<T> Deserialize<T>(JToken obj, bool? checkObject = null, JsonSerializer? serializer = null, int? requestId = null)
{ {
if (serializer == null) if (serializer == null)
serializer = defaultSerializer; serializer = defaultSerializer;
@ -166,17 +167,17 @@ namespace CryptoExchange.Net
{ {
if (obj is JObject o) if (obj is JObject o)
{ {
CheckObject(typeof(T), o); CheckObject(typeof(T), o, requestId);
} }
else if (obj is JArray j) else if (obj is JArray j)
{ {
if (j.HasValues && j[0] is JObject jObject) if (j.HasValues && j[0] is JObject jObject)
CheckObject(typeof(T).GetElementType(), jObject); CheckObject(typeof(T).GetElementType(), jObject, requestId);
} }
} }
catch (Exception e) catch (Exception e)
{ {
log.Write(LogVerbosity.Debug, "Failed to check response data: " + e.Message); log.Write(LogVerbosity.Debug, $"{(requestId != null ? $"[{ requestId}] " : "")}Failed to check response data: " + e.Message);
} }
} }
@ -184,19 +185,19 @@ namespace CryptoExchange.Net
} }
catch (JsonReaderException jre) catch (JsonReaderException jre)
{ {
var info = $"Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}. Received data: {obj}"; var info = $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}. Received data: {obj}";
log.Write(LogVerbosity.Error, info); log.Write(LogVerbosity.Error, info);
return new CallResult<T>(default, new DeserializeError(info)); return new CallResult<T>(default, new DeserializeError(info));
} }
catch (JsonSerializationException jse) catch (JsonSerializationException jse)
{ {
var info = $"Deserialize JsonSerializationException: {jse.Message}. Received data: {obj}"; var info = $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonSerializationException: {jse.Message}. Received data: {obj}";
log.Write(LogVerbosity.Error, info); log.Write(LogVerbosity.Error, info);
return new CallResult<T>(default, new DeserializeError(info)); return new CallResult<T>(default, new DeserializeError(info));
} }
catch (Exception ex) catch (Exception ex)
{ {
var info = $"Deserialize Unknown Exception: {ex.Message}. Received data: {obj}"; var info = $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize Unknown Exception: {ex.Message}. Received data: {obj}";
log.Write(LogVerbosity.Error, info); log.Write(LogVerbosity.Error, info);
return new CallResult<T>(default, new DeserializeError(info)); return new CallResult<T>(default, new DeserializeError(info));
} }
@ -208,8 +209,9 @@ namespace CryptoExchange.Net
/// <typeparam name="T">The type to deserialize into</typeparam> /// <typeparam name="T">The type to deserialize into</typeparam>
/// <param name="stream">The stream to deserialize</param> /// <param name="stream">The stream to deserialize</param>
/// <param name="serializer">A specific serializer to use</param> /// <param name="serializer">A specific serializer to use</param>
/// <param name="requestId">Id of the request</param>
/// <returns></returns> /// <returns></returns>
protected async Task<CallResult<T>> Deserialize<T>(Stream stream, JsonSerializer? serializer = null) protected async Task<CallResult<T>> Deserialize<T>(Stream stream, JsonSerializer? serializer = null, int? requestId = null)
{ {
if (serializer == null) if (serializer == null)
serializer = defaultSerializer; serializer = defaultSerializer;
@ -220,8 +222,8 @@ namespace CryptoExchange.Net
if (log.Level == LogVerbosity.Debug) if (log.Level == LogVerbosity.Debug)
{ {
var data = await reader.ReadToEndAsync().ConfigureAwait(false); var data = await reader.ReadToEndAsync().ConfigureAwait(false);
log.Write(LogVerbosity.Debug, $"Data received: {data}"); log.Write(LogVerbosity.Debug, $"{(requestId != null ? $"[{requestId}] ": "")}Data received: {data}");
return Deserialize<T>(data); return Deserialize<T>(data, null, serializer, requestId);
} }
using var jsonReader = new JsonTextReader(reader); using var jsonReader = new JsonTextReader(reader);
@ -232,7 +234,7 @@ namespace CryptoExchange.Net
if(stream.CanSeek) if(stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
var data = await ReadStream(stream).ConfigureAwait(false); var data = await ReadStream(stream).ConfigureAwait(false);
log.Write(LogVerbosity.Error, $"Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}, data: {data}"); log.Write(LogVerbosity.Error, $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonReaderException: {jre.Message}, Path: {jre.Path}, LineNumber: {jre.LineNumber}, LinePosition: {jre.LinePosition}, data: {data}");
return new CallResult<T>(default, new DeserializeError(data)); return new CallResult<T>(default, new DeserializeError(data));
} }
catch (JsonSerializationException jse) catch (JsonSerializationException jse)
@ -240,7 +242,7 @@ namespace CryptoExchange.Net
if (stream.CanSeek) if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
var data = await ReadStream(stream).ConfigureAwait(false); var data = await ReadStream(stream).ConfigureAwait(false);
log.Write(LogVerbosity.Error, $"Deserialize JsonSerializationException: {jse.Message}, data: {data}"); log.Write(LogVerbosity.Error, $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize JsonSerializationException: {jse.Message}, data: {data}");
return new CallResult<T>(default, new DeserializeError(data)); return new CallResult<T>(default, new DeserializeError(data));
} }
catch (Exception ex) catch (Exception ex)
@ -248,7 +250,7 @@ namespace CryptoExchange.Net
if (stream.CanSeek) if (stream.CanSeek)
stream.Seek(0, SeekOrigin.Begin); stream.Seek(0, SeekOrigin.Begin);
var data = await ReadStream(stream).ConfigureAwait(false); var data = await ReadStream(stream).ConfigureAwait(false);
log.Write(LogVerbosity.Error, $"Deserialize Unknown Exception: {ex.Message}, data: {data}"); log.Write(LogVerbosity.Error, $"{(requestId != null ? $"[{requestId}] " : "")}Deserialize Unknown Exception: {ex.Message}, data: {data}");
return new CallResult<T>(default, new DeserializeError(data)); return new CallResult<T>(default, new DeserializeError(data));
} }
} }
@ -259,7 +261,7 @@ namespace CryptoExchange.Net
return await reader.ReadToEndAsync().ConfigureAwait(false); return await reader.ReadToEndAsync().ConfigureAwait(false);
} }
private void CheckObject(Type type, JObject obj) private void CheckObject(Type type, JObject obj, int? requestId = null)
{ {
if (type == null) if (type == null)
return; return;
@ -273,7 +275,7 @@ namespace CryptoExchange.Net
if (!obj.HasValues && type != typeof(object)) if (!obj.HasValues && type != typeof(object))
{ {
log.Write(LogVerbosity.Warning, $"Expected `{type.Name}`, but received object was empty"); log.Write(LogVerbosity.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Expected `{type.Name}`, but received object was empty");
return; return;
} }
@ -299,7 +301,7 @@ namespace CryptoExchange.Net
{ {
if (!(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>))) if (!(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)))
{ {
log.Write(LogVerbosity.Warning, $"Local object doesn't have property `{token.Key}` expected in type `{type.Name}`"); log.Write(LogVerbosity.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Local object doesn't have property `{token.Key}` expected in type `{type.Name}`");
isDif = true; isDif = true;
} }
continue; continue;
@ -314,9 +316,9 @@ namespace CryptoExchange.Net
if (!IsSimple(propType) && propType != typeof(DateTime)) if (!IsSimple(propType) && propType != typeof(DateTime))
{ {
if (propType.IsArray && token.Value.HasValues && ((JArray)token.Value).Any() && ((JArray)token.Value)[0] is JObject) if (propType.IsArray && token.Value.HasValues && ((JArray)token.Value).Any() && ((JArray)token.Value)[0] is JObject)
CheckObject(propType.GetElementType(), (JObject)token.Value[0]); CheckObject(propType.GetElementType(), (JObject)token.Value[0], requestId);
else if (token.Value is JObject o) else if (token.Value is JObject o)
CheckObject(propType, o); CheckObject(propType, o, requestId);
} }
} }
@ -329,11 +331,11 @@ namespace CryptoExchange.Net
continue; continue;
isDif = true; isDif = true;
log.Write(LogVerbosity.Warning, $"Local object has property `{prop}` but was not found in received object of type `{type.Name}`"); log.Write(LogVerbosity.Warning, $"{(requestId != null ? $"[{requestId}] " : "")}Local object has property `{prop}` but was not found in received object of type `{type.Name}`");
} }
if (isDif) if (isDif)
log.Write(LogVerbosity.Debug, "Returned data: " + obj); log.Write(LogVerbosity.Debug, $"{(requestId != null ? $"[{ requestId}] " : "")}Returned data: " + obj);
} }
private static PropertyInfo? GetProperty(string name, IEnumerable<PropertyInfo> props) private static PropertyInfo? GetProperty(string name, IEnumerable<PropertyInfo> props)

View File

@ -224,6 +224,11 @@
The auth provider The auth provider
</summary> </summary>
</member> </member>
<member name="P:CryptoExchange.Net.BaseClient.ShouldCheckObjects">
<summary>
Should check objects for missing properties based on the model and the received JSON
</summary>
</member>
<member name="F:CryptoExchange.Net.BaseClient.lastId"> <member name="F:CryptoExchange.Net.BaseClient.lastId">
<summary> <summary>
The last used id The last used id
@ -259,7 +264,7 @@
<param name="data">The data to parse</param> <param name="data">The data to parse</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.String,System.Boolean,Newtonsoft.Json.JsonSerializer)"> <member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.String,System.Nullable{System.Boolean},Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
<summary> <summary>
Deserialize a string into an object Deserialize a string into an object
</summary> </summary>
@ -267,9 +272,10 @@
<param name="data">The data to deserialize</param> <param name="data">The data to deserialize</param>
<param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param> <param name="checkObject">Whether or not the parsing should be checked for missing properties (will output data to the logging if log verbosity is Debug)</param>
<param name="serializer">A specific serializer to use</param> <param name="serializer">A specific serializer to use</param>
<param name="requestId">Id of the request</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(Newtonsoft.Json.Linq.JToken,System.Boolean,Newtonsoft.Json.JsonSerializer)"> <member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(Newtonsoft.Json.Linq.JToken,System.Nullable{System.Boolean},Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
<summary> <summary>
Deserialize a JToken into an object Deserialize a JToken into an object
</summary> </summary>
@ -279,13 +285,14 @@
<param name="serializer">A specific serializer to use</param> <param name="serializer">A specific serializer to use</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.IO.Stream,Newtonsoft.Json.JsonSerializer)"> <member name="M:CryptoExchange.Net.BaseClient.Deserialize``1(System.IO.Stream,Newtonsoft.Json.JsonSerializer,System.Nullable{System.Int32})">
<summary> <summary>
Deserialize a stream into an object Deserialize a stream into an object
</summary> </summary>
<typeparam name="T">The type to deserialize into</typeparam> <typeparam name="T">The type to deserialize into</typeparam>
<param name="stream">The stream to deserialize</param> <param name="stream">The stream to deserialize</param>
<param name="serializer">A specific serializer to use</param> <param name="serializer">A specific serializer to use</param>
<param name="requestId">Id of the request</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.BaseClient.NextId"> <member name="M:CryptoExchange.Net.BaseClient.NextId">
@ -625,12 +632,13 @@
Request factory interface Request factory interface
</summary> </summary>
</member> </member>
<member name="M:CryptoExchange.Net.Interfaces.IRequestFactory.Create(System.Net.Http.HttpMethod,System.String)"> <member name="M:CryptoExchange.Net.Interfaces.IRequestFactory.Create(System.Net.Http.HttpMethod,System.String,System.Int32)">
<summary> <summary>
Create a request for an uri Create a request for an uri
</summary> </summary>
<param name="method"></param> <param name="method"></param>
<param name="uri"></param> <param name="uri"></param>
<param name="requestId"></param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.Interfaces.IRequestFactory.Configure(System.TimeSpan,CryptoExchange.Net.Objects.ApiProxy,System.Net.Http.HttpClient)"> <member name="M:CryptoExchange.Net.Interfaces.IRequestFactory.Configure(System.TimeSpan,CryptoExchange.Net.Objects.ApiProxy,System.Net.Http.HttpClient)">
@ -1588,6 +1596,11 @@
The api credentials The api credentials
</summary> </summary>
</member> </member>
<member name="P:CryptoExchange.Net.Objects.ClientOptions.ShouldCheckObjects">
<summary>
Should check objects for missing properties based on the model and the received JSON
</summary>
</member>
<member name="P:CryptoExchange.Net.Objects.ClientOptions.Proxy"> <member name="P:CryptoExchange.Net.Objects.ClientOptions.Proxy">
<summary> <summary>
Proxy to use Proxy to use
@ -1622,23 +1635,23 @@
The time the server has to respond to a request before timing out The time the server has to respond to a request before timing out
</summary> </summary>
</member> </member>
<member name="F:CryptoExchange.Net.Objects.RestClientOptions.HttpClient"> <member name="P:CryptoExchange.Net.Objects.RestClientOptions.HttpClient">
<summary> <summary>
http client Http client to use. If a HttpClient is provided in this property the RequestTimeout and Proxy options will be ignored and should be set on the provided HttpClient instance
</summary> </summary>
</member> </member>
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.String)"> <member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.String)">
<summary> <summary>
ctor ctor
</summary> </summary>
<param name="baseAddress"></param> <param name="baseAddress">The base address of the API</param>
</member> </member>
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.Net.Http.HttpClient,System.String)"> <member name="M:CryptoExchange.Net.Objects.RestClientOptions.#ctor(System.Net.Http.HttpClient,System.String)">
<summary> <summary>
ctor ctor
</summary> </summary>
<param name="baseAddress"></param> <param name="baseAddress">The base address of the API</param>
<param name="httpClient">Shared http client</param> <param name="httpClient">Shared http client instance</param>
</member> </member>
<member name="M:CryptoExchange.Net.Objects.RestClientOptions.Copy``1"> <member name="M:CryptoExchange.Net.Objects.RestClientOptions.Copy``1">
<summary> <summary>
@ -2042,12 +2055,13 @@
Request object Request object
</summary> </summary>
</member> </member>
<member name="M:CryptoExchange.Net.Requests.Request.#ctor(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpClient)"> <member name="M:CryptoExchange.Net.Requests.Request.#ctor(System.Net.Http.HttpRequestMessage,System.Net.Http.HttpClient,System.Int32)">
<summary> <summary>
Create request object for web request Create request object for web request
</summary> </summary>
<param name="request"></param> <param name="request"></param>
<param name="client"></param> <param name="client"></param>
<param name="requestId"></param>
</member> </member>
<member name="P:CryptoExchange.Net.Requests.Request.Content"> <member name="P:CryptoExchange.Net.Requests.Request.Content">
<inheritdoc /> <inheritdoc />
@ -2084,7 +2098,7 @@
<member name="M:CryptoExchange.Net.Requests.RequestFactory.Configure(System.TimeSpan,CryptoExchange.Net.Objects.ApiProxy,System.Net.Http.HttpClient)"> <member name="M:CryptoExchange.Net.Requests.RequestFactory.Configure(System.TimeSpan,CryptoExchange.Net.Objects.ApiProxy,System.Net.Http.HttpClient)">
<inheritdoc /> <inheritdoc />
</member> </member>
<member name="M:CryptoExchange.Net.Requests.RequestFactory.Create(System.Net.Http.HttpMethod,System.String)"> <member name="M:CryptoExchange.Net.Requests.RequestFactory.Create(System.Net.Http.HttpMethod,System.String,System.Int32)">
<inheritdoc /> <inheritdoc />
</member> </member>
<member name="T:CryptoExchange.Net.Requests.Response"> <member name="T:CryptoExchange.Net.Requests.Response">
@ -2210,7 +2224,7 @@
<param name="signed">Whether or not the request should be authenticated</param> <param name="signed">Whether or not the request should be authenticated</param>
<param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param> <param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param>
<param name="postPosition">Where the post parameters should be placed</param> <param name="postPosition">Where the post parameters should be placed</param>
<param name="arraySerialization">How array paramters should be serialized</param> <param name="arraySerialization">How array parameters should be serialized</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.RestClient.GetResponse``1(CryptoExchange.Net.Interfaces.IRequest,System.Threading.CancellationToken)"> <member name="M:CryptoExchange.Net.RestClient.GetResponse``1(CryptoExchange.Net.Interfaces.IRequest,System.Threading.CancellationToken)">
@ -2229,7 +2243,7 @@
<param name="data">Received data</param> <param name="data">Received data</param>
<returns>Null if not an error, Error otherwise</returns> <returns>Null if not an error, Error otherwise</returns>
</member> </member>
<member name="M:CryptoExchange.Net.RestClient.ConstructRequest(System.Uri,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,CryptoExchange.Net.Objects.PostParameters,CryptoExchange.Net.Objects.ArrayParametersSerialization)"> <member name="M:CryptoExchange.Net.RestClient.ConstructRequest(System.Uri,System.Net.Http.HttpMethod,System.Collections.Generic.Dictionary{System.String,System.Object},System.Boolean,CryptoExchange.Net.Objects.PostParameters,CryptoExchange.Net.Objects.ArrayParametersSerialization,System.Int32)">
<summary> <summary>
Creates a request object Creates a request object
</summary> </summary>
@ -2238,7 +2252,8 @@
<param name="parameters">The parameters of the request</param> <param name="parameters">The parameters of the request</param>
<param name="signed">Whether or not the request should be authenticated</param> <param name="signed">Whether or not the request should be authenticated</param>
<param name="postPosition">Where the post parameters should be placed</param> <param name="postPosition">Where the post parameters should be placed</param>
<param name="arraySerialization">How array paramters should be serialized</param> <param name="arraySerialization">How array parameters should be serialized</param>
<param name="requestId">Unique id of a request</param>
<returns></returns> <returns></returns>
</member> </member>
<member name="M:CryptoExchange.Net.RestClient.WriteParamBody(CryptoExchange.Net.Interfaces.IRequest,System.Collections.Generic.Dictionary{System.String,System.Object},System.String)"> <member name="M:CryptoExchange.Net.RestClient.WriteParamBody(CryptoExchange.Net.Interfaces.IRequest,System.Collections.Generic.Dictionary{System.String,System.Object},System.String)">
@ -2969,148 +2984,5 @@
<member name="M:CryptoExchange.Net.Sockets.WebsocketFactory.CreateWebsocket(CryptoExchange.Net.Logging.Log,System.String,System.Collections.Generic.IDictionary{System.String,System.String},System.Collections.Generic.IDictionary{System.String,System.String})"> <member name="M:CryptoExchange.Net.Sockets.WebsocketFactory.CreateWebsocket(CryptoExchange.Net.Logging.Log,System.String,System.Collections.Generic.IDictionary{System.String,System.String},System.Collections.Generic.IDictionary{System.String,System.String})">
<inheritdoc /> <inheritdoc />
</member> </member>
<member name="T:System.Diagnostics.CodeAnalysis.AllowNullAttribute">
<summary>
Specifies that <see langword="null"/> is allowed as an input even if the
corresponding type disallows it.
</summary>
</member>
<member name="M:System.Diagnostics.CodeAnalysis.AllowNullAttribute.#ctor">
<summary>
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.AllowNullAttribute"/> class.
</summary>
</member>
<member name="T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute">
<summary>
Specifies that <see langword="null"/> is disallowed as an input even if the
corresponding type allows it.
</summary>
</member>
<member name="M:System.Diagnostics.CodeAnalysis.DisallowNullAttribute.#ctor">
<summary>
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DisallowNullAttribute"/> class.
</summary>
</member>
<member name="T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute">
<summary>
Specifies that a method that will never return under any circumstance.
</summary>
</member>
<member name="M:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute.#ctor">
<summary>
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute"/> class.
</summary>
</member>
<member name="T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute">
<summary>
Specifies that the method will not return if the associated <see cref="T:System.Boolean"/>
parameter is passed the specified value.
</summary>
</member>
<member name="P:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute.ParameterValue">
<summary>
Gets the condition parameter value.
Code after the method is considered unreachable by diagnostics if the argument
to the associated parameter matches this value.
</summary>
</member>
<member name="M:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute.#ctor(System.Boolean)">
<summary>
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute"/>
class with the specified parameter value.
</summary>
<param name="parameterValue">
The condition parameter value.
Code after the method is considered unreachable by diagnostics if the argument
to the associated parameter matches this value.
</param>
</member>
<member name="T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute">
<summary>
Specifies that an output may be <see langword="null"/> even if the
corresponding type disallows it.
</summary>
</member>
<member name="M:System.Diagnostics.CodeAnalysis.MaybeNullAttribute.#ctor">
<summary>
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.MaybeNullAttribute"/> class.
</summary>
</member>
<member name="T:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute">
<summary>
Specifies that when a method returns <see cref="P:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.ReturnValue"/>,
the parameter may be <see langword="null"/> even if the corresponding type disallows it.
</summary>
</member>
<member name="P:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.ReturnValue">
<summary>
Gets the return value condition.
If the method returns this value, the associated parameter may be <see langword="null"/>.
</summary>
</member>
<member name="M:System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute.#ctor(System.Boolean)">
<summary>
Initializes the attribute with the specified return value condition.
</summary>
<param name="returnValue">
The return value condition.
If the method returns this value, the associated parameter may be <see langword="null"/>.
</param>
</member>
<member name="T:System.Diagnostics.CodeAnalysis.NotNullAttribute">
<summary>
Specifies that an output is not <see langword="null"/> even if the
corresponding type allows it.
</summary>
</member>
<member name="M:System.Diagnostics.CodeAnalysis.NotNullAttribute.#ctor">
<summary>
Initializes a new instance of the <see cref="T:System.Diagnostics.CodeAnalysis.NotNullAttribute"/> class.
</summary>
</member>
<member name="T:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute">
<summary>
Specifies that the output will be non-<see langword="null"/> if the
named parameter is non-<see langword="null"/>.
</summary>
</member>
<member name="P:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute.ParameterName">
<summary>
Gets the associated parameter name.
The output will be non-<see langword="null"/> if the argument to the
parameter specified is non-<see langword="null"/>.
</summary>
</member>
<member name="M:System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute.#ctor(System.String)">
<summary>
Initializes the attribute with the associated parameter name.
</summary>
<param name="parameterName">
The associated parameter name.
The output will be non-<see langword="null"/> if the argument to the
parameter specified is non-<see langword="null"/>.
</param>
</member>
<member name="T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute">
<summary>
Specifies that when a method returns <see cref="P:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue"/>,
the parameter will not be <see langword="null"/> even if the corresponding type allows it.
</summary>
</member>
<member name="P:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.ReturnValue">
<summary>
Gets the return value condition.
If the method returns this value, the associated parameter will not be <see langword="null"/>.
</summary>
</member>
<member name="M:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute.#ctor(System.Boolean)">
<summary>
Initializes the attribute with the specified return value condition.
</summary>
<param name="returnValue">
The return value condition.
If the method returns this value, the associated parameter will not be <see langword="null"/>.
</param>
</member>
</members> </members>
</doc> </doc>

View File

@ -29,7 +29,7 @@ namespace CryptoExchange.Net.Interfaces
/// <summary> /// <summary>
/// internal request id for tracing /// internal request id for tracing
/// </summary> /// </summary>
string RequestId { get; } int RequestId { get; }
/// <summary> /// <summary>
/// Set byte content /// Set byte content
/// </summary> /// </summary>

View File

@ -14,8 +14,9 @@ namespace CryptoExchange.Net.Interfaces
/// </summary> /// </summary>
/// <param name="method"></param> /// <param name="method"></param>
/// <param name="uri"></param> /// <param name="uri"></param>
/// <param name="requestId"></param>
/// <returns></returns> /// <returns></returns>
IRequest Create(HttpMethod method, string uri); IRequest Create(HttpMethod method, string uri, int requestId);
/// <summary> /// <summary>
/// Configure the requests created by this factory /// Configure the requests created by this factory

View File

@ -85,10 +85,12 @@ namespace CryptoExchange.Net.Objects
/// The api credentials /// The api credentials
/// </summary> /// </summary>
public ApiCredentials? ApiCredentials { get; set; } public ApiCredentials? ApiCredentials { get; set; }
/// <summary> /// <summary>
/// ShoouldCheckObjects /// Should check objects for missing properties based on the model and the received JSON
/// </summary> /// </summary>
public bool ShouldCheckObjects { get; set; } = true; public bool ShouldCheckObjects { get; set; } = true;
/// <summary> /// <summary>
/// Proxy to use /// Proxy to use
/// </summary> /// </summary>
@ -129,22 +131,24 @@ namespace CryptoExchange.Net.Objects
/// The time the server has to respond to a request before timing out /// The time the server has to respond to a request before timing out
/// </summary> /// </summary>
public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30); public TimeSpan RequestTimeout { get; set; } = TimeSpan.FromSeconds(30);
/// <summary> /// <summary>
/// http client /// Http client to use. If a HttpClient is provided in this property the RequestTimeout and Proxy options will be ignored and should be set on the provided HttpClient instance
/// </summary> /// </summary>
public HttpClient? HttpClient; public HttpClient? HttpClient { get; set; }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
/// <param name="baseAddress"></param> /// <param name="baseAddress">The base address of the API</param>
public RestClientOptions(string baseAddress): base(baseAddress) public RestClientOptions(string baseAddress): base(baseAddress)
{ {
} }
/// <summary> /// <summary>
/// ctor /// ctor
/// </summary> /// </summary>
/// <param name="baseAddress"></param> /// <param name="baseAddress">The base address of the API</param>
/// <param name="httpClient">Shared http client</param> /// <param name="httpClient">Shared http client instance</param>
public RestClientOptions(HttpClient httpClient, string baseAddress) : base(baseAddress) public RestClientOptions(HttpClient httpClient, string baseAddress) : base(baseAddress)
{ {
HttpClient = httpClient; HttpClient = httpClient;

View File

@ -22,11 +22,12 @@ namespace CryptoExchange.Net.Requests
/// </summary> /// </summary>
/// <param name="request"></param> /// <param name="request"></param>
/// <param name="client"></param> /// <param name="client"></param>
public Request(HttpRequestMessage request, HttpClient client) /// <param name="requestId"></param>
public Request(HttpRequestMessage request, HttpClient client, int requestId)
{ {
httpClient = client; httpClient = client;
this.request = request; this.request = request;
RequestId = Path.GetRandomFileName(); RequestId = requestId;
} }
/// <inheritdoc /> /// <inheritdoc />
@ -48,7 +49,7 @@ namespace CryptoExchange.Net.Requests
/// <inheritdoc /> /// <inheritdoc />
public Uri Uri => request.RequestUri; public Uri Uri => request.RequestUri;
/// <inheritdoc /> /// <inheritdoc />
public string RequestId { get; } public int RequestId { get; }
/// <inheritdoc /> /// <inheritdoc />
public void SetContent(string data, string contentType) public void SetContent(string data, string contentType)

View File

@ -36,12 +36,12 @@ namespace CryptoExchange.Net.Requests
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequest Create(HttpMethod method, string uri) public IRequest Create(HttpMethod method, string uri, int requestId)
{ {
if (httpClient == null) if (httpClient == null)
throw new InvalidOperationException("Cant create request before configuring http client"); throw new InvalidOperationException("Cant create request before configuring http client");
return new Request(new HttpRequestMessage(method, uri), httpClient); return new Request(new HttpRequestMessage(method, uri), httpClient, requestId);
} }
} }
} }

View File

@ -166,38 +166,39 @@ namespace CryptoExchange.Net
/// <param name="signed">Whether or not the request should be authenticated</param> /// <param name="signed">Whether or not the request should be authenticated</param>
/// <param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param> /// <param name="checkResult">Whether or not the resulting object should be checked for missing properties in the mapping (only outputs if log verbosity is Debug)</param>
/// <param name="postPosition">Where the post parameters should be placed</param> /// <param name="postPosition">Where the post parameters should be placed</param>
/// <param name="arraySerialization">How array paramters should be serialized</param> /// <param name="arraySerialization">How array parameters should be serialized</param>
/// <returns></returns> /// <returns></returns>
[return: NotNull] [return: NotNull]
protected virtual async Task<WebCallResult<T>> SendRequest<T>(Uri uri, HttpMethod method, CancellationToken cancellationToken, protected virtual async Task<WebCallResult<T>> SendRequest<T>(Uri uri, HttpMethod method, CancellationToken cancellationToken,
Dictionary<string, object>? parameters = null, bool signed = false, bool checkResult = true, PostParameters? postPosition = null, ArrayParametersSerialization? arraySerialization = null) where T : class Dictionary<string, object>? parameters = null, bool signed = false, bool checkResult = true, PostParameters? postPosition = null, ArrayParametersSerialization? arraySerialization = null) where T : class
{ {
log.Write(LogVerbosity.Debug, "Creating request for " + uri); var requestId = NextId();
log.Write(LogVerbosity.Debug, $"[{requestId}] Creating request for " + uri);
if (signed && authProvider == null) if (signed && authProvider == null)
{ {
log.Write(LogVerbosity.Warning, $"Request {uri.AbsolutePath} failed because no ApiCredentials were provided"); log.Write(LogVerbosity.Warning, $"[{requestId}] Request {uri.AbsolutePath} failed because no ApiCredentials were provided");
return new WebCallResult<T>(null, null, null, new NoApiCredentialsError()); return new WebCallResult<T>(null, null, null, new NoApiCredentialsError());
} }
var request = ConstructRequest(uri, method, parameters, signed, postPosition ?? postParametersPosition, arraySerialization ?? this.arraySerialization); var request = ConstructRequest(uri, method, parameters, signed, postPosition ?? postParametersPosition, arraySerialization ?? this.arraySerialization, requestId);
foreach (var limiter in RateLimiters) foreach (var limiter in RateLimiters)
{ {
var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour); var limitResult = limiter.LimitRequest(this, uri.AbsolutePath, RateLimitBehaviour);
if (!limitResult.Success) if (!limitResult.Success)
{ {
log.Write(LogVerbosity.Debug, $"Request {request.RequestId} {uri.AbsolutePath} failed because of rate limit"); log.Write(LogVerbosity.Debug, $"[{requestId}] Request {uri.AbsolutePath} failed because of rate limit");
return new WebCallResult<T>(null, null, null, limitResult.Error); return new WebCallResult<T>(null, null, null, limitResult.Error);
} }
if (limitResult.Data > 0) if (limitResult.Data > 0)
log.Write(LogVerbosity.Debug, $"Request {request.RequestId} {uri.AbsolutePath} was limited by {limitResult.Data}ms by {limiter.GetType().Name}"); log.Write(LogVerbosity.Debug, $"[{requestId}] Request {uri.AbsolutePath} was limited by {limitResult.Data}ms by {limiter.GetType().Name}");
} }
string? paramString = null; string? paramString = null;
if (method == HttpMethod.Post) if (method == HttpMethod.Post)
paramString = " with request body " + request.Content; paramString = " with request body " + request.Content;
log.Write(LogVerbosity.Debug, $"Sending {method}{(signed ? " signed" : "")} request to {request.Uri}{paramString ?? " "}{(apiProxy == null ? "" : $" via proxy {apiProxy.Host}")} with id {request.RequestId}"); log.Write(LogVerbosity.Debug, $"[{requestId}] Sending {method}{(signed ? " signed" : "")} request to {request.Uri}{paramString ?? " "}{(apiProxy == null ? "" : $" via proxy {apiProxy.Host}")}");
return await GetResponse<T>(request, cancellationToken).ConfigureAwait(false); return await GetResponse<T>(request, cancellationToken).ConfigureAwait(false);
} }
@ -224,7 +225,7 @@ namespace CryptoExchange.Net
var data = await reader.ReadToEndAsync().ConfigureAwait(false); var data = await reader.ReadToEndAsync().ConfigureAwait(false);
responseStream.Close(); responseStream.Close();
response.Close(); response.Close();
log.Write(LogVerbosity.Debug, $"Data for request {request.RequestId} received: {data}"); log.Write(LogVerbosity.Debug, $"[{request.RequestId}] Data received: {data}");
var parseResult = ValidateJson(data); var parseResult = ValidateJson(data);
if (!parseResult.Success) if (!parseResult.Success)
@ -233,12 +234,12 @@ namespace CryptoExchange.Net
if (error != null) if (error != null)
return WebCallResult<T>.CreateErrorResult(response.StatusCode, response.ResponseHeaders, error); return WebCallResult<T>.CreateErrorResult(response.StatusCode, response.ResponseHeaders, error);
var deserializeResult = Deserialize<T>(parseResult.Data); var deserializeResult = Deserialize<T>(parseResult.Data, null, null, request.RequestId);
return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, deserializeResult.Data, deserializeResult.Error); return new WebCallResult<T>(response.StatusCode, response.ResponseHeaders, deserializeResult.Data, deserializeResult.Error);
} }
else else
{ {
var desResult = await Deserialize<T>(responseStream).ConfigureAwait(false); var desResult = await Deserialize<T>(responseStream, null, request.RequestId).ConfigureAwait(false);
responseStream.Close(); responseStream.Close();
response.Close(); response.Close();
@ -249,7 +250,7 @@ namespace CryptoExchange.Net
{ {
using var reader = new StreamReader(responseStream); using var reader = new StreamReader(responseStream);
var data = await reader.ReadToEndAsync().ConfigureAwait(false); var data = await reader.ReadToEndAsync().ConfigureAwait(false);
log.Write(LogVerbosity.Debug, $"Error for request {request.RequestId} received: {data}"); log.Write(LogVerbosity.Debug, $"[{request.RequestId}] Error received: {data}");
responseStream.Close(); responseStream.Close();
response.Close(); response.Close();
var parseResult = ValidateJson(data); var parseResult = ValidateJson(data);
@ -258,7 +259,7 @@ namespace CryptoExchange.Net
} }
catch (HttpRequestException requestException) catch (HttpRequestException requestException)
{ {
log.Write(LogVerbosity.Warning, $"Request {request.RequestId} exception: " + requestException.Message); log.Write(LogVerbosity.Warning, $"[{request.RequestId}] Request exception: " + requestException.Message);
return new WebCallResult<T>(null, null, default, new ServerError(requestException.Message)); return new WebCallResult<T>(null, null, default, new ServerError(requestException.Message));
} }
catch (TaskCanceledException canceledException) catch (TaskCanceledException canceledException)
@ -266,14 +267,14 @@ namespace CryptoExchange.Net
if (canceledException.CancellationToken == cancellationToken) if (canceledException.CancellationToken == cancellationToken)
{ {
// Cancellation token cancelled // Cancellation token cancelled
log.Write(LogVerbosity.Warning, $"Request {request.RequestId} cancel requested"); log.Write(LogVerbosity.Warning, $"[{request.RequestId}] Request cancel requested");
return new WebCallResult<T>(null, null, default, new CancellationRequestedError()); return new WebCallResult<T>(null, null, default, new CancellationRequestedError());
} }
else else
{ {
// Request timed out // Request timed out
log.Write(LogVerbosity.Warning, $"Request {request.RequestId} timed out"); log.Write(LogVerbosity.Warning, $"[{request.RequestId}] Request timed out");
return new WebCallResult<T>(null, null, default, new WebError($"Request {request.RequestId} timed out")); return new WebCallResult<T>(null, null, default, new WebError($"[{request.RequestId}] Request timed out"));
} }
} }
} }
@ -297,9 +298,10 @@ namespace CryptoExchange.Net
/// <param name="parameters">The parameters of the request</param> /// <param name="parameters">The parameters of the request</param>
/// <param name="signed">Whether or not the request should be authenticated</param> /// <param name="signed">Whether or not the request should be authenticated</param>
/// <param name="postPosition">Where the post parameters should be placed</param> /// <param name="postPosition">Where the post parameters should be placed</param>
/// <param name="arraySerialization">How array paramters should be serialized</param> /// <param name="arraySerialization">How array parameters should be serialized</param>
/// <param name="requestId">Unique id of a request</param>
/// <returns></returns> /// <returns></returns>
protected virtual IRequest ConstructRequest(Uri uri, HttpMethod method, Dictionary<string, object>? parameters, bool signed, PostParameters postPosition, ArrayParametersSerialization arraySerialization) protected virtual IRequest ConstructRequest(Uri uri, HttpMethod method, Dictionary<string, object>? parameters, bool signed, PostParameters postPosition, ArrayParametersSerialization arraySerialization, int requestId)
{ {
if (parameters == null) if (parameters == null)
parameters = new Dictionary<string, object>(); parameters = new Dictionary<string, object>();
@ -312,7 +314,7 @@ namespace CryptoExchange.Net
uriString += "?" + parameters.CreateParamString(true, arraySerialization); uriString += "?" + parameters.CreateParamString(true, arraySerialization);
var contentType = requestBodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader; var contentType = requestBodyFormat == RequestBodyFormat.Json ? Constants.JsonContentHeader : Constants.FormContentHeader;
var request = RequestFactory.Create(method, uriString); var request = RequestFactory.Create(method, uriString, requestId);
request.Accept = Constants.JsonContentHeader; request.Accept = Constants.JsonContentHeader;
var headers = new Dictionary<string, string>(); var headers = new Dictionary<string, string>();