Skip to content

Commit

Permalink
Logging structure refactor
Browse files Browse the repository at this point in the history
This splits up pkg/runtime/log into pkg/log, pkg/internal/log, and
pkg/log/zap.

- pkg/log contains generic logging helpers, and the generic root logger
  It *only* depends on logr, and thus won't pull in Zap if you don't
  need it.

- pkg/internal/log contains the internal log handle (with the
  appropriate name).  Nothing outside CR should be logging with this
  handle anyway, so this enforces it.

- pkg/log/zap contains Zap-related setup code, and thus CR only has a
  dependency on Zap if you pull in this package.

- pkg/runtime/log remains as a deprecated package to ease refactoring
  and compatibility.  While practically a few types have been removed
  from this package, in reality most consumers shouldn't notice.
  • Loading branch information
DirectXMan12 committed Jan 9, 2019
1 parent b15adb2 commit c2254ef
Show file tree
Hide file tree
Showing 11 changed files with 433 additions and 205 deletions.
35 changes: 35 additions & 0 deletions pkg/internal/log/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
Copyright 2018 The Kubernetes 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 log contains utilities for fetching a new logger
// when one is not already available.
// Deprecated: use pkg/log
package log

import (
"github.com/go-logr/logr"

"sigs.k8s.io/controller-runtime/pkg/log"
)

var (
// RuntimeLog is a base parent logger for use inside controller-runtime.
RuntimeLog logr.Logger
)

func init() {
RuntimeLog = log.Log.WithName("controller-runtime")
}
File renamed without changes.
34 changes: 34 additions & 0 deletions pkg/log/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
Copyright 2018 The Kubernetes 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 log contains utilities for fetching a new logger
// when one is not already available.
// Deprecated: use pkg/log
package log

import (
"github.com/go-logr/logr"
)

// SetLogger sets a concrete logging implementation for all deferred Loggers.
func SetLogger(l logr.Logger) {
Log.Fulfill(l)
}

// Log is the base logger used by kubebuilder. It delegates
// to another logr.Logger. You *must* call SetLogger to
// get any actual logging.
var Log = NewDelegatingLogger(NullLogger{})
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ import (

func TestSource(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t, "Runtime Log Suite", []Reporter{printer.NewlineReporter{}})
RunSpecsWithDefaultAndCustomReporters(t, "Log Suite", []Reporter{printer.NewlineReporter{}})
}
143 changes: 1 addition & 142 deletions pkg/runtime/log/log_test.go → pkg/log/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,35 +17,12 @@ limitations under the License.
package log

import (
"bytes"
"encoding/json"
"io/ioutil"

"github.com/go-logr/logr"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
kapi "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
)

// testStringer is a fmt.Stringer
type testStringer struct{}

func (testStringer) String() string {
return "value"
}

// fakeSyncWriter is a fake zap.SyncerWriter that lets us test if sync was called
type fakeSyncWriter bool

func (w *fakeSyncWriter) Write(p []byte) (int, error) {
return len(p), nil
}
func (w *fakeSyncWriter) Sync() error {
*w = true
return nil
}

