Skip to content

Commit

Permalink
Merge pull request #1989 from bcbrock/cc-logging
Browse files Browse the repository at this point in the history
Added the non-'f' Go language chaincode logging APIs
  • Loading branch information
srderson committed Jun 27, 2016
2 parents b2e9316 + ef1c1bb commit 4e0b6ad
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 13 deletions.
74 changes: 66 additions & 8 deletions core/chaincode/shim/chaincode.go
Original file line number Diff line number Diff line change
Expand Up @@ -847,10 +847,33 @@ func (stub *ChaincodeStub) SetEvent(name string, payload []byte) error {

// ------------- Logging Control and Chaincode Loggers ---------------

// These facilities allow a Go language chaincode to control the logging level
// of its shim and to create its own consistent logging objects, without any
// knowledge of the underlying implementation or any other package
// requirements.
// As independent programs, Go language chaincodes can use any logging
// methodology they choose, from simple fmt.Printf() to os.Stdout, to
// decorated logs created by the author's favorite logging package. The
// chaincode "shim" interface, however, is defined by the Hyperledger fabric
// and implements its own logging methodology. This methodology currently
// includes severity-based logging control and a standard way of decorating
// the logs.
//
// The facilities defined here allow a Go language chaincode to control the
// logging level of its shim, and to create its own logs formatted
// consistently with, and temporally interleaved with the shim logs without
// any knowledge of the underlying implementation of the shim, and without any
// other package requirements. The lack of package requirements is especially
// important because even if the chaincode happened to explicitly use the same
// logging package as the shim, unless the chaincode is physically included as
// part of the hyperledger fabric source code tree it could actually end up
// using a distinct binary instance of the logging package, with different
// formats and severity levels than the binary package used by the shim.
//
// Another approach that might have been taken, and could potentially be taken
// in the future, would be for the chaincode to supply a logging object for
// the shim to use, rather than the other way around as implemented
// here. There would be some complexities associated with that approach, so
// for the moment we have chosen the simpler implementation below. The shim
// provides one or more abstract logging objects for the chaincode to use via
// the NewLogger() API, and allows the chaincode to control the severity level
// of shim logs using the SetLoggingLevel() API.

// LoggingLevel is an enumerated type of severity levels that control
// chaincode logging.
Expand Down Expand Up @@ -896,10 +919,10 @@ type ChaincodeLogger struct {
}

// NewLogger allows a Go language chaincode to create one or more logging
// objects whose logs will be consistent with, and interleaved with, logs
// created by the shim interface. The logs created by this object can be
// distinguished from shim logs by the name provided, which will aoppear in the
// logs.
// objects whose logs will be formatted consistently with, and temporally
// interleaved with the logs created by the shim interface. The logs created
// by this object can be distinguished from shim logs by the name provided,
// which will appear in the logs.
func NewLogger(name string) *ChaincodeLogger {
return &ChaincodeLogger{logging.MustGetLogger(name)}
}
Expand All @@ -917,6 +940,41 @@ func (c *ChaincodeLogger) IsEnabledFor(level LoggingLevel) bool {
return c.logger.IsEnabledFor(logging.Level(level))
}

// Debug logs will only appear if the ChaincodeLogger LoggingLevel is set to
// LogDebug.
func (c *ChaincodeLogger) Debug(args ...interface{}) {
c.logger.Debug(args...)
}

// Info logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogInfo or LogDebug.
func (c *ChaincodeLogger) Info(args ...interface{}) {
c.logger.Info(args...)
}

// Notice logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogNotice, LogInfo or LogDebug.
func (c *ChaincodeLogger) Notice(args ...interface{}) {
c.logger.Notice(args...)
}

// Warning logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogWarning, LogNotice, LogInfo or LogDebug.
func (c *ChaincodeLogger) Warning(args ...interface{}) {
c.logger.Warning(args...)
}

// Error logs will appear if the ChaincodeLogger LoggingLevel is set to
// LogError, LogWarning, LogNotice, LogInfo or LogDebug.
func (c *ChaincodeLogger) Error(args ...interface{}) {
c.logger.Error(args...)
}

// Critical logs always appear; They can not be disabled.
func (c *ChaincodeLogger) Critical(args ...interface{}) {
c.logger.Critical(args...)
}

// Debugf logs will only appear if the ChaincodeLogger LoggingLevel is set to
// LogDebug.
func (c *ChaincodeLogger) Debugf(format string, args ...interface{}) {
Expand Down
27 changes: 22 additions & 5 deletions core/chaincode/shim/shim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package shim

import (
"os"
"testing"

"github.com/op/go-logging"
Expand Down Expand Up @@ -94,14 +95,30 @@ func TestShimLogging(t *testing.T) {

// TestChaincodeLogging tests the logging APIs for chaincodes.
func TestChaincodeLogging(t *testing.T) {

// From start() - We can't call start() from this test
format := logging.MustStringFormatter("%{time:15:04:05.000} [%{module}] %{level:.4s} : %{message}")
backend := logging.NewLogBackend(os.Stderr, "", 0)
backendFormatter := logging.NewBackendFormatter(backend, format)
logging.SetBackend(backendFormatter).SetLevel(logging.Level(shimLoggingLevel), "shim")

foo := NewLogger("foo")
bar := NewLogger("bar")
foo.Debugf("Foo is debugging %d", 10)
bar.Infof("Bar in informational %d", "yes")

foo.Debugf("Foo is debugging: %d", 10)
bar.Infof("Bar is informational? %s.", "Yes")
foo.Noticef("NOTE NOTE NOTE")
bar.Warningf("Danger Danger %s %s", "Will", "Robinson")
foo.Errorf("I'm sorry Dave, I'm afraid I can't do that")
bar.Criticalf("PI is not equal to 3.14, we computed it as %f", 4.13)
bar.Warningf("Danger, Danger %s %s", "Will", "Robinson!")
foo.Errorf("I'm sorry Dave, I'm afraid I can't do that.")
bar.Criticalf("PI is not equal to 3.14, we computed it as %.2f", 4.13)

bar.Debug("Foo is debugging:", 10)
foo.Info("Bar is informational?", "Yes.")
bar.Notice("NOTE NOTE NOTE")
foo.Warning("Danger, Danger", "Will", "Robinson!")
bar.Error("I'm sorry Dave, I'm afraid I can't do that.")
foo.Critical("PI is not equal to", 3.14, ", we computed it as", 4.13)

foo.SetLevel(LogWarning)
if foo.IsEnabledFor(LogDebug) {
t.Errorf("'foo' should not be enabled for LogDebug")
Expand Down
10 changes: 10 additions & 0 deletions docs/dev-setup/logging-control.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ to the `LogLevel` API.
Formatted logging at various severity levels is provided by the functions

```
(c *ChaincodeLogger) Debug(args ...interface{})
(c *ChaincodeLogger) Info(args ...interface{})
(c *ChaincodeLogger) Notice(args ...interface{})
(c *ChaincodeLogger) Warning(args ...interface{})
(c *ChaincodeLogger) Error(args ...interface{})
(c *ChaincodeLogger) Critical(args ...interface{})
(c *ChaincodeLogger) Debugf(format string, args ...interface{})
(c *ChaincodeLogger) Infof(format string, args ...interface{})
(c *ChaincodeLogger) Noticef(format string, args ...interface{})
Expand All @@ -81,8 +88,11 @@ Formatted logging at various severity levels is provided by the functions
(c *ChaincodeLogger) Criticalf(format string, args ...interface{})
```

The `f` forms of the logging APIs provide for precise control over the formatting of the logs. The non-`f` forms of the APIs currently insert a space between the printed representations of the arguments, and arbitrarily choose the formats to use.

In the current implementation, the logs produced by the `shim` and a `ChaincodeLogger` are timestamped, marked with the logger *name* and severity level, and written to `stderr`. Note that logging level control is currently based on the *name* provided when the `ChaincodeLogger` is created. To avoid ambiguities, all `ChaincodeLogger` should be given unique names other than "shim". The logger *name* will appear in all log messages created by the logger. The `shim` logs as "shim".


Go language chaincodes can also control the logging level of the chaincode `shim` interface through the `SetLoggingLevel` API.

`SetLoggingLevel(LoggingLevel level)` - Control the logging level of the shim
Expand Down

0 comments on commit 4e0b6ad

Please sign in to comment.