Repository: CLImanga
Issue: create LOG system (file based) for CLImanga
PR: feat: add log system
The first issue I chose to work on for CLImanga was surprisingly simple at first glance:
"Create a logging system for the project."
Before opening this PR, I had never built a logging system in Go.
I mainly relied on:
-
printfin C console.logconsole.error
And honestly, I believed those were enough for most programs.
But as I began exploring the issue, I realized a real project requires far more like:
- Persistent logs
- Structured messages
- Error-level separation
- Execution tracing
- Debug-friendly information (file, line, timestamp)
This PR became my first deep dive into designing a custom, project-specific logging system.
Based on the issue and discussions with the project owner, the logging system needed to:
Write logs to a fixed file:
logs/latest.logProvide INFO and ERROR loggers
-
Include metadata:
- date
- time
- file name
- line number
-
Support helper utilities to easily log:
- the current function name
- the start/end of wrapped functions
Overwrite the log file on each program run to avoid unlimited growth
These requirements guided my design choices.
The final implementation is located in the log package and uses four Go standard libraries:
I used several libraries like: log, os, runtime, and time:
log:
Info = log.New(logFile, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
Error = log.New(logFile, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
The flags ensure each log entry includes:
- date
- time
- short file name + line number
os:
Used to manage file and directory operations:
- Create logs/ directory
- Open or create latest.log
- Truncate the file at every startup so the log doesn’t grow forever
os.MkdirAll("logs", 0755)
os.OpenFile("logs/latest.log", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
runtime:
This was the most interesting part.
I learned to use runtime.Caller to retrieve information about the function that called the logger helper:
pc, _, _, _ := runtime.Caller(1)
runtime.FuncForPC(pc).Name()
tip: if switch the number 1 as different number, it means how deeper layer want to know
This allowed me to:
- Dynamically capture function names
- Generate messages like:
INFO: Starting function main.DownloadManga
time:
Used to mark program start time in the log:
time.Now().Format(time.RFC3339)
This makes every run easy to identify when debugging.
When implementation, the following is the overall workflow of the logging system:
Initialization (Init())
When the program starts:
- Ensure the log directory exists
- Create or overwrite latest.log
- Create two loggers: Info and Error
- Write a startup banner with timestamp
func main() {
log.Init()
log.Info.Println("Application Started.")
catchProgramExit()
fmt.Println("Welcome to CLImanga!")
...
}
Function Wrapping (WrapFunction())
This helper automatically logs:
- When a function starts
- When it finishes Example:
func WrapFunction(fn func()) func() {
return func() {
Info.Printf("Starting function: %s", functionName)
fn()
Info.Printf("Finished function: %s", functionName)
}
}
This instantly improved debugging readability.
Logging caller function name (LogFunctionName())
This helper logs whichever function invoked it like:
func readChapter(mangaName *string, selectedChapter *manga.ChapterSelect, chapterList *[]manga.ChapterSelect, appInstance fyne.App, appWindow fyne.Window) {
log.LogFunctionName()
ui.DisplayChapter(appWindow, 'r', *mangaName, selectedChapter, chapterList)
appInstance.Run()
}
This was especially helpful during development, when tracing flow across multiple files.
Conclusion
Although this was a "small" feature, it became one of the most educational PRs I have worked on.
It taught me real-world logging techniques, improved my Go skills, and helped me contribute meaningful infrastructure to the CLImanga project.
This logging system will support future development and debugging — including my own future PRs.
Top comments (0)