Skip to main content

Log & Error Handling

Logging

Casbin uses the built-in log to print logs to the console by default, like:

2017/07/15 19:43:56 [Request: alice, data1, read ---> true]

Logging is not enabled by default. You can toggle it via Enforcer.EnableLog() or the last parameter of NewEnforcer().

note

We already support logging the model, enforce request, role, and policy in Golang. You can define your own log for logging Casbin. If you are using Python, pycasbin leverages the default Python logging mechanism. The pycasbin package makes a call to logging.getLogger() to set the logger. No special logging configuration is needed other than initializing the logger in the parent application. If no logging is initialized within the parent application, you will not see any log messages from pycasbin. At the same time, when you enable log in pycasbin, it will use the default log configuration. For other pycasbin extensions, you can refer to the Django logging docs if you are a Django user. For other Python users, you should refer to the Python logging docs to configure the logger.

Use different loggers for different enforcers

Every enforcer can have its own logger to log information, and it can be changed at runtime.

And you can use a proper logger via the last parameter of NewEnforcer(). If you are using this way to initialize your enforcer, you don't need to use the enabled parameter because the priority of the enabled field in the logger is higher.

// Set a default logger as enforcer e1's logger.
// This operation can also be seen as changing the logger of e1 at runtime.
e1.SetLogger(&Log.DefaultLogger{})

// Set another logger as enforcer e2's logger.
e2.SetLogger(&YouOwnLogger)

// Set your logger when initializing enforcer e3.
e3, _ := casbin.NewEnforcer("examples/rbac_model.conf", a, logger)

Supported loggers

We provide some loggers to help you log information.

LoggerAuthorDescription
Default logger (built-in)CasbinThe default logger using golang log.
Zap loggerCasbinUsing zap, provide json encoded log and you can customize more with your own zap-logger.

How to write a logger

Your logger should implement the Logger interface.

MethodTypeDescription
EnableLog()mandatoryControl whether to print the message.
IsEnabled()mandatoryShow the current logger's enabled status.
LogModel()mandatoryLog info related to the model.
LogEnforce()mandatoryLog info related to enforcing.
LogRole()mandatoryLog info related to the role.
LogPolicy()mandatoryLog info related to the policy.

You can pass your custom logger to Enforcer.SetLogger().

Here is an example of how to customize a logger for Golang:

import (
"fmt"
"log"
"strings"
)

// DefaultLogger is the implementation for a Logger using golang log.
type DefaultLogger struct {
enabled bool
}

func (l *DefaultLogger) EnableLog(enable bool) {
l.enabled = enable
}

func (l *DefaultLogger) IsEnabled() bool {
return l.enabled
}

func (l *DefaultLogger) LogModel(model [][]string) {
if !l.enabled {
return
}
var str strings.Builder
str.WriteString("Model: ")
for _, v := range model {
str.WriteString(fmt.Sprintf("%v\n", v))
}

log.Println(str.String())
}

func (l *DefaultLogger) LogEnforce(matcher string, request []interface{}, result bool, explains [][]string) {
if !l.enabled {
return
}

var reqStr strings.Builder
reqStr.WriteString("Request: ")
for i, rval := range request {
if i != len(request)-1 {
reqStr.WriteString(fmt.Sprintf("%v, ", rval))
} else {
reqStr.WriteString(fmt.Sprintf("%v", rval))
}
}
reqStr.WriteString(fmt.Sprintf(" ---> %t\n", result))

reqStr.WriteString("Hit Policy: ")
for i, pval := range explains {
if i != len(explains)-1 {
reqStr.WriteString(fmt.Sprintf("%v, ", pval))
} else {
reqStr.WriteString(fmt.Sprintf("%v \n", pval))
}
}

log.Println(reqStr.String())
}

func (l *DefaultLogger) LogPolicy(policy map[string][][]string) {
if !l.enabled {
return
}

var str strings.Builder
str.WriteString("Policy: ")
for k, v := range policy {
str.WriteString(fmt.Sprintf("%s : %v\n", k, v))
}

log.Println(str.String())
}

func (l *DefaultLogger) LogRole(roles []string) {
if !l.enabled {
return
}

log.Println("Roles: ", roles)
}

Error handling

Errors or panics may occur when you use Casbin for reasons like:

  1. Invalid syntax in the model file (.conf).
  2. Invalid syntax in the policy file (.csv).
  3. Custom errors from storage adapters, e.g., MySQL fails to connect.
  4. Casbin's bug.

There are five main functions you may need to be aware of for errors or panics:

FunctionBehavior on error
NewEnforcer()Returns an error
LoadModel()Returns an error
LoadPolicy()Returns an error
SavePolicy()Returns an error
Enforce()Returns an error
note

NewEnforcer() calls LoadModel() and LoadPolicy() internally. So you don't have to call the latter two when using NewEnforcer().

Enable and disable

The enforcer can be disabled via the Enforcer.EnableEnforce() function. When it's disabled, Enforcer.Enforce() will always return true. Other operations like adding or removing policies are not affected. Here's an example:

e := casbin.NewEnforcer("examples/basic_model.conf", "examples/basic_policy.csv")

// Will return false.
// By default, the enforcer is enabled.
e.Enforce("non-authorized-user", "data1", "read")

// Disable the enforcer at runtime.
e.EnableEnforce(false)

// Will return true for any request.
e.Enforce("non-authorized-user", "data1", "read")

// Enable the enforcer again.
e.EnableEnforce(true)

// Will return false.
e.Enforce("non-authorized-user", "data1", "read")