newtmgr/cli/log.go (320 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package cli import ( "encoding/hex" "encoding/json" "fmt" "sort" "strconv" "github.com/spf13/cobra" "mynewt.apache.org/newt/util" "mynewt.apache.org/newtmgr/newtmgr/nmutil" "mynewt.apache.org/newtmgr/nmxact/nmp" "mynewt.apache.org/newtmgr/nmxact/nmxutil" "mynewt.apache.org/newtmgr/nmxact/sesn" "mynewt.apache.org/newtmgr/nmxact/xact" ) var optLogShowFull bool // Converts the provided CBOR map to a JSON string. func logCborMsgText(cborMap []byte) (string, error) { cm, err := nmxutil.DecodeCborMap(cborMap) if err != nil { return "", err } msg, err := json.Marshal(cm) if err != nil { return "", util.ChildNewtError(err) } return string(msg), nil } type logShowCfg struct { Name string Last bool Index uint32 Timestamp int64 } func logShowParseArgs(args []string) (*logShowCfg, error) { cfg := &logShowCfg{} if len(args) < 1 { return cfg, nil } cfg.Name = args[0] if len(args) < 2 { return cfg, nil } if args[1] == "last" { cfg.Index = 0 cfg.Timestamp = -1 } else { u64, err := strconv.ParseUint(args[1], 0, 64) if err != nil { return nil, util.ChildNewtError(err) } if u64 > 0xffffffff { return nil, util.NewNewtError("index out of range") } cfg.Index = uint32(u64) } if len(args) < 3 || args[1] == "last" { return cfg, nil } ts, err := strconv.ParseInt(args[2], 0, 64) if err != nil { return nil, util.ChildNewtError(err) } cfg.Timestamp = ts return cfg, nil } func printLogShowRsp(rsp *nmp.LogShowRsp, printHdr bool) { if len(rsp.Logs) == 0 { fmt.Printf("(no logs retrieved)\n") return } for _, log := range rsp.Logs { if printHdr { fmt.Printf("Name: %s\n", log.Name) fmt.Printf("Type: %s\n", nmp.LogTypeToString(log.Type)) fmt.Printf("%10s %22s | %16s %16s %6s %8s %s\n", "[index]", "[timestamp]", "[module]", "[level]", "[type]", "[img]", "[message]") } for _, entry := range log.Entries { modText := fmt.Sprintf("%s (%d)", nmp.LogModuleToString(int(entry.Module)), entry.Module) levText := fmt.Sprintf("%s (%d)", nmp.LogLevelToString(int(entry.Level)), entry.Level) var err error msgText := "" switch entry.Type { case nmp.LOG_ENTRY_TYPE_STRING: msgText = string(entry.Msg) case nmp.LOG_ENTRY_TYPE_CBOR: msgText, err = logCborMsgText(entry.Msg) if err != nil { fmt.Printf("Error decoding CBOR entry: %s; "+ "idx=%d", err.Error(), entry.Index) msgText = hex.EncodeToString(entry.Msg) } case nmp.LOG_ENTRY_TYPE_BINARY: msgText = hex.EncodeToString(entry.Msg) default: fmt.Printf( "Error decoding entry: unknown entry type (%d); idx=%d", int(entry.Type), entry.Index) msgText = hex.EncodeToString(entry.Msg) } fmt.Printf("%10d %20dus | %16s %16s %6s %8s %s\n", entry.Index, entry.Timestamp, modText, levText, entry.Type, hex.EncodeToString(entry.ImgHash), msgText) } } } func logShowFullCmd(s sesn.Sesn, cfg *logShowCfg) error { if cfg.Name == "" { return util.FmtNewtError("must specify a single log to read when `-a` is used") } c := xact.NewLogShowFullCmd() c.SetTxOptions(nmutil.TxOptions()) c.Name = cfg.Name c.Index = cfg.Index first := true c.ProgressCb = func(_ *xact.LogShowFullCmd, rsp *nmp.LogShowRsp) { printLogShowRsp(rsp, first) first = false } _, err := c.Run(s) if err != nil { return err } return nil } func logShowPartialCmd(s sesn.Sesn, cfg *logShowCfg) error { c := xact.NewLogShowCmd() c.SetTxOptions(nmutil.TxOptions()) c.Name = cfg.Name c.Index = cfg.Index c.Timestamp = cfg.Timestamp res, err := c.Run(s) if err != nil { return err } sres := res.(*xact.LogShowResult) fmt.Printf("Status: %d\n", sres.Status()) fmt.Printf("Next index: %d\n", sres.Rsp.NextIndex) if len(sres.Rsp.Logs) == 0 { fmt.Printf("(no logs retrieved)\n") } else { printLogShowRsp(sres.Rsp, true) } return nil } func logShowCmd(cmd *cobra.Command, args []string) { cfg, err := logShowParseArgs(args) if err != nil { nmUsage(cmd, err) } s, err := GetSesn() if err != nil { nmUsage(nil, err) } if optLogShowFull { err = logShowFullCmd(s, cfg) } else { err = logShowPartialCmd(s, cfg) } if err != nil { nmUsage(nil, err) } } func logListCmd(cmd *cobra.Command, args []string) { s, err := GetSesn() if err != nil { nmUsage(nil, err) } c := xact.NewLogListCmd() c.SetTxOptions(nmutil.TxOptions()) res, err := c.Run(s) if err != nil { nmUsage(nil, util.ChildNewtError(err)) } sres := res.(*xact.LogListResult) if sres.Rsp.Rc != 0 { fmt.Printf("error: %d\n", sres.Rsp.Rc) return } sort.Strings(sres.Rsp.List) fmt.Printf("available logs:\n") for _, log := range sres.Rsp.List { fmt.Printf(" %s\n", log) } } func logModuleListCmd(cmd *cobra.Command, args []string) { s, err := GetSesn() if err != nil { nmUsage(nil, err) } c := xact.NewLogModuleListCmd() c.SetTxOptions(nmutil.TxOptions()) res, err := c.Run(s) if err != nil { nmUsage(nil, util.ChildNewtError(err)) } sres := res.(*xact.LogModuleListResult) if sres.Rsp.Rc != 0 { fmt.Printf("error: %d\n", sres.Rsp.Rc) return } names := make([]string, 0, len(sres.Rsp.Map)) for k, _ := range sres.Rsp.Map { names = append(names, k) } sort.Strings(names) fmt.Printf("available modules:\n") for _, name := range names { fmt.Printf(" %s (%d)\n", name, sres.Rsp.Map[name]) } } func logLevelListCmd(cmd *cobra.Command, args []string) { s, err := GetSesn() if err != nil { nmUsage(nil, err) } c := xact.NewLogLevelListCmd() c.SetTxOptions(nmutil.TxOptions()) res, err := c.Run(s) if err != nil { nmUsage(nil, util.ChildNewtError(err)) } sres := res.(*xact.LogLevelListResult) if sres.Rsp.Rc != 0 { fmt.Printf("error: %d\n", sres.Rsp.Rc) return } vals := make([]int, 0, len(sres.Rsp.Map)) revmap := make(map[int]string, len(sres.Rsp.Map)) for name, val := range sres.Rsp.Map { vals = append(vals, val) revmap[val] = name } sort.Ints(vals) fmt.Printf("available levels:\n") for _, val := range vals { fmt.Printf(" %d: %s\n", val, revmap[val]) } } func logClearCmd(cmd *cobra.Command, args []string) { s, err := GetSesn() if err != nil { nmUsage(nil, err) } c := xact.NewLogClearCmd() c.SetTxOptions(nmutil.TxOptions()) res, err := c.Run(s) if err != nil { nmUsage(nil, util.ChildNewtError(err)) } sres := res.(*xact.LogClearResult) if sres.Rsp.Rc != 0 { fmt.Printf("error: %d\n", sres.Rsp.Rc) return } fmt.Printf("done\n") } func logCmd() *cobra.Command { logCmd := &cobra.Command{ Use: "log", Short: "Manage logs on a device", Run: func(cmd *cobra.Command, args []string) { cmd.HelpFunc()(cmd, args) }, } logShowHelpText := "Show logs on a device. Optional log-name, min-index, and min-timestamp\nparameters can be specified to filter the logs to display.\n\n" logShowHelpText += "- log-name specifies the log to display. If log-name is not specified, all\nlogs are displayed.\n\n" logShowHelpText += "- min-index specifies to only display the log entries with an index value equal to or higher than min-index. " logShowHelpText += "If \"last\" is specified for min-index, the last\nlog entry is displayed.\n\n" logShowHelpText += "- min-timestamp specifies to only display the log entries with a timestamp\nequal to or later than min-timestamp. Log entries with a timestamp equal to\nmin-timestamp are only displayed if the entry index is equal to or higher than min-index.\n" logShowEx := nmutil.ToolInfo.ExeName + " log show -c myserial\n" logShowEx += nmutil.ToolInfo.ExeName + " log show reboot_log -c myserial\n" logShowEx += nmutil.ToolInfo.ExeName + " log show reboot_log last -c myserial\n" logShowEx += nmutil.ToolInfo.ExeName + " log show reboot_log 5 -c myserial\n" logShowEx += nmutil.ToolInfo.ExeName + " log show reboot_log 3 1122222 -c myserial\n" showCmd := &cobra.Command{ Use: "show [log-name [min-index [min-timestamp]]] -c <conn_profile>", Example: logShowEx, Long: logShowHelpText, Short: "Show the logs on a device", Run: logShowCmd, } showCmd.PersistentFlags().BoolVarP(&optLogShowFull, "all", "a", false, "read until end of log") logCmd.AddCommand(showCmd) clearCmd := &cobra.Command{ Use: "clear -c <conn_profile>", Short: "Clear the logs on a device", Example: logShowEx, Run: logClearCmd, } logCmd.AddCommand(clearCmd) moduleListCmd := &cobra.Command{ Use: "module_list -c <conn_profile>", Short: "Show the log module names", Run: logModuleListCmd, } logCmd.AddCommand(moduleListCmd) levelListCmd := &cobra.Command{ Use: "level_list -c <conn_profile>", Short: "Show the log levels", Run: logLevelListCmd, } logCmd.AddCommand(levelListCmd) ListCmd := &cobra.Command{ Use: "list -c <conn_profile>", Short: "Show the log names", Run: logListCmd, } logCmd.AddCommand(ListCmd) return logCmd }