github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/state/mgo/logger.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package mgo
     5  
     6  import (
     7  	"runtime"
     8  	"strings"
     9  
    10  	"github.com/juju/loggo"
    11  	"github.com/juju/mgo/v3"
    12  	"github.com/juju/mgo/v3/txn"
    13  )
    14  
    15  const (
    16  	mgoLoggerName    = "juju.mgo"
    17  	mgoTxnLoggerName = "juju.mgo.txn"
    18  )
    19  
    20  var mgoLogger = loggo.GetLogger(mgoLoggerName)
    21  var mgoTxnLogger = loggo.GetLogger(mgoTxnLoggerName)
    22  
    23  // ConfigureMgoLogging sets up juju/mgo package logging according
    24  // to the logging config value for "juju.mgo".
    25  func ConfigureMgoLogging() {
    26  	logLevel := mgoLogger.EffectiveLogLevel()
    27  	// mgo logging is quite verbose.
    28  	// mgo "debug" is similar to juju "trace".
    29  	mgo.SetDebug(logLevel == loggo.TRACE)
    30  	// Only output mgo logging for juju "debug" or greater.
    31  	if logLevel == loggo.UNSPECIFIED || logLevel >= loggo.INFO {
    32  		mgo.SetLogger(nil)
    33  		return
    34  	}
    35  	mgo.SetLogger(&mgoLogWriter{
    36  		logger: mgoLogger,
    37  	})
    38  
    39  	logLevel = mgoTxnLogger.EffectiveLogLevel()
    40  	txn.SetDebug(logLevel == loggo.DEBUG)
    41  	// Only output mgo txn logging for juju "info" or greater.
    42  	if logLevel == loggo.UNSPECIFIED || logLevel >= loggo.WARNING {
    43  		mgo.SetLogger(nil)
    44  		return
    45  	}
    46  	txn.SetLogger(&mgoTxnLogWriter{
    47  		logger: mgoLogger,
    48  	})
    49  }
    50  
    51  type mgoLogWriter struct {
    52  	logger loggo.Logger
    53  }
    54  
    55  // Output implements the mgo log_Logger interface.
    56  func (s *mgoLogWriter) Output(calldepth int, message string) error {
    57  	// If the output results from a debug function,
    58  	// log at trace level.
    59  	caller := callerFunc(calldepth - 1)
    60  	level := loggo.DEBUG
    61  	if strings.HasPrefix(caller, "debug") {
    62  		level = loggo.TRACE
    63  	}
    64  	s.logger.LogCallf(calldepth, level, message)
    65  	return nil
    66  }
    67  
    68  type mgoTxnLogWriter struct {
    69  	logger loggo.Logger
    70  }
    71  
    72  // Output implements the mgo log_Logger interface.
    73  func (s *mgoTxnLogWriter) Output(calldepth int, message string) error {
    74  	// If the output results from a debug function,
    75  	// log at debug level.
    76  	caller := callerFunc(calldepth - 1)
    77  	level := loggo.INFO
    78  	if strings.HasPrefix(caller, "debug") {
    79  		level = loggo.DEBUG
    80  	}
    81  	s.logger.LogCallf(calldepth, level, message)
    82  	return nil
    83  }
    84  
    85  // callerFunc returns the name of the function
    86  // at the specified call depth.
    87  func callerFunc(calldepth int) string {
    88  	var pcs [100]uintptr
    89  	n := runtime.Callers(calldepth+2, pcs[:])
    90  	frames := runtime.CallersFrames(pcs[:n])
    91  	frame, _ := frames.Next()
    92  	if frame.Func == nil {
    93  		return ""
    94  	}
    95  	parts := strings.Split(frame.Func.Name(), ".")
    96  	return parts[len(parts)-1]
    97  }