Skip to content
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,52 @@ litmusctl.exe <command> <subcommand> <subcommand> [options and parameters]
litmusctl version
```

## Verbose Mode

litmusctl supports a `--verbose` (or `-v`) flag to the enable detailed logging during command execution. This is useful for debugging and understanding what's happening behind the scenes.

### Usage

Add the `--verbose` or `-v` flag to any litmusctl command:

```shell
litmusctl <command> --verbose
# or
litmusctl <command> -v
```

### What Gets Logged

When verbose mode is enabled, litmusctl will print additional information including:

- API endpoints being accessed
- HTTP request methods and payloads
- Response status codes
- Internal processing steps
- Project and resource details during operations

### Examples

```shell
# Get projects with verbose logging
litmusctl get projects --verbose

# Create a project with detailed logs
litmusctl create project --name my-project -v

# Connect chaos infrastructure with debug information
litmusctl connect chaos-infra --verbose --name my-infra --non-interactive
```

### Default Behavior

By default, litmusctl operates in minimal logging mode, showing only:
- Error messages
- Warning messages
- User-facing output

Use `--verbose` when you need to troubleshoot issues or understand the internal operations of the CLI.

## Development Guide

You can find the local setup guide for **`litmusctl`** [here](DEVELOPMENT.md).
Expand Down
11 changes: 11 additions & 0 deletions pkg/apis/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package apis
import (
"bytes"
"net/http"

"github.com/litmuschaos/litmusctl/pkg/utils"
)

type SendRequestParams struct {
Expand All @@ -26,18 +28,27 @@ type SendRequestParams struct {
}

func SendRequest(params SendRequestParams, payload []byte, method string) (*http.Response, error) {
utils.Debugf("API Request - Method: %s, Endpoint: %s", method, params.Endpoint)
if len(payload) > 0 {
utils.Debugf("API Request - Payload: %s", string(payload))
}

req, err := http.NewRequest(method, params.Endpoint, bytes.NewBuffer(payload))
if err != nil {
utils.Errorf("Failed to create HTTP request: %v", err)
return &http.Response{}, err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", params.Token)
req.Header.Set("Referer", params.Endpoint)

utils.Debug("Sending HTTP request...")
resp, err := http.DefaultClient.Do(req)
if err != nil {
utils.Errorf("HTTP request failed: %v", err)
return &http.Response{}, err
}

utils.Debugf("API Response - Status Code: %d", resp.StatusCode)
return resp, nil
}
3 changes: 3 additions & 0 deletions pkg/cmd/connect/infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ var infraCmd = &cobra.Command{
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)

utils.Infof("Connecting chaos infrastructure with credentials for endpoint: %s", credentials.Endpoint)

nonInteractive, err := cmd.Flags().GetBool("non-interactive")
utils.PrintError(err)

Expand All @@ -61,6 +63,7 @@ var infraCmd = &cobra.Command{
utils.PrintError(err)

if newInfra.ProjectId == "" {
utils.Debug("Project ID not provided, fetching project details...")
userDetails, err := apis.GetProjectDetails(credentials)
utils.PrintError(err)

Expand Down
5 changes: 5 additions & 0 deletions pkg/cmd/create/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,12 @@ var projectCmd = &cobra.Command{
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)

utils.Debug("Starting project creation process...")

projectName, err := cmd.Flags().GetString("name")
utils.PrintError(err)
if projectName == "" {
utils.Debug("Project name not provided via flag, prompting user...")
// prompt to ask project name
prompt := promptui.Prompt{
Label: "Enter a project name",
Expand All @@ -56,13 +59,15 @@ var projectCmd = &cobra.Command{

projectName = result
}
utils.Debugf("Creating project with name: %s", projectName)
var response apis.CreateProjectResponse
response, err = apis.CreateProjectRequest(projectName, credentials)
if err != nil {
utils.Red.Printf("❌ Error creating project: %v\n", err)
} else {
fmt.Printf("Response: %+v\n", response)
projectID := response.Data.ID
utils.Debugf("Project created with ID: %s", projectID)
utils.White_B.Printf("Project '%s' created successfully with project ID - '%s'!🎉\n", projectName, projectID)
}
},
Expand Down
4 changes: 4 additions & 0 deletions pkg/cmd/get/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,15 @@ var projectsCmd = &cobra.Command{
credentials, err := utils.GetCredentials(cmd)
utils.PrintError(err)

utils.Debug("Fetching list of projects...")

outputFormat, _ := cmd.Flags().GetString("output")

projects, err := apis.ListProject(credentials)
utils.PrintError(err)

utils.Debugf("Retrieved %d projects", len(projects.Data.Projects))

switch outputFormat {
case "json":
utils.PrintInJsonFormat(projects.Data)
Expand Down
4 changes: 4 additions & 0 deletions pkg/cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func init() {
//rootCmd.PersistentFlags().StringVar(&kubeconfig, "kubeconfig", "", "kubeconfig file (default is $HOME/.kube/config")
rootCmd.PersistentFlags().BoolVar(&config2.SkipSSLVerify, "skipSSL", false, "skipSSL, litmusctl will skip ssl/tls verification while communicating with portal")
rootCmd.PersistentFlags().StringVar(&config2.CACert, "cacert", "", "cacert <path_to_crt_file> , custom ca certificate used for communicating with portal")
rootCmd.PersistentFlags().BoolVarP(&config2.VerboseMode, "verbose", "v", false, "enable verbose logging (shows request payloads, API endpoints, and additional details)")
}

// initConfig reads in config file and ENV variables if set.
Expand All @@ -104,6 +105,9 @@ func initConfig() {

viper.AutomaticEnv() // read in environment variables that match

// Configure verbose logging based on the flag
utils.ConfigureVerboseLogging()

if config2.SkipSSLVerify {
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
} else if config2.CACert != "" {
Expand Down
1 change: 1 addition & 0 deletions pkg/config/ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
var (
SkipSSLVerify bool = false
CACert string = ""
VerboseMode bool = false
)

func CreateNewLitmusCtlConfig(filename string, config types.LitmuCtlConfig) error {
Expand Down
94 changes: 94 additions & 0 deletions pkg/utils/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
Copyright © 2021 The LitmusChaos Authors

Licensed 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 utils

import (
"os"

"github.com/litmuschaos/litmusctl/pkg/config"
"github.com/sirupsen/logrus"
)

var Log *logrus.Logger

func init() {
Log = logrus.New()
Log.SetOutput(os.Stdout)
Log.SetFormatter(&logrus.TextFormatter{
DisableColors: false,
FullTimestamp: false,
})
// Default to Error level; will be set to Debug when verbose mode is enabled
Log.SetLevel(logrus.ErrorLevel)
}

// ConfigureVerboseLogging configures the logger based on verbose mode
func ConfigureVerboseLogging() {
if config.VerboseMode {
Log.SetLevel(logrus.DebugLevel)
Log.Debug("Verbose mode enabled")
} else {
Log.SetLevel(logrus.ErrorLevel)
}
}

// Debug logs a message at debug level (only shown in verbose mode)
func Debug(args ...interface{}) {
if config.VerboseMode {
Log.Debug(args...)
}
}

// Debugf logs a formatted message at debug level (only shown in verbose mode)
func Debugf(format string, args ...interface{}) {
if config.VerboseMode {
Log.Debugf(format, args...)
}
}

// Info logs a message at info level (only shown in verbose mode)
func Info(args ...interface{}) {
if config.VerboseMode {
Log.Info(args...)
}
}

// Infof logs a formatted message at info level (only shown in verbose mode)
func Infof(format string, args ...interface{}) {
if config.VerboseMode {
Log.Infof(format, args...)
}
}

// Warn logs a warning message (shown regardless of verbose mode)
func Warn(args ...interface{}) {
Log.Warn(args...)
}

// Warnf logs a formatted warning message (shown regardless of verbose mode)
func Warnf(format string, args ...interface{}) {
Log.Warnf(format, args...)
}

// Error logs an error message (shown regardless of verbose mode)
func Error(args ...interface{}) {
Log.Error(args...)
}

// Errorf logs a formatted error message (shown regardless of verbose mode)
func Errorf(format string, args ...interface{}) {
Log.Errorf(format, args...)
}