{"id":20560448,"url":"https://github.com/vodolaz095/vdlog","last_synced_at":"2026-04-20T15:02:06.378Z","repository":{"id":143254094,"uuid":"76123235","full_name":"vodolaz095/vdlog","owner":"vodolaz095","description":"Simple modular logger for Go","archived":false,"fork":false,"pushed_at":"2017-06-05T22:01:25.000Z","size":64,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-09-12T15:19:00.292Z","etag":null,"topics":["async","asynchronous","go","golang","json","lifecycle-events","logger","modular","sinks","telegram"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vodolaz095.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-12-10T16:11:25.000Z","updated_at":"2017-06-08T23:04:19.000Z","dependencies_parsed_at":null,"dependency_job_id":"c684c8dd-71e4-454a-897d-ba927fae92ac","html_url":"https://github.com/vodolaz095/vdlog","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vodolaz095/vdlog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vodolaz095%2Fvdlog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vodolaz095%2Fvdlog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vodolaz095%2Fvdlog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vodolaz095%2Fvdlog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vodolaz095","download_url":"https://codeload.github.com/vodolaz095/vdlog/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vodolaz095%2Fvdlog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32052534,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T11:35:06.609Z","status":"ssl_error","status_checked_at":"2026-04-20T11:34:48.899Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","asynchronous","go","golang","json","lifecycle-events","logger","modular","sinks","telegram"],"created_at":"2024-11-16T03:54:34.879Z","updated_at":"2026-04-20T15:02:06.338Z","avatar_url":"https://github.com/vodolaz095.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"vdlog\n======================\n[![Build Status](https://travis-ci.org/vodolaz095/vdlog.png?branch=master)](https://travis-ci.org/vodolaz095/vdlog)\n[![GoDoc](https://godoc.org/gopkg.in/vodolaz095/vdlog.v2?status.svg)](https://godoc.org/gopkg.in/vodolaz095/vdlog.v2)\n[![Go Report Card](https://goreportcard.com/badge/github.com/vodolaz095/vdlog)](https://goreportcard.com/report/github.com/vodolaz095/vdlog)\n\nStructured logger for Go.\n\nInfo\n======================\nPackage assumes that there are this event levels:\n\n- Error is a level for errors that can wake you on 4 hours past midnight\n\n- 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.\n\n- 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.\n\n- 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.\n\n- 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.\n\n- Silly is putting every fart to log.\n\nAfter events emitted, they are all send to buffered channel called `spine`.\nThan, each event is processed via separate goroutines using Sink functions applied to this event.\nYou can define sink function easily, see the source of `console.go` and `file.go` files.\nIf Sink function returns error, it is processed by `BrokenSinkReporter` function.\n\n\nLog storage support\n======================\n\nFrom the box - STDERR, local file, [Journald](https://wiki.archlinux.org/index.php/Systemd), \n[Loggly](https://loggly.com),  [Telegram](https://core.telegram.org/) channel or group via bot.\n\nWith little effort - anything.\nYou just need to implement simple function that will process events, like this `AddSink` one\n\n```go\n\n\tpackage main\n\n\timport (\n\t  \t\"fmt\"\n\t\t\"gopkg.in/vodolaz095/vdlog.v3\"\n\t)\n\n\tfunc main(){\n\t\t// it is custom sink for events of type `feedback`\n\t\tvdlog.AddSink(\"feedback\", func(e vdlog.Event) error {\n\t\t\tfmt.Println(\"Feedback\", e.String())\n\t\t\treturn nil\n\t\t})\n\n\t\tvdlog.EmitError(\"test\", fmt.Errorf(\"test %s\", \"error\"), vdlog.H{\"error\": \"error\"})\n\t\tvdlog.EmitWarn(\"test\", vdlog.H{\"warn\": \"warn\"})\n\t\t\n\t\t//this even will be processed by our defined sink `feedback`\n\t\tvdlog.EmitInfo(\"feedback\", vdlog.H{\"info\": \"info\"})\n\t\t\n\t\tvdlog.EmitVerbose(\"test\", vdlog.H{\"verbose\": \"verbose\"})\n\t\tvdlog.EmitDebug(\"test\", vdlog.H{\"debug\": \"debug\"})\n\t\tvdlog.EmitSilly(\"test\", vdlog.H{\"silly\": \"silly\"})\n\n\t\t//wait until all events are processed\n\t\tvdlog.FlushLogs()\n\t}\n\n\n```\n\n\nHow to send messages  to Telegram channel or group\n======================\n\n1. create telegram bot - see [https://core.telegram.org/bots](https://core.telegram.org/bots).\n2. invite this bot as admin of channel or as member of group you want to recieve log entries too.\n3. set up log sink using your bot token (something like this - `286759464:AAFRalklssMW9hsZ592O8CxZo63QU7KM7d0`)\n4. Get channel/group id using [console telegram client](https://github.com/vysheng/tg) and `channel_info` or `chat_info` commands\n5. Find proper id for channel. There is a [crazy solution](https://stackoverflow.com/a/39943226/1885921). For public/private channel you need to prepend `-100` to its id for notifications to be delivered.\n6. Enable sink for delivering events\n\n```go\n\n\tvdlog.LogToTelegram(\"286759464:AAFRalklssMW9hsZ592O8CxZo63QU7KM7d0\", \"-1001055587116\", vdlog.LevelInfo)\n\n```\n\n\n\n\nHow does logged events looks like (in JSON format)?\n======================\n\n```json\n\n      {\n        //unique UUID of event, the same reported to all sinks\n        \"uuid\": \"990d28fd-4461-480a-be7e-08abacc9bdeb\",\n        \"timestamp\": \"2017-02-26T19:45:13.398512249+03:00\",\n  \n        //type and level\n        \"level\": 2,\n        \"levelString\": \"INFO\",\n        \"type\": \"vdlogUnitTest\",\n  \n        //event payload, metadata\n        \"metadata\": {\n          \"someText\":\"text\",\n          \"someNumber\":10,\n          \"someArray\":[0,\"1\",\"b\"]\n        },\n  \n        //Server info\n        \"hostname\":\"server.local\",\n        \"pid\": 11337,\n  \n        //Where does the logger function was actually called\n        \"filename\": \"/var/www/localhost/index.php\",\n        \"line\": 2,\n        \"called\": \"/var/www/localhost/index.php:2\"\n      }\n\n```\n\nInstalling\n======================\n\n```shell\n\n   go get gopkg.in/vodolaz095/vdlog.v3\n\n```\n\nBasic example\n======================\n\n```go\n\n\tpackage main\n\n\timport (\n\t\t\"fmt\"\n\t\t\"gopkg.in/vodolaz095/vdlog.v3\"\n\t)\n\n\tfunc main(){\n\t\tvdlog.SetConsoleVerbosity(vdlog.LevelSilly)\n\t\tvdlog.SetConsoleJSON() //for pretty printing json\n\n\t\tvdlog.EmitError(\"test\", fmt.Errorf(\"test %s\", \"error\"), vdlog.H{\"error\": \"error\"})\n\t\tvdlog.EmitWarn(\"test\", vdlog.H{\"warn\": \"warn\"})\n\t\tvdlog.EmitInfo(\"feedback\", vdlog.H{\"info\": \"info\"})\n\t\tvdlog.EmitVerbose(\"test\", vdlog.H{\"verbose\": \"verbose\"})\n\t\tvdlog.EmitDebug(\"test\", vdlog.H{\"debug\": \"debug\"})\n\t\tvdlog.EmitSilly(\"test\", vdlog.H{\"silly\": \"silly\"})\n\n\n\t\t//wait until all events are processed\n\t\tvdlog.FlushLogs()\n\t}\n\n\n```\n\n\nFull example of usage\n======================\n\n```go\n\n\tpackage main\n\t\n\timport (\n\t  \"fmt\"\n\t  \"log\"\n  \t  \"gopkg.in/vodolaz095/vdlog.v3\"\n\t)\n\n\tfunc main() {\n\t\t/*\n\t\t *  Set Console Verbosity level\n\t\t */\n\t\tvdlog.SetConsoleVerbosity(vdlog.LevelSilly) //highest verbosity\n\t\tvdlog.SetConsoleVerbosity(vdlog.LevelDebug)\n\t\tvdlog.SetConsoleVerbosity(vdlog.LevelInfo)\n\t\tvdlog.SetConsoleVerbosity(vdlog.LevelWarn)\n\t\tvdlog.SetConsoleVerbosity(vdlog.LevelError) //lowest verbosity\n\t\n\t\t/*\n\t\t * Enable output to local file\n\t\t */\n\n\t\t//LogErrorsToFile outputs errors only\n\t\tvdlog.LogErrorsToFile(\"/var/log/my_vdlog_errors.log\")\n\n\t\t//LogNormalToFile outputs events from error to debug levels\n\t\tvdlog.LogNormalToFile(\"/var/log/my_vdlog.log\")\n\n\t\t//We can log defined level ranges to file\n\t\tvdlog.LogToFile(\"/var/log/onlyInfoAndWarn.log\", vdlog.LevelWarn, vdlog.LevelInfo)\n\n\n\t\t/*\n\t\t * Logging to Journalctl on local server (works only in linux!)\n\t\t */\n\t\tvdlog.LogToLocalJournald()\n\n\t\t/*\n\t\t * Logging to Journalctl on remote server (works only in linux!)\n\t\t */\n\t\tvdlog.LogToRemoteJournaldViaTCP(\"logger.example.org\", 514)\n\t\tvdlog.LogToRemoteJournaldViaUDP(\"logger.example.org\", 514)\n\n\t\t/*\n\t\t * Logging to Loggly.com\n\t\t */\n\t\tvdlog.LogToLoggly(\"{YOUR LOGGLY TOKEN PLS}\", true) //true = https, false = http\n\n\t\n\t\t/*\n\t\t * Add custom sink for storing events\n\t\t * Currently, this sink outputs to STDOUT only events from `feedback`\n\t\t * facility with level lower and including the `LevelInfo`\n\t\t * If Payload equals to `bad`, error is returned\n\t\t */\n\t\tvdlog.AddSink(\"feedback\", func(e vdlog.Event) error {\n\t\t\t// we ignore events not related for feedback facility\n\t\t\tif e.Facility != \"feedback\" {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\t//we ignore events of low priority\n\t\t\tif e.Level \u003e vdlog.LevelInfo {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\t//check if payload is the proper one\n\t\t\tif e.Payload == \"bad\" {\n\t\t\t\treturn fmt.Errorf(\"bad event\")\n\t\t\t}\n\t\n\t\t\t//start pretty printing\n\t\t\tfmt.Println(\"===================\")\n\t\t\tfmt.Printf(\"%v seconds ago event with level %s occured!\\n\",\n\t\t\t\te.Ago().Seconds(),\n\t\t\t\te.GetLevelString())\n\t\n\t\t\t//Output event as string\n\t\t\tfmt.Println(e.String())\n\t\t\t//will output something like\n\t\t\t// Dec 08 23:49:32 TestLoggerLog VERBOSE : verbose verbose\n\t\n\t\t\t//Output event as string with caller information - i.e.\n\t\t\t//where in source code does the message was called\n\t\t\tfmt.Println(e.StringWithCaller())\n\t\t\t//will output something like\n\t\t\t// Dec 08 23:49:32 TestLoggerLog VERBOSE \u003cFile: /home/vodolaz095/projects/go/src/bitbucket.org/vodolaz095/vdlog/vdlog_test.go:61\u003e: verbose verbose\n\t\n\t\t\t//Output JSON representation of message (slice of bytes converted to string)\n\t\t\tfmt.Println(\"JSON of event:\", string(e.ToJSON()))\n\t\n\t\t\tfmt.Println(\"===================\")\n\t\n\t\t\t//Sink processed event properly\n\t\t\treturn nil\n\t\t})\n\t\n\t\t/*\n\t\t * Add function to report sink misbehaviour - i.e. when it returns error\n\t\t */\n\t\tvdlog.BrokenSinkReporter = func(brokenSinkName string, eventThatCloggedIt vdlog.Event, errorRecievedFromSink error) {\n\t\t\tfmt.Printf(\"Sink %s is broken by event %s with error %s\", brokenSinkName, eventThatCloggedIt.String(), errorRecievedFromSink.Error())\n\t\t\tpanic(\"broken sink\")\n\t\t}\n\t\n\t\t/*\n\t\t * Using global logger\n\t\t */\n\t\tvdlog.EmitError(\"test\", fmt.Errorf(\"test %s\", \"error\"), vdlog.H{\"error\": \"error\"})\n\t\tvdlog.EmitWarn(\"test\", vdlog.H{\"warn\": \"warn\"})\n\t\tvdlog.EmitInfo(\"test\", vdlog.H{\"info\": \"info\"})\n\t\tvdlog.EmitVerbose(\"test\", vdlog.H{\"verbose\": \"verbose\"})\n\t\tvdlog.EmitDebug(\"test\",vdlog. H{\"debug\": \"debug\"})\n\t\tvdlog.EmitSilly(\"test\", vdlog.H{\"silly\": \"silly\"})\n\n\t\n\t\t/*\n\t\t * Using custom logger for `feedback` facility\n\t\t */\n\t\tfeedbackLogger := New(\"feedback\")\n\t\tfeedbackLogger.EmitError(fmt.Errorf(\"test %s\", \"error\"), H{\"error\": \"error\"})\n\t\tfeedbackLogger.EmitWarn(H{\"warn\": \"warn\"})\n\t\tfeedbackLogger.EmitInfo(H{\"info\": \"info\"})\n\t\tfeedbackLogger.EmitVerbose(H{\"verbose\": \"verbose\"})\n\t\tfeedbackLogger.EmitDebug(H{\"debug\": \"debug\"})\n\t\tfeedbackLogger.EmitSilly(H{\"silly\": \"silly\"})\n\n\t\t/*\n\t\t * Using popular https://godoc.org/log package\n\t\t */\n\t\tlog.SetOutput(vdlog.CreateIoWriter(vdlog.LevelError, \"test\"))\n\t\tlog.Printf(\"testing %s\", \"ioWriterLog\")\n\n\t\t//wait until all events are processed\n\t\tvdlog.FlushLogs()\n\t}\n\n```\n\n\nThe MIT License (MIT)\n==============================\n\nCopyright (c) 2016 Ostroumov Anatolij \u003costroumov095 at gmail dot com\u003e\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvodolaz095%2Fvdlog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvodolaz095%2Fvdlog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvodolaz095%2Fvdlog/lists"}