logs.um
A logging library.
Every log has a tag. The tag is used to distinguish between different parts of the program. Every tag can have specific log level, which is different from the global log level. This allows you to enable the debug logs just for the code you are debugging.
This library offers following loggin backends:
ConsoleBackend
- log tostdout
FileBackend
- log to file
interface Backend
Defines a logging backend.
type Backend* = interface {
enum LogLevel
type LogLevel* = int
const (
LogLevelDbg* = LogLevel(0)
LogLevelInf* = LogLevel(1)
LogLevelWrn* = LogLevel(2)
LogLevelSus* = LogLevel(3)
LogLevelErr* = LogLevel(4)
)
struct ConsoleBackend
Backend which logs to the console. This is the default backend.
type ConsoleBackend* = struct { }
struct FileBackend
Backend which logs to a file. Created using mkFileBackend
.
type FileBackend* = struct {
// Contains private fields
fn FileBackend.close
Closes the internal file.
fn (this: ^FileBackend) close*() {
fn mkFileBackend
Creates a FileBackend
. If rewrite
is false, the file will be appended.
fn mkFileBackend*(path: str, rewrite: bool = false): (FileBackend, std::Err) {
fn init
Initialize logs.um
. You need to call this before using any other functions.
fn init*() {
fn setBackend
Set the logging backend.
fn setBackend*(b: Backend) {
fn setGlobalLogLevel
Set the global log level.
fn setGlobalLogLevel*(level: LogLevel) {
fn setTagLogLevel
Set the tag specific log level.
fn setTagLogLevel*(tag: str, level: LogLevel) {
fn dbg
fn dbg*(tag: str, fmt: str, args: ..any) {
fn inf
fn inf*(tag: str, fmt: str, args: ..any) {
fn wrn
fn wrn*(tag: str, fmt: str, args: ..any) {
fn sus
fn sus*(tag: str, fmt: str, args: ..any) {
fn err
fn err*(tag: str, fmt: str, args: ..any) {
import (
"std.um"
"umbox/fmt/fmt.um"
)
//~~
// A logging library.
//
// Every log has a tag. The tag is used to distinguish between different parts
// of the program. Every tag can have specific log level, which is different
// from the global log level. This allows you to enable the debug logs just for
// the code you are debugging.
//
// This library offers following loggin backends:
// * `ConsoleBackend` - log to `stdout`
// * `FileBackend` - log to file
//~~
//~~interface Backend
// Defines a logging backend.
type Backend* = interface {
//~~
write(s: str)
}
//~~enum LogLevel
type LogLevel* = int
const (
LogLevelDbg* = LogLevel(0)
LogLevelInf* = LogLevel(1)
LogLevelWrn* = LogLevel(2)
LogLevelSus* = LogLevel(3)
LogLevelErr* = LogLevel(4)
)
//~~
type Config = struct {
level: LogLevel
tagLevel: map[str]LogLevel
backend: Backend
}
//~~struct ConsoleBackend
// Backend which logs to the console. This is the default backend.
type ConsoleBackend* = struct { }
//~~
fn (this: ^ConsoleBackend) write*(s: str) {
printf("%s", s)
}
//~~struct FileBackend
// Backend which logs to a file. Created using `mkFileBackend`.
type FileBackend* = struct {
// Contains private fields
//~~
f: std::File
}
fn (this: ^FileBackend) write*(s: str) {
fprintf(this.f, "%s", s)
}
//~~fn FileBackend.close
// Closes the internal file.
fn (this: ^FileBackend) close*() {
//~~
std::fclose(this.f)
}
//~~fn mkFileBackend
// Creates a `FileBackend`. If `rewrite` is false, the file will be appended.
fn mkFileBackend*(path: str, rewrite: bool = false): (FileBackend, std::Err) {
//~~
f, err := std::fopen(path, rewrite ? "w" : "a")
return { f }, err
}
var (
conf: Config = { }
)
//~~fn init
// Initialize `logs.um`. You need to call this before using any other functions.
fn init*() {
//~~
conf = {
level: LogLevelInf,
tagLevel: {},
backend: ConsoleBackend{}
}
}
//~~fn setBackend
// Set the logging backend.
fn setBackend*(b: Backend) {
//~~
conf.backend = b
}
//~~fn setGlobalLogLevel
// Set the global log level.
fn setGlobalLogLevel*(level: LogLevel) {
//~~
conf.level = level
}
//~~fn setTagLogLevel
// Set the tag specific log level.
fn setTagLogLevel*(tag: str, level: LogLevel) {
//~~
conf.tagLevel[tag] = level
}
fn getLogLevel(tag: str): LogLevel {
if validkey(conf.tagLevel, tag) {
return conf.tagLevel[tag]
}
return conf.level
}
fn logRaw(levelStr: str, tag: str, fmtStr: str, args: []any) {
dt := std::localtime(std::time())
conf.backend.write(sprintf("[%s]: %04d-%02d-%02dT%02d:%02d:%02d: %s: %s\n",
levelStr, dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, tag,
fmt::vsfmt(fmtStr, args))
)
}
//~~fn dbg
fn dbg*(tag: str, fmt: str, args: ..any) {
//~~
if getLogLevel(tag) > LogLevelDbg {
return
}
logRaw("DBG", tag, fmt, args)
}
//~~fn inf
fn inf*(tag: str, fmt: str, args: ..any) {
//~~
if getLogLevel(tag) > LogLevelInf {
return
}
logRaw("INF", tag, fmt, args)
}
//~~fn wrn
fn wrn*(tag: str, fmt: str, args: ..any) {
//~~
if getLogLevel(tag) > LogLevelWrn {
return
}
logRaw("WRN", tag, fmt, args)
}
//~~fn sus
fn sus*(tag: str, fmt: str, args: ..any) {
//~~
if getLogLevel(tag) > LogLevelSus {
return
}
logRaw("SUS", tag, fmt, args)
}
//~~fn err
fn err*(tag: str, fmt: str, args: ..any) {
//~~
if getLogLevel(tag) > LogLevelErr {
return
}
logRaw("ERR", tag, fmt, args)
}
fn main() {
init()
setTagLogLevel("debug.um", LogLevelDbg)
const TAG = "logs.um"
dbg("debug.um", "Debug log")
dbg(TAG, "Debug log")
inf(TAG, "Information log")
inf(TAG, "Testing the formatting \\{} {}", 3.141)
wrn(TAG, "Warning log")
sus(TAG, "Suspicious log")
err(TAG, "Error log")
fb, e := mkFileBackend("log.txt", true)
std::exitif(e)
setBackend(fb)
dbg("debug.um", "Debug log")
dbg(TAG, "Debug log")
inf(TAG, "Information log")
wrn(TAG, "Warning log")
sus(TAG, "Suspicious log")
err(TAG, "Error log")
}