// logInfo is the information for a particular fakeLogger message
type logInfo struct {
name []string
Expand Down Expand Up @@ -112,7 +89,7 @@ func (f *fakeLogger) Info(msg string, vals ...interface{}) {
func (f *fakeLogger) Enabled() bool { return true }
func (f *fakeLogger) V(lvl int) logr.InfoLogger { return f }

var _ = Describe("runtime log", func() {
var _ = Describe("logging", func() {

Describe("top-level logger", func() {
It("hold newly created loggers until a logger is set", func() {
Expand Down Expand Up @@ -238,122 +215,4 @@ var _ = Describe("runtime log", func() {
))
})
})

Describe("Zap logger setup", func() {
Context("with the default output", func() {
It("shouldn't fail when setting up production", func() {
Expect(ZapLogger(false)).NotTo(BeNil())
})

It("shouldn't fail when setting up development", func() {
Expect(ZapLogger(true)).NotTo(BeNil())
})
})

Context("with custom non-sync output", func() {
It("shouldn't fail when setting up production", func() {
Expect(ZapLoggerTo(ioutil.Discard, false)).NotTo(BeNil())
})

It("shouldn't fail when setting up development", func() {
Expect(ZapLoggerTo(ioutil.Discard, true)).NotTo(BeNil())
})
})

Context("when logging kubernetes objects", func() {
var logOut *bytes.Buffer
var logger logr.Logger

BeforeEach(func() {
logOut = new(bytes.Buffer)
By("setting up the logger")
// use production settings (false) to get just json output
logger = ZapLoggerTo(logOut, false)
})

It("should log a standard namespaced Kubernetes object name and namespace", func() {
pod := &kapi.Pod{}
pod.Name = "some-pod"
pod.Namespace = "some-ns"
logger.Info("here's a kubernetes object", "thing", pod)

outRaw := logOut.Bytes()
res := map[string]interface{}{}
Expect(json.Unmarshal(outRaw, &res)).To(Succeed())

Expect(res).To(HaveKeyWithValue("thing", map[string]interface{}{
"name": pod.Name,
"namespace": pod.Namespace,
}))
})

It("should work fine with normal stringers", func() {
logger.Info("here's a non-kubernetes stringer", "thing", testStringer{})
outRaw := logOut.Bytes()
res := map[string]interface{}{}
Expect(json.Unmarshal(outRaw, &res)).To(Succeed())

Expect(res).To(HaveKeyWithValue("thing", "value"))
})

It("should log a standard non-namespaced Kubernetes object name", func() {
node := &kapi.Node{}
node.Name = "some-node"
logger.Info("here's a kubernetes object", "thing", node)

outRaw := logOut.Bytes()
res := map[string]interface{}{}
Expect(json.Unmarshal(outRaw, &res)).To(Succeed())

Expect(res).To(HaveKeyWithValue("thing", map[string]interface{}{
"name": node.Name,
}))
})

It("should log a standard Kubernetes object's kind, if set", func() {
node := &kapi.Node{}
node.Name = "some-node"
node.APIVersion = "v1"
node.Kind = "Node"
logger.Info("here's a kubernetes object", "thing", node)

outRaw := logOut.Bytes()
res := map[string]interface{}{}
Expect(json.Unmarshal(outRaw, &res)).To(Succeed())

Expect(res).To(HaveKeyWithValue("thing", map[string]interface{}{
"name": node.Name,
"apiVersion": "v1",
"kind": "Node",
}))
})

It("should log a standard non-namespaced NamespacedName name", func() {
name := types.NamespacedName{Name: "some-node"}
logger.Info("here's a kubernetes object", "thing", name)

outRaw := logOut.Bytes()
res := map[string]interface{}{}
Expect(json.Unmarshal(outRaw, &res)).To(Succeed())

Expect(res).To(HaveKeyWithValue("thing", map[string]interface{}{
"name": name.Name,
}))
})

It("should log a standard namespaced NamespacedName name and namespace", func() {
name := types.NamespacedName{Name: "some-pod", Namespace: "some-ns"}
logger.Info("here's a kubernetes object", "thing", name)

outRaw := logOut.Bytes()
res := map[string]interface{}{}
Expect(json.Unmarshal(outRaw, &res)).To(Succeed())

Expect(res).To(HaveKeyWithValue("thing", map[string]interface{}{
"name": name.Name,
"namespace": name.Namespace,
}))
})
})
})
})
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2018 The Kubernetes Authors.
Copyright 2019 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -14,9 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// Package log contains utilities for fetching a new logger
// when one is not already available.
package log
package zap

import (
"fmt"
Expand Down Expand Up @@ -127,3 +125,4 @@ func (k *KubeAwareEncoder) EncodeEntry(entry zapcore.Entry, fields []zapcore.Fie

return k.Encoder.EncodeEntry(entry, fields)
}

68 changes: 68 additions & 0 deletions pkg/log/zap/zap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
Copyright 2019 The Kubernetes 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 zap contains helpers for setting up a new logr.Logger instance
// using the Zap logging framework.
package zap

import (
"io"
"os"
"time"

"github.com/go-logr/logr"
"github.com/go-logr/zapr"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)

// Logger is a Logger implementation.
// If development is true, a Zap development config will be used
// (stacktraces on warnings, no sampling), otherwise a Zap production
// config will be used (stacktraces on errors, sampling).
func Logger(development bool) logr.Logger {
return LoggerTo(os.Stderr, development)
}

// LoggerTo returns a new Logger implementation using Zap which logs
// to the given destination, instead of stderr. It otherise behaves like
// ZapLogger.
func LoggerTo(destWriter io.Writer, development bool) logr.Logger {
// this basically mimics New<type>Config, but with a custom sink
sink := zapcore.AddSync(destWriter)

var enc zapcore.Encoder
var lvl zap.AtomicLevel
var opts []zap.Option
if development {
encCfg := zap.NewDevelopmentEncoderConfig()
enc = zapcore.NewConsoleEncoder(encCfg)
lvl = zap.NewAtomicLevelAt(zap.DebugLevel)
opts = append(opts, zap.Development(), zap.AddStacktrace(zap.ErrorLevel))
} else {
encCfg := zap.NewProductionEncoderConfig()
enc = zapcore.NewJSONEncoder(encCfg)
lvl = zap.NewAtomicLevelAt(zap.InfoLevel)
opts = append(opts, zap.AddStacktrace(zap.WarnLevel),
zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewSampler(core, time.Second, 100, 100)
}))
}
opts = append(opts, zap.AddCallerSkip(1), zap.ErrorOutput(sink))
log := zap.New(zapcore.NewCore(&KubeAwareEncoder{Encoder: enc, Verbose: development}, sink, lvl))
log = log.WithOptions(opts...)
return zapr.NewLogger(log)
}
30 changes: 30 additions & 0 deletions pkg/log/zap/zap_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
Copyright 2018 The Kubernetes 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 zap

import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"sigs.k8s.io/controller-runtime/pkg/envtest/printer"
)

func TestSource(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecsWithDefaultAndCustomReporters(t, "Zap Log Suite", []Reporter{printer.NewlineReporter{}})
}
Loading

0 comments on commit c2254ef

Please sign in to comment.