Skip to content

Commit

Permalink
SNOW-856228 easy logging parser (#924)
Browse files Browse the repository at this point in the history
SNOW-856228 easy logging parser
  • Loading branch information
sfc-gh-knozderko authored Oct 13, 2023
1 parent 46bf297 commit dfb1c18
Show file tree
Hide file tree
Showing 2 changed files with 262 additions and 0 deletions.
87 changes: 87 additions & 0 deletions client_configuration.go
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)
}
}
175 changes: 175 additions & 0 deletions client_configuration_test.go
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
}

0 comments on commit dfb1c18

Please sign in to comment.