diff --git a/client/session/interface.go b/client/session/interface.go
index 4487de2..b38dfb5 100644
--- a/client/session/interface.go
+++ b/client/session/interface.go
@@ -2,7 +2,8 @@ package session
 
 import (
 	"context"
-	"log"
+	"log/slog"
+	"os"
 )
 
 // Session is an abstraction around session creation based on credentials from
@@ -16,7 +17,8 @@ type Session interface {
 // Must provides a helper that either creates a Session or dies trying.
 func Must(out Session, err error) Session {
 	if err != nil {
-		log.Fatalf("Could not create a Spacelift session: %v", err)
+		slog.Error("Could not create a Spacelift session", "err", err)
+		os.Exit(1)
 	}
 
 	return out
diff --git a/internal/cmd/module/local_preview.go b/internal/cmd/module/local_preview.go
index 1afd31f..8b612a9 100644
--- a/internal/cmd/module/local_preview.go
+++ b/internal/cmd/module/local_preview.go
@@ -3,19 +3,20 @@ package module
 import (
 	"context"
 	"fmt"
-	"log"
+	"log/slog"
 	"os"
 	"path/filepath"
 	"sync"
 	"time"
 
+	"golang.org/x/sync/errgroup"
+
 	"github.com/charmbracelet/bubbles/spinner"
 	tea "github.com/charmbracelet/bubbletea"
 	"github.com/charmbracelet/lipgloss"
 	"github.com/mholt/archiver/v3"
 	"github.com/shurcooL/graphql"
 	"github.com/urfave/cli/v2"
-	"golang.org/x/sync/errgroup"
 
 	"github.com/spacelift-io/spacectl/internal"
 	"github.com/spacelift-io/spacectl/internal/cmd/authenticated"
@@ -131,7 +132,8 @@ func localPreview() cli.ActionFunc {
 					})
 				}
 				if err := g.Wait(); err != nil {
-					log.Fatal("couldn't get runs: ", err)
+					slog.Error("couldn't get runs", "err", err)
+					os.Exit(1)
 				}
 				model.setRuns(newRuns)
 			}
diff --git a/internal/cmd/profile/login_command.go b/internal/cmd/profile/login_command.go
index 945098a..e2baab2 100644
--- a/internal/cmd/profile/login_command.go
+++ b/internal/cmd/profile/login_command.go
@@ -5,7 +5,7 @@ import (
 	"context"
 	"encoding/base64"
 	"fmt"
-	"log"
+	"log/slog"
 	"net"
 	"net/http"
 	"net/url"
@@ -14,11 +14,12 @@ import (
 	"syscall"
 	"time"
 
+	"golang.org/x/term"
+
 	"github.com/manifoldco/promptui"
 	"github.com/pkg/browser"
 	"github.com/pkg/errors"
 	"github.com/urfave/cli/v2"
-	"golang.org/x/term"
 
 	"github.com/spacelift-io/spacectl/client/session"
 	"github.com/spacelift-io/spacectl/internal"
@@ -226,11 +227,12 @@ func loginUsingWebBrowser(ctx *cli.Context, creds *session.StoredCredentials) er
 
 		infoPage, err := url.Parse(creds.Endpoint)
 		if err != nil {
-			log.Fatal(err)
+			slog.Error("Error parsing URL", "err", err)
+			os.Exit(1)
 		}
 
 		if handlerErr != nil {
-			log.Println(handlerErr)
+			slog.Error("login error", "err", handlerErr)
 			infoPage.Path = cliAuthFailurePage
 			http.Redirect(w, r, infoPage.String(), http.StatusTemporaryRedirect)
 		} else {
@@ -320,7 +322,8 @@ func serveOnOpenPort(host *string, port *int, handler func(w http.ResponseWriter
 	go func() {
 		if err := server.Serve(l); err != nil {
 			if !errors.Is(err, http.ErrServerClosed) {
-				log.Fatalf("could not start local server: %s", err)
+				slog.Error("could not start local server", "err", err)
+				os.Exit(1)
 			}
 		}
 	}()
diff --git a/main.go b/main.go
index 34ec509..451b985 100644
--- a/main.go
+++ b/main.go
@@ -1,7 +1,7 @@
 package main
 
 import (
-	"log"
+	"log/slog"
 	"os"
 	"time"
 
@@ -19,11 +19,15 @@ import (
 
 var version = "dev"
 var date = "2006-01-02T15:04:05Z"
+var loggingLevel = new(slog.LevelVar)
 
 func main() {
+
 	compileTime, err := time.Parse(time.RFC3339, date)
+
 	if err != nil {
-		log.Fatalf("Could not parse compilation date: %v", err)
+		slog.Error("Could not parse compilation date", "err", err)
+		os.Exit(1)
 	}
 	app := &cli.App{
 		Name:     "spacectl",
@@ -40,9 +44,58 @@ func main() {
 			versioncmd.Command(version),
 			workerpools.Command(),
 		},
+		Flags: []cli.Flag{
+			&cli.StringFlag{
+				Name:    "log-format",
+				Aliases: []string{"f"},
+				Usage:   "Log format: json, text",
+				Value:   "text",
+			},
+			&cli.StringFlag{
+				Name:    "log-level",
+				Aliases: []string{"l"},
+				Usage:   "Log level: debug, info, warn, error",
+				Value:   "info",
+			},
+		},
+		Before: func(c *cli.Context) error {
+			var logger *slog.Logger
+			switch c.String("log-format") {
+			case "json":
+				logger = slog.New(slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
+					AddSource: true,
+					Level:     loggingLevel,
+				}))
+			case "text":
+				logger = slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
+					AddSource: true,
+					Level:     loggingLevel,
+				}))
+			default:
+				return cli.Exit("Invalid log format", 1)
+			}
+
+			switch c.String("log-level") {
+			case "debug":
+				loggingLevel.Set(slog.LevelDebug)
+			case "info":
+				loggingLevel.Set(slog.LevelInfo)
+			case "warn":
+				loggingLevel.Set(slog.LevelWarn)
+			case "error":
+				loggingLevel.Set(slog.LevelError)
+			default:
+				return cli.Exit("Invalid log level", 1)
+			}
+
+			slog.SetDefault(logger)
+
+			return nil
+		},
 	}
 
 	if err := app.Run(os.Args); err != nil {
-		log.Fatal(err)
+		slog.Error("error", "err", err)
+		os.Exit(1)
 	}
 }