Skip to content

Commit

Permalink
Merge pull request #32 from antomys/feature/structured-logging
Browse files Browse the repository at this point in the history
feat: Logging to a structured logging
  • Loading branch information
Norhaven authored Apr 4, 2024
2 parents 465a54d + 394bdb1 commit adf1f5f
Show file tree
Hide file tree
Showing 4 changed files with 27 additions and 27 deletions.
20 changes: 10 additions & 10 deletions GrowthBook/Api/FeatureRefreshWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ public FeatureRefreshWorker(ILogger<FeatureRefreshWorker> logger, IHttpClientFac
_featuresApiEndpoint = $"{trimmedHostEndpoint}/api/features/{config.ClientKey}";
_serverSentEventsApiEndpoint = $"{trimmedHostEndpoint}/sub/{config.ClientKey}";

_logger.LogDebug($"Features GrowthBook API endpoint: '{_featuresApiEndpoint}'");
_logger.LogDebug($"Features GrowthBook API endpoint (Server Sent Events): '{_featuresApiEndpoint}'");
_logger.LogDebug("Features GrowthBook API endpoint: \'{FeaturesApiEndpoint}\'", _featuresApiEndpoint);
_logger.LogDebug("Features GrowthBook API endpoint (Server Sent Events): \'{FeaturesApiEndpoint}\'", _featuresApiEndpoint);
}

public void Cancel()
Expand All @@ -61,7 +61,7 @@ public void Cancel()

