blob: 13ec21d9998607a6a090a1ac04537cee369fb309 [file] [log] [blame]
// 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
}