From c1488673aa3a22bd87a6af080d8272cab678ec8f Mon Sep 17 00:00:00 2001 From: Adam Simon <adamosimoni@gmail.com> Date: Wed, 15 Nov 2023 09:29:15 +0100 Subject: [PATCH] Improve error handling consistency in prerequisite flag evaluation --- .../ConfigV2EvaluationTests.cs | 107 +++++++++++++----- .../EvaluationLogTests.cs | 14 +-- .../Helpers/LoggingHelper.cs | 4 +- .../circular_dependency_override.json | 28 ----- .../evaluationlog/circular_dependency.json | 14 --- .../circular_dependency.txt | 21 ---- .../data/test_circulardependency_v6.json | 48 ++++---- .../Evaluation/RolloutEvaluator.cs | 32 +++--- src/ConfigCatClient/Logging/LogMessages.cs | 5 - 9 files changed, 119 insertions(+), 154 deletions(-) delete mode 100644 src/ConfigCat.Client.Tests/data/evaluationlog/_overrides/circular_dependency_override.json delete mode 100644 src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency.json delete mode 100644 src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency/circular_dependency.txt diff --git a/src/ConfigCat.Client.Tests/ConfigV2EvaluationTests.cs b/src/ConfigCat.Client.Tests/ConfigV2EvaluationTests.cs index ce63e563..aa3f8adf 100644 --- a/src/ConfigCat.Client.Tests/ConfigV2EvaluationTests.cs +++ b/src/ConfigCat.Client.Tests/ConfigV2EvaluationTests.cs @@ -222,49 +222,96 @@ public void UnicodeMatrixTests(string configLocation, string settingKey, string userId, userEmail, userCountry, userCustomAttributeName, userCustomAttributeValue); } - [TestMethod] - public void CircularDependencyTest() + [DataTestMethod] + [DataRow("key1", "'key1' -> 'key1'")] + [DataRow("key2", "'key2' -> 'key3' -> 'key2'")] + [DataRow("key4", "'key4' -> 'key3' -> 'key2' -> 'key3'")] + public void PrerequisiteFlagCircularDependencyTest(string key, string dependencyCycle) { var config = new ConfigLocation.LocalFile("data", "test_circulardependency_v6.json").FetchConfig(); - var logEvents = new List<LogEvent>(); - var logger = LoggingHelper.CreateCapturingLogger(logEvents); - + var logger = new Mock<IConfigCatLogger>().Object.AsWrapper(); var evaluator = new RolloutEvaluator(logger); - const string key = "key1"; - var evaluationDetails = evaluator.Evaluate<object?>(config!.Settings, key, defaultValue: null, user: null, remoteConfig: null, logger); - - Assert.AreEqual(4, logEvents.Count); + var ex = Assert.ThrowsException<InvalidOperationException>(() => evaluator.Evaluate<object?>(config!.Settings, key, defaultValue: null, user: null, remoteConfig: null, logger)); - Assert.AreEqual(3, logEvents.Count(evt => evt.EventId == 3005)); + StringAssert.Contains(ex.Message, "Circular dependency detected"); + StringAssert.Contains(ex.Message, dependencyCycle); + } - Assert.IsTrue(logEvents.Any(evt => evt.Level == LogLevel.Warning - && (string?)evt.Message.ArgValues[1] == "key1" - && (string?)evt.Message.ArgValues[2] == "'key1' -> 'key1'")); + [DataTestMethod] + [DataRow("stringDependsOnBool", "mainBoolFlag", true, "Dog")] + [DataRow("stringDependsOnBool", "mainBoolFlag", false, "Cat")] + [DataRow("stringDependsOnBool", "mainBoolFlag", "1", null)] + [DataRow("stringDependsOnBool", "mainBoolFlag", 1, null)] + [DataRow("stringDependsOnBool", "mainBoolFlag", 1.0, null)] + [DataRow("stringDependsOnBool", "mainBoolFlag", new[] { true }, null)] + [DataRow("stringDependsOnBool", "mainBoolFlag", null, null)] + [DataRow("stringDependsOnString", "mainStringFlag", "private", "Dog")] + [DataRow("stringDependsOnString", "mainStringFlag", "Private", "Cat")] + [DataRow("stringDependsOnString", "mainStringFlag", true, null)] + [DataRow("stringDependsOnString", "mainStringFlag", 1, null)] + [DataRow("stringDependsOnString", "mainStringFlag", 1.0, null)] + [DataRow("stringDependsOnString", "mainStringFlag", new[] { "private" }, null)] + [DataRow("stringDependsOnString", "mainStringFlag", null, null)] + [DataRow("stringDependsOnInt", "mainIntFlag", 2, "Dog")] + [DataRow("stringDependsOnInt", "mainIntFlag", 1, "Cat")] + [DataRow("stringDependsOnInt", "mainIntFlag", "2", null)] + [DataRow("stringDependsOnInt", "mainIntFlag", true, null)] + [DataRow("stringDependsOnInt", "mainIntFlag", 2.0, null)] + [DataRow("stringDependsOnInt", "mainIntFlag", new[] { 2 }, null)] + [DataRow("stringDependsOnInt", "mainIntFlag", null, null)] + [DataRow("stringDependsOnDouble", "mainDoubleFlag", 0.1, "Dog")] + [DataRow("stringDependsOnDouble", "mainDoubleFlag", 0.11, "Cat")] + [DataRow("stringDependsOnDouble", "mainDoubleFlag", "0.1", null)] + [DataRow("stringDependsOnDouble", "mainDoubleFlag", true, null)] + [DataRow("stringDependsOnDouble", "mainDoubleFlag", 1, null)] + [DataRow("stringDependsOnDouble", "mainDoubleFlag", new[] { 0.1 }, null)] + [DataRow("stringDependsOnDouble", "mainDoubleFlag", null, null)] + public async Task PrerequisiteFlagComparisonValueTypeMismatchTest(string key, string prerequisiteFlagKey, object? prerequisiteFlagValue, object? expectedValue) + { + var cdnLocation = (ConfigLocation.Cdn)new FlagDependencyMatrixTestsDescriptor().ConfigLocation; - Assert.IsTrue(logEvents.Any(evt => evt.Level == LogLevel.Warning - && (string?)evt.Message.ArgValues[1] == "key2" - && (string?)evt.Message.ArgValues[2] == "'key1' -> 'key2' -> 'key1'")); + var logEvents = new List<LogEvent>(); + var logger = LoggingHelper.CreateCapturingLogger(logEvents); - Assert.IsTrue(logEvents.Any(evt => evt.Level == LogLevel.Warning - && (string?)evt.Message.ArgValues[1] == "key3" - && (string?)evt.Message.ArgValues[2] == "'key1' -> 'key3' -> 'key3'")); + var overrideDictionary = new Dictionary<string, object> { [prerequisiteFlagKey] = prerequisiteFlagValue! }; - var evaluateLogEvent = logEvents.FirstOrDefault(evt => evt.Level == LogLevel.Info && evt.EventId == 5000); - Assert.IsNotNull(evaluateLogEvent); + var options = new ConfigCatClientOptions + { + FlagOverrides = FlagOverrides.LocalDictionary(overrideDictionary, OverrideBehaviour.LocalOverRemote), + PollingMode = PollingModes.ManualPoll, + Logger = logger + }; + cdnLocation.ConfigureBaseUrl(options); - StringAssert.Matches((string?)evaluateLogEvent.Message.ArgValues[0], new Regex( - "THEN 'key1-prereq1' => " + Regex.Escape(RolloutEvaluator.CircularDependencyError) + Environment.NewLine - + @"\s+" + Regex.Escape(RolloutEvaluator.TargetingRuleIgnoredMessage))); + using var client = new ConfigCatClient(cdnLocation.SdkKey, options); + await client.ForceRefreshAsync(); - StringAssert.Matches((string?)evaluateLogEvent.Message.ArgValues[0], new Regex( - "THEN 'key2-prereq1' => " + Regex.Escape(RolloutEvaluator.CircularDependencyError) + Environment.NewLine - + @"\s+" + Regex.Escape(RolloutEvaluator.TargetingRuleIgnoredMessage))); + var actualValue = await client.GetValueAsync(key, (object?)null); + Assert.AreEqual(expectedValue, actualValue); - StringAssert.Matches((string?)evaluateLogEvent.Message.ArgValues[0], new Regex( - "THEN 'key3-prereq1' => " + Regex.Escape(RolloutEvaluator.CircularDependencyError) + Environment.NewLine - + @"\s+" + Regex.Escape(RolloutEvaluator.TargetingRuleIgnoredMessage))); + if (expectedValue is null) + { + var errors = logEvents.Where(evt => evt.Level == LogLevel.Error).ToArray(); + Assert.AreEqual(1, errors.Length); + Assert.AreEqual(1002, errors[0].EventId); + var ex = errors[0].Exception; + Assert.IsInstanceOfType(ex, typeof(InvalidOperationException)); + + if (prerequisiteFlagValue == null) + { + StringAssert.Contains(ex!.Message, "Setting value is null"); + } + else if (prerequisiteFlagValue.GetType().ToSettingType() == Setting.UnknownType) + { + StringAssert.Matches(ex!.Message, new Regex("Setting value '[^']+' is of an unsupported type")); + } + else + { + StringAssert.Matches(ex!.Message, new Regex("Type mismatch between comparison value '[^']+' and prerequisite flag '[^']+'")); + } + } } [DataTestMethod] diff --git a/src/ConfigCat.Client.Tests/EvaluationLogTests.cs b/src/ConfigCat.Client.Tests/EvaluationLogTests.cs index 54f6faf2..87852101 100644 --- a/src/ConfigCat.Client.Tests/EvaluationLogTests.cs +++ b/src/ConfigCat.Client.Tests/EvaluationLogTests.cs @@ -136,16 +136,6 @@ public void ComparatorsTests(string testSetName, string? sdkKey, string? baseUrl RunTest(testSetName, sdkKey, baseUrlOrOverrideFileName, key, defaultValue, userObject, expectedReturnValue, expectedLogFileName); } - private static IEnumerable<object?[]> GetPrerequisiteFlagConditionsWithCircularDependencyTests() => GetTests("circular_dependency"); - - [DataTestMethod] - [DynamicData(nameof(GetPrerequisiteFlagConditionsWithCircularDependencyTests), DynamicDataSourceType.Method)] - public void PrerequisiteFlagConditionsWithCircularDependencyTests(string testSetName, string? sdkKey, string? baseUrlOrOverrideFileName, - string key, string? defaultValue, string userObject, string? expectedReturnValue, string expectedLogFileName) - { - RunTest(testSetName, sdkKey, baseUrlOrOverrideFileName, key, defaultValue, userObject, expectedReturnValue, expectedLogFileName); - } - private static IEnumerable<object?[]> GetEpochDateValidationTests() => GetTests("epoch_date_validation"); [DataTestMethod] @@ -243,7 +233,7 @@ private static void RunTest(string testSetName, string? sdkKey, string? baseUrlO } var logEvents = new List<LogEvent>(); - var logger = LoggingHelper.CreateCapturingLogger(logEvents); + var logger = LoggingHelper.CreateCapturingLogger(logEvents).AsWrapper(); ConfigLocation configLocation = sdkKey is { Length: > 0 } ? new ConfigLocation.Cdn(sdkKey, baseUrlOrOverrideFileName) @@ -313,7 +303,7 @@ public void EvaluationLogShouldBeBuiltOnlyWhenNecessary(LogLevel logLevel, bool var settings = new ConfigLocation.Cdn("configcat-sdk-1/PKDVCLf-Hq-h-kCzMp-L7Q/AG6C1ngVb0CvM07un6JisQ").FetchConfigCached().Settings; var logEvents = new List<LogEvent>(); - var logger = LoggingHelper.CreateCapturingLogger(logEvents, logLevel); + var logger = LoggingHelper.CreateCapturingLogger(logEvents, logLevel).AsWrapper(); var evaluator = new RolloutEvaluator(logger); diff --git a/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs b/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs index 3cfef7aa..1ed18ede 100644 --- a/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs +++ b/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs @@ -24,7 +24,7 @@ public static LoggerWrapper AsWrapper(this IConfigCatLogger logger, Hooks? hooks return new LoggerWrapper(logger, hooks); } - public static LoggerWrapper CreateCapturingLogger(List<LogEvent> logEvents, LogLevel logLevel = LogLevel.Info) + public static IConfigCatLogger CreateCapturingLogger(List<LogEvent> logEvents, LogLevel logLevel = LogLevel.Info) { var loggerMock = new Mock<IConfigCatLogger>(); @@ -33,6 +33,6 @@ public static LoggerWrapper CreateCapturingLogger(List<LogEvent> logEvents, LogL loggerMock.Setup(logger => logger.Log(It.IsAny<LogLevel>(), It.IsAny<LogEventId>(), ref It.Ref<FormattableLogMessage>.IsAny, It.IsAny<Exception>())) .Callback(delegate (LogLevel level, LogEventId eventId, ref FormattableLogMessage msg, Exception ex) { logEvents.Add(new LogEvent(level, eventId, ref msg, ex)); }); - return loggerMock.Object.AsWrapper(); + return loggerMock.Object; } } diff --git a/src/ConfigCat.Client.Tests/data/evaluationlog/_overrides/circular_dependency_override.json b/src/ConfigCat.Client.Tests/data/evaluationlog/_overrides/circular_dependency_override.json deleted file mode 100644 index 0e9d9d95..00000000 --- a/src/ConfigCat.Client.Tests/data/evaluationlog/_overrides/circular_dependency_override.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "p": { - "u": "https://cdn-global.configcat.com", - "r": 0 - }, - "f": { - "key1": { - "t": 1, - "v": { "s": "value1" }, - "r": [ - {"c": [{"p": {"f": "key2", "c": 0, "v": {"s": "fourth"}}}], "s": {"v": {"s": "first"}}}, - {"c": [{"p": {"f": "key3", "c": 0, "v": {"s": "value3"}}}], "s": {"v": {"s": "second"}}} - ] - }, - "key2": { - "t": 1, - "v": { "s": "value2" }, - "r": [ - {"c": [{"p": {"f": "key1", "c": 0, "v": {"s": "value1"}}}], "s": {"v": {"s": "third"}}}, - {"c": [{"p": {"f": "key3", "c": 0, "v": {"s": "value3"}}}], "s": {"v": {"s": "fourth"}}} - ] - }, - "key3": { - "t": 1, - "v": { "s": "value3" } - } - } -} diff --git a/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency.json b/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency.json deleted file mode 100644 index cb50d22f..00000000 --- a/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "jsonOverride": "circular_dependency_override.json", - "tests": [ - { - "key": "key1", - "defaultValue": "default", - "user": { - "Identifier": "1234" - }, - "returnValue": "first", - "expectedLog": "circular_dependency.txt" - } - ] -} diff --git a/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency/circular_dependency.txt b/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency/circular_dependency.txt deleted file mode 100644 index 3dcbcb0f..00000000 --- a/src/ConfigCat.Client.Tests/data/evaluationlog/circular_dependency/circular_dependency.txt +++ /dev/null @@ -1,21 +0,0 @@ -WARNING [3005] Cannot evaluate condition (Flag 'key1' EQUALS 'value1') for setting 'key2' (circular dependency detected between the following depending flags: 'key1' -> 'key2' -> 'key1'). Please check your feature flag definition and eliminate the circular dependency. -INFO [5000] Evaluating 'key1' for User '{"Identifier":"1234"}' - Evaluating targeting rules and applying the first match if any: - - IF Flag 'key2' EQUALS 'fourth' - ( - Evaluating prerequisite flag 'key2': - Evaluating targeting rules and applying the first match if any: - - IF Flag 'key1' EQUALS 'value1' THEN 'third' => cannot evaluate, circular dependency detected - The current targeting rule is ignored and the evaluation continues with the next rule. - - IF Flag 'key3' EQUALS 'value3' - ( - Evaluating prerequisite flag 'key3': - Prerequisite flag evaluation result: 'value3'. - Condition (Flag 'key3' EQUALS 'value3') evaluates to true. - ) - THEN 'fourth' => MATCH, applying rule - Prerequisite flag evaluation result: 'fourth'. - Condition (Flag 'key2' EQUALS 'fourth') evaluates to true. - ) - THEN 'first' => MATCH, applying rule - Returning 'first'. diff --git a/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json b/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json index d8a7d047..a8a9e176 100644 --- a/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json +++ b/src/ConfigCat.Client.Tests/data/test_circulardependency_v6.json @@ -6,7 +6,7 @@ "f": { "key1": { "t": 1, - "v": { "s": "value1" }, + "v": { "s": "key1-value" }, "r": [ { "c": [ @@ -14,59 +14,53 @@ "p": { "f": "key1", "c": 0, - "v": { "s": "key1-prereq1" } + "v": { "s": "key1-prereq" } } } ], - "s": { "v": { "s": "key1-prereq1" } } - }, - { - "c": [ - { - "p": { - "f": "key2", - "c": 0, - "v": { "s": "key1-prereq2" } - } - } - ], - "s": { "v": { "s": "key1-prereq2" } } - }, + "s": { "v": { "s": "key1-prereq" } } + } + ] + }, + "key2": { + "t": 1, + "v": { "s": "key2-value" }, + "r": [ { "c": [ { "p": { "f": "key3", "c": 0, - "v": { "s": "key1-prereq3" } + "v": { "s": "key3-prereq" } } } ], - "s": { "v": { "s": "key1-prereq3" } } + "s": { "v": { "s": "key2-prereq" } } } ] }, - "key2": { + "key3": { "t": 1, - "v": { "s": "value2" }, + "v": { "s": "key3-value" }, "r": [ { "c": [ { "p": { - "f": "key1", + "f": "key2", "c": 0, - "v": { "s": "key2-prereq1" } + "v": { "s": "key2-prereq" } } } ], - "s": { "v": { "s": "key2-prereq1" } } + "s": { "v": { "s": "key3-prereq" } } } ] }, - "key3": { + "key4": { "t": 1, - "v": { "s": "value3" }, + "v": { "s": "key4-value" }, "r": [ { "c": [ @@ -74,11 +68,11 @@ "p": { "f": "key3", "c": 0, - "v": { "s": "key3-prereq1" } + "v": { "s": "key3-prereq" } } } ], - "s": { "v": { "s": "key3-prereq1" } } + "s": { "v": { "s": "key4-prereq" } } } ] } diff --git a/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs b/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs index 8784fdba..a86eeed4 100644 --- a/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs +++ b/src/ConfigCatClient/Evaluation/RolloutEvaluator.cs @@ -13,7 +13,6 @@ internal sealed class RolloutEvaluator : IRolloutEvaluator internal const string MissingUserObjectError = "cannot evaluate, User Object is missing"; internal const string MissingUserAttributeError = "cannot evaluate, the User.{0} attribute is missing"; internal const string InvalidUserAttributeError = "cannot evaluate, the User.{0} attribute is invalid ({1})"; - internal const string CircularDependencyError = "cannot evaluate, circular dependency detected"; internal const string TargetingRuleIgnoredMessage = "The current targeting rule is ignored and the evaluation continues with the next rule."; @@ -46,7 +45,7 @@ public EvaluateResult Evaluate<T>(T defaultValue, ref EvaluateContext context, [ returnValue = default!; try { - var result = EvaluateSetting(ref context); + EvaluateResult result; if (typeof(T) != typeof(object)) { @@ -64,13 +63,18 @@ public EvaluateResult Evaluate<T>(T defaultValue, ref EvaluateContext context, [ throw new InvalidOperationException( "The type of a setting must match the type of the specified default value. " + $"Setting's type was {context.Setting.SettingType} but the default value's type was {typeof(T)}. " - + $"Please use a default value which corresponds to the setting type {context.Setting.SettingType}."); + + $"Please use a default value which corresponds to the setting type {context.Setting.SettingType}. " + + "Learn more: https://configcat.com/docs/sdk-reference/dotnet/#setting-type-mapping"); } + result = EvaluateSetting(ref context); + returnValue = result.Value.GetValue<T>(expectedSettingType)!; } else { + result = EvaluateSetting(ref context); + returnValue = (T)(context.Setting.SettingType != Setting.UnknownType ? result.Value.GetValue(context.Setting.SettingType)! : result.Value.GetValue()!); @@ -281,7 +285,7 @@ private bool EvaluateConditions<TCondition>(TCondition[] conditions, TargetingRu case PrerequisiteFlagCondition prerequisiteFlagCondition: conditionResult = EvaluatePrerequisiteFlagCondition(prerequisiteFlagCondition, ref context, out error); - newLineBeforeThen = error is null || error != CircularDependencyError || conditions.Length > 1; + newLineBeforeThen = true; break; case SegmentCondition segmentCondition: @@ -721,10 +725,12 @@ private bool EvaluatePrerequisiteFlagCondition(PrerequisiteFlagCondition conditi throw new InvalidOperationException("Prerequisite flag key is missing or invalid."); } - var comparisonValue = condition.ComparisonValue.GetValue(throwIfInvalid: false); - if (comparisonValue is null || comparisonValue.GetType().ToSettingType() != prerequisiteFlag.SettingType) + var comparisonValue = EnsureComparisonValue(condition.ComparisonValue.GetValue(throwIfInvalid: false)); + + var expectedSettingType = comparisonValue.GetType().ToSettingType(); + if (prerequisiteFlag.SettingType != Setting.UnknownType && prerequisiteFlag.SettingType != expectedSettingType) { - EnsureComparisonValue<string>(null); + throw new InvalidOperationException($"Type mismatch between comparison value '{comparisonValue}' and prerequisite flag '{prerequisiteFlagKey}'."); } context.VisitedFlags.Add(context.Key); @@ -732,11 +738,7 @@ private bool EvaluatePrerequisiteFlagCondition(PrerequisiteFlagCondition conditi { context.VisitedFlags.Add(prerequisiteFlagKey!); var dependencyCycle = new StringListFormatter(context.VisitedFlags).ToString("a", CultureInfo.InvariantCulture); - this.logger.CircularDependencyDetected(condition.ToString(), context.Key, dependencyCycle); - - context.VisitedFlags.RemoveRange(context.VisitedFlags.Count - 2, 2); - error = CircularDependencyError; - return false; + throw new InvalidOperationException($"Circular dependency detected between the following depending flags: {dependencyCycle}."); } var prerequisiteFlagContext = new EvaluateContext(prerequisiteFlagKey!, prerequisiteFlag!, ref context); @@ -750,13 +752,13 @@ private bool EvaluatePrerequisiteFlagCondition(PrerequisiteFlagCondition conditi context.VisitedFlags.RemoveAt(context.VisitedFlags.Count - 1); - var prerequisiteFlagValue = prerequisiteFlagEvaluateResult.Value.GetValue(prerequisiteFlag!.SettingType, throwIfInvalid: false); + var prerequisiteFlagValue = prerequisiteFlagEvaluateResult.Value.GetValue(expectedSettingType, throwIfInvalid: true)!; var comparator = condition.Comparator; var result = comparator switch { - PrerequisiteFlagComparator.Equals => prerequisiteFlagValue is not null && prerequisiteFlagValue.Equals(comparisonValue), - PrerequisiteFlagComparator.NotEquals => prerequisiteFlagValue is not null && !prerequisiteFlagValue.Equals(comparisonValue), + PrerequisiteFlagComparator.Equals => prerequisiteFlagValue.Equals(comparisonValue), + PrerequisiteFlagComparator.NotEquals => !prerequisiteFlagValue.Equals(comparisonValue), _ => throw new InvalidOperationException("Comparison operator is invalid.") }; diff --git a/src/ConfigCatClient/Logging/LogMessages.cs b/src/ConfigCatClient/Logging/LogMessages.cs index 34de3d30..193e1239 100644 --- a/src/ConfigCatClient/Logging/LogMessages.cs +++ b/src/ConfigCatClient/Logging/LogMessages.cs @@ -136,11 +136,6 @@ public static FormattableLogMessage UserObjectAttributeIsInvalid(this LoggerWrap $"Cannot evaluate condition ({condition}) for setting '{key}' ({reason}). Please check the User.{attributeName} attribute and make sure that its value corresponds to the comparison operator.", "CONDITION", "KEY", "REASON", "ATTRIBUTE_NAME"); - public static FormattableLogMessage CircularDependencyDetected(this LoggerWrapper logger, string condition, string key, string dependencyCycle) => logger.LogInterpolated( - LogLevel.Warning, 3005, - $"Cannot evaluate condition ({condition}) for setting '{key}' (circular dependency detected between the following depending flags: {dependencyCycle}). Please check your feature flag definition and eliminate the circular dependency.", - "CONDITION", "KEY", "DEPENDENCY_CYCLE"); - public static FormattableLogMessage ConfigServiceCannotInitiateHttpCalls(this LoggerWrapper logger) => logger.Log( LogLevel.Warning, 3200, "Client is in offline mode, it cannot initiate HTTP calls.");