public async Task<IDictionary<string, Feature>> RefreshCacheFromApi(CancellationToken? cancellationToken = null)
{
_logger.LogInformation($"Making an HTTP request to the default Features API endpoint '{_featuresApiEndpoint}'");
_logger.LogInformation("Making an HTTP request to the default Features API endpoint \'{FeaturesApiEndpoint}\'", _featuresApiEndpoint);

var httpClient = _httpClientFactory.CreateClient(ConfiguredClients.DefaultApiClient);

Expand Down Expand Up @@ -120,8 +120,8 @@ private Task ListenForServerSentEvents()
{
try
{
_logger.LogInformation($"Making an HTTP request to server sent events endpoint '{_serverSentEventsApiEndpoint}'");
_logger.LogInformation("Making an HTTP request to server sent events endpoint \'{ServerSentEventsApiEndpoint}\'", _serverSentEventsApiEndpoint);

var httpClient = _httpClientFactory.CreateClient(ConfiguredClients.ServerSentEventsApiClient);

await httpClient.UpdateWithFeaturesStreamFrom(_serverSentEventsApiEndpoint, _logger, _config, _serverSentEventsListenerCancellation.Token, async features =>
Expand All @@ -133,11 +133,11 @@ await httpClient.UpdateWithFeaturesStreamFrom(_serverSentEventsApiEndpoint, _log
}
catch(HttpRequestException ex)
{
_logger.LogError(ex, $"Encountered an HTTP exception during request to server sent events endpoint '{_serverSentEventsApiEndpoint}'");
_logger.LogError(ex, "Encountered an HTTP exception during request to server sent events endpoint \'{ServerSentEventsApiEndpoint}\'", _serverSentEventsApiEndpoint);
}
catch(Exception ex)
{
_logger.LogError(ex, $"Encountered an unhandled exception during request to server sent events endpoint '{_serverSentEventsApiEndpoint}'");
_logger.LogError(ex, "Encountered an unhandled exception during request to server sent events endpoint \'{ServerSentEventsApiEndpoint}\'", _serverSentEventsApiEndpoint);
}
}

Expand All @@ -151,16 +151,16 @@ private IDictionary<string, Feature> GetFeaturesFrom(string json)

if (featuresResponse.EncryptedFeatures.IsNullOrWhitespace())
{
_logger.LogInformation($"API response JSON contained no encrypted features, returning '{featuresResponse.FeatureCount}' unencrypted features");
_logger.LogInformation("API response JSON contained no encrypted features, returning \'{FeaturesResponseFeatureCount}\' unencrypted features", featuresResponse.FeatureCount);
return featuresResponse.Features;
}

_logger.LogInformation("API response JSON contained encrypted features, decrypting them now");
_logger.LogDebug($"Attempting to decrypt features with the provided decryption key '{_config.DecryptionKey}'");
_logger.LogDebug("Attempting to decrypt features with the provided decryption key \'{ConfigDecryptionKey}\'", _config.DecryptionKey);

var decryptedFeaturesJson = featuresResponse.EncryptedFeatures.DecryptWith(_config.DecryptionKey);

_logger.LogDebug($"Completed attempt to decrypt features which resulted in plaintext value of '{decryptedFeaturesJson}'");
_logger.LogDebug("Completed attempt to decrypt features which resulted in plaintext value of \'{DecryptedFeaturesJson}\'", decryptedFeaturesJson);

var jsonObject = JObject.Parse(decryptedFeaturesJson);

Expand Down
4 changes: 2 additions & 2 deletions GrowthBook/Api/FeatureRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public async Task<IDictionary<string, Feature>> GetFeatures(GrowthBookRetrievalO
if (_cache.IsCacheExpired || options?.ForceRefresh == true)
{
_logger.LogInformation("Cache has expired or option to force refresh was set, refreshing the cache from the API");
_logger.LogDebug($"Cache expired: '{_cache.IsCacheExpired}' and option to force refresh: '{options?.ForceRefresh}'");
_logger.LogDebug("Cache expired: \'{CacheIsCacheExpired}\' and option to force refresh: \'{OptionsForceRefresh}\'", _cache.IsCacheExpired, options?.ForceRefresh);

var refreshTask = _backgroundRefreshWorker.RefreshCacheFromApi(cancellationToken);

Expand All @@ -54,7 +54,7 @@ public async Task<IDictionary<string, Feature>> GetFeatures(GrowthBookRetrievalO
if (_cache.FeatureCount == 0 || options?.WaitForCompletion == true)
{
_logger.LogInformation("Either cache currently has no features or the option to wait for completion was set, waiting for cache to refresh");
_logger.LogDebug($"Feature count: '{_cache.FeatureCount}' and option to wait for completion: '{options?.WaitForCompletion}'");
_logger.LogDebug("Feature count: \'{CacheFeatureCount}\' and option to wait for completion: \'{OptionsWaitForCompletion}\'", _cache.FeatureCount, options?.WaitForCompletion);

return await refreshTask;
}
Expand Down
18 changes: 9 additions & 9 deletions GrowthBook/GrowthBook.cs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ private void TryAssignExperimentResult(Experiment experiment, ExperimentResult r
}

private ExperimentResult RunExperiment(Experiment experiment, string featureId)
{
{
// 1. Abort if there aren't enough variations present.

if (experiment.Variations.IsNull() || experiment.Variations.Count < 2)
Expand Down Expand Up @@ -387,7 +387,7 @@ private ExperimentResult RunExperiment(Experiment experiment, string featureId)

if (hashValue.IsNullOrWhitespace())
{
_logger.LogDebug($"Aborting experiment, unable to locate a value for the experiment hash attribute '{experiment.HashAttribute}'");
_logger.LogDebug("Aborting experiment, unable to locate a value for the experiment hash attribute \'{ExperimentHashAttribute}\'", experiment.HashAttribute);
return GetExperimentResult(experiment, featureId: featureId);
}

Expand All @@ -399,11 +399,11 @@ private ExperimentResult RunExperiment(Experiment experiment, string featureId)
{
_logger.LogDebug("Aborting experiment, filters have been applied and matched this run");
return GetExperimentResult(experiment, featureId: featureId);
}
}
}
else if (experiment.Namespace != null && !ExperimentUtilities.InNamespace(hashValue, experiment.Namespace))
{
_logger.LogDebug($"Aborting experiment, not within the specified namespace '{experiment.Namespace}'");
_logger.LogDebug("Aborting experiment, not within the specified namespace \'{ExperimentNamespace}\'", experiment.Namespace);
return GetExperimentResult(experiment, featureId: featureId);
}

Expand Down Expand Up @@ -448,7 +448,7 @@ private ExperimentResult RunExperiment(Experiment experiment, string featureId)

// 12. Run the experiment and track the result if we haven't seen this one before.

_logger.LogInformation($"Participation in experiment with key '{experiment.Key}' is allowed, running the experiment");
_logger.LogInformation("Participation in experiment with key \'{ExperimentKey}\' is allowed, running the experiment", experiment.Key);
var result = GetExperimentResult(experiment, assigned, true, featureId, variationHash);

TryToTrack(experiment, result);
Expand All @@ -475,7 +475,7 @@ private bool IsFilteredOut(IEnumerable<Filter> filters)

if (hashValue.IsNullOrWhitespace())
{
_logger.LogDebug($"Attributes are missing a filter's hash attribute of '{filter.Attribute}', marking as filtered out");
_logger.LogDebug("Attributes are missing a filter\'s hash attribute of \'{FilterAttribute}\', marking as filtered out", filter.Attribute);
return true;
}

Expand All @@ -485,7 +485,7 @@ private bool IsFilteredOut(IEnumerable<Filter> filters)

if (!isInAnyRange)
{
_logger.LogDebug($"This run is not in any range associated with a filter, marking as filtered out");
_logger.LogDebug("This run is not in any range associated with a filter, marking as filtered out");
return true;
}
}
Expand All @@ -505,7 +505,7 @@ private bool IsIncludedInRollout(string seed, string hashAttribute = null, Bucke

if (hashValue is null)
{
_logger.LogDebug($"Attributes do not have a value for hash attribute '{hashAttribute}', marking as excluded from rollout");
_logger.LogDebug("Attributes do not have a value for hash attribute \'{HashAttribute}\', marking as excluded from rollout", hashAttribute);
return false;
}

Expand Down Expand Up @@ -588,7 +588,7 @@ private void TryToTrack(Experiment experiment, ExperimentResult result)
}
catch (Exception ex)
{
_logger.LogError(ex, $"Encountered unhandled exception during tracking callback for experiment with combined key '{key}'");
_logger.LogError(ex, "Encountered unhandled exception during tracking callback for experiment with combined key \'{Key}\'", key);
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions GrowthBook/Providers/ConditionEvaluationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal sealed class ConditionEvaluationProvider : IConditionEvaluationProvider
public bool EvalCondition(JToken attributes, JObject condition)
{
_logger.LogInformation("Beginning to evaluate attributes based on the provided JSON condition");
_logger.LogDebug($"Attribute evaluation is based on the JSON condition '{condition}'");
_logger.LogDebug("Attribute evaluation is based on the JSON condition \'{Condition}\'", condition);

if (condition.ContainsKey("$or"))
{
Expand Down Expand Up @@ -108,7 +108,7 @@ private bool EvalAnd(JToken attributes, JArray conditions)
/// <returns>True if the condition value matches the attribute value.</returns>
private bool EvalConditionValue(JToken conditionValue, JToken attributeValue)
{
_logger.LogDebug($"Evaluating condition value '{conditionValue}'");
_logger.LogDebug("Evaluating condition value \'{ConditionValue}\'", conditionValue);

if (conditionValue.Type == JTokenType.Object)
{
Expand Down Expand Up @@ -143,7 +143,7 @@ private bool ElemMatch(JObject condition, JToken attributeValue)
{
if (attributeValue?.Type != JTokenType.Array)
{
_logger.LogDebug($"Unable to match array elements with a non-array type of '{attributeValue.Type}'");
_logger.LogDebug("Unable to match array elements with a non-array type of '{AttributeValueType}'", attributeValue.Type);
return false;
}

Expand All @@ -162,7 +162,7 @@ private bool ElemMatch(JObject condition, JToken attributeValue)

return false;
}

/// <summary>
/// A switch that handles all the possible operators.
/// </summary>
Expand All @@ -172,7 +172,7 @@ private bool ElemMatch(JObject condition, JToken attributeValue)
/// <returns></returns>
private bool EvalOperatorCondition(string op, JToken attributeValue, JToken conditionValue)
{
_logger.LogDebug($"Evaluating operator condition '{op}'");
_logger.LogDebug("Evaluating operator condition \'{Op}\'", op);

if (op == "$eq")
{
Expand Down Expand Up @@ -311,7 +311,7 @@ private bool EvalOperatorCondition(string op, JToken attributeValue, JToken cond
return CompareVersions(attributeValue, conditionValue, x => x >= 0);
}

_logger.LogWarning($"Unable to handle unsupported operator condition '{op}', failing the condition");
_logger.LogWarning("Unable to handle unsupported operator condition \'{Op}\', failing the condition", op);

return false;
}
Expand Down

0 comments on commit adf1f5f

Please sign in to comment.