Repository: kubernetes/klog
Pull request #91 (2019-08-27 15:23:33)

Starting v2 release following API break
In cd60aa438f9c750641d5feacd2999e58df8af28f, we changed the API leading
to issues like:
```
src/k8s.io/client-go/transport/round_trippers.go:70:11: cannot convert klog.V(9) (type klog.Verbose) to type bool
```

So we should move to v2 of klog

Change-Id: Ibf270e0e6dd326fa91d18140e92139455cbe4de0

<!--  Thanks for sending a pull request!  Here are some tips for you:
1. If this is your first time, read our contributor guidelines https://git.k8s.io/community/contributors/guide/pull-requests.md#the-pull-request-submit-process and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. If you want *faster* PR reviews, read how: https://git.k8s.io/community/contributors/guide/pull-requests.md#best-practices-for-faster-reviews
3. Follow the instructions for writing a release note: https://git.k8s.io/community/contributors/guide/release-notes.md
4. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
5. If this PR changes image versions, please title this PR "Bump <image name> from x.x.x to y.y.y."
-->

**What this PR does / why we need it**:

**Which issue(s) this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close the issue(s) when PR gets merged)*:
Fixes #

**Special notes for your reviewer**:

_Please confirm that if this PR changes any image versions, then that's the sole change this PR makes._

**Release note**:
<!--  Write your release note:
1. Enter your extended release note in the below block. If the PR requires additional action from users switching to the new release, include the string "action required".
2. If no release note is required, just write "NONE".
-->
```release-note

```

DirectXMan12: I replied a bit to your comment in https://github.com/kubernetes/klog/pull/20#issuecomment-523140608 and https://github.com/kubernetes/klog/pull/20#issuecomment-523141779.

TL;DR: this is a decent stopgap till we can get through a KEP to switch all of client-go and friends over to structured logging natively.  That's a lot more work longterm, though (I'd like to get it done, but even if we were to start now, it'd still be a while).
dims: +1 to get this in AND cut a release-1.x branch AND v1.0.0 tag at the commit just prior to #20
liggitt: I would recommend reverting #20, changing to v2, then considering reintroducing #20 
DirectXMan12: we may want to do the /v2 branch approach as well, since a) it gives us a path to do bugfixes on v1 and b) considering the number of people trying to `go get` w/o module support.
dims: /hold cancel
liggitt: do you need to change `go_import_path: k8s.io/klog/v2` in travis?
liggitt: and update the readme to note the minimum go version
liggitt: might need something like this in travis:
```
go_import_path: k8s.io/klog/v2
dist: xenial
go:
  - 1.11.x
  - 1.12.x
  - 1.13.x
```
liggitt: ```
=== RUN   TestHeaderWithDir
--- FAIL: TestHeaderWithDir (0.00s)
    klog_test.go:217: log format error: 0 elements, error input does not match format:
        I0102 15:04:05.067890    1234 v2/klog_test.go:212] test
    klog_test.go:223: log format error: got:
        	"I0102 15:04:05.067890    1234 v2/klog_test.go:212] test\n"
        want:	"I0102 15:04:05.067890    1234 klog/klog_test.go:0] test\n"
```



Patch:
diff --git a/.travis.yml b/.travis.yml
index 5677664..0e3fc33 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,11 +1,10 @@
 language: go
-go_import_path: k8s.io/klog
+go_import_path: k8s.io/klog/v2
 dist: xenial
 go:
-  - 1.9.x
-  - 1.10.x
   - 1.11.x
   - 1.12.x
+  - 1.13.x
 script:
   - go get -t -v ./...
   - diff -u <(echo -n) <(gofmt -d .)
diff --git a/README.md b/README.md
index 841468b..04a5b46 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,8 @@ How to use klog
 - If you want to redirect everything logged using klog somewhere else (say syslog!), you can use `klog.SetOutput()` method and supply a `io.Writer`. (See `examples/set_output/usage_set_output.go`)
 - For more logging conventions (See [Logging Conventions](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md))
 
+**NOTE**: please use the newer go versions that support semantic import versioning in modules, ideally go 1.11.4 or greater.
+
 ### Coexisting with glog
 This package can be used side by side with glog. [This example](examples/coexist_glog/coexist_glog.go) shows how to initialize and syncronize flags from the global `flag.CommandLine` FlagSet. In addition, the example makes use of stderr as combined output by setting `alsologtostderr` (or `logtostderr`) to `true`.
 
