Usage:
package main
import (
"github.com/quan-to/slog"
"time"
)
var log = slog.Scope("MAIN")
func Call0(i slog.Instance, arg0 string) {
l := i.SubScope("Call0").WithFields(map[string]interface{}{
"arg0": arg0,
})
l.Await("Doing some work")
time.Sleep(time.Second)
l.Done("Finished some work")
l.Note("Not sure what I'm doing...")
l.Info("Calling Call1")
Call1(l, "call1arg")
l.Done("Exiting")
}
func Call1(i slog.Instance, huebr string) {
l := i.SubScope("Call1").WithFields(map[string]interface{}{
"huebr": huebr,
})
l.Info("Calling Call2")
Call2(l, "abcde")
l.Warn("Call 1 finished")
}
func Call2(i slog.Instance, pop string) {
l := i.SubScope("Call2").WithFields(map[string]interface{}{
"pop": pop,
})
l.IO("Doing some IO")
l.Error("I'm useless. Please fix-me")
}
func main() {
slog.SetScopeLength(40) // Expand Scope pad length
log = log.Tag("REQ001") // Tag current as REQ001
log.Info("Starting program")
Call0(log, "MyArg0")
Call1(log, "Call1Arg")
Call2(log, "Call2Arg")
}
Output:
There are 2 types of outputs: Pipe Delimited Text (default) and JSON.
To change the type of output, call the function SetLogFormat:
slog.SetLogFormat(JSON)
The slog output is expected to be like this:
2019-09-16T15:35:52-03:00 | I | IO | REQ001 | MAIN > Call0 > Call1 > Call2 | test.go:38 | Doing some IO | {"arg0":"MyArg0","huebr":"call1arg","pop":"abcde"}
There are some fields that are optional (like filename/line number) but it should always follow the same pattern:
DATETIME | LEVEL | OPERATION | TAG | SCOPE | [FILENAME:LINE NUMBER] | MESSAGE | LOG FIELDS
DATETIME
=> An ISO Datetime when the log is displayedLEVEL
=> The level of the log lineI
=> INFO - Shows an information usually to track what's happening inside an applicationW
=> WARN - Shows an warning regarding something that went in a way that might require some attentionE
=> ERROR - Shows an application error that can be expected or notD
=> DEBUG - Shows some debug information to help tracking issuesF
=> FATAL - Shows an error that will quit the application in that point
TAG
=> Line log tag. Use this for tracking related log lines. For example with a HTTP Request IDSCOPE
=> The scope of the current log. Use this to trace the chain of calls inside the application. For example in a context changeFILENAME: LINE NUMBER
=> OPTIONAL When ShowLines is enabled, it will show the filename and the line number of the caller of the slog library. Use this on debug mode to see which piece of code called the log library. Disabled by defaultMESSAGE
=> The messageLOG FIELDS
=> When an instance is created usingWithFields
call, the fields will be serialized to either JSON or Key-Value depending on the configuration of the log instance. Defaults to JSON
The output is expected to be in this format:
{
"level":"info",
"op":"MSG",
"msg":"Processing rule 123",
"scope":"PriceCalc",
"tag":"RULE_ENGINE",
"time":"2020-02-07T15:36:20-03:00",
"customField01":"123"
}
The fields are the same as from Pipe Delimited Text and work the same way, except that the Key-Value option is not available for the LOG FIELDS
.
The library implements the concept of operation type. This describes which type of operation the log line represents.
IO
=> Anytime you do a I/O Operation such as Database or File Read/WriteAWAIT
=> Anytime you will do a asynchronous operation that will block the flowDONE
=> After anyAWAIT
operation you should always use aDONE
to log that the operation that did theAWAIT
log line has finished.NOTE
=> Also know as Verbose, that operation is used when you're just letting the log reader some note about the operationMSG
=> Common messages such like normal information (which does not fit in other operations)
There are some syntax sugars to make easy to use Log Levels with Log Operations together:
- Warning Messages
WarnDone
=> Same aslog.Operation(DONE).Warn(message)
WarnNote
=> Same aslog.Operation(NOTE).Warn(message)
WarnAwait
=> Same aslog.Operation(AWAIT).Warn(message)
WarnSuccess
=> Same aslog.Operation(DONE).Warn(message)
WarnIO
=> Same aslog.Operation(IO).Warn(message)
- Error Messages
ErrorDone
=> Same aslog.Operation(DONE).Error(message)
ErrorNote
=> Same aslog.Operation(NOTE).Error(message)
ErrorAwait
=> Same aslog.Operation(AWAIT).Error(message)
ErrorSuccess
=> Same aslog.Operation(DONE).Error(message)
ErrorIO
=> Same aslog.Operation(IO).Error(message)
- Debug Messages
DebugDone
=> Same aslog.Operation(DONE).Debug(message)
DebugNote
=> Same aslog.Operation(NOTE).Debug(message)
DebugAwait
=> Same aslog.Operation(AWAIT).Debug(message)
DebugSuccess
=> Same aslog.Operation(DONE).Debug(message)
DebugIO
=> Same aslog.Operation(IO).Debug(message)
Use these syntax sugars whenever is possible, instead of calling Operation(X).Level
directly.
If a multiline log is displayed, the library will correctly ident all the messages:
2019-09-16T15:39:42-03:00 | I | MSG | REQ001 | MAIN | Multiline call
Thats the second line
Thats the third line | {}
- After calling a
Await
, you should always call aDone
orSuccess
- Don't add extensive fields to
WithFields
as it will pollute the log - Avoid multiline logs as this make parsing hard.
- Avoid using pipes
|
in your log message or fields - Instead of
Operation(AWAIT).Warn
useWarnAwait
- Use
Tag
to indentify calls in the same flow (for example on a HTTP Request) - All
LogInstance
calls returns a newLogInstance
with the modified data. It will never change it's parent data making it completely immutable.