-
Notifications
You must be signed in to change notification settings - Fork 125
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SNOW-856228 easy logging parser (#924)
SNOW-856228 easy logging parser
- Loading branch information
1 parent
46bf297
commit dfb1c18
Showing
2 changed files
with
262 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
// Copyright (c) 2023 Snowflake Computing Inc. All rights reserved. | ||
|
||
package gosnowflake | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"os" | ||
"strings" | ||
) | ||
|
||
// log levels for easy logging | ||
const ( | ||
levelOff string = "OFF" // log level for logging switched off | ||
levelError string = "ERROR" // error log level | ||
levelWarn string = "WARN" // warn log level | ||
levelInfo string = "INFO" // info log level | ||
levelDebug string = "DEBUG" // debug log level | ||
levelTrace string = "TRACE" // trace log level | ||
) | ||
|
||
// ClientConfig config root | ||
type ClientConfig struct { | ||
Common *ClientConfigCommonProps `json:"common"` | ||
} | ||
|
||
// ClientConfigCommonProps properties from "common" section | ||
type ClientConfigCommonProps struct { | ||
LogLevel string `json:"log_level,omitempty"` | ||
LogPath string `json:"log_path,omitempty"` | ||
} | ||
|
||
func parseClientConfiguration(filePath string) (*ClientConfig, error) { | ||
if filePath == "" { | ||
return nil, nil | ||
} | ||
fileContents, err := os.ReadFile(filePath) | ||
if err != nil { | ||
return nil, parsingClientConfigError(err) | ||
} | ||
var clientConfig ClientConfig | ||
err = json.Unmarshal(fileContents, &clientConfig) | ||
if err != nil { | ||
return nil, parsingClientConfigError(err) | ||
} | ||
err = validateClientConfiguration(&clientConfig) | ||
if err != nil { | ||
return nil, parsingClientConfigError(err) | ||
} | ||
return &clientConfig, nil | ||
} | ||
|
||
func parsingClientConfigError(err error) error { | ||
return fmt.Errorf("parsing client config failed: %w", err) | ||
} | ||
|
||
func validateClientConfiguration(clientConfig *ClientConfig) error { | ||
if clientConfig == nil { | ||
return errors.New("client config not found") | ||
} | ||
if clientConfig.Common == nil { | ||
return errors.New("common section in client config not found") | ||
} | ||
return validateLogLevel(*clientConfig) | ||
} | ||
|
||
func validateLogLevel(clientConfig ClientConfig) error { | ||
var logLevel = clientConfig.Common.LogLevel | ||
if logLevel != "" { | ||
_, error := toLogLevel(logLevel) | ||
if error != nil { | ||
return error | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func toLogLevel(logLevelString string) (string, error) { | ||
var logLevel = strings.ToUpper(logLevelString) | ||
switch logLevel { | ||
case levelOff, levelError, levelWarn, levelInfo, levelDebug, levelTrace: | ||
return logLevel, nil | ||
default: | ||
return "", errors.New("unknown log level: " + logLevelString) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
// Copyright (c) 2023 Snowflake Computing Inc. All rights reserved. | ||
|
||
package gosnowflake | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path" | ||
"strings" | ||
"testing" | ||
) | ||
|
||
func TestParseConfiguration(t *testing.T) { | ||
dir := t.TempDir() | ||
testCases := []struct { | ||
testName string | ||
fileName string | ||
fileContents string | ||
expectedLogLevel string | ||
expectedLogPath string | ||
}{ | ||
{ | ||
testName: "TestWithLogLevelUpperCase", | ||
fileName: "config_1.json", | ||
fileContents: `{ | ||
"common": { | ||
"log_level" : "INFO", | ||
"log_path" : "/some-path/some-directory" | ||
} | ||
}`, | ||
expectedLogLevel: "INFO", | ||
expectedLogPath: "/some-path/some-directory", | ||
}, | ||
{ | ||
testName: "TestWithLogLevelLowerCase", | ||
fileName: "config_2.json", | ||
fileContents: `{ | ||
"common": { | ||
"log_level" : "info", | ||
"log_path" : "/some-path/some-directory" | ||
} | ||
}`, | ||
expectedLogLevel: "info", | ||
expectedLogPath: "/some-path/some-directory", | ||
}, | ||
{ | ||
testName: "TestWithMissingValues", | ||
fileName: "config_3.json", | ||
fileContents: `{ | ||
"common": {} | ||
}`, | ||
expectedLogLevel: "", | ||
expectedLogPath: "", | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
t.Run(tc.testName, func(t *testing.T) { | ||
fileName := createFile(t, tc.fileName, tc.fileContents, dir) | ||
|
||
config, err := parseClientConfiguration(fileName) | ||
|
||
if err != nil { | ||
t.Fatalf("Error should be nil but was %s", err) | ||
} | ||
if config.Common.LogLevel != tc.expectedLogLevel { | ||
t.Errorf("Log level should be %s but was %s", tc.expectedLogLevel, config.Common.LogLevel) | ||
} | ||
if config.Common.LogPath != tc.expectedLogPath { | ||
t.Errorf("Log path should be %s but was %s", tc.expectedLogPath, config.Common.LogPath) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestParseAllLogLevels(t *testing.T) { | ||
dir := t.TempDir() | ||
for _, logLevel := range []string{"OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"} { | ||
t.Run(logLevel, func(t *testing.T) { | ||
fileContents := fmt.Sprintf(`{ | ||
"common": { | ||
"log_level" : "%s", | ||
"log_path" : "/some-path/some-directory" | ||
} | ||
}`, logLevel) | ||
fileName := createFile(t, fmt.Sprintf("config_%s.json", logLevel), fileContents, dir) | ||
|
||
config, err := parseClientConfiguration(fileName) | ||
|
||
if err != nil { | ||
t.Fatalf("Error should be nil but was: %s", err) | ||
} | ||
if config.Common.LogLevel != logLevel { | ||
t.Errorf("Log level should be %s but was %s", logLevel, config.Common.LogLevel) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestParseConfigurationFails(t *testing.T) { | ||
dir := t.TempDir() | ||
testCases := []struct { | ||
testName string | ||
fileName string | ||
FileContents string | ||
expectedErrorMessageToContain string | ||
}{ | ||
{ | ||
testName: "TestWithWrongLogLevel", | ||
fileName: "config_1.json", | ||
FileContents: `{ | ||
"common": { | ||
"log_level" : "something weird", | ||
"log_path" : "/some-path/some-directory" | ||
} | ||
}`, | ||
expectedErrorMessageToContain: "unknown log level", | ||
}, | ||
{ | ||
testName: "TestWithWrongTypeOfLogLevel", | ||
fileName: "config_2.json", | ||
FileContents: `{ | ||
"common": { | ||
"log_level" : 15, | ||
"log_path" : "/some-path/some-directory" | ||
} | ||
}`, | ||
expectedErrorMessageToContain: "ClientConfigCommonProps.common.log_level", | ||
}, | ||
{ | ||
testName: "TestWithWrongTypeOfLogPath", | ||
fileName: "config_3.json", | ||
FileContents: `{ | ||
"common": { | ||
"log_level" : "INFO", | ||
"log_path" : true | ||
} | ||
}`, | ||
expectedErrorMessageToContain: "ClientConfigCommonProps.common.log_path", | ||
}, | ||
{ | ||
testName: "TestWithoutCommon", | ||
fileName: "config_4.json", | ||
FileContents: "{}", | ||
expectedErrorMessageToContain: "common section in client config not found", | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
t.Run(tc.testName, func(t *testing.T) { | ||
fileName := createFile(t, tc.fileName, tc.FileContents, dir) | ||
|
||
_, err := parseClientConfiguration(fileName) | ||
|
||
if err == nil { | ||
t.Fatal("Error should not be nil but was nil") | ||
} | ||
errMessage := fmt.Sprint(err) | ||
expectedPrefix := "parsing client config failed" | ||
if !strings.HasPrefix(errMessage, expectedPrefix) { | ||
t.Errorf("Error message: \"%s\" should start with prefix: \"%s\"", errMessage, expectedPrefix) | ||
} | ||
if !strings.Contains(errMessage, tc.expectedErrorMessageToContain) { | ||
t.Errorf("Error message: \"%s\" should contain given phrase: \"%s\"", errMessage, tc.expectedErrorMessageToContain) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func createFile(t *testing.T, fileName string, fileContents string, directory string) string { | ||
fullFileName := path.Join(directory, fileName) | ||
err := os.WriteFile(fullFileName, []byte(fileContents), 0644) | ||
if err != nil { | ||
t.Fatal("Could not create file") | ||
} | ||
return fullFileName | ||
} |