Repository: kubernetes/klog (go)

Issue:
Implement klog.KObj klog.KRef functions
/kind feature

**Describe the solution you'd like**
Klog should implement standard klog.KObj and klog.KRef functions needed for referencing kubernetes objects.

```go
func KObj(obj ObjectMeta) ObjectRef
func KRef(namespace, name string) ObjectRef

type ObjectRef struct {
  Name      string `json:"name"`
  Namespace string `json:"namespace,omitempty"`
}
```

Specifications are described within [KEP](https://github.com/kubernetes/enhancements/blob/master/keps/sig-instrumentation/20191115-structured-logging.md#new-klog-methods)

**Anything else you would like to add:**
Part of Structured Logging effort https://github.com/kubernetes/enhancements/issues/1602
/help
/priority important-soon
/sig instrumentation
/milestone v1.19


Pull request description:
Implement klog.KObj klog.KRef functions
<!--  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**:
Implements helper functions for log format described in structured logging kep
https://github.com/kubernetes/enhancements/blob/master/keps/sig-instrumentation/20191115-structured-logging.md#helper-functions-for-log-format


**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 #127

**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
NONE
```

/cc @serathius

Patch:
diff --git a/klog.go b/klog.go
index 1bd1c2e..6dabe70 100644
--- a/klog.go
+++ b/klog.go
@@ -1370,3 +1370,40 @@ func Exitf(format string, args ...interface{}) {
 	atomic.StoreUint32(&fatalNoStacks, 1)
 	logging.printf(fatalLog, logging.logr, format, args...)
 }
+
+// ObjectRef references a kubernetes object
+type ObjectRef struct {
+	Name      string `json:"name"`
+	Namespace string `json:"namespace,omitempty"`
+}
+
+func (ref ObjectRef) String() string {
+	if ref.Namespace != "" {
+		return fmt.Sprintf("%s/%s", ref.Namespace, ref.Name)
+	}
+	return ref.Name
+}
+
+// KMetadata is a subset of the kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
+// this interface may expand in the future, but will always be a subset of the
+// kubernetes k8s.io/apimachinery/pkg/apis/meta/v1.Object interface
+type KMetadata interface {
+	GetName() string
+	GetNamespace() string
+}
+
+// KObj returns ObjectRef from ObjectMeta
+func KObj(obj KMetadata) ObjectRef {
+	return ObjectRef{
+		Name:      obj.GetName(),
+		Namespace: obj.GetNamespace(),
+	}
+}
+
+// KRef returns ObjectRef from name and namespace
+func KRef(namespace, name string) ObjectRef {
+	return ObjectRef{
+		Name:      name,
+		Namespace: namespace,
+	}
+}


Test patch:
diff --git a/klog_test.go b/klog_test.go
index fc1fdfd..2461729 100644
--- a/klog_test.go
+++ b/klog_test.go
@@ -639,3 +639,117 @@ func TestInitFlags(t *testing.T) {
 		t.Fatal("Expected log_file_max_size to be 2048")
 	}
 }
+
+func TestInfoObjectRef(t *testing.T) {
+	setFlags()
+	defer logging.swap(logging.newBuffers())
+
+	tests := []struct {
+		name string
+		ref  ObjectRef
+		want string
+	}{
+		{
+			name: "with ns",
+			ref: ObjectRef{
+				Name:      "test-name",
+				Namespace: "test-ns",
+			},
+			want: "test-ns/test-name",
+		},
+		{
+			name: "without ns",
+			ref: ObjectRef{
+				Name:      "test-name",
+				Namespace: "",
+			},
+			want: "test-name",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			Info(tt.ref)
+			if !contains(infoLog, tt.want, t) {
+				t.Errorf("expected %v, got %v", tt.want, contents(infoLog))
+			}
+		})
+	}
+}
+
+type mockKmeta struct {
+	name, ns string
+}
+
+func (m mockKmeta) GetName() string {
+	return m.name
+}
+func (m mockKmeta) GetNamespace() string {
+	return m.ns
+}
+
+func TestKObj(t *testing.T) {
+	tests := []struct {
+		name string
+		obj  KMetadata
+		want ObjectRef
+	}{
+		{
+			name: "with ns",
+			obj:  mockKmeta{"test-name", "test-ns"},
+			want: ObjectRef{
+				Name:      "test-name",
+				Namespace: "test-ns",
+			},
+		},
+		{
+			name: "without ns",
+			obj:  mockKmeta{"test-name", ""},
+			want: ObjectRef{
+				Name: "test-name",
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if KObj(tt.obj) != tt.want {
+				t.Errorf("expected %v, got %v", tt.want, KObj(tt.obj))
+			}
+		})
+	}
+}
+
+func TestKRef(t *testing.T) {
+	tests := []struct {
+		testname  string
+		name      string
+		namespace string
+		want      ObjectRef
+	}{
+		{
+			testname:  "with ns",
+			name:      "test-name",
+			namespace: "test-ns",
+			want: ObjectRef{
+				Name:      "test-name",
+				Namespace: "test-ns",
+			},
+		},
+		{
+			testname: "without ns",
+			name:     "test-name",
+			want: ObjectRef{
+				Name: "test-name",
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.testname, func(t *testing.T) {
+			if KRef(tt.namespace, tt.name) != tt.want {
+				t.Errorf("expected %v, got %v", tt.want, KRef(tt.namespace, tt.name))
+			}
+		})
+	}
+}