Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-990111: Add easy logging improvements #849

Merged
merged 47 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
5bd6504
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 13, 2024
3cfcc69
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Jan 13, 2024
e0dbec7
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 15, 2024
7da9e4f
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 15, 2024
8a9e74a
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
e592ce4
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
55b0694
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
387ab55
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
9383efc
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
dcf951e
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
2e7c648
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
1d208cf
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
b2d3c10
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
98b716d
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
7646360
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
5911f7a
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
9d2596c
SNOW-990111: Add easy logging improvements
sfc-gh-ext-simba-lf Jan 16, 2024
8d31672
SNOW-990111: Refactor easy logging improvements
sfc-gh-ext-simba-lf Jan 17, 2024
e185fbd
SNOW-990111: Refactor easy logging improvements
sfc-gh-ext-simba-lf Jan 17, 2024
bcd0936
SNOW-990111: Refactor easy logging improvements
sfc-gh-ext-simba-lf Jan 19, 2024
08e3785
SNOW-990111: Refactor easy logging improvements
sfc-gh-ext-simba-lf Jan 19, 2024
e40116a
SNOW-990111: Refactor easy logging changes
sfc-gh-ext-simba-lf Jan 23, 2024
47d07bf
SNOW-990111: Refactor easy logging changes
sfc-gh-ext-simba-lf Jan 23, 2024
1c43705
SNOW-990111: Refactor easy logging changes
sfc-gh-ext-simba-lf Jan 23, 2024
eff9425
SNOW-990111: Add test for EasyLoggerUtil
sfc-gh-ext-simba-lf Jan 24, 2024
f708021
SNOW-990111: Modify config finder tests
sfc-gh-ext-simba-lf Jan 24, 2024
8837fe7
SNOW-990111: Modify config finder tests
sfc-gh-ext-simba-lf Jan 24, 2024
5db9b0b
Merge branch 'master' of https://github.com/snowflakedb/snowflake-con…
sfc-gh-ext-simba-lf Jan 24, 2024
16a9979
SNOW-990111: Modify config finder tests
sfc-gh-ext-simba-lf Jan 24, 2024
190308d
SNOW-990111: Modify config finder tests
sfc-gh-ext-simba-lf Jan 24, 2024
49832a8
SNOW-990111: Modify config finder tests
sfc-gh-ext-simba-lf Jan 24, 2024
e17dcf3
SNOW-990111: Modify config finder tests
sfc-gh-ext-simba-lf Jan 24, 2024
ea8d52e
SNOW-990111: Modify config finder tests
sfc-gh-ext-simba-lf Jan 24, 2024
3fabd05
SNOW-990111: Remove umask test and create dotnet subfolder with Mono.…
sfc-gh-ext-simba-lf Jan 25, 2024
5635772
SNOW-990111: Rename EasyLoggingUtil and edit EasyLoggerManagerTest
sfc-gh-ext-simba-lf Jan 25, 2024
31fefeb
SNOW-990111: Update EasyLoggingStarterTest
sfc-gh-ext-simba-lf Jan 25, 2024
bb70c74
SNOW-990111: Refactor tests
sfc-gh-ext-simba-lf Jan 25, 2024
9f2c874
SNOW-990111: Refactor hash set to list
sfc-gh-ext-simba-lf Jan 26, 2024
16335ab
SNOW-990111: Modify check to allow permissions without user write
sfc-gh-ext-simba-lf Jan 26, 2024
0f266fc
SNOW-990111: Refactor getting home directory
sfc-gh-ext-simba-lf Jan 26, 2024
a5be03f
Refactor UnixOperations and clean up
sfc-gh-ext-simba-lf Jan 30, 2024
6524998
Refactor UnixOperations and clean up
sfc-gh-ext-simba-lf Jan 30, 2024
59945da
Refactor checking directory permissions
sfc-gh-ext-simba-lf Jan 30, 2024
17e9efe
Revert log directory check, remove unused variable, and refactor perm…
sfc-gh-ext-simba-lf Jan 31, 2024
065941f
Remove unused variable
sfc-gh-ext-simba-lf Jan 31, 2024
1c0babc
SNOW-990111-: Modify warning message
sfc-gh-ext-simba-lf Feb 1, 2024
dd824e6
SNOW-990111: Move where logging is executed
sfc-gh-ext-simba-lf Feb 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

using System;
using System.IO;
using System.Runtime.InteropServices;
using Moq;
using NUnit.Framework;
using Snowflake.Data.Configuration;
using Snowflake.Data.Core.Tools;
using static Snowflake.Data.Tests.UnitTests.Configuration.EasyLoggingConfigGenerator;

