All checks were successful
Go CI/CD / go-ci (push) Successful in 10m3s
Reviewed-on: #2 Reviewed-by: Cloud Administrator <cloud-admin@noreply.gitstormr.dev> Co-authored-by: Rene Nochebuena <code-raider@noreply.gitstormr.dev> Co-committed-by: Rene Nochebuena <code-raider@noreply.gitstormr.dev>
256 lines
7.2 KiB
Go
256 lines
7.2 KiB
Go
package stonelog
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// logEntry represents the structure for a single log entry.
|
|
// Time is the timestamp of the log entry in RFC3339 format.
|
|
// Level specifies the severity level of the log message.
|
|
// Caller describes the source file and line generating the log.
|
|
// Message contains the actual log details or description.
|
|
type logEntry struct {
|
|
Time string `json:"time"`
|
|
Level string `json:"level"`
|
|
Caller string `json:"caller"`
|
|
Message string `json:"message"`
|
|
}
|
|
|
|
// StoneLevel represents the severity level of logging categories.
|
|
type StoneLevel int
|
|
|
|
// String returns the string representation of the StoneLevel.
|
|
func (l StoneLevel) String() string {
|
|
return stoneLevelNames[l]
|
|
}
|
|
|
|
// Color returns the color code associated with the StoneLevel.
|
|
func (l StoneLevel) Color() string {
|
|
return levelColors[l]
|
|
}
|
|
|
|
// TRACE represents the trace log level.
|
|
// DEBUG represents the debug log level.
|
|
// INFO represents the info log level.
|
|
// WARN represents the warning log level.
|
|
// ERROR represents the error log level.
|
|
// FATAL represents the fatal log level.
|
|
// PANIC represents the panic log level.
|
|
const (
|
|
TRACE StoneLevel = iota
|
|
DEBUG
|
|
INFO
|
|
WARN
|
|
ERROR
|
|
FATAL
|
|
PANIC
|
|
)
|
|
|
|
// stoneLevelNames maps StoneLevel constants to their string representations.
|
|
var stoneLevelNames = map[StoneLevel]string{
|
|
TRACE: "TRACE",
|
|
DEBUG: "DEBUG",
|
|
INFO: "INFO",
|
|
WARN: "WARN",
|
|
ERROR: "ERROR",
|
|
FATAL: "FATAL",
|
|
PANIC: "PANIC",
|
|
}
|
|
|
|
// ANSI color codes for terminal text formatting.
|
|
const (
|
|
colorReset = "\033[0m"
|
|
colorRed = "\033[31m"
|
|
colorGreen = "\033[32m"
|
|
colorYellow = "\033[33m"
|
|
colorMagenta = "\033[35m"
|
|
colorCyan = "\033[36m"
|
|
colorWhite = "\033[37m"
|
|
)
|
|
|
|
// levelColors maps StoneLevel constants to their corresponding color codes.
|
|
var levelColors = map[StoneLevel]string{
|
|
TRACE: colorWhite,
|
|
DEBUG: colorCyan,
|
|
INFO: colorGreen,
|
|
WARN: colorYellow,
|
|
ERROR: colorRed,
|
|
FATAL: colorMagenta,
|
|
PANIC: colorRed + "\033[1m",
|
|
}
|
|
|
|
// stoneLogger is a logger instance for logging stone-related operations.
|
|
// currentLogLevel indicates the active logging level for stoneLogger.
|
|
// disableCharacterSuffixes disables suffixes in log outputs when true.
|
|
var (
|
|
stoneLogger *log.Logger
|
|
currentLogLevel StoneLevel
|
|
useJSON bool
|
|
callerFrameDepth = 3
|
|
disableCharacterSuffixes bool
|
|
)
|
|
|
|
// InitStoneLog initializes the logging system with the specified parameters.
|
|
// level sets the logging severity level threshold.
|
|
// jsonMode determines whether logs should be output in JSON format.
|
|
// disableSuffixes disables character suffixes in log messages.
|
|
func InitStoneLog(level StoneLevel, jsonMode, disableSuffixes bool) {
|
|
currentLogLevel = level
|
|
stoneLogger = log.New(os.Stdout, "", 0)
|
|
useJSON = jsonMode
|
|
disableCharacterSuffixes = disableSuffixes
|
|
}
|
|
|
|
// SetLogLevel sets the current log level to the specified StoneLevel.
|
|
func SetLogLevel(level StoneLevel) {
|
|
if level < TRACE || level > PANIC {
|
|
return
|
|
}
|
|
|
|
currentLogLevel = level
|
|
Observation("Log level set to %v", level)
|
|
}
|
|
|
|
// Trace logs a message at the TRACE using the specified format and arguments.
|
|
func Trace(format string, args ...interface{}) {
|
|
logMessage(TRACE, format, args...)
|
|
}
|
|
|
|
// Debug logs a message at the DEBUG level using the provided format and arguments.
|
|
func Debug(format string, args ...interface{}) {
|
|
logMessage(DEBUG, format, args...)
|
|
}
|
|
|
|
// Observation logs a message at the INFO level with optional formatting arguments.
|
|
func Observation(format string, args ...interface{}) {
|
|
logMessage(INFO, format, args...)
|
|
}
|
|
|
|
// Hypothesis logs a message with WARN level using the provided format and arguments.
|
|
func Hypothesis(format string, args ...interface{}) {
|
|
logMessage(WARN, format, args...)
|
|
}
|
|
|
|
// Failure logs a message at the ERROR level using the provided format and arguments.
|
|
func Failure(format string, args ...interface{}) {
|
|
logMessage(ERROR, format, args...)
|
|
}
|
|
|
|
// Meltdown logs a message at the FATAL level and terminates the program.
|
|
func Meltdown(format string, args ...interface{}) {
|
|
logMessage(FATAL, format, args...)
|
|
}
|
|
|
|
// Panic logs a message with PANIC severity and terminates the application.
|
|
func Panic(format string, args ...interface{}) {
|
|
logMessage(PANIC, format, args...)
|
|
}
|
|
|
|
// getCallerInfo retrieves the file name and line number of the caller.
|
|
// It returns the information in the format "file:line" or "unknown:0" on failure.
|
|
func getCallerInfo() string {
|
|
_, file, line, ok := runtime.Caller(callerFrameDepth)
|
|
if !ok {
|
|
return "unknown:0"
|
|
}
|
|
return filepath.Base(file) + ":" + strconv.Itoa(line)
|
|
}
|
|
|
|
// logMessage handles the logging of messages with varying severity levels.
|
|
// If the log level is below the configured threshold, it is ignored.
|
|
// Formats the message, adds a random prefix, and appends optional suffixes.
|
|
// Converts the output to JSON format if enabled or applies a custom formatter.
|
|
// Executes fatal or panic behaviors when specified severity levels are met.
|
|
func logMessage(level StoneLevel, format string, args ...interface{}) {
|
|
if level < FATAL && currentLogLevel > level {
|
|
return
|
|
}
|
|
|
|
msg := fmt.Sprintf(format, args...)
|
|
msg = getRandomPrefix(level) + " " + msg
|
|
|
|
// Determine if success or failure quote is needed
|
|
isSuccess := level <= WARN
|
|
|
|
if !disableCharacterSuffixes {
|
|
msg += getRandomQuote(isSuccess)
|
|
}
|
|
|
|
var formatted string
|
|
|
|
if useJSON {
|
|
caller := getCallerInfo()
|
|
|
|
entry := logEntry{
|
|
Time: time.Now().UTC().Format(time.RFC3339),
|
|
Level: level.String(),
|
|
Caller: caller,
|
|
Message: msg,
|
|
}
|
|
jsonData, _ := json.Marshal(entry)
|
|
formatted = string(jsonData)
|
|
} else {
|
|
formatted = stoneFormat(level, msg)
|
|
}
|
|
|
|
// Handle special logging behaviors for FATAL and PANIC
|
|
switch level {
|
|
case FATAL:
|
|
stoneLogger.Fatalln(formatted)
|
|
case PANIC:
|
|
stoneLogger.Panicln(formatted)
|
|
default:
|
|
stoneLogger.Println(formatted)
|
|
}
|
|
}
|
|
|
|
// getRandomPrefix returns a random prefix string based on the given StoneLevel.
|
|
// It selects from predefined lists corresponding to the severity of the level.
|
|
func getRandomPrefix(level StoneLevel) string {
|
|
// Map StoneLevel to their respective prefix slices
|
|
prefixMap := map[StoneLevel][]string{
|
|
TRACE: stoneTracePrefixes,
|
|
DEBUG: stoneDebugPrefixes,
|
|
INFO: stoneInfoPrefixes,
|
|
WARN: stoneWarnPrefixes,
|
|
ERROR: stoneErrorPrefixes,
|
|
FATAL: stonePanicPrefixes,
|
|
PANIC: stonePanicPrefixes,
|
|
}
|
|
|
|
prefixes := prefixMap[level]
|
|
|
|
// Select and return a random prefix
|
|
return prefixes[rand.Intn(len(prefixes))]
|
|
}
|
|
|
|
// getRandomQuote returns a random character quote based on success or failure.
|
|
// The isSuccess parameter indicates if the quote is for a success (true) or failure.
|
|
func getRandomQuote(isSuccess bool) string {
|
|
if isSuccess {
|
|
idx := rand.Intn(len(characterSuccessQuoteSuffixes))
|
|
return characterSuccessQuoteSuffixes[idx]
|
|
} else {
|
|
idx := rand.Intn(len(characterFailQuoteSuffixes))
|
|
return characterFailQuoteSuffixes[idx]
|
|
}
|
|
}
|
|
|
|
// stoneFormat formats a log message with level, timestamp, caller, and color.
|
|
func stoneFormat(level StoneLevel, msg string) string {
|
|
now := time.Now().UTC().Format(time.RFC3339)
|
|
caller := getCallerInfo()
|
|
return fmt.Sprintf(
|
|
"%s%s [%s] [%s] %s%s", level.Color(), now, level.String(), caller, msg,
|
|
colorReset,
|
|
)
|
|
}
|