Skip to content

Commit

Permalink
feat: localized logging for app, cli, and config frameworks
Browse files Browse the repository at this point in the history
  • Loading branch information
tucats committed Jan 20, 2025
1 parent 6845c75 commit f90d644
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 99 deletions.
4 changes: 2 additions & 2 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}",
"env": {"EGO_DEFAULT_LOGGING":"APP","EGO_LOG_FORMAT":"json"},
"args": ["run", "test.ego"]
"env": {"EGO_DEFAULT_LOGGING":"CLI","EGO_LOG_FORMAT":"json"},
"args": [ "run", "test.ego"]
},
{
"name": "Detached ego server",
Expand Down
10 changes: 5 additions & 5 deletions app-cli/app/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ func LogFileAction(c *cli.Context) error {
func EnvAction(c *cli.Context) error {
count := loadEnvSettings()

ui.Log(ui.AppLogger, "Loaded %d settings from environment variables", count)
ui.Log(ui.AppLogger, "log.app.env.load", "count", count)

return nil
}
Expand All @@ -120,7 +120,7 @@ func SetAction(c *cli.Context) error {
}

if err := config.ValidateKey(item); err != nil {
ui.Log(ui.AppLogger, "Invalid --set operation: %s, %v", item, err)
ui.Log(ui.AppLogger, "log.app.set.invalid", "item", item, "error", err)

return err
}
Expand All @@ -134,7 +134,7 @@ func SetAction(c *cli.Context) error {
func MaxProcsAction(c *cli.Context) error {
if maxProcs, present := c.FindGlobal().Integer("maxcpus"); present {
if maxProcs > 1 {
ui.Log(ui.AppLogger, "Set max cpus: %d", maxProcs)
ui.Log(ui.AppLogger, "log.app.maxcpus", "count", maxProcs)

runtime.GOMAXPROCS(maxProcs)

Expand All @@ -145,7 +145,7 @@ func MaxProcsAction(c *cli.Context) error {
}
}
} else {
ui.Log(ui.AppLogger, "Invalid value for --maxcpus: %d", maxProcs)
ui.Log(ui.AppLogger, "log.app.invalid.value", "item", "--maxcpus", "value", maxProcs)

return errors.ErrInvalidInteger.Context(maxProcs)
}
Expand Down Expand Up @@ -225,7 +225,7 @@ func UseProfileAction(c *cli.Context) error {
name, _ := c.String("profile")
settings.UseProfile(name)

ui.Log(ui.AppLogger, "Using profile %s", name)
ui.Log(ui.AppLogger, "log.app.using.profile", "name", name)
settings.Load(c.AppName, name)

return nil
Expand Down
10 changes: 10 additions & 0 deletions app-cli/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,16 @@ func (app *App) Run(grammar []cli.Option, args []string) error {
// are enabled. If the list contains invalid logger names, an error is
// returned. The logger names are not case-sensitive.
func SetDefaultLoggers() error {
logFormat := os.Getenv(defs.EgoLogFormat)
if logFormat != "" {
logFormat = strings.ToLower(logFormat)
if logFormat != "json" && logFormat != "text" {
return errors.ErrInvalidLogFormat.Context(logFormat)
}

ui.LogFormat = logFormat
}

logList := os.Getenv(defs.EgoDefaultLogging)
if strings.TrimSpace(logList) == "" {
return nil
Expand Down
6 changes: 3 additions & 3 deletions app-cli/app/library.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func LibraryInit() error {
return nil
}

ui.Log(ui.AppLogger, "Attempt to access library failed, %v", err)
ui.Log(ui.AppLogger, "log.runtime.lib.error", "error", err)

// Unzip the embedded zip file into the library directory.
// The replace option is set to false, so we won't replace
Expand All @@ -86,7 +86,7 @@ func InstallLibrary(path string, replace bool) error {
return err
}

ui.Log(ui.AppLogger, "Extracting library to %s", path)
ui.Log(ui.AppLogger, "log.runtime.lib.extract", "path", path)

// Extract the files in the archive.
for _, f := range r.File {
Expand Down Expand Up @@ -119,7 +119,7 @@ func extractFile(f *zip.File, path string, replace bool) error {
path = strings.TrimSuffix(strings.TrimSuffix(path, "/"), defs.LibPathName)

path = filepath.Join(path, name)
ui.Log(ui.AppLogger, "Extracting %s to %s", name, path)
ui.Log(ui.AppLogger, "log.runtime.lib.extract.item", "item", name, "path", path)

if f.FileInfo().IsDir() {
if err := os.MkdirAll(path, ownerFilePerm); err != nil {
Expand Down
22 changes: 11 additions & 11 deletions app-cli/app/logon.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ func Logon(c *cli.Context) error {

// Lets not log this until we're successfully prompted for missing input
// and validated that the expiration is okay.
ui.Log(ui.RestLogger, "Logon URL is %s", url)
ui.Log(ui.RestLogger, "log.logon.url", "url", url)

// Turn logon server address and endpoint into full URL.
url = strings.TrimSuffix(url, "/") + defs.ServicesLogonPath
Expand Down Expand Up @@ -132,7 +132,7 @@ func Logon(c *cli.Context) error {
Password: "********",
Expiration: expiration}, "", " ")

ui.Log(ui.RestLogger, "REST Request:\n%s", string(b))
ui.Log(ui.RestLogger, "log.logon.request", "body", string(b))
}

req.Header.Set("Accept", defs.JSONMediaType)
Expand All @@ -143,7 +143,7 @@ func Logon(c *cli.Context) error {
// This is a gross hack, but I don't yet know how to determine the
// specific error value.
if !strings.Contains(err.Error(), "auto redirect is disabled") {
ui.Log(ui.RestLogger, "POST %s; failed %v", url, err)
ui.Log(ui.RestLogger, "log.logon.post.error", "url", url, "error", err)

return errors.New(err)
}
Expand All @@ -152,13 +152,13 @@ func Logon(c *cli.Context) error {
if r.StatusCode() == http.StatusMovedPermanently {
url = r.Header().Get("Location")
if url != "" {
ui.Log(ui.RestLogger, "Redirecting to %s", url)
ui.Log(ui.RestLogger, "log.logon.redirecting", "url", url)

continue
}
}

ui.Log(ui.RestLogger, "REST POST %s; status %d", url, r.StatusCode())
ui.Log(ui.RestLogger, "log.logon.post.status", "url", url, "status", r.StatusCode())

break
}
Expand All @@ -173,9 +173,9 @@ func Logon(c *cli.Context) error {
b := r.Body()

if len(b) > 0 {
ui.Log(ui.RestLogger, "REST Request:\n%s", string(b))
ui.Log(ui.RestLogger, "log.logon.request", "body", string(b))
} else {
ui.Log(ui.RestLogger, "REST Request: <empty>")
ui.Log(ui.RestLogger, "log.logon.request", "body", "<empty>")
}
}

Expand Down Expand Up @@ -207,14 +207,14 @@ func storeLogonToken(r *resty.Response, user string) error {
payload := defs.LogonResponse{}

if err := json.Unmarshal(r.Body(), &payload); err != nil {
ui.Log(ui.RestLogger, "[%d] Bad payload: %v", 0, err)
ui.Log(ui.RestLogger, "log.logon.payload.error", "error", err)

return errors.New(err).In("logon")
}

if ui.IsActive(ui.RestLogger) {
b, _ := json.MarshalIndent(payload, "", " ")
ui.Log(ui.RestLogger, "REST Response:\n%s", string(b))
ui.Log(ui.RestLogger, "log.logon.response", "body", string(b))
}

settings.Set(defs.LogonTokenSetting, payload.Token)
Expand Down Expand Up @@ -319,7 +319,7 @@ func resolveServerName(name string) (string, error) {
normalizedName = "https://" + name + port

settings.SetDefault(defs.ApplicationServerSetting, normalizedName)
ui.Log(ui.RestLogger, "Checking for heartbeat at %s", normalizedName)
ui.Log(ui.RestLogger, "log.rest.heartbeat", "name", normalizedName)

err = rest.Exchange(defs.AdminHeartbeatPath, http.MethodGet, nil, nil, defs.LogonAgent)
if err == nil {
Expand All @@ -331,7 +331,7 @@ func resolveServerName(name string) (string, error) {

settings.SetDefault(defs.ApplicationServerSetting, normalizedName)

ui.Log(ui.RestLogger, "Checking for heartbeat at %s", normalizedName)
ui.Log(ui.RestLogger, "log.rest.heartbeat", "name", normalizedName)

err = rest.Exchange(defs.AdminHeartbeatPath, http.MethodGet, nil, nil, defs.LogonAgent)
if err != nil {
Expand Down
7 changes: 4 additions & 3 deletions app-cli/cli/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ func (c *Context) ResolveEnvironmentVariables() error {
// not to the local entry which is a copy of the item...
for found, entry := range c.Grammar {
if !entry.Found && entry.EnvVar > "" {
if value, wasFound := os.LookupEnv(entry.EnvVar); wasFound {
ui.Log(ui.CLILogger, "resolving env %s = \"%s\"", entry.EnvVar, value)
if value, wasFound := os.LookupEnv(entry.EnvVar); wasFound && value != "" {
ui.Log(ui.CLILogger, "log.cli.env.resolve", "name", entry.EnvVar, "value", value)

c.Grammar[found].Found = true

Expand All @@ -47,7 +47,8 @@ func (c *Context) ResolveEnvironmentVariables() error {
}

if c.Grammar[found].Action != nil {
ui.Log(ui.CLILogger, "Invoking %s handler for value %#v", c.Grammar[found].LongName, c.Grammar[found].Value)
ui.Log(ui.CLILogger, "log.cli.handler",
"name", c.Grammar[found].LongName, "value", c.Grammar[found].Value)

err = c.Grammar[found].Action(c)
}
Expand Down
50 changes: 22 additions & 28 deletions app-cli/cli/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,7 @@ func (c *Context) parseGrammar(args []string) error {
if len(parmList) > 0 {
list := strings.Join(parmList, ", ")

plural := "s"
if len(parmList) == 1 {
plural = ""
}

ui.Log(ui.CLILogger, "Unexpected parameter%s already parsed: %s", plural, list)
ui.Log(ui.CLILogger, "log.cli.unexp.parm.parsed", "list", list)

return errors.ErrUnrecognizedCommand.Context(parmList[0])
}
Expand Down Expand Up @@ -136,15 +131,15 @@ func parseToken(c *Context, state *parseState) error {
option := state.args[state.currentArg]
state.parsedSoFar = state.currentArg

ui.Log(ui.CLILogger, "Processing token: %s", option)
ui.Log(ui.CLILogger, "log.cli.token", "token", option)

// Are we now only eating parameter values?
if state.parametersOnly {
globalContext := c.FindGlobal()
globalContext.Parameters = append(globalContext.Parameters, option)
count := len(globalContext.Parameters)

ui.Log(ui.CLILogger, "added parameter %d", count)
ui.Log(ui.CLILogger, "log.cli.parm", "count", count)

return nil
}
Expand All @@ -163,7 +158,7 @@ func parseToken(c *Context, state *parseState) error {
state.parametersOnly = true
state.helpVerb = false

ui.Log(ui.CLILogger, "All remaining tokens are parameters")
ui.Log(ui.CLILogger, "log.cli.remaining.parms")

return nil
}
Expand All @@ -179,7 +174,7 @@ func parseToken(c *Context, state *parseState) error {
}

if location != nil {
ui.Log(ui.CLILogger, "Setting value for option %s", location.LongName)
ui.Log(ui.CLILogger, "log.cli.set.name", "name", location.LongName)
}

// If it was an option (short or long) and not found, this is an error.
Expand Down Expand Up @@ -219,7 +214,7 @@ func parseToken(c *Context, state *parseState) error {
return err
}

ui.Log(ui.CLILogger, "Option value set to %#v", location.Value)
ui.Log(ui.CLILogger, "log.cli.set.value", location.Value)

// After parsing the option value, if there is an action routine, call it
if location.Action != nil {
Expand Down Expand Up @@ -269,7 +264,7 @@ func findDefaultVerb(c *Context) *Option {
if entry.DefaultVerb {
defaultVerb = &c.Grammar[index]

ui.Log(ui.CLILogger, "Registering default verb %s", defaultVerb.LongName)
ui.Log(ui.CLILogger, "log.cli.set.default", "verb", defaultVerb.LongName)
}
}

Expand All @@ -285,12 +280,12 @@ func invokeAction(c *Context) error {

if g.Expected == -99 {
if g.MinParams > 0 {
ui.Log(ui.CLILogger, "Parameters expected: at least %d, found %d", g.MinParams, g.ParameterCount())
ui.Log(ui.CLILogger, "log.cli.parm.expect.min", "min", g.MinParams, "count", g.ParameterCount())
} else {
ui.Log(ui.CLILogger, "Parameters expected: varying, found %d", g.ParameterCount())
ui.Log(ui.CLILogger, "log.cli.parm.expect.var", "count", g.ParameterCount())
}
} else {
ui.Log(ui.CLILogger, "Parameters expected: %d found %d", g.Expected, g.ParameterCount())
ui.Log(ui.CLILogger, "log.cli.parm.expect.num", "want", g.Expected, "count", g.ParameterCount())
}

if g.Expected == 0 && len(g.Parameters) > 0 {
Expand All @@ -316,11 +311,11 @@ func invokeAction(c *Context) error {
}

if c.Action != nil {
ui.Log(ui.CLILogger, "Invoking command action")
ui.Log(ui.CLILogger, "log.cli.invoke")

err = c.Action(c)
} else {
ui.Log(ui.CLILogger, "No command action was ever specified during parsing")
ui.Log(ui.CLILogger, "log.cli.no.action")
ShowHelp(c)

return errors.ErrExit
Expand Down Expand Up @@ -391,12 +386,12 @@ func validateOption(location *Option, value string, hasValue bool) error {
unsupported := false

for _, platform := range location.Unsupported {
ui.Log(ui.CLILogger, "Verifying option supported on platform %s", platform)
ui.Log(ui.CLILogger, "log.cli.platform.check", "platform", platform)

if runtime.GOOS == platform {
unsupported = true

ui.Log(ui.CLILogger, "Option value unsupported on platform %s", platform)
ui.Log(ui.CLILogger, "log.cli.platform.unsupported", "platform", platform)

break
}
Expand All @@ -416,7 +411,7 @@ func evaluatePossibleSubcommand(c *Context, option string, args []string, curren
}

if defaultVerb != nil {
ui.Log(ui.CLILogger, "Using default verb %s", defaultVerb.LongName)
ui.Log(ui.CLILogger, "log.cli.default.verb", "verb", defaultVerb.LongName)

return true, doSubcommand(c, defaultVerb, args, parsedSoFar-1)
}
Expand All @@ -425,7 +420,7 @@ func evaluatePossibleSubcommand(c *Context, option string, args []string, curren
g.Parameters = append(g.Parameters, option)
count := len(g.Parameters)

ui.Log(ui.CLILogger, "Unclaimed token added parameter %d", count)
ui.Log(ui.CLILogger, "log.cli.unclaimed", "count", count)

return false, nil
}
Expand All @@ -444,7 +439,7 @@ func doIfSubcommand(c *Context, option string, args []string, currentArg int) (b

if (isAlias || entry.LongName == option) && entry.OptionType == Subcommand {
for _, platform := range entry.Unsupported {
ui.Log(ui.CLILogger, "Verifying platform support for subcommand; not supported on platform %s", platform)
ui.Log(ui.CLILogger, "log.cli.platform.subcommand", "platform", platform)

if runtime.GOOS == platform {
return true, errors.ErrUnsupportedOnOS.Context(entry.LongName)
Expand Down Expand Up @@ -481,11 +476,10 @@ func findShortName(c *Context, isShort bool, name string, location *Option) *Opt
func doDefaultSubcommand(parsedSoFar int, c *Context, defaultVerb *Option, args []string) error {
parsedSoFar = parsedSoFar - c.ParameterCount() + 1

ui.Log(ui.CLILogger, "Using default verb %s", defaultVerb.LongName)
ui.Log(ui.CLILogger, "log.cli.default.verb", "verb", defaultVerb.LongName)

if parsedSoFar < len(args) {
ui.Log(ui.CLILogger, "Passing remaining arguments to default action: %v",
args[parsedSoFar+1:])
ui.Log(ui.CLILogger, "log.cli.default.action.args", "args", args[parsedSoFar+1:])
}

if parsedSoFar > len(args) {
Expand Down Expand Up @@ -526,17 +520,17 @@ func doSubcommand(c *Context, entry *Option, args []string, currentArg int) erro
if entry.Action != nil {
subContext.Action = entry.Action

ui.Log(ui.CLILogger, "Saving action routine in subcommand context")
ui.Log(ui.CLILogger, "log.cli.saving.action")
}

ui.Log(ui.CLILogger, "Transferring control to subgrammar for %s", entry.LongName)
ui.Log(ui.CLILogger, "log.cli.subgrammar", "verb", entry.LongName)

if len(args) == 0 {
return subContext.parseGrammar([]string{})
}

tokens := args[currentArg+1:]
ui.Log(ui.CLILogger, "Remaining tokens %v", tokens)
ui.Log(ui.CLILogger, "log.cli.tokens", "tokens", tokens)

return subContext.parseGrammar(tokens)
}
Loading

0 comments on commit f90d644

Please sign in to comment.