2019-04-02 10:48:31 +03:00
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package log
import (
"fmt"
"sync"
"time"
)
// Event represents a logging event
type Event struct {
level Level
msg string
caller string
filename string
line int
time time . Time
stacktrace string
}
// EventLogger represents the behaviours of a logger
type EventLogger interface {
LogEvent ( event * Event ) error
Close ( )
Flush ( )
GetLevel ( ) Level
GetStacktraceLevel ( ) Level
GetName ( ) string
2020-07-06 03:07:07 +03:00
ReleaseReopen ( ) error
2019-04-02 10:48:31 +03:00
}
// ChannelledLog represents a cached channel to a LoggerProvider
type ChannelledLog struct {
name string
provider string
queue chan * Event
loggerProvider LoggerProvider
flush chan bool
close chan bool
closed chan bool
}
// NewChannelledLog a new logger instance with given logger provider and config.
func NewChannelledLog ( name , provider , config string , bufferLength int64 ) ( * ChannelledLog , error ) {
if log , ok := providers [ provider ] ; ok {
l := & ChannelledLog {
queue : make ( chan * Event , bufferLength ) ,
flush : make ( chan bool ) ,
close : make ( chan bool ) ,
closed : make ( chan bool ) ,
}
l . loggerProvider = log ( )
if err := l . loggerProvider . Init ( config ) ; err != nil {
return nil , err
}
l . name = name
l . provider = provider
go l . Start ( )
return l , nil
}
return nil , ErrUnknownProvider { provider }
}
// Start processing the ChannelledLog
func ( l * ChannelledLog ) Start ( ) {
for {
select {
case event , ok := <- l . queue :
if ! ok {
l . closeLogger ( )
return
}
l . loggerProvider . LogEvent ( event )
case _ , ok := <- l . flush :
if ! ok {
l . closeLogger ( )
return
}
l . loggerProvider . Flush ( )
2019-06-12 22:41:28 +03:00
case <- l . close :
2019-04-02 10:48:31 +03:00
l . closeLogger ( )
return
}
}
}
// LogEvent logs an event to this ChannelledLog
func ( l * ChannelledLog ) LogEvent ( event * Event ) error {
select {
case l . queue <- event :
return nil
case <- time . After ( 60 * time . Second ) :
// We're blocked!
return ErrTimeout {
Name : l . name ,
Provider : l . provider ,
}
}
}
func ( l * ChannelledLog ) closeLogger ( ) {
l . loggerProvider . Flush ( )
l . loggerProvider . Close ( )
l . closed <- true
}
// Close this ChannelledLog
func ( l * ChannelledLog ) Close ( ) {
l . close <- true
<- l . closed
}
// Flush this ChannelledLog
func ( l * ChannelledLog ) Flush ( ) {
l . flush <- true
}
2020-07-06 03:07:07 +03:00
// ReleaseReopen this ChannelledLog
func ( l * ChannelledLog ) ReleaseReopen ( ) error {
return l . loggerProvider . ReleaseReopen ( )
}
2019-04-02 10:48:31 +03:00
// GetLevel gets the level of this ChannelledLog
func ( l * ChannelledLog ) GetLevel ( ) Level {
return l . loggerProvider . GetLevel ( )
}
// GetStacktraceLevel gets the level of this ChannelledLog
func ( l * ChannelledLog ) GetStacktraceLevel ( ) Level {
return l . loggerProvider . GetStacktraceLevel ( )
}
// GetName returns the name of this ChannelledLog
func ( l * ChannelledLog ) GetName ( ) string {
return l . name
}
// MultiChannelledLog represents a cached channel to a LoggerProvider
type MultiChannelledLog struct {
name string
bufferLength int64
queue chan * Event
mutex sync . Mutex
loggers map [ string ] EventLogger
flush chan bool
close chan bool
started bool
level Level
stacktraceLevel Level
closed chan bool
2020-07-06 03:07:07 +03:00
paused chan bool
2019-04-02 10:48:31 +03:00
}
// NewMultiChannelledLog a new logger instance with given logger provider and config.
func NewMultiChannelledLog ( name string , bufferLength int64 ) * MultiChannelledLog {
m := & MultiChannelledLog {
name : name ,
queue : make ( chan * Event , bufferLength ) ,
flush : make ( chan bool ) ,
bufferLength : bufferLength ,
loggers : make ( map [ string ] EventLogger ) ,
level : NONE ,
stacktraceLevel : NONE ,
close : make ( chan bool ) ,
closed : make ( chan bool ) ,
2020-07-06 03:07:07 +03:00
paused : make ( chan bool ) ,
2019-04-02 10:48:31 +03:00
}
return m
}
// AddLogger adds a logger to this MultiChannelledLog
func ( m * MultiChannelledLog ) AddLogger ( logger EventLogger ) error {
m . mutex . Lock ( )
name := logger . GetName ( )
if _ , has := m . loggers [ name ] ; has {
m . mutex . Unlock ( )
return ErrDuplicateName { name }
}
m . loggers [ name ] = logger
if logger . GetLevel ( ) < m . level {
m . level = logger . GetLevel ( )
}
if logger . GetStacktraceLevel ( ) < m . stacktraceLevel {
m . stacktraceLevel = logger . GetStacktraceLevel ( )
}
m . mutex . Unlock ( )
go m . Start ( )
return nil
}
// DelLogger removes a sub logger from this MultiChannelledLog
// NB: If you delete the last sublogger this logger will simply drop
// log events
func ( m * MultiChannelledLog ) DelLogger ( name string ) bool {
m . mutex . Lock ( )
logger , has := m . loggers [ name ]
if ! has {
m . mutex . Unlock ( )
return false
}
delete ( m . loggers , name )
m . internalResetLevel ( )
m . mutex . Unlock ( )
logger . Flush ( )
logger . Close ( )
return true
}
// GetEventLogger returns a sub logger from this MultiChannelledLog
func ( m * MultiChannelledLog ) GetEventLogger ( name string ) EventLogger {
m . mutex . Lock ( )
defer m . mutex . Unlock ( )
return m . loggers [ name ]
}
// GetEventLoggerNames returns a list of names
func ( m * MultiChannelledLog ) GetEventLoggerNames ( ) [ ] string {
m . mutex . Lock ( )
defer m . mutex . Unlock ( )
var keys [ ] string
for k := range m . loggers {
keys = append ( keys , k )
}
return keys
}
func ( m * MultiChannelledLog ) closeLoggers ( ) {
m . mutex . Lock ( )
for _ , logger := range m . loggers {
logger . Flush ( )
logger . Close ( )
}
m . mutex . Unlock ( )
m . closed <- true
}
2020-07-06 03:07:07 +03:00
// Pause pauses this Logger
func ( m * MultiChannelledLog ) Pause ( ) {
m . paused <- true
}
// Resume resumes this Logger
func ( m * MultiChannelledLog ) Resume ( ) {
m . paused <- false
}
// ReleaseReopen causes this logger to tell its subloggers to release and reopen
func ( m * MultiChannelledLog ) ReleaseReopen ( ) error {
m . mutex . Lock ( )
defer m . mutex . Unlock ( )
var accumulatedErr error
for _ , logger := range m . loggers {
if err := logger . ReleaseReopen ( ) ; err != nil {
if accumulatedErr == nil {
accumulatedErr = fmt . Errorf ( "Error whilst reopening: %s Error: %v" , logger . GetName ( ) , err )
} else {
accumulatedErr = fmt . Errorf ( "Error whilst reopening: %s Error: %v & %v" , logger . GetName ( ) , err , accumulatedErr )
}
}
}
return accumulatedErr
}
2019-04-02 10:48:31 +03:00
// Start processing the MultiChannelledLog
func ( m * MultiChannelledLog ) Start ( ) {
m . mutex . Lock ( )
if m . started {
m . mutex . Unlock ( )
return
}
m . started = true
m . mutex . Unlock ( )
2020-07-06 03:07:07 +03:00
paused := false
2019-04-02 10:48:31 +03:00
for {
2020-07-06 03:07:07 +03:00
if paused {
select {
case paused = <- m . paused :
if ! paused {
m . ResetLevel ( )
}
case _ , ok := <- m . flush :
if ! ok {
m . closeLoggers ( )
return
}
m . mutex . Lock ( )
for _ , logger := range m . loggers {
logger . Flush ( )
}
m . mutex . Unlock ( )
case <- m . close :
m . closeLoggers ( )
return
}
continue
}
2019-04-02 10:48:31 +03:00
select {
2020-07-06 03:07:07 +03:00
case paused = <- m . paused :
if paused && m . level < INFO {
m . level = INFO
}
2019-04-02 10:48:31 +03:00
case event , ok := <- m . queue :
if ! ok {
m . closeLoggers ( )
return
}
m . mutex . Lock ( )
for _ , logger := range m . loggers {
err := logger . LogEvent ( event )
if err != nil {
fmt . Println ( err )
}
}
m . mutex . Unlock ( )
case _ , ok := <- m . flush :
if ! ok {
m . closeLoggers ( )
return
}
m . mutex . Lock ( )
for _ , logger := range m . loggers {
logger . Flush ( )
}
m . mutex . Unlock ( )
case <- m . close :
m . closeLoggers ( )
return
}
}
}
// LogEvent logs an event to this MultiChannelledLog
func ( m * MultiChannelledLog ) LogEvent ( event * Event ) error {
select {
case m . queue <- event :
return nil
2020-07-06 03:07:07 +03:00
case <- time . After ( 100 * time . Millisecond ) :
2019-04-02 10:48:31 +03:00
// We're blocked!
return ErrTimeout {
Name : m . name ,
Provider : "MultiChannelledLog" ,
}
}
}
// Close this MultiChannelledLog
func ( m * MultiChannelledLog ) Close ( ) {
m . close <- true
<- m . closed
}
// Flush this ChannelledLog
func ( m * MultiChannelledLog ) Flush ( ) {
m . flush <- true
}
// GetLevel gets the level of this MultiChannelledLog
func ( m * MultiChannelledLog ) GetLevel ( ) Level {
return m . level
}
// GetStacktraceLevel gets the level of this MultiChannelledLog
func ( m * MultiChannelledLog ) GetStacktraceLevel ( ) Level {
return m . stacktraceLevel
}
func ( m * MultiChannelledLog ) internalResetLevel ( ) Level {
m . level = NONE
for _ , logger := range m . loggers {
level := logger . GetLevel ( )
if level < m . level {
m . level = level
}
level = logger . GetStacktraceLevel ( )
if level < m . stacktraceLevel {
m . stacktraceLevel = level
}
}
return m . level
}
// ResetLevel will reset the level of this MultiChannelledLog
func ( m * MultiChannelledLog ) ResetLevel ( ) Level {
m . mutex . Lock ( )
defer m . mutex . Unlock ( )
return m . internalResetLevel ( )
}
// GetName gets the name of this MultiChannelledLog
func ( m * MultiChannelledLog ) GetName ( ) string {
return m . name
}