chore: improve logging and add trace logging with --trace flag

This commit is contained in:
Mateusz Filipowicz
2025-02-07 17:05:35 +01:00
committed by Mateusz Filipowicz
parent 399c5cde86
commit f396b2f712
3 changed files with 56 additions and 43 deletions

View File

@@ -7,6 +7,7 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
log "github.com/sirupsen/logrus"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
@@ -20,24 +21,6 @@ import (
"github.com/xor-gate/ar" "github.com/xor-gate/ar"
) )
func sanitizeExtractedPath(filePath, destinationDir string) (string, error) {
absDestinationDir, err := filepath.Abs(destinationDir)
if err != nil {
return "", err
}
absFilePath, err := filepath.Abs(filepath.Join(destinationDir, filePath))
if err != nil {
return "", err
}
if !strings.HasPrefix(absFilePath, absDestinationDir) {
return "", fmt.Errorf("invalid file path: %s", filePath)
}
return absFilePath, nil
}
func DownloadAndExtract(downloadUrl url.URL, outputDir string) error { func DownloadAndExtract(downloadUrl url.URL, outputDir string) error {
targetInfo, err := os.Stat(outputDir) targetInfo, err := os.Stat(outputDir)
if err != nil { if err != nil {
@@ -51,16 +34,19 @@ func DownloadAndExtract(downloadUrl url.URL, outputDir string) error {
} }
// download fields, create // download fields, create
log.Debugf("downloading UniFi Controller package from: %s", downloadUrl.String())
jarFile, err := downloadJar(downloadUrl, outputDir) jarFile, err := downloadJar(downloadUrl, outputDir)
if err != nil { if err != nil {
return err return err
} }
log.Debugf("extracting JSON files with API structures from: %s to: %s", jarFile, outputDir)
err = extractJSON(jarFile, outputDir) err = extractJSON(jarFile, outputDir)
if err != nil { if err != nil {
return err return err
} }
log.Debugf("JSON files extracted to: %s", outputDir)
targetInfo, err = os.Stat(outputDir) targetInfo, err = os.Stat(outputDir)
if err != nil { if err != nil {
return err return err
@@ -113,6 +99,7 @@ func downloadJar(downloadUrl url.URL, outputDir string) (string, error) {
var aceJar *os.File var aceJar *os.File
log.Debugln("extracting ace.jar from downloaded controller package")
for { for {
header, err := tarReader.Next() header, err := tarReader.Next()
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
@@ -142,10 +129,28 @@ func downloadJar(downloadUrl url.URL, outputDir string) (string, error) {
} }
defer aceJar.Close() defer aceJar.Close()
log.Debugf("ace.jar extracted to: %s", aceJar.Name())
return aceJar.Name(), nil return aceJar.Name(), nil
} }
func sanitizeExtractedPath(filePath, destinationDir string) (string, error) {
absDestinationDir, err := filepath.Abs(destinationDir)
if err != nil {
return "", err
}
absFilePath, err := filepath.Abs(filepath.Join(destinationDir, filepath.Base(filePath)))
if err != nil {
return "", err
}
if !strings.HasPrefix(absFilePath, absDestinationDir) {
return "", fmt.Errorf("invalid file path: %s", filePath)
}
return absFilePath, nil
}
func extractJSON(jarFile, fieldsDir string) error { func extractJSON(jarFile, fieldsDir string) error {
jarZip, err := zip.OpenReader(jarFile) jarZip, err := zip.OpenReader(jarFile)
if err != nil { if err != nil {
@@ -153,6 +158,7 @@ func extractJSON(jarFile, fieldsDir string) error {
} }
defer jarZip.Close() defer jarZip.Close()
log.Tracef("opened jar %s with %d files", jarFile, len(jarZip.File))
for _, f := range jarZip.File { for _, f := range jarZip.File {
if !strings.HasPrefix(f.Name, "api/fields/") || path.Ext(f.Name) != ".json" { if !strings.HasPrefix(f.Name, "api/fields/") || path.Ext(f.Name) != ".json" {
// skip file // skip file
@@ -160,11 +166,11 @@ func extractJSON(jarFile, fieldsDir string) error {
} }
err = func() error { err = func() error {
log.Tracef("extracting %s", f.Name)
src, err := f.Open() src, err := f.Open()
if err != nil { if err != nil {
return err return err
} }
dstPath, err := sanitizeExtractedPath(f.Name, fieldsDir) dstPath, err := sanitizeExtractedPath(f.Name, fieldsDir)
if err != nil { if err != nil {
return err return err
@@ -177,6 +183,7 @@ func extractJSON(jarFile, fieldsDir string) error {
defer dst.Close() defer dst.Close()
_, err = io.Copy(dst, src) _, err = io.Copy(dst, src)
log.Debugf("extracted %s", f.Name)
if err != nil { if err != nil {
return err return err
} }
@@ -202,18 +209,22 @@ func extractJSON(jarFile, fieldsDir string) error {
return fmt.Errorf("unable to unmarshal settings: %w", err) return fmt.Errorf("unable to unmarshal settings: %w", err)
} }
for k, v := range settings { log.Debugf("splitting Settings.json into individual setting files")
fileName := fmt.Sprintf("Setting%s.json", strcase.ToCamel(k)) for settingKey, settingValue := range settings {
settingName := strcase.ToCamel(settingKey)
fileName := fmt.Sprintf("Setting%s.json", settingName)
log.Tracef("splitting %s", fileName)
data, err := json.MarshalIndent(v, "", " ") data, err := json.MarshalIndent(settingValue, "", " ")
if err != nil { if err != nil {
return fmt.Errorf("unable to marshal setting %q: %w", k, err) return fmt.Errorf("unable to marshal setting %q: %w", settingKey, err)
} }
err = os.WriteFile(filepath.Join(fieldsDir, fileName), data, 0o755) err = os.WriteFile(filepath.Join(fieldsDir, fileName), data, 0o755)
if err != nil { if err != nil {
return fmt.Errorf("unable to write new settings file: %w", err) return fmt.Errorf("unable to write new settings file: %w", err)
} }
log.Tracef("splitted %s into %s", settingKey, fileName)
} }
// TODO: cleanup JSON // TODO: cleanup JSON

View File

@@ -278,7 +278,7 @@ func (r *Resource) fieldInfoFromValidation(name string, validation interface{})
} }
} }
if validation != "" && normalized != "" { if validation != "" && normalized != "" {
log.Debugf("normalize %q to %q", validation, normalized) log.Tracef("normalize %q to %q", validation, normalized)
} }
omitEmpty = omitEmpty || (!strings.Contains(validation, "^$") && !strings.HasSuffix(fieldName, "ID")) omitEmpty = omitEmpty || (!strings.Contains(validation, "^$") && !strings.HasSuffix(fieldName, "ID"))

View File

@@ -18,13 +18,15 @@ func usage() {
flag.PrintDefaults() flag.PrintDefaults()
} }
func setupLogging(debugEnabled bool) { func setupLogging(debugEnabled, traceEnabled bool) {
log.SetFormatter(&log.TextFormatter{ log.SetFormatter(&log.TextFormatter{
DisableTimestamp: true, DisableTimestamp: true,
DisableLevelTruncation: true, DisableLevelTruncation: true,
FullTimestamp: false, FullTimestamp: false,
}) })
if debugEnabled { if traceEnabled {
log.SetLevel(log.TraceLevel)
} else if debugEnabled {
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)
} else { } else {
log.SetLevel(log.InfoLevel) log.SetLevel(log.InfoLevel)
@@ -36,18 +38,19 @@ func main() {
versionBaseDirFlag := flag.String("version-base-dir", ".", "The base directory for version JSON files") versionBaseDirFlag := flag.String("version-base-dir", ".", "The base directory for version JSON files")
outputDirFlag := flag.String("output-dir", ".", "The output directory of the generated Go code") outputDirFlag := flag.String("output-dir", ".", "The output directory of the generated Go code")
downloadOnly := flag.Bool("download-only", false, "Only download and build the fields JSON directory, do not generate") downloadOnly := flag.Bool("download-only", false, "Only download and build the API structures JSON directory, do not generate")
debugFlag := flag.Bool("debug", false, "Enable debug logging") debugFlag := flag.Bool("debug", false, "Enable debug logging")
traceFlag := flag.Bool("trace", false, "Enable trace logging")
flag.Parse() flag.Parse()
setupLogging(*debugFlag) setupLogging(*debugFlag, *traceFlag)
specifiedVersion := strings.TrimSpace(flag.Arg(0)) specifiedVersion := strings.TrimSpace(flag.Arg(0))
if specifiedVersion == "" { if specifiedVersion == "" {
specifiedVersion = LatestVersionMarker // default to latest version specifiedVersion = LatestVersionMarker // default to latest version
} }
unifiVersion, err := determineUnifiVersion(specifiedVersion) unifiVersion, err := determineUnifiVersion(specifiedVersion)
if err != nil { if err != nil {
log.Fatalf("unable to determine version and download URL for Unifi version %s", specifiedVersion) log.Fatalf("unable to determine version and download URL for Unifi version %s: %s", specifiedVersion, err)
panic(err) panic(err)
} }
@@ -56,41 +59,40 @@ func main() {
wd, err := os.Getwd() wd, err := os.Getwd()
if err != nil { if err != nil {
log.Fatalln("unable to determine working directory") log.Fatalf("unable to determine working directory: %s", err)
panic(err) panic(err)
} }
fieldsDir := filepath.Join(wd, *versionBaseDirFlag, fmt.Sprintf("v%s", unifiVersion.Version)) structuresDir := filepath.Join(wd, *versionBaseDirFlag, fmt.Sprintf("v%s", unifiVersion.Version))
log.Infoln("Downloading UniFi Controller field definitions...") log.Infoln("Downloading UniFi Controller API structures definitions...")
err = DownloadAndExtract(*unifiVersion.DownloadUrl, fieldsDir) err = DownloadAndExtract(*unifiVersion.DownloadUrl, structuresDir)
if err != nil { if err != nil {
log.Fatalf("unable to download and extract UniFi Controller field definitions: %s", err) log.Fatalf("unable to download and extract UniFi Controller API structures definitions: %s", err)
panic(err) panic(err)
} }
log.Infof("Downloaded UniFi Controller field definitions in %s", fieldsDir) log.Infof("Downloaded UniFi Controller API structures definitions in %s", structuresDir)
if *downloadOnly { if *downloadOnly {
log.Infoln("Fields JSON ready!") log.Infoln("Structure JSONs ready!")
os.Exit(0) os.Exit(0)
} }
log.Infoln("Generating resources code...") log.Infoln("Generating resources code...")
outDir := filepath.Join(wd, *outputDirFlag) outDir := filepath.Join(wd, *outputDirFlag)
if err = generateCode(fieldsDir, outDir); err != nil { if err = generateCode(structuresDir, outDir); err != nil {
log.Fatalln("unable to generate resources code") log.Fatalf("unable to generate resources code: %s", err)
panic(err) panic(err)
} }
log.Infof("Writing version file...") log.Infof("Writing version file...")
if err = writeVersionFile(unifiVersion.Version, outDir); err != nil { if err = writeVersionFile(unifiVersion.Version, outDir); err != nil {
log.Fatalf("failed to write version file to %s", outDir) log.Fatalf("failed to write version file to %s: %s", outDir, err)
panic(err) panic(err)
} }
workingDir, _ := os.Getwd() basepath := filepath.Dir(wd)
basepath := filepath.Dir(workingDir)
if err = writeVersionRepoMarkerFile(unifiVersion.Version, basepath); err != nil { if err = writeVersionRepoMarkerFile(unifiVersion.Version, basepath); err != nil {
log.Fatalf("failed to write version file to %s", basepath) log.Fatalf("failed to write version file to %s: %s", basepath, err)
panic(err) panic(err)
} }