diff --git a/examples/coexist_glog/coexist_glog.go b/examples/coexist_glog/coexist_glog.go
index dd2a364..960a9b9 100644
--- a/examples/coexist_glog/coexist_glog.go
+++ b/examples/coexist_glog/coexist_glog.go
@@ -4,7 +4,7 @@ import (
 	"flag"
 
 	"github.com/golang/glog"
-	"k8s.io/klog"
+	"k8s.io/klog/v2"
 )
 
 func main() {
diff --git a/examples/klogr/main.go b/examples/klogr/main.go
index a502642..9b1b7e2 100644
--- a/examples/klogr/main.go
+++ b/examples/klogr/main.go
@@ -3,8 +3,8 @@ package main
 import (
 	"flag"
 
-	"k8s.io/klog"
-	"k8s.io/klog/klogr"
+	"k8s.io/klog/v2"
+	"k8s.io/klog/v2/klogr"
 )
 
 type myError struct {
diff --git a/examples/log_file/usage_log_file.go b/examples/log_file/usage_log_file.go
index 42b7d89..00f27f3 100644
--- a/examples/log_file/usage_log_file.go
+++ b/examples/log_file/usage_log_file.go
@@ -2,7 +2,7 @@ package main
 
 import (
 	"flag"
-	"k8s.io/klog"
+	"k8s.io/klog/v2"
 )
 
 func main() {
diff --git a/examples/set_output/usage_set_output.go b/examples/set_output/usage_set_output.go
index 6d4d5c5..3cdd388 100644
--- a/examples/set_output/usage_set_output.go
+++ b/examples/set_output/usage_set_output.go
@@ -4,7 +4,7 @@ import (
 	"bytes"
 	"flag"
 	"fmt"
-	"k8s.io/klog"
+	"k8s.io/klog/v2"
 )
 
 func main() {
diff --git a/go.mod b/go.mod
index 3877d85..84921ad 100644
--- a/go.mod
+++ b/go.mod
@@ -1,4 +1,4 @@
-module k8s.io/klog
+module k8s.io/klog/v2
 
 go 1.12
 
diff --git a/klog.go b/klog.go
index 2712ce0..2a7ab34 100644
--- a/klog.go
+++ b/klog.go
@@ -87,6 +87,8 @@ import (
 	"sync"
 	"sync/atomic"
 	"time"
+
+	"github.com/go-logr/logr"
 )
 
 // severity identifies the sort of log: info, warning etc. It also implements
@@ -142,7 +144,7 @@ func (s *severity) Set(value string) error {
 	if v, ok := severityByName(value); ok {
 		threshold = v
 	} else {
-		v, err := strconv.ParseInt(value, 10, 32)
+		v, err := strconv.Atoi(value)
 		if err != nil {
 			return err
 		}
@@ -226,7 +228,7 @@ func (l *Level) Get() interface{} {
 
 // Set is part of the flag.Value interface.
 func (l *Level) Set(value string) error {
-	v, err := strconv.ParseInt(value, 10, 32)
+	v, err := strconv.Atoi(value)
 	if err != nil {
 		return err
 	}
@@ -294,7 +296,7 @@ func (m *moduleSpec) Set(value string) error {
 			return errVmoduleSyntax
 		}
 		pattern := patLev[0]
-		v, err := strconv.ParseInt(patLev[1], 10, 32)
+		v, err := strconv.Atoi(patLev[1])
 		if err != nil {
 			return errors.New("syntax error: expect comma-separated list of filename=N")
 		}
@@ -497,6 +499,9 @@ type loggingT struct {
 
 	// If true, add the file directory to the header
 	addDirHeader bool
+
+	// If set, all output will be redirected unconditionally to the provided logr.Logger
+	logr logr.Logger
 }
 
 // buffer holds a byte Buffer for reuse. The zero value is ready for use.
@@ -679,44 +684,68 @@ func (buf *buffer) someDigits(i, d int) int {
 	return copy(buf.tmp[i:], buf.tmp[j:])
 }
 
-func (l *loggingT) println(s severity, args ...interface{}) {
+func (l *loggingT) println(s severity, logr logr.InfoLogger, args ...interface{}) {
 	buf, file, line := l.header(s, 0)
+	// if logr is set, we clear the generated header as we rely on the backing
+	// logr implementation to print headers
+	if logr != nil {
+		l.putBuffer(buf)
+		buf = l.getBuffer()
+	}
 	fmt.Fprintln(buf, args...)
-	l.output(s, buf, file, line, false)
+	l.output(s, logr, buf, file, line, false)
 }
 
-func (l *loggingT) print(s severity, args ...interface{}) {
-	l.printDepth(s, 1, args...)
+func (l *loggingT) print(s severity, logr logr.InfoLogger, args ...interface{}) {
+	l.printDepth(s, logr, 1, args...)
 }
 
-func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) {
+func (l *loggingT) printDepth(s severity, logr logr.InfoLogger, depth int, args ...interface{}) {
 	buf, file, line := l.header(s, depth)
+	// if logr is set, we clear the generated header as we rely on the backing
+	// logr implementation to print headers
+	if logr != nil {
+		l.putBuffer(buf)
+		buf = l.getBuffer()
+	}
 	fmt.Fprint(buf, args...)
 	if buf.Bytes()[buf.Len()-1] != '\n' {
 		buf.WriteByte('\n')
 	}
-	l.output(s, buf, file, line, false)
+	l.output(s, logr, buf, file, line, false)
 }
 
-func (l *loggingT) printf(s severity, format string, args ...interface{}) {
+func (l *loggingT) printf(s severity, logr logr.InfoLogger, format string, args ...interface{}) {
 	buf, file, line := l.header(s, 0)
+	// if logr is set, we clear the generated header as we rely on the backing
+	// logr implementation to print headers
+	if logr != nil {
+		l.putBuffer(buf)
+		buf = l.getBuffer()
+	}
 	fmt.Fprintf(buf, format, args...)
 	if buf.Bytes()[buf.Len()-1] != '\n' {
 		buf.WriteByte('\n')
 	}
-	l.output(s, buf, file, line, false)
+	l.output(s, logr, buf, file, line, false)
 }
 
 // printWithFileLine behaves like print but uses the provided file and line number.  If
 // alsoLogToStderr is true, the log message always appears on standard error; it
 // will also appear in the log file unless --logtostderr is set.
-func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) {
+func (l *loggingT) printWithFileLine(s severity, logr logr.InfoLogger, file string, line int, alsoToStderr bool, args ...interface{}) {
 	buf := l.formatHeader(s, file, line)
+	// if logr is set, we clear the generated header as we rely on the backing
+	// logr implementation to print headers
+	if logr != nil {
+		l.putBuffer(buf)
+		buf = l.getBuffer()
+	}
 	fmt.Fprint(buf, args...)
 	if buf.Bytes()[buf.Len()-1] != '\n' {
 		buf.WriteByte('\n')
 	}
-	l.output(s, buf, file, line, alsoToStderr)
+	l.output(s, logr, buf, file, line, alsoToStderr)
 }
 
 // redirectBuffer is used to set an alternate destination for the logs
@@ -736,6 +765,18 @@ func (rb *redirectBuffer) Write(bytes []byte) (n int, err error) {
 	return rb.w.Write(bytes)
 }
 
+// SetLogger will set the backing logr implementation for klog.
+// If set, all log lines will be supressed from the regular Output, and
+// redirected to the logr implementation.
+// All log lines include the 'severity', 'file' and 'line' values attached as
+// structured logging values.
+// Use as:
+//   ...
+//   klog.SetLogger(zapr.NewLogger(zapLog))
+func SetLogger(logr logr.Logger) {
+	logging.logr = logr
+}
+
 // SetOutput sets the output destination for all severities
 func SetOutput(w io.Writer) {
 	logging.mu.Lock()
@@ -763,7 +804,7 @@ func SetOutputBySeverity(name string, w io.Writer) {
 }
 
 // output writes the data to the log files and releases the buffer.
-func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) {
+func (l *loggingT) output(s severity, log logr.InfoLogger, buf *buffer, file string, line int, alsoToStderr bool) {
 	l.mu.Lock()
 	if l.traceLocation.isSet() {
 		if l.traceLocation.match(file, line) {
@@ -771,7 +812,15 @@ func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoTo
 		}
 	}
 	data := buf.Bytes()
-	if l.toStderr {
+	if log != nil {
+		// TODO: set 'severity' and caller information as structured log info
+		// keysAndValues := []interface{}{"severity", severityName[s], "file", file, "line", line}
+		if s == errorLog {
+			l.logr.Error(nil, string(data))
+		} else {
+			log.Info(string(data))
+		}
+	} else if l.toStderr {
 		os.Stderr.Write(data)
 	} else {
 		if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() {
@@ -1076,7 +1125,7 @@ func (lb logBridge) Write(b []byte) (n int, err error) {
 	}
 	// printWithFileLine with alsoToStderr=true, so standard log messages
 	// always appear on standard error.
-	logging.printWithFileLine(severity(lb), file, line, true, text)
+	logging.printWithFileLine(severity(lb), logging.logr, file, line, true, text)
 	return len(b), nil
 }
 
@@ -1108,13 +1157,23 @@ func (l *loggingT) setV(pc uintptr) Level {
 
 // Verbose is a boolean type that implements Infof (like Printf) etc.
 // See the documentation of V for more information.
-type Verbose bool
+type Verbose struct {
+	enabled bool
+	logr    logr.InfoLogger
+}
+
+func newVerbose(level Level, b bool) Verbose {
+	if logging.logr == nil {
+		return Verbose{b, nil}
+	}
+	return Verbose{b, logging.logr.V(int(level))}
+}
 
 // V reports whether verbosity at the call site is at least the requested level.
-// The returned value is a boolean of type Verbose, which implements Info, Infoln
+// The returned value is a struct of type Verbose, which implements Info, Infoln
 // and Infof. These methods will write to the Info log if called.
 // Thus, one may write either
-//	if klog.V(2) { klog.Info("log this") }
+//	if glog.V(2).Enabled() { klog.Info("log this") }
 // or
 //	klog.V(2).Info("log this")
 // The second form is shorter but the first is cheaper if logging is off because it does
@@ -1130,7 +1189,7 @@ func V(level Level) Verbose {
 
 	// Here is a cheap but safe test to see if V logging is enabled globally.
 	if logging.verbosity.get() >= level {
-		return Verbose(true)
+		return newVerbose(level, true)
 	}
 
 	// It's off globally but it vmodule may still be set.
@@ -1142,138 +1201,145 @@ func V(level Level) Verbose {
 		logging.mu.Lock()
 		defer logging.mu.Unlock()
 		if runtime.Callers(2, logging.pcs[:]) == 0 {
-			return Verbose(false)
+			return newVerbose(level, false)
 		}
 		v, ok := logging.vmap[logging.pcs[0]]
 		if !ok {
 			v = logging.setV(logging.pcs[0])
 		}
-		return Verbose(v >= level)
+		return newVerbose(level, v >= level)
 	}
-	return Verbose(false)
+	return newVerbose(level, false)
+}
+
+// Enabled will return true if this log level is enabled, guarded by the value
+// of v.
+// See the documentation of V for usage.
+func (v Verbose) Enabled() bool {
+	return v.enabled
 }
 
 // Info is equivalent to the global Info function, guarded by the value of v.
 // See the documentation of V for usage.
 func (v Verbose) Info(args ...interface{}) {
-	if v {
-		logging.print(infoLog, args...)
+	if v.enabled {
+		logging.print(infoLog, v.logr, args...)
 	}
 }
 
 // Infoln is equivalent to the global Infoln function, guarded by the value of v.
 // See the documentation of V for usage.
 func (v Verbose) Infoln(args ...interface{}) {
-	if v {
-		logging.println(infoLog, args...)
+	if v.enabled {
+		logging.println(infoLog, v.logr, args...)
 	}
 }
 
 // Infof is equivalent to the global Infof function, guarded by the value of v.
 // See the documentation of V for usage.
 func (v Verbose) Infof(format string, args ...interface{}) {
-	if v {
-		logging.printf(infoLog, format, args...)
+	if v.enabled {
+		logging.printf(infoLog, v.logr, format, args...)
 	}
 }
 
 // Info logs to the INFO log.
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Info(args ...interface{}) {
-	logging.print(infoLog, args...)
+	logging.print(infoLog, logging.logr, args...)
 }
 
 // InfoDepth acts as Info but uses depth to determine which call frame to log.
 // InfoDepth(0, "msg") is the same as Info("msg").
 func InfoDepth(depth int, args ...interface{}) {
-	logging.printDepth(infoLog, depth, args...)
+	logging.printDepth(infoLog, logging.logr, depth, args...)
 }
 
 // Infoln logs to the INFO log.
 // Arguments are handled in the manner of fmt.Println; a newline is always appended.
 func Infoln(args ...interface{}) {
-	logging.println(infoLog, args...)
+	logging.println(infoLog, logging.logr, args...)
 }
 
 // Infof logs to the INFO log.
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Infof(format string, args ...interface{}) {
-	logging.printf(infoLog, format, args...)
+	logging.printf(infoLog, logging.logr, format, args...)
 }
 
 // Warning logs to the WARNING and INFO logs.
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Warning(args ...interface{}) {
-	logging.print(warningLog, args...)
+	logging.print(warningLog, logging.logr, args...)
 }
 
 // WarningDepth acts as Warning but uses depth to determine which call frame to log.
 // WarningDepth(0, "msg") is the same as Warning("msg").
 func WarningDepth(depth int, args ...interface{}) {
-	logging.printDepth(warningLog, depth, args...)
+	logging.printDepth(warningLog, logging.logr, depth, args...)
 }
 
 // Warningln logs to the WARNING and INFO logs.
 // Arguments are handled in the manner of fmt.Println; a newline is always appended.
 func Warningln(args ...interface{}) {
-	logging.println(warningLog, args...)
+	logging.println(warningLog, logging.logr, args...)
 }
 
 // Warningf logs to the WARNING and INFO logs.
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Warningf(format string, args ...interface{}) {
-	logging.printf(warningLog, format, args...)
+	logging.printf(warningLog, logging.logr, format, args...)
 }
 
 // Error logs to the ERROR, WARNING, and INFO logs.
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Error(args ...interface{}) {
-	logging.print(errorLog, args...)
+	logging.print(errorLog, logging.logr, args...)
 }
 
 // ErrorDepth acts as Error but uses depth to determine which call frame to log.
 // ErrorDepth(0, "msg") is the same as Error("msg").
 func ErrorDepth(depth int, args ...interface{}) {
-	logging.printDepth(errorLog, depth, args...)
+	logging.printDepth(errorLog, logging.logr, depth, args...)
 }
 
 // Errorln logs to the ERROR, WARNING, and INFO logs.
 // Arguments are handled in the manner of fmt.Println; a newline is always appended.
 func Errorln(args ...interface{}) {
-	logging.println(errorLog, args...)
+	logging.println(errorLog, logging.logr, args...)
 }
 
 // Errorf logs to the ERROR, WARNING, and INFO logs.
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Errorf(format string, args ...interface{}) {
-	logging.printf(errorLog, format, args...)
+	logging.printf(errorLog, logging.logr, format, args...)
 }
 
 // Fatal logs to the FATAL, ERROR, WARNING, and INFO logs,
 // including a stack trace of all running goroutines, then calls os.Exit(255).
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Fatal(args ...interface{}) {
-	logging.print(fatalLog, args...)
+	logging.print(fatalLog, logging.logr, args...)
 }
 
 // FatalDepth acts as Fatal but uses depth to determine which call frame to log.
 // FatalDepth(0, "msg") is the same as Fatal("msg").
 func FatalDepth(depth int, args ...interface{}) {
-	logging.printDepth(fatalLog, depth, args...)
+	logging.printDepth(fatalLog, logging.logr, depth, args...)
 }
 
 // Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs,
 // including a stack trace of all running goroutines, then calls os.Exit(255).
 // Arguments are handled in the manner of fmt.Println; a newline is always appended.
 func Fatalln(args ...interface{}) {
-	logging.println(fatalLog, args...)
+	logging.println(fatalLog, logging.logr, args...)
 }
 
 // Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
 // including a stack trace of all running goroutines, then calls os.Exit(255).
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Fatalf(format string, args ...interface{}) {
-	logging.printf(fatalLog, format, args...)
+	logging.printf(fatalLog, logging.logr, format, args...)
 }
 
 // fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks.
@@ -1284,25 +1350,25 @@ var fatalNoStacks uint32
 // Arguments are handled in the manner of fmt.Print; a newline is appended if missing.
 func Exit(args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
-	logging.print(fatalLog, args...)
+	logging.print(fatalLog, logging.logr, args...)
 }
 
 // ExitDepth acts as Exit but uses depth to determine which call frame to log.
 // ExitDepth(0, "msg") is the same as Exit("msg").
 func ExitDepth(depth int, args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
-	logging.printDepth(fatalLog, depth, args...)
+	logging.printDepth(fatalLog, logging.logr, depth, args...)
 }
 
 // Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
 func Exitln(args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
-	logging.println(fatalLog, args...)
+	logging.println(fatalLog, logging.logr, args...)
 }
 
 // Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1).
 // Arguments are handled in the manner of fmt.Printf; a newline is appended if missing.
 func Exitf(format string, args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
-	logging.printf(fatalLog, format, args...)
+	logging.printf(fatalLog, logging.logr, format, args...)
 }
diff --git a/klogr/klogr.go b/klogr/klogr.go
index e145950..8e10665 100644
--- a/klogr/klogr.go
+++ b/klogr/klogr.go
@@ -10,7 +10,7 @@ import (
 	"sort"
 
 	"github.com/go-logr/logr"
-	"k8s.io/klog"
+	"k8s.io/klog/v2"
 )
 
 // New returns a logr.Logger which is implemented by klog.
@@ -149,7 +149,7 @@ func (l klogger) Info(msg string, kvList ...interface{}) {
 }
 
 func (l klogger) Enabled() bool {
-	return bool(klog.V(klog.Level(l.level)))
+	return bool(klog.V(klog.Level(l.level)).Enabled())
 }
 
 func (l klogger) Error(err error, msg string, kvList ...interface{}) {


Test patch:
diff --git a/integration_tests/internal/main.go b/integration_tests/internal/main.go
index 2801c17..0ac83c1 100644
--- a/integration_tests/internal/main.go
+++ b/integration_tests/internal/main.go
@@ -18,7 +18,7 @@ import (
 	"fmt"
 	"os"
 
-	"k8s.io/klog"
+	"k8s.io/klog/v2"
 )
 
 func main() {
diff --git a/klog_test.go b/klog_test.go
index 76a780c..fae5a2f 100644
--- a/klog_test.go
+++ b/klog_test.go
@@ -211,7 +211,7 @@ func TestHeaderWithDir(t *testing.T) {
 	pid = 1234
 	Info("test")
 	var line int
-	format := "I0102 15:04:05.067890    1234 klog/klog_test.go:%d] test\n"
+	format := "I0102 15:04:05.067890    1234 v2/klog_test.go:%d] test\n"
 	n, err := fmt.Sscanf(contents(infoLog), format, &line)
 	if n != 1 || err != nil {
 		t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
@@ -286,13 +286,13 @@ func TestVmoduleOn(t *testing.T) {
 	defer logging.swap(logging.newBuffers())
 	logging.vmodule.Set("klog_test=2")
 	defer logging.vmodule.Set("")
-	if !V(1) {
+	if !V(1).Enabled() {
 		t.Error("V not enabled for 1")
 	}
-	if !V(2) {
+	if !V(2).Enabled() {
 		t.Error("V not enabled for 2")
 	}
-	if V(3) {
+	if V(3).Enabled() {
 		t.Error("V enabled for 3")
 	}
 	V(2).Info("test")
@@ -311,7 +311,7 @@ func TestVmoduleOff(t *testing.T) {
 	logging.vmodule.Set("notthisfile=2")
 	defer logging.vmodule.Set("")
 	for i := 1; i <= 3; i++ {
-		if V(Level(i)) {
+		if V(Level(i)).Enabled() {
 			t.Errorf("V enabled for %d", i)
 		}
 	}
@@ -375,7 +375,7 @@ func testVmoduleGlob(pat string, match bool, t *testing.T) {
 	defer logging.swap(logging.newBuffers())
 	defer logging.vmodule.Set("")
 	logging.vmodule.Set(pat)
-	if V(2) != Verbose(match) {
+	if V(2).Enabled() != match {
 		t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match)
 	}
 }
diff --git a/klogr/klogr_test.go b/klogr/klogr_test.go
index baa9045..ce59406 100644
--- a/klogr/klogr_test.go
+++ b/klogr/klogr_test.go
@@ -5,7 +5,7 @@ import (
 	"flag"
 	"testing"
 
-	"k8s.io/klog"
+	"k8s.io/klog/v2"
 
 	"github.com/go-logr/logr"
 )