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

Implement Easy Logging using a config file #747

Open
wants to merge 75 commits into
base: master
Choose a base branch
from

Conversation

sfc-gh-ext-simba-nl
Copy link
Collaborator

@sfc-gh-ext-simba-nl sfc-gh-ext-simba-nl commented Oct 2, 2024

For SNOW-981602: Implement Easy Logging using a config file

Add several new global attributes:
SF_GLOBAL_CLIENT_CONFIG_FILE: To set the client config file so libsnowflakeclient knows where to look for the config file
SF_GLOBAL_LOG_LEVEL: To allow the user to get the log level since libsnowflakeclient handles log setup
SF_GLOBAL_LOG_PATH: To allow the user to get the log path

@sfc-gh-ext-simba-nl sfc-gh-ext-simba-nl marked this pull request as ready for review October 11, 2024 16:43
@sfc-gh-ext-simba-nl sfc-gh-ext-simba-nl requested a review from a team as a code owner October 11, 2024 16:43
@@ -25,6 +28,47 @@ void test_log_str_to_level(void **unused) {
}

#ifndef _WIN32
/**
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test case to cover the behavior in log_init(), e.g. having a config file in place, after calling snowflake_global_init() check the log settings are as expected.

{
#if defined(WIN32) || defined(_WIN64)
// 4. Try user home dir
std::string homeDir = sf_getenv_s("USERPROFILE", envbuf, sizeof(envbuf));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sf_getenv_s could return NULL and cause runtime exception thrown.


// Private =====================================================================
////////////////////////////////////////////////////////////////////////////////
bool ClientConfigParser::checkFileExists(const std::string& in_filePath)
Copy link
Collaborator

@sfc-gh-ext-simba-hx sfc-gh-ext-simba-hx Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are on c++17 already maybe we could use std::filesystem::is_regular_file() directly?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::filesystem isn't available on osx10.14 and boost has a debug assertion on 32-bit windows with MT build

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we resolve debug assertion issue with 32-bit windows build and use boost filesystem

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think using boost filesystem was able to fix the issue

lib/client.c Outdated
char client_config_file[MAX_PATH] = {0};
snowflake_global_get_attribute(
SF_GLOBAL_CLIENT_CONFIG_FILE, client_config_file, sizeof(client_config_file));
client_config clientConfig = { .logLevel = "", .logPath = "" };
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memory leak currently with the buffer allocated in load_client_config().
When you try to free the buffer, initialize with const string could be a problem.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if client config is not initialized .logLevel and .logPath should be null

}

////////////////////////////////////////////////////////////////////////////////
void load_client_config(
Copy link
Collaborator

@sfc-gh-ext-simba-hx sfc-gh-ext-simba-hx Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems we are throwing exception from loadClientConfig(). We need to catch that and return error. C interface doesn't expect exception.
Please add error test cases as well this will be caught if we have error test cases.

lib/client.c Outdated
@@ -36,6 +37,8 @@ sf_bool DEBUG;
sf_bool SF_OCSP_CHECK;
char *SF_HEADER_USER_AGENT = NULL;

static char *CLIENT_CONFIG_FILE = NULL;
static char* LOG_LEVEL;
Copy link
Collaborator

@sfc-gh-ext-simba-hx sfc-gh-ext-simba-hx Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not to set NULL as others? Better not to add LOG_LEVEL as we've had number loglevel maintained with log_set_level() and log_get_level() already. Having another one could cause mismatch.

lib/client.c Outdated
case SF_GLOBAL_CLIENT_CONFIG_FILE:
alloc_buffer_and_copy(&CLIENT_CONFIG_FILE, value);
break;
case SF_GLOBAL_LOG_LEVEL:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer SF_GLOBAL_LOG_PATH and SF_GLOBAL_LOG_LEVEL to be read only attributes since application already have enough ways to do log settings.

SF_GLOBAL_OCSP_CHECK
SF_GLOBAL_OCSP_CHECK,
SF_GLOBAL_CLIENT_CONFIG_FILE,
SF_GLOBAL_LOG_LEVEL,
Copy link
Collaborator

@sfc-gh-ext-simba-hx sfc-gh-ext-simba-hx Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remind me why we need SF_GLOBAL_LOG_LEVEL and SF_GLOBAL_LOG_PATH?
Please having a brief note of the interface and usage in either the ticket or the PR description. That could help understanding the implementation and later for documentation as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated description

cpp/lib/ClientConfigParser.cpp Show resolved Hide resolved
cpp/lib/ClientConfigParser.cpp Outdated Show resolved Hide resolved
FILE* configFile;
try
{
configFile = sf_fopen(&configFile, in_filePath.c_str(), "r");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use std::fstream and std::filesystem APIs?

Copy link
Collaborator Author

@sfc-gh-ext-simba-nl sfc-gh-ext-simba-nl Oct 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately std::filesystem isn't in osx10.14 yet so it can't be used and there seems to be a 32-bit windows debug assertion issue with reading file with fstream when built with MT

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That means we cannot use std::fstream at all? Can you show me the assertion?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly, it only happens for Windows 32-bit built with MT. All other configurations seem to work fine. If you prefer, I can ifdef it to not run when it's windows debug and it should work fine. The assertion says something like this:
Debug Assertion Failed!

File: minkernel\crts\ucrt\src\appcrt\lowio\read.cpp

Expression: _osfile(fh) & FOPEN

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this local issue? Does it appear on CI? I think you might be building the project incorrectly. See: https://stackoverflow.com/questions/5984144/assertion-error-in-crt-calling-osfile-in-vs-2008/6010756#6010756

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using fstream and boost filesystem on my PR without issues

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still running into this issue with the tests run on jenkins. Basically the test run just hangs after it hits the assertion error. In the mean time, I've added an ifdef to to run easy logging when in 32-bit debug. I've locally tested running on /MDd and it runs fine, but currently I think we build with dynamic runtime set to OFF

cpp/lib/ClientConfigParser.cpp Show resolved Hide resolved
@sfc-gh-ext-simba-nl sfc-gh-ext-simba-nl changed the base branch from master to master-2.0.0 October 29, 2024 00:03
ClientConfigParser configParser;
configParser.loadClientConfig(in_configFilePath, *out_clientConfig);
} catch (std::exception e) {
sf_fprintf(stderr, e.what());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use logger here?

{
char envbuf[MAX_PATH + 1];
std::string derivedConfigPath = "";
if (!in_configFilePath.empty())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use ealy return instead of else nesting

// USERPROFILE is empty, try HOMEDRIVE and HOMEPATH
char* homeDriveEnv;
char* homePathEnv;
if ((homeDriveEnv = sf_getenv_s("HOMEDRIVE", envbuf, sizeof(envbuf))) && (strlen(homeDriveEnv) != 0) &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can write it like this?

char* homeDriveEnv = sf_getenv_s("HOMEDRIVE", envbuf, sizeof(envbuf)
if (homeDriveEnv && strlen(homeDriveEnv) != 0)

}
else
{
#if defined(WIN32) || defined(_WIN64)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move finding home directory to another function.

FILE* configFile;
try
{
configFile = sf_fopen(&configFile, in_filePath.c_str(), "r");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this local issue? Does it appear on CI? I think you might be building the project incorrectly. See: https://stackoverflow.com/questions/5984144/assertion-error-in-crt-calling-osfile-in-vs-2008/6010756#6010756


// Private =====================================================================
////////////////////////////////////////////////////////////////////////////////
bool ClientConfigParser::checkFileExists(const std::string& in_filePath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we resolve debug assertion issue with 32-bit windows build and use boost filesystem


// Public ======================================================================
////////////////////////////////////////////////////////////////////////////////
ClientConfigParser::ClientConfigParser()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change the name? Maybe EasyLoggingConfigParser

snowflake_global_get_attribute(
SF_GLOBAL_CLIENT_CONFIG_FILE, client_config_file, sizeof(client_config_file));
client_config clientConfig = { .logLevel = "", .logPath = "" };
load_client_config(client_config_file, &clientConfig);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C strings are allocated by this function, we should have corresponding fuction to deallocate the string free_client_config

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now we are leaking memory

lib/client.c Outdated
char client_config_file[MAX_PATH] = {0};
snowflake_global_get_attribute(
SF_GLOBAL_CLIENT_CONFIG_FILE, client_config_file, sizeof(client_config_file));
client_config clientConfig = { .logLevel = "", .logPath = "" };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think if client config is not initialized .logLevel and .logPath should be null

{
namespace Client
{
struct ClientConfigException : public std::exception
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need a class if it has no member variables?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just using it as a wrapper for exception to pass along the error message. I saw other classes having a similar exception struct, but I can just make the function return false instead of throwing an exception if you prefer.

@sfc-gh-ext-simba-nl sfc-gh-ext-simba-nl changed the base branch from master-2.0.0 to master November 15, 2024 00:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants