diff --git a/Dockerfile.openapi b/Dockerfile.openapi
index a71b541b..37a30a90 100755
--- a/Dockerfile.openapi
+++ b/Dockerfile.openapi
@@ -1,17 +1,27 @@
-FROM openapitools/openapi-generator-cli:v7.16.0
+# Multi-arch base image with Java for openapi-generator
+FROM eclipse-temurin:17-jdk
 
 # -o APT::Sandbox::User=root is a workaround for rootless podman setgroups error in Prow env
 RUN apt-get -o APT::Sandbox::User=root update
 RUN apt-get -o APT::Sandbox::User=root install -y make sudo git wget
 
-# Install Go 1.24
-RUN wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz && \
-    tar -C /usr/local -xzf go1.24.0.linux-amd64.tar.gz && \
-    rm go1.24.0.linux-amd64.tar.gz
+# Install Go 1.24.9 (auto-detect architecture)
+RUN ARCH=$(dpkg --print-architecture) && \
+    if [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then \
+        GO_ARCH="arm64"; \
+    else \
+        GO_ARCH="amd64"; \
+    fi && \
+    wget https://go.dev/dl/go1.24.9.linux-${GO_ARCH}.tar.gz && \
+    tar -C /usr/local -xzf go1.24.9.linux-${GO_ARCH}.tar.gz && \
+    rm go1.24.9.linux-${GO_ARCH}.tar.gz
+
+# Download openapi-generator-cli
+RUN wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.18.0/openapi-generator-cli-7.18.0.jar \
+    -O /usr/local/bin/openapi-generator-cli.jar
 
 RUN mkdir -p /local
 
-
 # Copy the rest of the project
 COPY openapi/openapi.yaml /local/openapi/openapi.yaml
 
@@ -20,12 +30,16 @@ ENV GOPATH="/uhc"
 ENV GOBIN /usr/local/go/bin/
 ENV CGO_ENABLED=0
 
-# these git and go flags to avoid self signed certificate errors
-
 WORKDIR /local
 
 # Install go-bindata
 RUN go install -a github.com/go-bindata/go-bindata/...@v3.1.2
-RUN bash /usr/local/bin/docker-entrypoint.sh generate -i /local/openapi/openapi.yaml -g go -o /local/pkg/api/openapi
+
+# Generate OpenAPI client
+RUN java -jar /usr/local/bin/openapi-generator-cli.jar generate \
+    -i /local/openapi/openapi.yaml \
+    -g go \
+    -o /local/pkg/api/openapi
+
 RUN rm /local/pkg/api/openapi/go.mod /local/pkg/api/openapi/go.sum
 RUN rm -r /local/pkg/api/openapi/test
diff --git a/cmd/hyperfleet-api/environments/framework.go b/cmd/hyperfleet-api/environments/framework.go
index deaf8f7f..32797fd6 100755
--- a/cmd/hyperfleet-api/environments/framework.go
+++ b/cmd/hyperfleet-api/environments/framework.go
@@ -1,16 +1,18 @@
 package environments
 
 import (
+	"log/slog"
 	"os"
 	"strings"
 
-	"github.com/golang/glog"
 	"github.com/spf13/pflag"
 
 	"github.com/openshift-hyperfleet/hyperfleet-api/cmd/hyperfleet-api/environments/registry"
+	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/api"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/client/ocm"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/config"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/errors"
+	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger"
 )
 
 func init() {
@@ -64,25 +66,37 @@ func (e *Env) AddFlags(flags *pflag.FlagSet) error {
 // This should be called after the e.Config has been set appropriately though AddFlags and pasing, done elsewhere
 // The environment does NOT handle flag parsing
 func (e *Env) Initialize() error {
-	glog.Infof("Initializing %s environment", e.Name)
+	// Initialize structured logging first, before any other operations
+	logger.Initialize(logger.Config{
+		Level:   e.Config.Logging.Level,
+		Format:  e.Config.Logging.Format,
+		Output:  e.Config.Logging.Output,
+		Version: api.Version,
+	})
+
+	slog.Info("Initializing environment", "environment", e.Name)
 
 	envImpl, found := environments[e.Name]
 	if !found {
-		glog.Fatalf("Unknown runtime environment: %s", e.Name)
+		slog.Error("Unknown runtime environment", "environment", e.Name)
+		os.Exit(1)
 	}
 
 	if err := envImpl.OverrideConfig(e.Config); err != nil {
-		glog.Fatalf("Failed to configure ApplicationConfig: %s", err)
+		slog.Error("Failed to configure ApplicationConfig", "error", err)
+		os.Exit(1)
 	}
 
 	messages := environment.Config.ReadFiles()
 	if len(messages) != 0 {
-		glog.Fatalf("unable to read configuration files:\n%s", strings.Join(messages, "\n"))
+		slog.Error("Unable to read configuration files", "errors", strings.Join(messages, "\n"))
+		os.Exit(1)
 	}
 
 	// each env will set db explicitly because the DB impl has a `once` init section
 	if err := envImpl.OverrideDatabase(&e.Database); err != nil {
-		glog.Fatalf("Failed to configure Database: %s", err)
+		slog.Error("Failed to configure Database", "error", err)
+		os.Exit(1)
 	}
 
 	err := e.LoadClients()
@@ -90,12 +104,14 @@ func (e *Env) Initialize() error {
 		return err
 	}
 	if err := envImpl.OverrideClients(&e.Clients); err != nil {
-		glog.Fatalf("Failed to configure Clients: %s", err)
+		slog.Error("Failed to configure Clients", "error", err)
+		os.Exit(1)
 	}
 
 	e.LoadServices()
 	if err := envImpl.OverrideServices(&e.Services); err != nil {
-		glog.Fatalf("Failed to configure Services: %s", err)
+		slog.Error("Failed to configure Services", "error", err)
+		os.Exit(1)
 	}
 
 	seedErr := e.Seed()
@@ -104,7 +120,8 @@ func (e *Env) Initialize() error {
 	}
 
 	if err := envImpl.OverrideHandlers(&e.Handlers); err != nil {
-		glog.Fatalf("Failed to configure Handlers: %s", err)
+		slog.Error("Failed to configure Handlers", "error", err)
+		os.Exit(1)
 	}
 
 	return nil
@@ -136,13 +153,13 @@ func (e *Env) LoadClients() error {
 
 	// Create OCM Authz client
 	if e.Config.OCM.EnableMock {
-		glog.Infof("Using Mock OCM Authz Client")
+		slog.Info("Using Mock OCM Authz Client")
 		e.Clients.OCM, err = ocm.NewClientMock(ocmConfig)
 	} else {
 		e.Clients.OCM, err = ocm.NewClient(ocmConfig)
 	}
 	if err != nil {
-		glog.Errorf("Unable to create OCM Authz client: %s", err.Error())
+		slog.Error("Unable to create OCM Authz client", "error", err)
 		return err
 	}
 
@@ -152,12 +169,12 @@ func (e *Env) LoadClients() error {
 func (e *Env) Teardown() {
 	if e.Database.SessionFactory != nil {
 		if err := e.Database.SessionFactory.Close(); err != nil {
-			glog.Errorf("Error closing database session factory: %s", err.Error())
+			slog.Error("Error closing database session factory", "error", err)
 		}
 	}
 	if e.Clients.OCM != nil {
 		if err := e.Clients.OCM.Close(); err != nil {
-			glog.Errorf("Error closing OCM client: %v", err)
+			slog.Error("Error closing OCM client", "error", err)
 		}
 	}
 }
@@ -165,7 +182,7 @@ func (e *Env) Teardown() {
 func setConfigDefaults(flags *pflag.FlagSet, defaults map[string]string) error {
 	for name, value := range defaults {
 		if err := flags.Set(name, value); err != nil {
-			glog.Errorf("Error setting flag %s: %v", name, err)
+			slog.Error("Error setting flag", "flag", name, "error", err)
 			return err
 		}
 	}
diff --git a/cmd/hyperfleet-api/main.go b/cmd/hyperfleet-api/main.go
index 7048b44f..f4bdcc3d 100755
--- a/cmd/hyperfleet-api/main.go
+++ b/cmd/hyperfleet-api/main.go
@@ -1,9 +1,9 @@
 package main
 
 import (
-	"flag"
+	"log/slog"
+	"os"
 
-	"github.com/golang/glog"
 	"github.com/spf13/cobra"
 
 	"github.com/openshift-hyperfleet/hyperfleet-api/cmd/hyperfleet-api/migrate"
@@ -20,20 +20,6 @@ import (
 // nolint
 
 func main() {
-	// This is needed to make `glog` believe that the flags have already been parsed, otherwise
-	// every log messages is prefixed by an error message stating the the flags haven't been
-	// parsed.
-	if err := flag.CommandLine.Parse([]string{}); err != nil {
-		glog.Fatalf("Failed to parse flags: %v", err)
-	}
-
-	//pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
-
-	// Always log to stderr by default
-	if err := flag.Set("logtostderr", "true"); err != nil {
-		glog.Infof("Unable to set logtostderr to true")
-	}
-
 	rootCmd := &cobra.Command{
 		Use:  "hyperfleet",
 		Long: "hyperfleet serves as a template for new microservices",
@@ -47,6 +33,7 @@ func main() {
 	rootCmd.AddCommand(migrateCmd, serveCmd)
 
 	if err := rootCmd.Execute(); err != nil {
-		glog.Fatalf("error running command: %v", err)
+		slog.Error("Error running command", "error", err)
+		os.Exit(1)
 	}
 }
diff --git a/cmd/hyperfleet-api/migrate/cmd.go b/cmd/hyperfleet-api/migrate/cmd.go
index adcbb71b..e472762f 100755
--- a/cmd/hyperfleet-api/migrate/cmd.go
+++ b/cmd/hyperfleet-api/migrate/cmd.go
@@ -3,13 +3,14 @@ package migrate
 import (
 	"context"
 	"flag"
+	"log/slog"
+	"os"
 
-	"github.com/golang/glog"
-	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/db/db_session"
 	"github.com/spf13/cobra"
 
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/config"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/db"
+	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/db/db_session"
 )
 
 var dbConfig = config.NewDatabaseConfig()
@@ -31,11 +32,13 @@ func NewMigrateCommand() *cobra.Command {
 func runMigrate(_ *cobra.Command, _ []string) {
 	err := dbConfig.ReadFiles()
 	if err != nil {
-		glog.Fatal(err)
+		slog.Error("Failed to read database config", "error", err)
+		os.Exit(1)
 	}
 
 	connection := db_session.NewProdFactory(dbConfig)
 	if err := db.Migrate(connection.New(context.Background())); err != nil {
-		glog.Fatal(err)
+		slog.Error("Migration failed", "error", err)
+		os.Exit(1)
 	}
 }
diff --git a/cmd/hyperfleet-api/servecmd/cmd.go b/cmd/hyperfleet-api/servecmd/cmd.go
index dce3fd3f..73773adb 100755
--- a/cmd/hyperfleet-api/servecmd/cmd.go
+++ b/cmd/hyperfleet-api/servecmd/cmd.go
@@ -1,7 +1,9 @@
 package servecmd
 
 import (
-	"github.com/golang/glog"
+	"log/slog"
+	"os"
+
 	"github.com/spf13/cobra"
 
 	"github.com/openshift-hyperfleet/hyperfleet-api/cmd/hyperfleet-api/environments"
@@ -17,7 +19,8 @@ func NewServeCommand() *cobra.Command {
 	}
 	err := environments.Environment().AddFlags(cmd.PersistentFlags())
 	if err != nil {
-		glog.Fatalf("Unable to add environment flags to serve command: %s", err.Error())
+		slog.Error("Unable to add environment flags to serve command", "error", err)
+		os.Exit(1)
 	}
 
 	return cmd
@@ -26,7 +29,8 @@ func NewServeCommand() *cobra.Command {
 func runServe(cmd *cobra.Command, args []string) {
 	err := environments.Environment().Initialize()
 	if err != nil {
-		glog.Fatalf("Unable to initialize environment: %s", err.Error())
+		slog.Error("Unable to initialize environment", "error", err)
+		os.Exit(1)
 	}
 
 	// Run the servers
diff --git a/cmd/hyperfleet-api/server/api_server.go b/cmd/hyperfleet-api/server/api_server.go
index 58fce4ab..4e7f5e7f 100755
--- a/cmd/hyperfleet-api/server/api_server.go
+++ b/cmd/hyperfleet-api/server/api_server.go
@@ -3,13 +3,14 @@ package server
 import (
 	"context"
 	"fmt"
+	"log/slog"
 	"net"
 	"net/http"
+	"os"
 	"time"
 
 	_ "github.com/auth0/go-jwt-middleware"
 	_ "github.com/golang-jwt/jwt/v4"
-	"github.com/golang/glog"
 	gorillahandlers "github.com/gorilla/handlers"
 	sdk "github.com/openshift-online/ocm-sdk-go"
 	"github.com/openshift-online/ocm-sdk-go/authentication"
@@ -37,9 +38,9 @@ func NewAPIServer() Server {
 
 	if env().Config.Server.EnableJWT {
 		// Create the logger for the authentication handler:
-		authnLogger, err := sdk.NewGlogLoggerBuilder().
-			InfoV(glog.Level(1)).
-			DebugV(glog.Level(5)).
+		authnLogger, err := sdk.NewStdLoggerBuilder().
+			Streams(os.Stdout, os.Stderr).
+			Debug(false).
 			Build()
 		check(err, "Unable to create authentication logger")
 
@@ -117,16 +118,16 @@ func (s apiServer) Serve(listener net.Listener) {
 		}
 
 		// Serve with TLS
-		glog.Infof("Serving with TLS at %s", env().Config.Server.BindAddress)
+		slog.Info("Serving with TLS", "address", env().Config.Server.BindAddress)
 		err = s.httpServer.ServeTLS(listener, env().Config.Server.HTTPSCertFile, env().Config.Server.HTTPSKeyFile)
 	} else {
-		glog.Infof("Serving without TLS at %s", env().Config.Server.BindAddress)
+		slog.Info("Serving without TLS", "address", env().Config.Server.BindAddress)
 		err = s.httpServer.Serve(listener)
 	}
 
 	// Web server terminated.
 	check(err, "Web server terminated with errors")
-	glog.Info("Web server terminated")
+	slog.Info("Web server terminated")
 }
 
 // Listen only start the listener, not the server.
@@ -139,7 +140,8 @@ func (s apiServer) Listen() (listener net.Listener, err error) {
 func (s apiServer) Start() {
 	listener, err := s.Listen()
 	if err != nil {
-		glog.Fatalf("Unable to start API server: %s", err)
+		slog.Error("Unable to start API server", "error", err)
+		os.Exit(1)
 	}
 	s.Serve(listener)
 
@@ -147,7 +149,7 @@ func (s apiServer) Start() {
 	// we need to explicitly close Go's sql connection pool.
 	// this needs to be called *exactly* once during an app's lifetime.
 	if err := env().Database.SessionFactory.Close(); err != nil {
-		glog.Errorf("Error closing database connection: %v", err)
+		slog.Error("Error closing database connection", "error", err)
 	}
 }
 
diff --git a/cmd/hyperfleet-api/server/healthcheck_server.go b/cmd/hyperfleet-api/server/healthcheck_server.go
index f6772346..3aa79622 100755
--- a/cmd/hyperfleet-api/server/healthcheck_server.go
+++ b/cmd/hyperfleet-api/server/healthcheck_server.go
@@ -3,11 +3,11 @@ package server
 import (
 	"context"
 	"fmt"
+	"log/slog"
 	"net"
 	"net/http"
 
 	health "github.com/docker/go-healthcheck"
-	"github.com/golang/glog"
 	"github.com/gorilla/mux"
 )
 
@@ -50,14 +50,14 @@ func (s healthCheckServer) Start() {
 		}
 
 		// Serve with TLS
-		glog.Infof("Serving HealthCheck with TLS at %s", env().Config.HealthCheck.BindAddress)
+		slog.Info("Serving HealthCheck with TLS", "address", env().Config.HealthCheck.BindAddress)
 		err = s.httpServer.ListenAndServeTLS(env().Config.Server.HTTPSCertFile, env().Config.Server.HTTPSKeyFile)
 	} else {
-		glog.Infof("Serving HealthCheck without TLS at %s", env().Config.HealthCheck.BindAddress)
+		slog.Info("Serving HealthCheck without TLS", "address", env().Config.HealthCheck.BindAddress)
 		err = s.httpServer.ListenAndServe()
 	}
 	check(err, "HealthCheck server terminated with errors")
-	glog.Infof("HealthCheck server terminated")
+	slog.Info("HealthCheck server terminated")
 }
 
 func (s healthCheckServer) Stop() error {
diff --git a/cmd/hyperfleet-api/server/logging/formatter_json.go b/cmd/hyperfleet-api/server/logging/formatter_json.go
index 9d0062cf..4b704afe 100755
--- a/cmd/hyperfleet-api/server/logging/formatter_json.go
+++ b/cmd/hyperfleet-api/server/logging/formatter_json.go
@@ -4,8 +4,8 @@ import (
 	"encoding/json"
 	"io"
 	"net/http"
-
-	"github.com/golang/glog"
+	"os"
+	"strings"
 )
 
 func NewJSONLogFormatter() *jsonLogFormatter {
@@ -16,13 +16,19 @@ type jsonLogFormatter struct{}
 
 var _ LogFormatter = &jsonLogFormatter{}
 
+// isDebugEnabled checks if debug logging is enabled via LOG_LEVEL
+func isDebugEnabled() bool {
+	level := strings.ToLower(os.Getenv("LOG_LEVEL"))
+	return level == "debug"
+}
+
 func (f *jsonLogFormatter) FormatRequestLog(r *http.Request) (string, error) {
 	jsonlog := jsonRequestLog{
 		Method:     r.Method,
 		RequestURI: r.RequestURI,
 		RemoteAddr: r.RemoteAddr,
 	}
-	if glog.V(10) {
+	if isDebugEnabled() {
 		jsonlog.Header = r.Header
 		jsonlog.Body = r.Body
 	}
@@ -36,7 +42,7 @@ func (f *jsonLogFormatter) FormatRequestLog(r *http.Request) (string, error) {
 
 func (f *jsonLogFormatter) FormatResponseLog(info *ResponseInfo) (string, error) {
 	jsonlog := jsonResponseLog{Header: nil, Status: info.Status, Elapsed: info.Elapsed}
-	if glog.V(10) {
+	if isDebugEnabled() {
 		jsonlog.Body = string(info.Body[:])
 	}
 	log, err := json.Marshal(jsonlog)
diff --git a/cmd/hyperfleet-api/server/metrics_server.go b/cmd/hyperfleet-api/server/metrics_server.go
index 6fa14b02..5de249a9 100755
--- a/cmd/hyperfleet-api/server/metrics_server.go
+++ b/cmd/hyperfleet-api/server/metrics_server.go
@@ -3,6 +3,7 @@ package server
 import (
 	"context"
 	"fmt"
+	"log/slog"
 	"net"
 	"net/http"
 
@@ -10,7 +11,6 @@ import (
 
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/api"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/handlers"
-	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger"
 )
 
 func NewMetricsServer() Server {
@@ -45,7 +45,6 @@ func (s metricsServer) Serve(listener net.Listener) {
 }
 
 func (s metricsServer) Start() {
-	log := logger.NewOCMLogger(context.Background())
 	var err error
 	if env().Config.Metrics.EnableHTTPS {
 		if env().Config.Server.HTTPSCertFile == "" || env().Config.Server.HTTPSKeyFile == "" {
@@ -56,14 +55,14 @@ func (s metricsServer) Start() {
 		}
 
 		// Serve with TLS
-		log.Infof("Serving Metrics with TLS at %s", env().Config.Server.BindAddress)
+		slog.Info("Serving Metrics with TLS", "address", env().Config.Metrics.BindAddress)
 		err = s.httpServer.ListenAndServeTLS(env().Config.Server.HTTPSCertFile, env().Config.Server.HTTPSKeyFile)
 	} else {
-		log.Infof("Serving Metrics without TLS at %s", env().Config.Metrics.BindAddress)
+		slog.Info("Serving Metrics without TLS", "address", env().Config.Metrics.BindAddress)
 		err = s.httpServer.ListenAndServe()
 	}
 	check(err, "Metrics server terminated with errors")
-	log.Infof("Metrics server terminated")
+	slog.Info("Metrics server terminated")
 }
 
 func (s metricsServer) Stop() error {
diff --git a/cmd/hyperfleet-api/server/server.go b/cmd/hyperfleet-api/server/server.go
index 5d197f7e..811d8946 100755
--- a/cmd/hyperfleet-api/server/server.go
+++ b/cmd/hyperfleet-api/server/server.go
@@ -1,12 +1,11 @@
 package server
 
 import (
+	"log/slog"
 	"net"
 	"net/http"
 	"os"
 	"strings"
-
-	"github.com/golang/glog"
 )
 
 type Server interface {
@@ -26,7 +25,7 @@ func removeTrailingSlash(next http.Handler) http.Handler {
 // Exit on error
 func check(err error, msg string) {
 	if err != nil && err != http.ErrServerClosed {
-		glog.Errorf("%s: %s", msg, err)
+		slog.Error(msg, "error", err)
 		os.Exit(1)
 	}
 }
diff --git a/go.mod b/go.mod
index 88bdc019..0d4f6a81 100755
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,6 @@ require (
 	github.com/ghodss/yaml v1.0.0
 	github.com/go-gormigrate/gormigrate/v2 v2.0.0
 	github.com/golang-jwt/jwt/v4 v4.5.2
-	github.com/golang/glog v1.2.5
 	github.com/google/uuid v1.6.0
 	github.com/gorilla/handlers v1.4.2
 	github.com/gorilla/mux v1.8.0
@@ -65,6 +64,7 @@ require (
 	github.com/go-openapi/jsonpointer v0.21.0 // indirect
 	github.com/go-openapi/swag v0.23.0 // indirect
 	github.com/go-sql-driver/mysql v1.8.1 // indirect
+	github.com/golang/glog v1.2.5 // indirect
 	github.com/golang/protobuf v1.5.4 // indirect
 	github.com/google/go-cmp v0.7.0 // indirect
 	github.com/gorilla/css v1.0.0 // indirect
diff --git a/pkg/api/error.go b/pkg/api/error.go
index f5d990aa..010d8506 100755
--- a/pkg/api/error.go
+++ b/pkg/api/error.go
@@ -1,13 +1,14 @@
 package api
 
 import (
+	"context"
 	"encoding/json"
 	"fmt"
 	"net/http"
 	"os"
 
-	"github.com/golang/glog"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/errors"
+	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger"
 )
 
 // SendNotFound sends a 404 response with some details about the non existing resource.
@@ -38,8 +39,7 @@ func SendNotFound(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusNotFound)
 	_, err = w.Write(data)
 	if err != nil {
-		err = fmt.Errorf("can't send response body for request '%s'", r.URL.Path)
-		glog.Error(err)
+		logger.NewOCMLogger(r.Context()).Extra("path", r.URL.Path).Extra("error", err.Error()).Error("Can't send response body")
 		return
 	}
 }
@@ -59,8 +59,7 @@ func SendUnauthorized(w http.ResponseWriter, r *http.Request, message string) {
 	w.WriteHeader(http.StatusUnauthorized)
 	_, err = w.Write(data)
 	if err != nil {
-		err = fmt.Errorf("can't send response body for request '%s'", r.URL.Path)
-		glog.Error(err)
+		logger.NewOCMLogger(r.Context()).Extra("path", r.URL.Path).Extra("error", err.Error()).Error("Can't send response body")
 		return
 	}
 }
@@ -70,12 +69,7 @@ func SendPanic(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	_, err := w.Write(panicBody)
 	if err != nil {
-		err = fmt.Errorf(
-			"can't send panic response for request '%s': %s",
-			r.URL.Path,
-			err.Error(),
-		)
-		glog.Error(err)
+		logger.NewOCMLogger(r.Context()).Extra("path", r.URL.Path).Extra("error", err.Error()).Error("Can't send panic response")
 	}
 }
 
@@ -101,11 +95,7 @@ func init() {
 	// Convert it to JSON:
 	panicBody, err = json.Marshal(panicError)
 	if err != nil {
-		err = fmt.Errorf(
-			"can't create the panic error body: %s",
-			err.Error(),
-		)
-		glog.Error(err)
+		logger.NewOCMLogger(context.Background()).Extra("error", err.Error()).Error("Can't create the panic error body")
 		os.Exit(1)
 	}
 }
diff --git a/pkg/auth/authz_middleware_mock.go b/pkg/auth/authz_middleware_mock.go
index 9a0a3458..cf19f46a 100755
--- a/pkg/auth/authz_middleware_mock.go
+++ b/pkg/auth/authz_middleware_mock.go
@@ -3,7 +3,7 @@ package auth
 import (
 	"net/http"
 
-	"github.com/golang/glog"
+	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger"
 )
 
 type authzMiddlewareMock struct{}
@@ -16,7 +16,7 @@ func NewAuthzMiddlewareMock() AuthorizationMiddleware {
 
 func (a authzMiddlewareMock) AuthorizeApi(next http.Handler) http.Handler {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		glog.Infof("Mock authz allows <any>/<any> for %q/%q", r.Method, r.URL)
+		logger.NewOCMLogger(r.Context()).Extra("method", r.Method).Extra("url", r.URL.String()).Info("Mock authz allows <any>/<any>")
 		next.ServeHTTP(w, r)
 	})
 }
diff --git a/pkg/config/config.go b/pkg/config/config.go
index f7c44ffd..d5851d93 100755
--- a/pkg/config/config.go
+++ b/pkg/config/config.go
@@ -18,6 +18,7 @@ type ApplicationConfig struct {
 	HealthCheck *HealthCheckConfig `json:"health_check"`
 	Database    *DatabaseConfig    `json:"database"`
 	OCM         *OCMConfig         `json:"ocm"`
+	Logging     *LoggingConfig     `json:"logging"`
 }
 
 func NewApplicationConfig() *ApplicationConfig {
@@ -27,6 +28,7 @@ func NewApplicationConfig() *ApplicationConfig {
 		HealthCheck: NewHealthCheckConfig(),
 		Database:    NewDatabaseConfig(),
 		OCM:         NewOCMConfig(),
+		Logging:     NewLoggingConfig(),
 	}
 }
 
@@ -37,6 +39,7 @@ func (c *ApplicationConfig) AddFlags(flagset *pflag.FlagSet) {
 	c.HealthCheck.AddFlags(flagset)
 	c.Database.AddFlags(flagset)
 	c.OCM.AddFlags(flagset)
+	c.Logging.AddFlags(flagset)
 }
 
 func (c *ApplicationConfig) ReadFiles() []string {
@@ -49,6 +52,7 @@ func (c *ApplicationConfig) ReadFiles() []string {
 		{c.OCM.ReadFiles, "OCM"},
 		{c.Metrics.ReadFiles, "Metrics"},
 		{c.HealthCheck.ReadFiles, "HealthCheck"},
+		{c.Logging.ReadFiles, "Logging"},
 	}
 	var messages []string
 	for _, rf := range readFiles {
diff --git a/pkg/config/logging.go b/pkg/config/logging.go
new file mode 100644
index 00000000..6edd3e85
--- /dev/null
+++ b/pkg/config/logging.go
@@ -0,0 +1,56 @@
+package config
+
+import (
+	"os"
+
+	"github.com/spf13/pflag"
+)
+
+// LoggingConfig contains configuration for structured logging
+type LoggingConfig struct {
+	// Level is the minimum log level (debug, info, warn, error)
+	Level string `json:"level"`
+	// Format is the output format (text, json)
+	Format string `json:"format"`
+	// Output is the destination (stdout, stderr)
+	Output string `json:"output"`
+}
+
+// NewLoggingConfig creates a new LoggingConfig with defaults
+// Precedence: flags → environment variables → defaults
+// Env vars are read here as initial values, flags can override via AddFlags()
+func NewLoggingConfig() *LoggingConfig {
+	cfg := &LoggingConfig{
+		Level:  "info",
+		Format: "text",
+		Output: "stdout",
+	}
+
+	// Apply environment variables as overrides to defaults
+	// Flags (via AddFlags + Parse) will override these if explicitly set
+	if level := os.Getenv("LOG_LEVEL"); level != "" {
+		cfg.Level = level
+	}
+	if format := os.Getenv("LOG_FORMAT"); format != "" {
+		cfg.Format = format
+	}
+	if output := os.Getenv("LOG_OUTPUT"); output != "" {
+		cfg.Output = output
+	}
+
+	return cfg
+}
+
+// AddFlags adds logging configuration flags
+func (c *LoggingConfig) AddFlags(flagset *pflag.FlagSet) {
+	flagset.StringVar(&c.Level, "log-level", c.Level, "Minimum log level: debug, info, warn, error")
+	flagset.StringVar(&c.Format, "log-format", c.Format, "Log output format: text, json")
+	flagset.StringVar(&c.Output, "log-output", c.Output, "Log output destination: stdout, stderr")
+}
+
+// ReadFiles is a no-op for LoggingConfig.
+// Environment variables are read in NewLoggingConfig() to ensure proper precedence:
+// flags → environment variables → defaults
+func (c *LoggingConfig) ReadFiles() error {
+	return nil
+}
diff --git a/pkg/db/db_session/test.go b/pkg/db/db_session/test.go
index 1262e9f6..70bad020 100755
--- a/pkg/db/db_session/test.go
+++ b/pkg/db/db_session/test.go
@@ -4,9 +4,9 @@ import (
 	"context"
 	"database/sql"
 	"fmt"
+	"log/slog"
 	"time"
 
-	"github.com/golang/glog"
 	"github.com/lib/pq"
 
 	"gorm.io/driver/postgres"
@@ -51,12 +51,12 @@ func (f *Test) Init(config *config.DatabaseConfig) {
 	// Only the first time
 	once.Do(func() {
 		if err := initDatabase(config, db.Migrate); err != nil {
-			glog.Errorf("error initializing test database: %s", err)
+			slog.Error("Error initializing test database", "error", err)
 			return
 		}
 
 		if err := resetDB(config); err != nil {
-			glog.Errorf("error resetting test database: %s", err)
+			slog.Error("Error resetting test database", "error", err)
 			return
 		}
 	})
diff --git a/pkg/db/db_session/testcontainer.go b/pkg/db/db_session/testcontainer.go
index 9c288cb7..c301b3e4 100755
--- a/pkg/db/db_session/testcontainer.go
+++ b/pkg/db/db_session/testcontainer.go
@@ -4,9 +4,10 @@ import (
 	"context"
 	"database/sql"
 	"fmt"
+	"net/url"
+	"os"
 	"time"
 
-	"github.com/golang/glog"
 	"github.com/testcontainers/testcontainers-go"
 	"github.com/testcontainers/testcontainers-go/modules/postgres"
 	"github.com/testcontainers/testcontainers-go/wait"
@@ -17,6 +18,7 @@ import (
 
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/config"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/db"
+	pkglogger "github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger"
 )
 
 type Testcontainer struct {
@@ -40,8 +42,9 @@ func NewTestcontainerFactory(config *config.DatabaseConfig) *Testcontainer {
 
 func (f *Testcontainer) Init(config *config.DatabaseConfig) {
 	ctx := context.Background()
+	log := pkglogger.NewOCMLogger(ctx)
 
-	glog.Infof("Starting PostgreSQL testcontainer...")
+	log.Info("Starting PostgreSQL testcontainer...")
 
 	// Create PostgreSQL container
 	container, err := postgres.Run(ctx,
@@ -54,7 +57,8 @@ func (f *Testcontainer) Init(config *config.DatabaseConfig) {
 				WithStartupTimeout(60*time.Second)),
 	)
 	if err != nil {
-		glog.Fatalf("Failed to start PostgreSQL testcontainer: %s", err)
+		log.Extra("error", err.Error()).Error("Failed to start PostgreSQL testcontainer")
+		os.Exit(1)
 	}
 
 	f.container = container
@@ -62,15 +66,22 @@ func (f *Testcontainer) Init(config *config.DatabaseConfig) {
 	// Get connection string from container
 	connStr, err := container.ConnectionString(ctx, "sslmode=disable")
 	if err != nil {
-		glog.Fatalf("Failed to get connection string from testcontainer: %s", err)
+		log.Extra("error", err.Error()).Error("Failed to get connection string from testcontainer")
+		os.Exit(1)
 	}
 
-	glog.Infof("PostgreSQL testcontainer started at: %s", connStr)
+	// Log sanitized connection info (without credentials)
+	if parsedURL, parseErr := url.Parse(connStr); parseErr == nil {
+		log.Extra("host", parsedURL.Host).Extra("database", config.Name).Info("PostgreSQL testcontainer started")
+	} else {
+		log.Info("PostgreSQL testcontainer started")
+	}
 
 	// Open SQL connection
 	f.sqlDB, err = sql.Open("postgres", connStr)
 	if err != nil {
-		glog.Fatalf("Failed to connect to testcontainer database: %s", err)
+		log.Extra("error", err.Error()).Error("Failed to connect to testcontainer database")
+		os.Exit(1)
 	}
 
 	// Configure connection pool
@@ -93,16 +104,18 @@ func (f *Testcontainer) Init(config *config.DatabaseConfig) {
 		PreferSimpleProtocol: true,
 	}), conf)
 	if err != nil {
-		glog.Fatalf("Failed to connect GORM to testcontainer database: %s", err)
+		log.Extra("error", err.Error()).Error("Failed to connect GORM to testcontainer database")
+		os.Exit(1)
 	}
 
 	// Run migrations
-	glog.Infof("Running database migrations on testcontainer...")
+	log.Info("Running database migrations on testcontainer...")
 	if err := db.Migrate(f.g2); err != nil {
-		glog.Fatalf("Failed to run migrations on testcontainer: %s", err)
+		log.Extra("error", err.Error()).Error("Failed to run migrations on testcontainer")
+		os.Exit(1)
 	}
 
-	glog.Infof("Testcontainer database initialized successfully")
+	log.Info("Testcontainer database initialized successfully")
 }
 
 func (f *Testcontainer) DirectDB() *sql.DB {
@@ -127,21 +140,22 @@ func (f *Testcontainer) CheckConnection() error {
 
 func (f *Testcontainer) Close() error {
 	ctx := context.Background()
+	log := pkglogger.NewOCMLogger(ctx)
 
 	// Close SQL connection
 	if f.sqlDB != nil {
 		if err := f.sqlDB.Close(); err != nil {
-			glog.Errorf("Error closing SQL connection: %s", err)
+			log.Extra("error", err.Error()).Error("Error closing SQL connection")
 		}
 	}
 
 	// Terminate container
 	if f.container != nil {
-		glog.Infof("Stopping PostgreSQL testcontainer...")
+		log.Info("Stopping PostgreSQL testcontainer...")
 		if err := f.container.Terminate(ctx); err != nil {
 			return fmt.Errorf("failed to terminate testcontainer: %s", err)
 		}
-		glog.Infof("PostgreSQL testcontainer stopped")
+		log.Info("PostgreSQL testcontainer stopped")
 	}
 
 	return nil
@@ -150,6 +164,7 @@ func (f *Testcontainer) Close() error {
 func (f *Testcontainer) ResetDB() {
 	// For testcontainers, we can just truncate all tables
 	ctx := context.Background()
+	log := pkglogger.NewOCMLogger(ctx)
 	g2 := f.New(ctx)
 
 	// Truncate all business tables in the correct order (respecting FK constraints)
@@ -163,17 +178,19 @@ func (f *Testcontainer) ResetDB() {
 	for _, table := range tables {
 		if g2.Migrator().HasTable(table) {
 			if err := g2.Exec(fmt.Sprintf("TRUNCATE TABLE %s CASCADE", table)).Error; err != nil {
-				glog.Errorf("Error truncating table %s: %s", table, err)
+				log.Extra("table", table).Extra("error", err.Error()).Error("Error truncating table")
 			}
 		}
 	}
 }
 
 func (f *Testcontainer) NewListener(ctx context.Context, channel string, callback func(id string)) {
+	log := pkglogger.NewOCMLogger(ctx)
+
 	// Get the connection string for the listener
 	connStr, err := f.container.ConnectionString(ctx, "sslmode=disable")
 	if err != nil {
-		glog.Errorf("Failed to get connection string for listener: %s", err)
+		log.Extra("error", err.Error()).Error("Failed to get connection string for listener")
 		return
 	}
 
diff --git a/pkg/db/migrations.go b/pkg/db/migrations.go
index e65e7af4..8369ee68 100755
--- a/pkg/db/migrations.go
+++ b/pkg/db/migrations.go
@@ -2,9 +2,10 @@ package db
 
 import (
 	"context"
+	"log/slog"
+	"os"
 
 	"github.com/go-gormigrate/gormigrate/v2"
-	"github.com/golang/glog"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/db/migrations"
 
 	"gorm.io/gorm"
@@ -30,7 +31,8 @@ func MigrateTo(sessionFactory SessionFactory, migrationID string) {
 	m := newGormigrate(g2)
 
 	if err := m.MigrateTo(migrationID); err != nil {
-		glog.Fatalf("Could not migrate: %v", err)
+		slog.Error("Could not migrate", "migrationID", migrationID, "error", err)
+		os.Exit(1)
 	}
 }
 
diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go
index d29fa7a8..8985681b 100755
--- a/pkg/errors/errors.go
+++ b/pkg/errors/errors.go
@@ -2,11 +2,10 @@ package errors
 
 import (
 	"fmt"
+	"log/slog"
 	"net/http"
 	"strconv"
 
-	"github.com/golang/glog"
-
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/api/openapi"
 )
 
@@ -114,7 +113,7 @@ func New(code ServiceErrorCode, reason string, values ...interface{}) *ServiceEr
 	var err *ServiceError
 	exists, err := Find(code)
 	if !exists {
-		glog.Errorf("Undefined error code used: %d", code)
+		slog.Error("Undefined error code used", "code", code)
 		err = &ServiceError{
 			Code:     ErrorGeneral,
 			Reason:   "Unspecified error",
diff --git a/pkg/handlers/openapi.go b/pkg/handlers/openapi.go
index 5fee3177..c38e1b01 100755
--- a/pkg/handlers/openapi.go
+++ b/pkg/handlers/openapi.go
@@ -3,10 +3,10 @@ package handlers
 import (
 	"embed"
 	"io/fs"
+	"log/slog"
 	"net/http"
 
 	"github.com/ghodss/yaml"
-	"github.com/golang/glog"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/api"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/errors"
 	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/logger"
@@ -38,7 +38,7 @@ func NewOpenAPIHandler() (*openAPIHandler, error) {
 			err,
 		)
 	}
-	glog.Info("Loaded fully resolved OpenAPI specification from embedded pkg/api/openapi/api/openapi.yaml")
+	slog.Info("Loaded fully resolved OpenAPI specification from embedded pkg/api/openapi/api/openapi.yaml")
 
 	// Load the OpenAPI UI HTML content
 	uiContent, err := fs.ReadFile(openapiui, "openapi-ui.html")
@@ -48,7 +48,7 @@ func NewOpenAPIHandler() (*openAPIHandler, error) {
 			err,
 		)
 	}
-	glog.Info("Loaded OpenAPI UI HTML from embedded file")
+	slog.Info("Loaded OpenAPI UI HTML from embedded file")
 
 	return &openAPIHandler{
 		openAPIDefinitions: data,
diff --git a/pkg/logger/context.go b/pkg/logger/context.go
new file mode 100644
index 00000000..088117c6
--- /dev/null
+++ b/pkg/logger/context.go
@@ -0,0 +1,91 @@
+package logger
+
+import (
+	"context"
+)
+
+// Context keys for logging fields
+type contextKey string
+
+const (
+	// TraceIDKey is the context key for distributed trace ID (OpenTelemetry)
+	TraceIDKey contextKey = "trace_id"
+	// SpanIDKey is the context key for current span ID (OpenTelemetry)
+	SpanIDKey contextKey = "span_id"
+	// RequestIDKey is the context key for HTTP request ID
+	RequestIDKey contextKey = "request_id"
+	// AccountIDKey is the context key for account ID
+	AccountIDKey contextKey = "accountID"
+	// TransactionIDKey is the context key for transaction ID
+	TransactionIDKey contextKey = "txid"
+)
+
+// WithTraceID adds a trace ID to the context
+func WithTraceID(ctx context.Context, traceID string) context.Context {
+	return context.WithValue(ctx, TraceIDKey, traceID)
+}
+
+// GetTraceID retrieves the trace ID from context
+func GetTraceID(ctx context.Context) string {
+	if traceID, ok := ctx.Value(TraceIDKey).(string); ok {
+		return traceID
+	}
+	return ""
+}
+
+// WithSpanID adds a span ID to the context
+func WithSpanID(ctx context.Context, spanID string) context.Context {
+	return context.WithValue(ctx, SpanIDKey, spanID)
+}
+
+// GetSpanID retrieves the span ID from context
+func GetSpanID(ctx context.Context) string {
+	if spanID, ok := ctx.Value(SpanIDKey).(string); ok {
+		return spanID
+	}
+	return ""
+}
+
+// WithRequestID adds a request ID to the context
+func WithRequestID(ctx context.Context, requestID string) context.Context {
+	return context.WithValue(ctx, RequestIDKey, requestID)
+}
+
+// GetRequestID retrieves the request ID from context
+func GetRequestID(ctx context.Context) string {
+	if requestID, ok := ctx.Value(RequestIDKey).(string); ok {
+		return requestID
+	}
+	return ""
+}
+
+// NOTE: WithOperationID and GetOperationID are defined in operationid_middleware.go
+// The operation ID uses OpIDKey from that file for context storage
+
+// WithAccountID adds an account ID to the context
+func WithAccountID(ctx context.Context, accountID string) context.Context {
+	return context.WithValue(ctx, AccountIDKey, accountID)
+}
+
+// GetAccountID retrieves the account ID from context
+func GetAccountID(ctx context.Context) string {
+	if accountID := ctx.Value(AccountIDKey); accountID != nil {
+		if s, ok := accountID.(string); ok {
+			return s
+		}
+	}
+	return ""
+}
+
+// WithTransactionID adds a transaction ID to the context
+func WithTransactionID(ctx context.Context, txid int64) context.Context {
+	return context.WithValue(ctx, TransactionIDKey, txid)
+}
+
+// GetTransactionID retrieves the transaction ID from context
+func GetTransactionID(ctx context.Context) int64 {
+	if txid, ok := ctx.Value(TransactionIDKey).(int64); ok {
+		return txid
+	}
+	return 0
+}
diff --git a/pkg/logger/http.go b/pkg/logger/http.go
new file mode 100644
index 00000000..14ed938f
--- /dev/null
+++ b/pkg/logger/http.go
@@ -0,0 +1,57 @@
+package logger
+
+import (
+	"log/slog"
+	"net/http"
+	"time"
+)
+
+// HTTP request logging helpers per HyperFleet Logging Specification.
+// These functions create slog attributes for API-specific fields:
+//   - method: HTTP method (GET, POST, etc.)
+//   - path: Request path
+//   - status_code: HTTP response status code
+//   - duration_ms: Request duration in milliseconds
+//   - user_agent: Client user agent string
+
+// HTTPMethod returns an slog attribute for the HTTP method
+func HTTPMethod(method string) slog.Attr {
+	return slog.String("method", method)
+}
+
+// HTTPPath returns an slog attribute for the request path
+func HTTPPath(path string) slog.Attr {
+	return slog.String("path", path)
+}
+
+// HTTPStatusCode returns an slog attribute for the response status code
+func HTTPStatusCode(code int) slog.Attr {
+	return slog.Int("status_code", code)
+}
+
+// HTTPDuration returns an slog attribute for request duration in milliseconds
+func HTTPDuration(d time.Duration) slog.Attr {
+	return slog.Int64("duration_ms", d.Milliseconds())
+}
+
+// HTTPUserAgent returns an slog attribute for the user agent
+func HTTPUserAgent(ua string) slog.Attr {
+	return slog.String("user_agent", ua)
+}
+
+// HTTPRequestAttrs returns common HTTP request attributes for logging
+func HTTPRequestAttrs(r *http.Request) []slog.Attr {
+	return []slog.Attr{
+		HTTPMethod(r.Method),
+		HTTPPath(r.URL.Path),
+		HTTPUserAgent(r.UserAgent()),
+	}
+}
+
+// HTTPResponseAttrs returns HTTP response attributes for logging
+func HTTPResponseAttrs(statusCode int, duration time.Duration) []slog.Attr {
+	return []slog.Attr{
+		HTTPStatusCode(statusCode),
+		HTTPDuration(duration),
+	}
+}
diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go
index dafca42e..c80d0e67 100755
--- a/pkg/logger/logger.go
+++ b/pkg/logger/logger.go
@@ -3,125 +3,276 @@ package logger
 import (
 	"context"
 	"fmt"
+	"io"
+	"log/slog"
+	"os"
 	"strings"
+	"sync"
+)
+
+// Global logger configuration
+var (
+	globalLogger *slog.Logger
+	globalMu     sync.RWMutex
+	initOnce     sync.Once
 
-	"github.com/golang/glog"
-	"github.com/openshift-hyperfleet/hyperfleet-api/pkg/util"
+	// Build info set at initialization
+	component = "api"
+	version   = "unknown"
+	hostname  = "unknown"
 )
 
-type OCMLogger interface {
-	V(level int32) OCMLogger
-	Infof(format string, args ...interface{})
-	Extra(key string, value interface{}) OCMLogger
-	Info(message string)
-	Warning(message string)
-	Error(message string)
-	Fatal(message string)
+// Config holds the logger configuration
+type Config struct {
+	Level   string // debug, info, warn, error
+	Format  string // text, json
+	Output  string // stdout, stderr
+	Version string // application version
 }
 
-var _ OCMLogger = &logger{}
+// Initialize sets up the global logger with the given configuration
+func Initialize(cfg Config) {
+	globalMu.Lock()
+	defer globalMu.Unlock()
 
-type extra map[string]interface{}
+	// Set version
+	if cfg.Version != "" {
+		version = cfg.Version
+	}
 
-type logger struct {
-	context   context.Context
-	level     int32
-	accountID string
-	// TODO username is unused, should we be logging it? Could be pii
-	username string
-	extra    extra
-}
+	// Get hostname
+	if h, err := os.Hostname(); err == nil {
+		hostname = h
+	}
 
-// NewOCMLogger creates a new logger instance with a default verbosity of 1
-func NewOCMLogger(ctx context.Context) OCMLogger {
-	logger := &logger{
-		context:   ctx,
-		level:     1,
-		extra:     make(extra),
-		accountID: util.GetAccountIDFromContext(ctx),
+	// Determine output writer
+	var output io.Writer
+	switch strings.ToLower(cfg.Output) {
+	case "stderr":
+		output = os.Stderr
+	default:
+		output = os.Stdout
 	}
-	return logger
+
+	// Determine log level
+	var level slog.Level
+	switch strings.ToLower(cfg.Level) {
+	case "debug":
+		level = slog.LevelDebug
+	case "warn", "warning":
+		level = slog.LevelWarn
+	case "error":
+		level = slog.LevelError
+	default:
+		level = slog.LevelInfo
+	}
+
+	// Create handler options
+	opts := &slog.HandlerOptions{
+		Level: level,
+		ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
+			// Rename time to timestamp for HyperFleet standard
+			if a.Key == slog.TimeKey {
+				a.Key = "timestamp"
+			}
+			// Rename msg to message for HyperFleet standard
+			if a.Key == slog.MessageKey {
+				a.Key = "message"
+			}
+			return a
+		},
+	}
+
+	// Create handler based on format
+	var handler slog.Handler
+	switch strings.ToLower(cfg.Format) {
+	case "json":
+		handler = slog.NewJSONHandler(output, opts)
+	default:
+		handler = slog.NewTextHandler(output, opts)
+	}
+
+	// Wrap handler to add default attributes
+	handler = &contextHandler{
+		Handler: handler,
+	}
+
+	// Create and set global logger with base attributes
+	globalLogger = slog.New(handler).With(
+		slog.String("component", component),
+		slog.String("version", version),
+		slog.String("hostname", hostname),
+	)
+
+	// Set as default logger
+	slog.SetDefault(globalLogger)
 }
 
-func (l *logger) prepareLogPrefix(message string, extra extra) string {
-	prefix := " "
+// contextHandler wraps a slog.Handler to add context-based attributes
+type contextHandler struct {
+	slog.Handler
+}
 
-	if txid, ok := l.context.Value("txid").(int64); ok {
-		prefix = fmt.Sprintf("[tx_id=%d]%s", txid, prefix)
+func (h *contextHandler) Handle(ctx context.Context, r slog.Record) error {
+	// Add trace_id if present in context (OpenTelemetry)
+	if traceID := GetTraceID(ctx); traceID != "" {
+		r.AddAttrs(slog.String("trace_id", traceID))
 	}
 
-	if l.accountID != "" {
-		prefix = fmt.Sprintf("[accountID=%s]%s", l.accountID, prefix)
+	// Add span_id if present in context (OpenTelemetry)
+	if spanID := GetSpanID(ctx); spanID != "" {
+		r.AddAttrs(slog.String("span_id", spanID))
 	}
 
-	if opid, ok := l.context.Value(OpIDKey).(string); ok {
-		prefix = fmt.Sprintf("[opid=%s]%s", opid, prefix)
+	// Add request_id if present in context
+	if requestID := GetRequestID(ctx); requestID != "" {
+		r.AddAttrs(slog.String("request_id", requestID))
 	}
 
-	var args []string
-	for k, v := range extra {
-		args = append(args, fmt.Sprintf("%s=%v", k, v))
+	// Add operation_id if present in context
+	if opID := GetOperationID(ctx); opID != "" {
+		r.AddAttrs(slog.String("operation_id", opID))
 	}
 
-	return fmt.Sprintf("%s %s %s", prefix, message, strings.Join(args, " "))
+	return h.Handler.Handle(ctx, r)
+}
+
+func (h *contextHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+	return &contextHandler{Handler: h.Handler.WithAttrs(attrs)}
+}
+
+func (h *contextHandler) WithGroup(name string) slog.Handler {
+	return &contextHandler{Handler: h.Handler.WithGroup(name)}
 }
 
-func (l *logger) prepareLogPrefixf(format string, args ...interface{}) string {
-	orig := fmt.Sprintf(format, args...)
-	prefix := " "
+// Default returns the global logger, initializing with defaults if necessary
+func Default() *slog.Logger {
+	globalMu.RLock()
+	l := globalLogger
+	globalMu.RUnlock()
 
-	if txid, ok := l.context.Value("txid").(int64); ok {
-		prefix = fmt.Sprintf("[tx_id=%d]%s", txid, prefix)
+	if l == nil {
+		// Use sync.Once to ensure atomic initialization
+		initOnce.Do(func() {
+			Initialize(Config{
+				Level:  "info",
+				Format: "text",
+				Output: "stdout",
+			})
+		})
+		globalMu.RLock()
+		l = globalLogger
+		globalMu.RUnlock()
 	}
+	return l
+}
+
+// With returns a logger with the given attributes
+func With(args ...any) *slog.Logger {
+	return Default().With(args...)
+}
+
+// OCMLogger is the legacy interface for backward compatibility
+type OCMLogger interface {
+	V(level int32) OCMLogger
+	Infof(format string, args ...interface{})
+	Extra(key string, value interface{}) OCMLogger
+	Info(message string)
+	Warning(message string)
+	Error(message string)
+	Fatal(message string)
+}
+
+var _ OCMLogger = &legacyLogger{}
 
-	if l.accountID != "" {
-		prefix = fmt.Sprintf("[accountID=%s]%s", l.accountID, prefix)
+type legacyLogger struct {
+	ctx    context.Context
+	logger *slog.Logger
+	level  int32
+	attrs  []any
+}
+
+// NewOCMLogger creates a new logger instance with a default verbosity of 1
+// This maintains backward compatibility with existing code
+func NewOCMLogger(ctx context.Context) OCMLogger {
+	l := Default()
+
+	// Add context-based attributes that are not handled by contextHandler
+	// Note: operation_id is added by contextHandler.Handle to avoid duplication
+	attrs := []any{}
+
+	if accountID := GetAccountID(ctx); accountID != "" {
+		attrs = append(attrs, slog.String("account_id", accountID))
+	}
+
+	if txid := GetTransactionID(ctx); txid != 0 {
+		attrs = append(attrs, slog.Int64("tx_id", txid))
 	}
 
-	if opid, ok := l.context.Value(OpIDKey).(string); ok {
-		prefix = fmt.Sprintf("[opid=%s]%s", opid, prefix)
+	if len(attrs) > 0 {
+		l = l.With(attrs...)
 	}
 
-	return fmt.Sprintf("%s%s", prefix, orig)
+	return &legacyLogger{
+		ctx:    ctx,
+		logger: l,
+		level:  1,
+		attrs:  []any{},
+	}
 }
 
-func (l *logger) V(level int32) OCMLogger {
-	return &logger{
-		context:   l.context,
-		accountID: l.accountID,
-		username:  l.username,
-		level:     level,
+func (l *legacyLogger) V(level int32) OCMLogger {
+	return &legacyLogger{
+		ctx:    l.ctx,
+		logger: l.logger,
+		level:  level,
+		attrs:  l.attrs,
 	}
 }
 
-// Infof doesn't trigger Sentry error
-func (l *logger) Infof(format string, args ...interface{}) {
-	prefixed := l.prepareLogPrefixf(format, args...)
-	glog.V(glog.Level(l.level)).Infof("%s", prefixed)
+func (l *legacyLogger) Infof(format string, args ...interface{}) {
+	// V() levels > 1 are treated as debug
+	if l.level > 1 {
+		l.logger.With(l.attrs...).DebugContext(l.ctx, sprintf(format, args...))
+	} else {
+		l.logger.With(l.attrs...).InfoContext(l.ctx, sprintf(format, args...))
+	}
 }
 
-func (l *logger) Extra(key string, value interface{}) OCMLogger {
-	l.extra[key] = value
-	return l
+func (l *legacyLogger) Extra(key string, value interface{}) OCMLogger {
+	newAttrs := make([]any, len(l.attrs), len(l.attrs)+2)
+	copy(newAttrs, l.attrs)
+	newAttrs = append(newAttrs, slog.Any(key, value))
+
+	return &legacyLogger{
+		ctx:    l.ctx,
+		logger: l.logger,
+		level:  l.level,
+		attrs:  newAttrs,
+	}
 }
 
-func (l *logger) Info(message string) {
-	l.log(message, glog.V(glog.Level(l.level)).Infoln)
+func (l *legacyLogger) Info(message string) {
+	l.logger.With(l.attrs...).InfoContext(l.ctx, message)
 }
 
-func (l *logger) Warning(message string) {
-	l.log(message, glog.Warningln)
+func (l *legacyLogger) Warning(message string) {
+	l.logger.With(l.attrs...).WarnContext(l.ctx, message)
 }
 
-func (l *logger) Error(message string) {
-	l.log(message, glog.Errorln)
+func (l *legacyLogger) Error(message string) {
+	l.logger.With(l.attrs...).ErrorContext(l.ctx, message)
 }
 
-func (l *logger) Fatal(message string) {
-	l.log(message, glog.Fatalln)
+func (l *legacyLogger) Fatal(message string) {
+	l.logger.With(l.attrs...).ErrorContext(l.ctx, message)
+	os.Exit(1)
 }
 
-func (l *logger) log(message string, glogFunc func(args ...interface{})) {
-	prefixed := l.prepareLogPrefix(message, l.extra)
-	glogFunc(prefixed)
+// sprintf is a helper for formatting strings
+func sprintf(format string, args ...interface{}) string {
+	if len(args) == 0 {
+		return format
+	}
+	return fmt.Sprintf(format, args...)
 }
diff --git a/test/helper.go b/test/helper.go
index f79b9fae..9b728907 100755
--- a/test/helper.go
+++ b/test/helper.go
@@ -6,6 +6,7 @@ import (
 	"encoding/base64"
 	"encoding/json"
 	"fmt"
+	"log/slog"
 	"os"
 	"strings"
 	"sync"
@@ -14,7 +15,6 @@ import (
 
 	"github.com/bxcodec/faker/v3"
 	"github.com/golang-jwt/jwt/v4"
-	"github.com/golang/glog"
 	"github.com/google/uuid"
 	"github.com/segmentio/ksuid"
 	"github.com/spf13/pflag"
@@ -76,10 +76,11 @@ func NewHelper(t *testing.T) *Helper {
 		env := environments.Environment()
 		err = env.AddFlags(pflag.CommandLine)
 		if err != nil {
-			glog.Fatalf("Unable to add environment flags: %s", err.Error())
+			slog.Error("Unable to add environment flags", "error", err)
+			os.Exit(1)
 		}
 		if logLevel := os.Getenv("LOGLEVEL"); logLevel != "" {
-			glog.Infof("Using custom loglevel: %s", logLevel)
+			slog.Info("Using custom loglevel", "level", logLevel)
 			// Intentionally ignore error from Set — acceptable for tests
 			_ = pflag.CommandLine.Set("-v", logLevel)
 		}
@@ -87,7 +88,8 @@ func NewHelper(t *testing.T) *Helper {
 
 		err = env.Initialize()
 		if err != nil {
-			glog.Fatalf("Unable to initialize testing environment: %s", err.Error())
+			slog.Error("Unable to initialize testing environment", "error", err)
+			os.Exit(1)
 		}
 
 		helper = &Helper{
@@ -137,12 +139,13 @@ func (helper *Helper) startAPIServer() {
 	helper.APIServer = server.NewAPIServer()
 	listener, err := helper.APIServer.Listen()
 	if err != nil {
-		glog.Fatalf("Unable to start Test API server: %s", err)
+		slog.Error("Unable to start Test API server", "error", err)
+		os.Exit(1)
 	}
 	go func() {
-		glog.V(10).Info("Test API server started")
+		slog.Debug("Test API server started")
 		helper.APIServer.Serve(listener)
-		glog.V(10).Info("Test API server stopped")
+		slog.Debug("Test API server stopped")
 	}()
 }
 
@@ -156,59 +159,62 @@ func (helper *Helper) stopAPIServer() error {
 func (helper *Helper) startMetricsServer() {
 	helper.MetricsServer = server.NewMetricsServer()
 	go func() {
-		glog.V(10).Info("Test Metrics server started")
+		slog.Debug("Test Metrics server started")
 		helper.MetricsServer.Start()
-		glog.V(10).Info("Test Metrics server stopped")
+		slog.Debug("Test Metrics server stopped")
 	}()
 }
 
 func (helper *Helper) stopMetricsServer() {
 	if err := helper.MetricsServer.Stop(); err != nil {
-		glog.Fatalf("Unable to stop metrics server: %s", err.Error())
+		slog.Error("Unable to stop metrics server", "error", err)
+		os.Exit(1)
 	}
 }
 
 func (helper *Helper) startHealthCheckServer() {
 	helper.HealthCheckServer = server.NewHealthCheckServer()
 	go func() {
-		glog.V(10).Info("Test health check server started")
+		slog.Debug("Test health check server started")
 		helper.HealthCheckServer.Start()
-		glog.V(10).Info("Test health check server stopped")
+		slog.Debug("Test health check server stopped")
 	}()
 }
 
 func (helper *Helper) RestartServer() {
 	if err := helper.stopAPIServer(); err != nil {
-		glog.Warningf("unable to stop api server on restart: %v", err)
+		slog.Warn("unable to stop api server on restart", "error", err)
 	}
 	helper.startAPIServer()
-	glog.V(10).Info("Test API server restarted")
+	slog.Debug("Test API server restarted")
 }
 
 func (helper *Helper) RestartMetricsServer() {
 	helper.stopMetricsServer()
 	helper.startMetricsServer()
-	glog.V(10).Info("Test metrics server restarted")
+	slog.Debug("Test metrics server restarted")
 }
 
 func (helper *Helper) Reset() {
-	glog.Infof("Reseting testing environment")
+	slog.Info("Resetting testing environment")
 	env := environments.Environment()
 	// Reset the configuration
 	env.Config = config.NewApplicationConfig()
 
 	// Re-read command-line configuration into a NEW flagset
 	// This new flag set ensures we don't hit conflicts defining the same flag twice
-	// Also on reset, we don't care to be re-defining 'v' and other glog flags
+	// Also on reset, we don't care to be re-defining 'v' and other flags
 	flagset := pflag.NewFlagSet(helper.NewID(), pflag.ContinueOnError)
 	if err := env.AddFlags(flagset); err != nil {
-		glog.Fatalf("Unable to add environment flags on Reset: %s", err.Error())
+		slog.Error("Unable to add environment flags on Reset", "error", err)
+		os.Exit(1)
 	}
 	pflag.Parse()
 
 	err := env.Initialize()
 	if err != nil {
-		glog.Fatalf("Unable to reset testing environment: %s", err.Error())
+		slog.Error("Unable to reset testing environment", "error", err)
+		os.Exit(1)
 	}
 	helper.AppConfig = env.Config
 	helper.RestartServer()
diff --git a/test/integration/integration_test.go b/test/integration/integration_test.go
index d0b68ffa..b2158e69 100755
--- a/test/integration/integration_test.go
+++ b/test/integration/integration_test.go
@@ -2,19 +2,18 @@ package integration
 
 import (
 	"flag"
+	"log/slog"
 	"os"
 	"path/filepath"
 	"runtime"
 	"testing"
 
-	"github.com/golang/glog"
-
 	"github.com/openshift-hyperfleet/hyperfleet-api/test"
 )
 
 func TestMain(m *testing.M) {
 	flag.Parse()
-	glog.Infof("Starting integration test using go version %s", runtime.Version())
+	slog.Info("Starting integration test", "go_version", runtime.Version())
 
 	// Set OPENAPI_SCHEMA_PATH for integration tests if not already set
 	// This enables schema validation middleware during tests
@@ -23,7 +22,7 @@ func TestMain(m *testing.M) {
 		// Use runtime.Caller to find this file's path
 		_, filename, _, ok := runtime.Caller(0)
 		if !ok {
-			glog.Warningf("Failed to determine current file path via runtime.Caller, skipping OPENAPI_SCHEMA_PATH setup")
+			slog.Warn("Failed to determine current file path via runtime.Caller, skipping OPENAPI_SCHEMA_PATH setup")
 		} else {
 			// filename is like: /path/to/repo/test/integration/integration_test.go
 			// Navigate up: integration_test.go -> integration -> test -> repo
@@ -36,10 +35,10 @@ func TestMain(m *testing.M) {
 
 			// Verify the schema file exists before setting the env var
 			if _, err := os.Stat(schemaPath); err != nil {
-				glog.Warningf("Schema file not found at %s: %v, skipping OPENAPI_SCHEMA_PATH setup", schemaPath, err)
+				slog.Warn("Schema file not found, skipping OPENAPI_SCHEMA_PATH setup", "path", schemaPath, "error", err)
 			} else {
 				_ = os.Setenv("OPENAPI_SCHEMA_PATH", schemaPath)
-				glog.Infof("Set OPENAPI_SCHEMA_PATH=%s for integration tests", schemaPath)
+				slog.Info("Set OPENAPI_SCHEMA_PATH for integration tests", "path", schemaPath)
 			}
 		}
 	}
