Add a structured logging utility with custom prefixes and suffixes
Some checks failed
Go CI/CD / go-ci (push) Has been cancelled
Some checks failed
Go CI/CD / go-ci (push) Has been cancelled
Introduce a logging system supporting multiple severity levels, JSON formatting, and customizable prefixes and suffixes inspired by Dr. Stone. Includes random success and failure quotes to make logging more dynamic and engaging.
This commit is contained in:
parent
c92275602f
commit
94c6e882fb
64
README.md
64
README.md
@ -19,6 +19,70 @@ The most scientifically badass logging library from the Kingdom of Science!
|
|||||||
go get gitstormr.dev/stone-utils/stonelog@latest
|
go get gitstormr.dev/stone-utils/stonelog@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## ⚡ Initialization
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitstormr.dev/stone-utils/stonelog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Basic configuration (INFO level by default)
|
||||||
|
stonelog.InitStoneLog(stonelog.INFO, false, false) // false = text mode (true = JSON), false = keep suffixes (true = don't add character quotes)
|
||||||
|
|
||||||
|
// Example logs
|
||||||
|
stonelog.Observation("System initialized successfully!")
|
||||||
|
stonelog.Failure("Reactor core temperature critical!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📜 Log Levels (Scientific Style!)
|
||||||
|
|
||||||
|
| Function | Level Equivalent | Example Output |
|
||||||
|
|---------------|--------------------|--------------------------------------------------|
|
||||||
|
| Trace() | TRACE | 🔍 Science traces: Entering function X |
|
||||||
|
| Debug() | DEBUG | 🤔 Hypothesis forming: Variable X = 42 |
|
||||||
|
| Observation() | INFO | ✅ Experiment successful: User logged in |
|
||||||
|
| Hypothesis() | WARN | ⚠️ Anomaly detected: High memory usage |
|
||||||
|
| Failure() | ERROR | 💥 Critical malfunction: DB disconnected |
|
||||||
|
| Meltdown() | FATAL | ☠️ FINAL EXPERIMENT FAILED: Server exploded |
|
||||||
|
| Panic() | PANIC | 🚨 CHAIN REACTION DETECTED: Unrecoverable error |
|
||||||
|
|
||||||
|
## 🎛️ Changing Log Level
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Dynamic change! (e.g., enable DEBUG in production)
|
||||||
|
stonelog.SetLogLevel(stonelog.DEBUG)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔬 JSON Mode (For Production)
|
||||||
|
|
||||||
|
```go
|
||||||
|
stonelog.InitStoneLab(stonelog.STONE_DEBUG, true, false) // true = JSON, false = Keep suffixes
|
||||||
|
// Output: {"time":"2023-07-15T12:00:00Z","level":"OBSERVATION","message":"✅ Experiment successful: System ready","caller":"main.go:15"}
|
||||||
|
```
|
||||||
|
|
||||||
|
## **🚨 WINDOWS COLOR SUPPORT NOTICE 🚨**
|
||||||
|
|
||||||
|
*"HEY WINDOWS USERS! YOUR TERMINALS NEED SOME SCIENCE!" - Chrome*
|
||||||
|
|
||||||
|
🔬 **Current Limitations:**
|
||||||
|
- Colors don't work in old Windows Command Prompt (CMD)
|
||||||
|
- Looks boring in black & white (like stone tablets!)
|
||||||
|
- Works perfectly in modern terminals
|
||||||
|
|
||||||
|
💡 **Senku-Approved Solutions:**
|
||||||
|
1. Install **Windows Terminal** (Microsoft Store)
|
||||||
|
2. Use **WSL** (Windows Subsystem for Linux)
|
||||||
|
3. Try **VS Code** or **Git Bash**
|
||||||
|
|
||||||
|
*"This temporary setback represents just 0.0000001% of our scientific progress!"*
|
||||||
|
- **Dr. Senku Ishigami**
|
||||||
|
|
||||||
|
*(Chrome whispers: "Pssst... Linux terminals are SO BADASS!")* 🐧🔥
|
||||||
|
|
||||||
**Join the Scientific Revolution!**
|
**Join the Scientific Revolution!**
|
||||||
|
|
||||||
> "This isn't just logging - it's 10 billion percent scientific progress!" - Senku Ishigami
|
> "This isn't just logging - it's 10 billion percent scientific progress!" - Senku Ishigami
|
||||||
|
264
log.go
Normal file
264
log.go
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
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) {
|
||||||
|
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 based on the provided StoneLevel.
|
||||||
|
// It selects a prefix from predefined lists corresponding to the log level.
|
||||||
|
func getRandomPrefix(level StoneLevel) string {
|
||||||
|
switch level {
|
||||||
|
case TRACE:
|
||||||
|
idx := rand.Intn(len(stoneTracePrefixes))
|
||||||
|
return stoneTracePrefixes[idx]
|
||||||
|
|
||||||
|
case DEBUG:
|
||||||
|
idx := rand.Intn(len(stoneDebugPrefixes))
|
||||||
|
return stoneDebugPrefixes[idx]
|
||||||
|
|
||||||
|
case INFO:
|
||||||
|
idx := rand.Intn(len(stoneInfoPrefixes))
|
||||||
|
return stoneInfoPrefixes[idx]
|
||||||
|
|
||||||
|
case WARN:
|
||||||
|
idx := rand.Intn(len(stoneWarnPrefixes))
|
||||||
|
return stoneWarnPrefixes[idx]
|
||||||
|
case ERROR:
|
||||||
|
idx := rand.Intn(len(stoneErrorPrefixes))
|
||||||
|
return stoneErrorPrefixes[idx]
|
||||||
|
|
||||||
|
case FATAL:
|
||||||
|
idx := rand.Intn(len(stonePanicPrefixes))
|
||||||
|
return stonePanicPrefixes[idx]
|
||||||
|
|
||||||
|
default:
|
||||||
|
idx := rand.Intn(len(stoneInfoPrefixes))
|
||||||
|
return stoneInfoPrefixes[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
)
|
||||||
|
}
|
30
prefixes.go
30
prefixes.go
@ -1,10 +1,17 @@
|
|||||||
package stonelog
|
package stonelog
|
||||||
|
|
||||||
|
// stoneTracePrefixes contains prefixes for trace-level scientific messages.
|
||||||
|
// stoneDebugPrefixes contains prefixes for debug-level diagnostics and processes.
|
||||||
|
// stoneInfoPrefixes contains prefixes for informational scientific outcomes.
|
||||||
|
// stoneWarnPrefixes contains prefixes for warning-level messages of potential risks.
|
||||||
|
// stoneErrorPrefixes contains prefixes for error-level critical issues detected.
|
||||||
|
// stoneFatalPrefixes contains prefixes for fatal-level critical failures.
|
||||||
|
// stonePanicPrefixes contains prefixes for panic-level catastrophic failures.
|
||||||
var (
|
var (
|
||||||
stoneTracePrefixes = []string{
|
stoneTracePrefixes = []string{
|
||||||
"Science traces",
|
"Science traces:",
|
||||||
"Lab sensors detect",
|
"Lab sensors detect:",
|
||||||
"Microscope focus on",
|
"Microscope focus on:",
|
||||||
"Quantum scanner active:",
|
"Quantum scanner active:",
|
||||||
"Data particles observed:",
|
"Data particles observed:",
|
||||||
}
|
}
|
||||||
@ -41,10 +48,19 @@ var (
|
|||||||
"Hypothesis disproven:",
|
"Hypothesis disproven:",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stoneFatalPrefixes = []string{
|
||||||
|
"SCIENTIFIC APOCALYPSE:",
|
||||||
|
"FINAL EXPERIMENT FAILED:",
|
||||||
|
"LABORATORY SHUTDOWN:",
|
||||||
|
"CORE CRITICAL:",
|
||||||
|
"RADIOACTIVE TERMINATION:",
|
||||||
|
}
|
||||||
|
|
||||||
stonePanicPrefixes = []string{
|
stonePanicPrefixes = []string{
|
||||||
"¡¡¡CATASTROPHIC FAILURE!!!",
|
"CHAIN REACTION DETECTED:",
|
||||||
"Emergency protocol FAILED:",
|
"SYSTEMIC COLLAPSE:",
|
||||||
"LEAVE THE LAB!",
|
"QUANTUM CATASTROPHE:",
|
||||||
"¡¡¡IMMINENT NUCLEAR FUSION!!!",
|
"GALACTIC-LEVEL BUG DETECTED:",
|
||||||
|
"T-REX IN THE DATACENTER:",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
47
suffixes.go
Normal file
47
suffixes.go
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
package stonelog
|
||||||
|
|
||||||
|
// characterSuccessQuoteSuffixes contains success quote suffixes by characters.
|
||||||
|
// characterFailQuoteSuffixes contains failure quote suffixes by characters.
|
||||||
|
var (
|
||||||
|
characterSuccessQuoteSuffixes = []string{
|
||||||
|
" // Senku: '10 BILLION POINTS FOR SCIENCE! REVIVAL SUCCESSFUL!'",
|
||||||
|
" // Senku: 'Mwa-ha-ha! Another victory for the Kingdom of Science!'",
|
||||||
|
" // Senku: 'E=mc², baby! That’s how you optimize!'",
|
||||||
|
" // Senku: 'This experiment... is a 10 billion percent success!'",
|
||||||
|
|
||||||
|
" // Chrome: 'SO BADASS! SENKU, YOU’RE A GENIUS!'",
|
||||||
|
" // Chrome: 'HOLY CRAP, SCIENCE JUST PUNCHED THE SKY!'",
|
||||||
|
" // Chrome: 'WE’RE OFFICIALLY WIZARDS NOW!'",
|
||||||
|
|
||||||
|
" // Kohaku: 'Heh. Even I could’ve done that... maybe.'",
|
||||||
|
" // Kohaku: 'Science + fists = unstoppable!'",
|
||||||
|
|
||||||
|
" // Ryusui: 'NAVIGATION SUCCESSFUL! Time to monetize this!'",
|
||||||
|
" // Ryusui: 'Money can’t buy this... BUT I’LL TRY!'",
|
||||||
|
|
||||||
|
" // Kaseki: 'HOT BLOODED ENGINEERING... PERFECTED! (ᗒᗣᗕ)՞'",
|
||||||
|
" // Kaseki: 'I’LL CARVE A STATUE TO COMMEMORATE THIS MOMENT!'",
|
||||||
|
}
|
||||||
|
|
||||||
|
characterFailQuoteSuffixes = []string{
|
||||||
|
" // Senku: '10 BILLION REASONS TO FIX THIS... NOW.'",
|
||||||
|
" // Senku: 'This failure is... statistically impressive.'",
|
||||||
|
" // Senku: 'Mwa-ha-ha... *nervous laugh*... reboot everything.'",
|
||||||
|
|
||||||
|
" // Chrome: 'NOT BADASS! NOT BADASS AT ALL! (╥﹏╥)'",
|
||||||
|
" // Chrome: 'MY BRAIN HURTS! IS THIS HOW SCIENCE WORKS?!'",
|
||||||
|
|
||||||
|
" // Kohaku: 'Ugh. Can I just smash the server with a rock?'",
|
||||||
|
" // Kohaku: 'Senku, your science is broken. FIX IT.'",
|
||||||
|
|
||||||
|
" // Gen: 'Ah~... so this is how the world ends~?'",
|
||||||
|
" // Gen: 'Mentally calculating... yep, we’re doomed~.'",
|
||||||
|
|
||||||
|
" // Tsukasa: '...This is why I opposed technology.'",
|
||||||
|
|
||||||
|
" // Kaseki: 'BACK IN MY DAY, ERRORS WERE FIXED WITH A HAMMER! 🔨'",
|
||||||
|
" // Kaseki: 'MY SOUL... IT BURNS WITH DEBUGGING RAGE!!!'",
|
||||||
|
|
||||||
|
" // Francois: 'I’ll prepare a funeral tea for the deceased process.'",
|
||||||
|
}
|
||||||
|
)
|
Loading…
x
Reference in New Issue
Block a user