Log & Error Handling
Logging
Casbin uses the built-in log
to print logs to console by default like:
2017/07/15 19:43:56 [Request: alice, data1, read ---> true]
The logging is not enabled by default. You can toggle it via Enforcer.EnableLog()
or the last parameter of NewEnforcer()
.
We already support logging the model, enforce request, role, 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 intitilized within the parent application you will not see any log messages from pycasbin.
Use different logger for different enforcer
Every enforcer could have its own logger to log info, and it could be changed at run-time.
And you could use a proper logger via the last paramter of NewEnforcer()
, if you using this way to initialize your enforcer, you needn't use the enabled parameter, cause the priority of the enabled field in logger is higher.
// Set a default logger as enforcer e1's logger.
// This operation could also be seen as changing the logger of e1 at run-time.
e1.SetLogger(&Log.DefaultLogger{})
// Set another logger as enforcer e2's logger.
e2.SetLogger(&YouOwnLogger)
// Set your logger when initialize enforcer e3.
e3, _ := casbin.NewEnforcer("examples/rbac_model.conf", a, logger)
Supported loggers
We provide some loggers to help you log information.
- Go
- PHP
Logger | Author | Description |
---|---|---|
Defatule logger (built-in) | Casbin | The default logger using golang log. |
Zap logger | Casbin | Using zap, provide json encoded log and you could customize more with your own zap-logger. |
Logger | Author | Description |
---|---|---|
psr3-bridge logger | Casbin | Provides a PSR-3 compliant bridge. |
How to write a logger
Your logger should implement the Logger interface.
Method | Type | Description |
---|---|---|
EnableLog() | mandatory | Control whether print the message. |
IsEnabled() | mandatory | Show the current logger's enabled status. |
LogModel() | mandatory | Log info related to model. |
LogEnforce() | mandatory | Log info related to enforce. |
LogRole() | mandatory | Log info related to role. |
LogPolicy() | mandatory | Log info related to policy. |
You can pass your custom logger
to Enforcer.SetLogger()
.
Here is an example about 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
Error or panic may happen when you use Casbin for reasons like:
- Invalid syntax in model file (.conf).
- Invalid syntax in policy file (.csv).
- Custom error from storage adapters, e.g., MySQL fails to connect.
- Casbin's bug.
There are five main functions you may need to care about for error or panic:
Function | Behavior on error |
---|---|
NewEnforcer() | Return error |
LoadModel() | Return error |
LoadPolicy() | Return error |
SavePolicy() | Return error |
Enforce() | Return error |
NewEnforcer()
calls LoadModel()
and LoadPolicy()
inside. So you don't have to call the latter two calls when using NewEnforcer()
.
Enable & 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 policy is 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 run-time.
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")