Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 139 additions & 0 deletions log/default_logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// Copyright 2026 The casbin Authors. All Rights Reserved.
//
// 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

import (
"fmt"
"io"
"os"
"strings"
"time"
)

// DefaultLogger is the default implementation of the Logger interface.
type DefaultLogger struct {
output io.Writer
eventTypes map[EventType]bool
logCallback func(entry *LogEntry) error
}

// NewDefaultLogger creates a new DefaultLogger instance.
// If no output is set via SetOutput, it defaults to os.Stdout.
func NewDefaultLogger() *DefaultLogger {
return &DefaultLogger{
output: os.Stdout,
eventTypes: make(map[EventType]bool),
}
}

// SetOutput sets the output destination for the logger.
// It can be set to a buffer or any io.Writer.
func (l *DefaultLogger) SetOutput(w io.Writer) {
if w != nil {
l.output = w
}
}

// SetEventTypes sets the event types that should be logged.
// Only events matching these types will have IsActive set to true.
func (l *DefaultLogger) SetEventTypes(eventTypes []EventType) error {
l.eventTypes = make(map[EventType]bool)
for _, et := range eventTypes {
l.eventTypes[et] = true
}
return nil
}

// OnBeforeEvent is called before an event occurs.
// It sets the StartTime and determines if the event should be active based on configured event types.
func (l *DefaultLogger) OnBeforeEvent(entry *LogEntry) error {
if entry == nil {
return fmt.Errorf("log entry is nil")
}

entry.StartTime = time.Now()

// Set IsActive based on whether this event type is enabled
// If no event types are configured, all events are considered active
if len(l.eventTypes) == 0 {
entry.IsActive = true
} else {
entry.IsActive = l.eventTypes[entry.EventType]
}

return nil
}

// OnAfterEvent is called after an event completes.
// It calculates the duration, logs the entry if active, and calls the user callback if set.
func (l *DefaultLogger) OnAfterEvent(entry *LogEntry) error {
if entry == nil {
return fmt.Errorf("log entry is nil")
}

entry.EndTime = time.Now()
entry.Duration = entry.EndTime.Sub(entry.StartTime)

// Only log if the event is active
if entry.IsActive && l.output != nil {
if err := l.writeLog(entry); err != nil {
return err
}
}

// Call user-provided callback if set
if l.logCallback != nil {
if err := l.logCallback(entry); err != nil {
return err
}
}

return nil
}

// SetLogCallback sets a user-provided callback function.
// The callback is called at the end of OnAfterEvent.
func (l *DefaultLogger) SetLogCallback(callback func(entry *LogEntry) error) error {
l.logCallback = callback
return nil
}

// writeLog writes the log entry to the configured output.
func (l *DefaultLogger) writeLog(entry *LogEntry) error {
var logMessage string

switch entry.EventType {
case EventEnforce:
logMessage = fmt.Sprintf("[%s] Enforce: subject=%s, object=%s, action=%s, domain=%s, allowed=%v, duration=%v\n",
entry.EventType, entry.Subject, entry.Object, entry.Action, entry.Domain, entry.Allowed, entry.Duration)
case EventAddPolicy, EventRemovePolicy:
logMessage = fmt.Sprintf("[%s] RuleCount=%d, duration=%v\n",
entry.EventType, entry.RuleCount, entry.Duration)
case EventLoadPolicy, EventSavePolicy:
logMessage = fmt.Sprintf("[%s] RuleCount=%d, duration=%v\n",
entry.EventType, entry.RuleCount, entry.Duration)
default:
logMessage = fmt.Sprintf("[%s] duration=%v\n",
entry.EventType, entry.Duration)
}

if entry.Error != nil {
logMessage = strings.TrimSuffix(logMessage, "\n")
logMessage = fmt.Sprintf("%s Error: %v\n", logMessage, entry.Error)
}

_, err := l.output.Write([]byte(logMessage))
return err
}
Loading