namespace Snowflake.Data.Tests.UnitTests.Configuration
{
Expand All @@ -16,7 +18,7 @@ public class EasyLoggingConfigFinderTest
{
private const string InputConfigFilePath = "input_config.json";
private const string EnvironmentalConfigFilePath = "environmental_config.json";
private const string HomeDirectory = "/home/user";
private static readonly string HomeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
private static readonly string s_driverConfigFilePath = Path.Combine(".", EasyLoggingConfigFinder.ClientConfigFileName);
private static readonly string s_homeConfigFilePath = Path.Combine(HomeDirectory, EasyLoggingConfigFinder.ClientConfigFileName);
private static readonly string s_tempConfigFilePath = Path.Combine(Path.GetTempPath(), EasyLoggingConfigFinder.ClientConfigFileName);
Expand Down Expand Up @@ -104,7 +106,42 @@ public void TestThatTakesFilePathFromHomeLocationWhenNoInputParamEnvironmentVarN
}

[Test]
public void TestThatTakesFilePathFromTempDirectoryWhenNoOtherWaysPossible()
public void TestThatTakesFilePathFromHomeDirectoryWhenNoOtherWaysPossible()
{
// arrange
MockFileOnHomePath();

// act
var filePath = t_finder.FindConfigFilePath(null);

// assert
Assert.AreEqual(s_homeConfigFilePath, filePath);
}

[Test]
[Ignore("This test requires manual interaction and therefore cannot be run in CI")]
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
public void TestThatConfigFileIsNotUsedIfOthersCanModifyTheConfigFile()
{
// Note: In order to trigger the error, the config file permissions should allow other users to modify the file
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
{
// arrange
var configFilePath = CreateConfigTempFile(Config(EasyLoggingLogLevel.Warn.ToString(), InputConfigFilePath));

// act
var thrown = Assert.Throws<Exception>(() => t_finder.FindConfigFilePath(configFilePath));

// assert
Assert.IsNotNull(thrown);
Assert.AreEqual(thrown.Message, "Error due to other users having permission to modify the config file");

// cleanup
File.Delete(configFilePath);
}
}

[Test]
public void TestThatDoesNotTakeFilePathFromTempDirectoryWhenNoOtherWaysPossible()
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
{
// arrange
MockFileOnTempPath();
Expand All @@ -113,7 +150,7 @@ public void TestThatTakesFilePathFromTempDirectoryWhenNoOtherWaysPossible()
var filePath = t_finder.FindConfigFilePath(null);

// assert
Assert.AreEqual(s_tempConfigFilePath, filePath);
Assert.Null(filePath);
}

[Test]
Expand Down
34 changes: 33 additions & 1 deletion Snowflake.Data.Tests/UnitTests/Logger/EasyLoggerManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using NUnit.Framework;
using Snowflake.Data.Configuration;
using Snowflake.Data.Core;
Expand All @@ -21,7 +22,7 @@ public class EasyLoggerManagerTest
private const string WarnMessage = "Easy logging Warn message";
private const string ErrorMessage = "Easy logging Error message";
private const string FatalMessage = "Easy logging Fatal message";
private static readonly string s_logsDirectory = Path.GetTempPath();
private static readonly string s_logsDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);

[ThreadStatic]
private static string t_directoryLogPath;
Expand Down Expand Up @@ -92,6 +93,37 @@ public void TestThatLogsToProperFileWithProperLogLevelOnly()
Assert.That(logLines, Has.Exactly(1).Matches<string>(s => s.Contains(FatalMessage)));
}

[Test]
public void TestThatUnknownFieldsAreLogged()
{
// arrange
string ConfigWithUnknownFields = @"{
""common"": {
""log_level"": ""warn"",
""log_path"": ""path"",
""fake_log_field_1"": ""abc"",
""fake_log_field_2"": ""123""
}
}";
var configFilePath = Guid.NewGuid().ToString() + ".json";
using (var writer = File.CreateText(configFilePath))
{
writer.Write(ConfigWithUnknownFields.Replace("\\", "\\\\"));
}
EasyLoggerManager.Instance.ReconfigureEasyLogging(EasyLoggingLogLevel.Warn, t_directoryLogPath);

// act
var parser = new EasyLoggingConfigParser();
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
var config = parser.Parse(configFilePath);
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved

// assert
var logLines = File.ReadLines(FindLogFilePath(t_directoryLogPath));
Assert.That(logLines, Has.Exactly(2).Matches<string>(s => s.Contains($"Unknown field from config: ")));
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved

// cleanup
File.Delete(configFilePath);
}

