harbor/src/jobservice/logger/base.go

259 lines
6.5 KiB
Go

// Copyright Project Harbor Authors
//
// 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 logger
import (
"context"
"errors"
"fmt"
"sort"
"sync"
"github.com/goharbor/harbor/src/jobservice/config"
"github.com/goharbor/harbor/src/jobservice/logger/getter"
"github.com/goharbor/harbor/src/jobservice/logger/sweeper"
)
const (
systemKeyServiceLogger = "system.jobServiceLogger"
systemKeyLogDataGetter = "system.logDataGetter"
)
var singletons sync.Map
// GetLogger gets an unified logger entry for logging per the passed settings.
// The logger may built based on the multiple registered logger backends.
//
// loggerOptions ...Option : logger options
//
// If failed, a nil logger and a non-nil error will be returned.
// Otherwise, a non nil logger is returned with nil error.
func GetLogger(loggerOptions ...Option) (Interface, error) {
lOptions := &options{
values: make(map[string][]OptionItem),
}
// Config
for _, op := range loggerOptions {
op.Apply(lOptions)
}
// No options specified, enable std as default
if len(loggerOptions) == 0 {
defaultOp := BackendOption(NameStdOutput, "", nil)
defaultOp.Apply(lOptions)
}
// Create backends
loggers := make([]Interface, 0)
for name, ops := range lOptions.values {
d, o := IsKnownLogger(name)
if !o {
return nil, fmt.Errorf("no logger registered for name '%s'", name)
}
var (
l Interface
ok bool
)
// Singleton
if d.Singleton {
var li interface{}
li, ok = singletons.Load(name)
if ok {
l = li.(Interface)
}
}
if !ok {
var err error
l, err = d.Logger(ops...)
if err != nil {
return nil, err
}
// Cache it
singletons.Store(name, l)
}
// Append to the logger list as logger entry backends
loggers = append(loggers, l)
}
return NewEntry(loggers), nil
}
// GetSweeper gets an unified sweeper controller for sweeping purpose.
//
// context context.Context : system contex used to control the sweeping loops
// sweeperOptions ...Option : sweeper options
//
// If failed, a nil sweeper and a non-nil error will be returned.
// Otherwise, a non nil sweeper is returned with nil error.
func GetSweeper(context context.Context, sweeperOptions ...Option) (sweeper.Interface, error) {
// No default sweeper will provdie
// If no one is configured, directly return nil with error
if len(sweeperOptions) == 0 {
return nil, errors.New("no options provided for creating sweeper controller")
}
sOptions := &options{
values: make(map[string][]OptionItem),
}
// Config
for _, op := range sweeperOptions {
op.Apply(sOptions)
}
sweepers := make([]sweeper.Interface, 0)
for name, ops := range sOptions.values {
if !HasSweeper(name) {
return nil, fmt.Errorf("no sweeper provided for the logger %s", name)
}
d, _ := IsKnownLogger(name)
s, err := d.Sweeper(ops...)
if err != nil {
return nil, err
}
sweepers = append(sweepers, s)
}
return NewSweeperController(context, sweepers), nil
}
// GetLogDataGetter return the 1st matched log data getter interface
//
// loggerOptions ...Option : logger options
//
// If failed,
//
// configured but initialize failed: a nil log data getter and a non-nil error will be returned.
// no getter configured: a nil log data getter with a nil error are returned
//
// Otherwise, a non nil log data getter is returned with nil error.
func GetLogDataGetter(loggerOptions ...Option) (getter.Interface, error) {
if len(loggerOptions) == 0 {
// If no options, directly return nil interface with error
return nil, errors.New("no options provided to create log data getter")
}
lOptions := &options{
values: make(map[string][]OptionItem),
}
// Config
for _, op := range loggerOptions {
op.Apply(lOptions)
}
// Iterate with specified order
keys := make([]string, 0)
for k := range lOptions.values {
keys = append(keys, k)
}
// Sort
sort.Strings(keys)
for _, k := range keys {
if HasGetter(k) {
// 1st match
d := knownLoggers[k]
theGetter, err := d.Getter(lOptions.values[k]...)
if err != nil {
return nil, err
}
return theGetter, nil
}
}
// No one configured
return nil, nil
}
// Init the loggers and sweepers
func Init(ctx context.Context) error {
// For loggers
options := make([]Option, 0)
// For sweepers
sOptions := make([]Option, 0)
for _, lc := range config.DefaultConfig.LoggerConfigs {
// Inject logger depth here for FILE and STD logger to avoid configuring it in the yaml
// For logger of job service itself, the depth should be 6
if lc.Name == NameFile || lc.Name == NameStdOutput {
if lc.Settings == nil {
lc.Settings = map[string]interface{}{}
}
lc.Settings["depth"] = 6
}
options = append(options, BackendOption(lc.Name, lc.Level, lc.Settings))
if lc.Sweeper != nil {
sOptions = append(sOptions, SweeperOption(lc.Name, lc.Sweeper.Duration, lc.Sweeper.Settings))
}
}
// Get loggers for job service
lg, err := GetLogger(options...)
if err != nil {
return err
}
// Avoid data race issue
// Do not cache this system std logger with name `NameStdOutput` as caching causes conflict with job std logger.
singletons.Delete(NameStdOutput)
singletons.Store(systemKeyServiceLogger, lg)
jOptions := make([]Option, 0)
// Append configured sweepers in job loggers if existing
for _, lc := range config.DefaultConfig.JobLoggerConfigs {
jOptions = append(jOptions, BackendOption(lc.Name, lc.Level, lc.Settings))
if lc.Sweeper != nil {
sOptions = append(sOptions, SweeperOption(lc.Name, lc.Sweeper.Duration, lc.Sweeper.Settings))
}
}
// Get log data getter with the same options of job loggers
g, err := GetLogDataGetter(jOptions...)
if err != nil {
return err
}
if g != nil {
// Avoid data race issue
singletons.Store(systemKeyLogDataGetter, g)
}
// If sweepers configured
if len(sOptions) > 0 {
// Get the sweeper controller
swp, err := GetSweeper(ctx, sOptions...)
if err != nil {
return fmt.Errorf("create logger sweeper error: %s", err)
}
// Start sweep loop
_, err = swp.Sweep()
if err != nil {
return fmt.Errorf("start logger sweeper error: %s", err)
}
}
return nil
}