vdlog
Structured logger for Go.
Info
Package assumes that there are this event levels:
-
Error is a level for errors that can wake you on 4 hours past midnight
-
Warn is a level for an unexpected technical or business event happened, customers may be affected, but probably no immediate human intervention is required. On call people won't be called immediately, but support personnel will want to review these issues asap to understand what the impact is. Basically any issue that needs to be tracked but may not require immediate intervention. For example, CPU load is higher than usual, but bot critical.
-
Info are for things we want to see at high volume in case we need to forensically analyze an issue. System lifecycle events (system start, stop) go here. "Session" lifecycle events (login, logout, etc.) go here. Significant boundary events should be considered as well (e.g. database calls, remote API calls). Typical business exceptions can go here (e.g. login failed due to bad credentials). Any other event you think you'll need to see in production at high volume goes here.
-
Verbose just about everything that doesn't make the "info" cut... any message that is helpful in tracking the flow through the system and isolating issues, especially during the development and QA phases. We use "debug" level logs for entry/exit of most non-trivial methods and marking interesting events and decision points inside methods.
-
Debug is for extremely detailed and potentially high volume logs that you don't typically want enabled even during normal development. Examples include dumping a full object hierarchy, logging some state during every iteration of a large loop, etc.
-
Silly is putting every fart to log.
After events emitted, they are all send to buffered channel called spine
.
Than, each event is processed via separate goroutines using Sink functions applied to this event.
You can define sink function easily, see the source of console.go
and file.go
files.
If Sink function returns error, it is processed by BrokenSinkReporter
function.
Log storage support
From the box - STDERR, local file, Journald,
Loggly, Telegram channel or group via bot.
With little effort - anything.
You just need to implement simple function that will process events, like this AddSink
one
package main
import (
"fmt"
"gopkg.in/vodolaz095/vdlog.v3"
)
func main(){
vdlog.AddSink("feedback", func(e vdlog.Event) error {
fmt.Println("Feedback", e.String())
return nil
})
vdlog.EmitError("test", fmt.Errorf("test %s", "error"), vdlog.H{"error": "error"})
vdlog.EmitWarn("test", vdlog.H{"warn": "warn"})
vdlog.EmitInfo("feedback", vdlog.H{"info": "info"})
vdlog.EmitVerbose("test", vdlog.H{"verbose": "verbose"})
vdlog.EmitDebug("test", vdlog.H{"debug": "debug"})
vdlog.EmitSilly("test", vdlog.H{"silly": "silly"})
vdlog.FlushLogs()
}
How to send messages to Telegram channel or group
- create telegram bot - see https://core.telegram.org/bots.
- invite this bot as admin of channel or as member of group you want to recieve log entries too.
- set up log sink using your bot token (something like this -
286759464:AAFRalklssMW9hsZ592O8CxZo63QU7KM7d0
) - Get channel/group id using console telegram client and
channel_info
or chat_info
commands - Find proper id for channel. There is a crazy solution. For public/private channel you need to prepend
-100
to its id for notifications to be delivered. - Enable sink for delivering events
vdlog.LogToTelegram("286759464:AAFRalklssMW9hsZ592O8CxZo63QU7KM7d0", "-1001055587116", vdlog.LevelInfo)
How does logged events looks like (in JSON format)?
{
"uuid": "990d28fd-4461-480a-be7e-08abacc9bdeb",
"timestamp": "2017-02-26T19:45:13.398512249+03:00",
"level": 2,
"levelString": "INFO",
"type": "vdlogUnitTest",
"metadata": {
"someText":"text",
"someNumber":10,
"someArray":[0,"1","b"]
},
"hostname":"server.local",
"pid": 11337,
"filename": "/var/www/localhost/index.php",
"line": 2,
"called": "/var/www/localhost/index.php:2"
}
Installing
go get gopkg.in/vodolaz095/vdlog.v3
Basic example
package main
import (
"fmt"
"gopkg.in/vodolaz095/vdlog.v3"
)
func main(){
vdlog.SetConsoleVerbosity(vdlog.LevelSilly)
vdlog.SetConsoleJSON()
vdlog.EmitError("test", fmt.Errorf("test %s", "error"), vdlog.H{"error": "error"})
vdlog.EmitWarn("test", vdlog.H{"warn": "warn"})
vdlog.EmitInfo("feedback", vdlog.H{"info": "info"})
vdlog.EmitVerbose("test", vdlog.H{"verbose": "verbose"})
vdlog.EmitDebug("test", vdlog.H{"debug": "debug"})
vdlog.EmitSilly("test", vdlog.H{"silly": "silly"})
vdlog.FlushLogs()
}
Full example of usage
package main
import (
"fmt"
"log"
"gopkg.in/vodolaz095/vdlog.v3"
)
func main() {
vdlog.SetConsoleVerbosity(vdlog.LevelSilly)
vdlog.SetConsoleVerbosity(vdlog.LevelDebug)
vdlog.SetConsoleVerbosity(vdlog.LevelInfo)
vdlog.SetConsoleVerbosity(vdlog.LevelWarn)
vdlog.SetConsoleVerbosity(vdlog.LevelError)
vdlog.LogErrorsToFile("/var/log/my_vdlog_errors.log")
vdlog.LogNormalToFile("/var/log/my_vdlog.log")
vdlog.LogToFile("/var/log/onlyInfoAndWarn.log", vdlog.LevelWarn, vdlog.LevelInfo)
vdlog.LogToLocalJournald()
vdlog.LogToRemoteJournaldViaTCP("logger.example.org", 514)
vdlog.LogToRemoteJournaldViaUDP("logger.example.org", 514)
vdlog.LogToLoggly("{YOUR LOGGLY TOKEN PLS}", true)
vdlog.AddSink("feedback", func(e vdlog.Event) error {
if e.Facility != "feedback" {
return nil
}
if e.Level > vdlog.LevelInfo {
return nil
}
if e.Payload == "bad" {
return fmt.Errorf("bad event")
}
fmt.Println("===================")
fmt.Printf("%v seconds ago event with level %s occured!\n",
e.Ago().Seconds(),
e.GetLevelString())
fmt.Println(e.String())
fmt.Println(e.StringWithCaller())
fmt.Println("JSON of event:", string(e.ToJSON()))
fmt.Println("===================")
return nil
})
vdlog.BrokenSinkReporter = func(brokenSinkName string, eventThatCloggedIt vdlog.Event, errorRecievedFromSink error) {
fmt.Printf("Sink %s is broken by event %s with error %s", brokenSinkName, eventThatCloggedIt.String(), errorRecievedFromSink.Error())
panic("broken sink")
}
vdlog.EmitError("test", fmt.Errorf("test %s", "error"), vdlog.H{"error": "error"})
vdlog.EmitWarn("test", vdlog.H{"warn": "warn"})
vdlog.EmitInfo("test", vdlog.H{"info": "info"})
vdlog.EmitVerbose("test", vdlog.H{"verbose": "verbose"})
vdlog.EmitDebug("test",vdlog. H{"debug": "debug"})
vdlog.EmitSilly("test", vdlog.H{"silly": "silly"})
feedbackLogger := New("feedback")
feedbackLogger.EmitError(fmt.Errorf("test %s", "error"), H{"error": "error"})
feedbackLogger.EmitWarn(H{"warn": "warn"})
feedbackLogger.EmitInfo(H{"info": "info"})
feedbackLogger.EmitVerbose(H{"verbose": "verbose"})
feedbackLogger.EmitDebug(H{"debug": "debug"})
feedbackLogger.EmitSilly(H{"silly": "silly"})
log.SetOutput(vdlog.CreateIoWriter(vdlog.LevelError, "test"))
log.Printf("testing %s", "ioWriterLog")
vdlog.FlushLogs()
}
The MIT License (MIT)
Copyright (c) 2016 Ostroumov Anatolij
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.