private static string RandomLogsDirectoryPath()
{
var randomName = Path.GetRandomFileName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.IO;
using System.Runtime.InteropServices;
using Moq;
using NUnit.Framework;
using Snowflake.Data.Configuration;
Expand All @@ -16,8 +17,8 @@ namespace Snowflake.Data.Tests.UnitTests.Session
[TestFixture]
public class EasyLoggingStarterTest
{

private const string LogPath = "/some-logs-path/some-folder";
private static readonly string HomeDirectory = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
private static readonly string LogPath = Path.Combine(HomeDirectory, "some-logs-path/some-folder");
private const string ConfigPath = "/some-path/config.json";
private const string AnotherConfigPath = "/another/path";
private static readonly string s_expectedLogPath = Path.Combine(LogPath, "dotnet");
Expand All @@ -39,7 +40,15 @@ public class EasyLoggingStarterTest
LogPath = LogPath
}
};


private static readonly ClientConfig s_configWithNoLogPath = new ClientConfig
{
CommonProps = new ClientConfigCommonProps
{
LogLevel = "Info"
}
};

[ThreadStatic]
private static Mock<EasyLoggingConfigProvider> t_easyLoggingProvider;

Expand All @@ -49,6 +58,9 @@ public class EasyLoggingStarterTest
[ThreadStatic]
private static Mock<DirectoryOperations> t_directoryOperations;

[ThreadStatic]
private static Mock<EnvironmentOperations> t_environmentOperations;

[ThreadStatic]
private static EasyLoggingStarter t_easyLoggerStarter;

Expand All @@ -58,9 +70,60 @@ public void BeforeEach()
t_easyLoggingProvider = new Mock<EasyLoggingConfigProvider>();
t_easyLoggerManager = new Mock<EasyLoggerManager>();
t_directoryOperations = new Mock<DirectoryOperations>();
t_easyLoggerStarter = new EasyLoggingStarter(t_easyLoggingProvider.Object, t_easyLoggerManager.Object, t_directoryOperations.Object);
t_environmentOperations = new Mock<EnvironmentOperations>();
t_easyLoggerStarter = new EasyLoggingStarter(t_easyLoggingProvider.Object, t_easyLoggerManager.Object, t_directoryOperations.Object, t_environmentOperations.Object);
}


[Test]
public void TestThatCreatedDirectoryPermissionsFollowUmask()
{
// Note: To test with a different value than the default umask, it will have to be set before running this test
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
{
// arrange
t_easyLoggingProvider
.Setup(provider => provider.ProvideConfig(ConfigPath))
.Returns(s_configWithInfoLevel);
t_directoryOperations
.Setup(provider => provider.Exists(ConfigPath))
.Returns(Directory.Exists(ConfigPath));
t_directoryOperations
.Setup(provider => provider.CreateDirectory(s_expectedLogPath))
.Returns(Directory.CreateDirectory(s_expectedLogPath));

// act
t_easyLoggerStarter.Init(ConfigPath);
var umask = EasyLoggerUtil.AllPermissions - int.Parse(EasyLoggerUtil.CallBash("umask"));
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
string commandParameters = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "-c '%a'" : "-f %A";
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
var dirPermissions = EasyLoggerUtil.CallBash($"stat {commandParameters} {s_expectedLogPath}");

// assert
Assert.IsTrue(umask >= int.Parse(dirPermissions));
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved

// cleanup
Directory.Delete(s_expectedLogPath);
}
}

[Test]
public void TestThatThrowsErrorWhenLogPathAndHomeDirectoryIsNotSet()
{
// arrange
t_easyLoggingProvider
.Setup(provider => provider.ProvideConfig(ConfigPath))
.Returns(s_configWithNoLogPath);
t_environmentOperations
.Setup(provider => provider.GetFolderPath(Environment.SpecialFolder.UserProfile))
.Returns("");

// act
var thrown = Assert.Throws<Exception>(() => t_easyLoggerStarter.Init(ConfigPath));

// assert
Assert.IsNotNull(thrown);
Assert.AreEqual(thrown.Message, "No log path found for easy logging. Home directory is not configured and log path is not provided");
}

[Test]
public void TestThatConfiguresEasyLoggingOnlyOnceWhenInitializedWithConfigPath()
{
Expand Down
63 changes: 50 additions & 13 deletions Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

using System;
using System.IO;
using System.Runtime.InteropServices;
using Mono.Unix;
using Snowflake.Data.Core.Tools;
using Snowflake.Data.Log;

Expand Down Expand Up @@ -33,24 +35,34 @@

public virtual string FindConfigFilePath(string configFilePathFromConnectionString)
{
return GetFilePathFromInputParameter(configFilePathFromConnectionString)
?? GetFilePathEnvironmentVariable()
?? GetFilePathFromDriverLocation()
?? GetFilePathFromHomeDirectory()
?? GetFilePathFromTempDirectory();
var configFilePath = GetFilePathFromInputParameter(configFilePathFromConnectionString, "connection string")
?? GetFilePathEnvironmentVariable()
?? GetFilePathFromDriverLocation()
?? GetFilePathFromHomeDirectory();
if (configFilePath != null)
{
CheckIfValidPermissions(configFilePath);
}
return configFilePath;
}

private string GetFilePathEnvironmentVariable()
{
var filePath = _environmentOperations.GetEnvironmentVariable(ClientConfigEnvironmentName);
return GetFilePathFromInputParameter(filePath);
return GetFilePathFromInputParameter(filePath, "environment variable");
}

private string GetFilePathFromTempDirectory() => SearchForConfigInDirectory(Path.GetTempPath, "temp");

private string GetFilePathFromHomeDirectory() => SearchForConfigInDirectory(GetHomeDirectory, "home");

private string GetFilePathFromInputParameter(string filePath) => string.IsNullOrEmpty(filePath) ? null : filePath;

private string GetFilePathFromInputParameter(string filePath, string inputDescription)
{
if (string.IsNullOrEmpty(filePath))
{
return null;
}
s_logger.Info($"Using config file specified from {inputDescription}");
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
return filePath;
}

private string GetHomeDirectory() =>_environmentOperations.GetFolderPath(Environment.SpecialFolder.UserProfile);

Expand All @@ -61,13 +73,13 @@
try
{
var directory = directoryProvider.Invoke();
if (string.IsNullOrEmpty(directory))
if (string.IsNullOrEmpty(directory) || !Directory.Exists(directory))
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
{
return null;
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
}

var filePath = Path.Combine(directory, ClientConfigFileName);
return OnlyIfFileExists(filePath);
return OnlyIfFileExists(filePath, directoryDescription);
}
catch (Exception e)
{
Expand All @@ -76,6 +88,31 @@
}
}

private string OnlyIfFileExists(string filePath) => _fileOperations.Exists(filePath) ? filePath : null;
private string OnlyIfFileExists(string filePath, string directoryDescription)
{
if (_fileOperations.Exists(filePath))
{
s_logger.Info($"Using config file specified from {directoryDescription} directory");
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
return filePath;
}
return null;
}

private void CheckIfValidPermissions(string filePath)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
return;

// Check if others have permissions to modify the file and fail if so
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
var fileInfo = new UnixFileInfo(filePath);
if (fileInfo.Exists &&
((fileInfo.FileAccessPermissions & FileAccessPermissions.GroupWrite) != 0 ||
(fileInfo.FileAccessPermissions & FileAccessPermissions.OtherWrite) != 0))
{
var errorMessage = "Error due to other users having permission to modify the config file";
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
s_logger.Error(errorMessage);
throw new Exception(errorMessage);

Check warning on line 114 in Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs

View check run for this annotation

Codecov / codecov/patch

Snowflake.Data/Configuration/EasyLoggingConfigFinder.cs#L111-L114

Added lines #L111 - L114 were not covered by tests
}
}
}
}
22 changes: 22 additions & 0 deletions Snowflake.Data/Configuration/EasyLoggingConfigParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Snowflake.Data.Log;

namespace Snowflake.Data.Configuration
Expand Down Expand Up @@ -44,6 +48,7 @@ private ClientConfig TryToParseFile(string fileContent)
try {
var config = JsonConvert.DeserializeObject<ClientConfig>(fileContent);
Validate(config);
CheckForUnknownFields(fileContent);
return config;
}
catch (Exception e)
Expand All @@ -61,5 +66,22 @@ private void Validate(ClientConfig config)
EasyLoggingLogLevelExtensions.From(config.CommonProps.LogLevel);
}
}

private void CheckForUnknownFields(string fileContent)
{
// Parse the specified config file and get the key-value pairs from the "common" section
List<string> knownProperties = new List<string>();
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
foreach (var property in typeof(ClientConfigCommonProps).GetProperties())
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
{
var jsonPropertyAttribute = property.GetCustomAttribute<JsonPropertyAttribute>();
knownProperties.Add(jsonPropertyAttribute.PropertyName);
}

JObject.Parse(fileContent).GetValue("common")
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
.Cast<JProperty>()
.Where(property => knownProperties.Contains(property.Name))
sfc-gh-knozderko marked this conversation as resolved.
Show resolved Hide resolved
.ToList()
.ForEach(unknownKey => s_logger.Warn($"Unknown field from config: {unknownKey.Name}"));
}
}
}
Loading
Loading