{"id":44642616,"url":"https://github.com/actforgood/xlog","last_synced_at":"2026-02-14T19:04:37.117Z","repository":{"id":144422981,"uuid":"488376934","full_name":"actforgood/xlog","owner":"actforgood","description":"Golang Logging Package","archived":false,"fork":false,"pushed_at":"2024-05-17T13:00:07.000Z","size":114,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-05-19T00:33:09.779Z","etag":null,"topics":["go","logger","structured-logging","xlog"],"latest_commit_sha":null,"homepage":"","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/actforgood.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":"AUTHORS","dei":null}},"created_at":"2022-05-03T22:09:33.000Z","updated_at":"2024-05-19T00:33:09.779Z","dependencies_parsed_at":"2023-11-08T08:38:57.922Z","dependency_job_id":null,"html_url":"https://github.com/actforgood/xlog","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/actforgood/xlog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxlog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxlog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxlog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxlog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/actforgood","download_url":"https://codeload.github.com/actforgood/xlog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/actforgood%2Fxlog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29452623,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-14T15:52:44.973Z","status":"ssl_error","status_checked_at":"2026-02-14T15:52:11.208Z","response_time":53,"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":["go","logger","structured-logging","xlog"],"created_at":"2026-02-14T19:04:36.425Z","updated_at":"2026-02-14T19:04:37.109Z","avatar_url":"https://github.com/actforgood.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Xlog\n\n[![Build Status](https://github.com/actforgood/xlog/actions/workflows/build.yml/badge.svg)](https://github.com/actforgood/xlog/actions/workflows/build.yml)\n[![License](https://img.shields.io/badge/license-MIT-blue)](https://raw.githubusercontent.com/actforgood/xlog/main/LICENSE)\n[![Coverage Status](https://coveralls.io/repos/github/actforgood/xlog/badge.svg?branch=main)](https://coveralls.io/github/actforgood/xlog?branch=main)\n[![Goreportcard](https://goreportcard.com/badge/github.com/actforgood/xlog)](https://goreportcard.com/report/github.com/actforgood/xlog)\n[![Go Reference](https://pkg.go.dev/badge/github.com/actforgood/xlog.svg)](https://pkg.go.dev/github.com/actforgood/xlog)  \n\n---  \n\nPackage `xlog` provides a structured leveled Logger implemented in two different strategies: synchronous and asynchronous.  \nThe logs can be formatted in JSON, logfmt, custom text format.  \n\n---\n\n\u003e [!NOTE]\n\u003e `xlog (v1)` will no longer be maintained as `log/slog` standard lib was introduced in Go 1.21.\n\n### Installation\n\n```shell\n$ go get github.com/actforgood/xlog\n```\n\n\n### Supported level APIs:\n* Critical\n* Error\n* Warning\n* Info\n* Debug\n* Log // arbitrary log\n\n\n### Common options\nA logger will need a `CommonOpts` through which you can configure some default keys and values used by the logger.\n```go\nxOpts := xlog.NewCommonOpts() // instantiates new CommonOpts object with default values.\n``` \n###### Configuring `level` options for a log.  \nExample of applicability:  \n\n* you may want to log from Error above messages on production env, and all levels on dev env.  \n* you may want to log messages by different level in different sources - see also `ExampleMultiLogger_splitMessagesByLevel` from doc reference.  \n\n```go\nxOpts.MinLevel = xlog.FixedLevelProvider(xlog.LevelDebug) // by default is LevelWarning\nxOpts.MaxLevel = xlog.FixedLevelProvider(xlog.LevelInfo) // by default is LevelCritical\nxOpts.LevelKey = \"level\" // by default is \"lvl\"\nxOpts.LevelLabels = map[xlog.Level]string{ // by default \"CRITICAL\", \"ERROR\", \"WARN\", \"INFO\", \"DEBUG\" are used\n    xlog.LevelCritical: \"CRT\",\n    xlog.LevelError: \"ERR\",\n    xlog.LevelWarning: \"WRN\",\n    xlog.LevelInfo: \"INF\",\n    xlog.LevelDebug: \"DBG\", \n}\n```  \nCheck also the `xlog.EnvLevelProvider` - to get the level from OS's env.  \nYou can make your own `xlog.LevelProvider` - to get the level from a remote API/other source, for example.  \n\n###### Configuring `time` options for a log.\n```go\nxOpts.Time    = xlog.UTCTimeProvider(time.RFC3339) // by default is time.RFC3339Nano\nxOpts.TimeKey = \"t\" // by default is \"date\"\n```\nCheck also the `xlog.LocalTimeProvider` - to get time in local server timezone.  \nYou can make your own `xlog.Provider` if needed for more custom logic.  \n\n###### Configuring `source` options for a log.\n```go\nxOpts.Source = xlog.SourceProvider(4, 2) // by default logs full path with a stack level of 4.\nxOpts.SourceKey = \"source\" // by default is \"src\"\n```\nBy setting `SourceKey` to blank, you can disable source logging.\nBy changing the first parameter in `SourceProvider`, you can manipulate the level in the stack trace.\nBy changing the second parameter in `SourceProvider`, you can manipulate how many levels in the path to be logged.\nExample:\n```go\nxlog.SourceProvider(4, 0) // =\u003e \"src\":\"/Users/JohnDoe/work/go/xlog/example.go:65\" (full path)\nxlog.SourceProvider(4, 1) // =\u003e \"src\":\"/example.go:65\"\nxlog.SourceProvider(4, 2) // =\u003e \"src\":\"/xlog/example.go:65\"\nxlog.SourceProvider(4, 3) // =\u003e \"src\":\"/go/xlog/example.go:65\"\n...\n```\n\n###### Configuring additional key-values to be logged with every log.\n```go\nxOpts.AdditionalKeyValues = []any{\n\t\"app\", \"demoXlog\",\n\t\"env\", \"prod\",\n\t\"release\", \"v1.10.0\",\n}\n```\n\n###### Configuring an I/O / formatting error handler for errors that may occur during logging.\nBy design, logger contract does not return error from its methods.\nA no operation `ErrorHandler` is set by default. You can change it to something else\nif suitable. For example, log with standard go logger the error.\n```go\nxOpts.ErrHandler = func(err error, keyValues []any) {\n\t// import \"log\"\n\tlog.Printf(\"An error occurred during logging. err = %v, logParams = %v\", err, keyValues)\n}\n```\n\n\n### Loggers\n\n##### SyncLogger\n`SyncLogger` is a `Logger` which writes logs synchronously.  \nIt just calls underlying writer with each log call.  \nNote: if used in a concurrent context, log writes are not concurrent safe, unless the writer is concurrent safe. See also `NewSyncWriter` on this matter.  \nExample of usage:\n```go\nxLogger := xlog.NewSyncLogger(os.Stdout)\ndefer xLogger.Close()\nxLogger.Error(\n\txlog.MsgKey, \"Could not read file\",\n\txlog.ErrorKey, io.ErrUnexpectedEOF,\n\t\"file\", \"/some/file\",\n)\n```\nYou can change the formatter (json is default), and common options like:\n```go\nxOpts := xlog.NewCommonOpts()\nxOpts.MinLevel = xlog.FixedLevelProvider(xlog.LevelInfo)\nxLogger := xlog.NewSyncLogger(\n\tos.Stdout,\n\txlog.SyncLoggerWithOptions(xOpts),\n\txlog.SyncLoggerWithFormatter(xlog.LogfmtFormatter),\n)\ndefer xLogger.Close()\n```\n\n##### AsyncLogger\n`AsyncLogger` is a `Logger` which writes logs asynchronously.  \nNote: if used in a concurrent context, log writes are concurrent safe if only one worker is configured to process the logs. Otherwise, log writes are not concurrent safe, unless the writer is concurrent safe. See also `NewSyncWriter` and `AsyncLoggerWithWorkersNo` on this matter.  \nExample of usage:\n```go\nxLogger := xlog.NewAsyncLogger(os.Stdout)\ndefer xLogger.Close()\nxLogger.Error(\n\txlog.MsgKey, \"Could not read file\",\n\txlog.ErrorKey, io.ErrUnexpectedEOF,\n\t\"file\", \"/some/file\",\n)\n```\nYou can change some options on it like:\n```go\nxOpts := xlog.NewCommonOpts()\nxOpts.MinLevel = xlog.FixedLevelProvider(xlog.LevelInfo)\nxLogger := xlog.NewAsyncLogger(\n\tos.Stdout,\n\txlog.AsyncLoggerWithOptions(xOpts),\n\txlog.AsyncLoggerWithFormatter(xlog.LogfmtFormatter),     // defaults to json\n\txlog.AsyncLoggerWithWorkersNo(uint16(runtime.NumCPU())), // defaults to 1\n\txlog.AsyncLoggerWithChannelSize(512),                    // defaults to 256\n)\ndefer xLogger.Close()\n```\n\n###### Benchmark example between sync / async loggers\n```\ngo test -run=^# -benchmem -benchtime=5s -bench \".*(sequential|parallel)\"\ngoos: darwin\ngoarch: amd64\npkg: github.com/actforgood/xlog\ncpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz\nBenchmarkAsyncLogger_json_withDiscardWriter_with256ChanSize_with1Worker_sequential-8     1855483              3241 ns/op            1704 B/op         32 allocs/op\nBenchmarkAsyncLogger_json_withDiscardWriter_with256ChanSize_with1Worker_parallel-8       1713628              3565 ns/op            1704 B/op         32 allocs/op\nBenchmarkSyncLogger_json_withDiscardWriter_sequential-8                                   984081              5269 ns/op            1696 B/op         32 allocs/op\nBenchmarkSyncLogger_json_withDiscardWriter_parallel-8                                    3394920              1797 ns/op            1696 B/op         32 allocs/op\n```\nNote how in a high concurrency context (*_parallel*) the sync logger actually behaves more well than async one.\n\n##### MultiLogger\n`MultiLogger` is a composite `Logger` capable of logging to multiple loggers.  \nExample of usage:\n```go\nxLogger := xlog.NewMultiLogger(loggerA, loggerB)\ndefer xLogger.Close()\nxLogger.Error(\n\txlog.MsgKey, \"Could not read file\",\n\txlog.ErrorKey, io.ErrUnexpectedEOF,\n\t\"file\", \"/some/file\",\n)\n```\n\n##### NopLogger\n`NopLogger` is a no-operation `Logger` which does nothing. It simply ignores any log.  \nYou can use it when benchmarking another component that uses logger, for example, in order for the logging process not to interfere with the main component's bench stats.\n\n##### MockLogger\n`MockLogger` is a mock for `Logger` contract, to be used in Unit Tests.\n\n\n### Formats\n\n##### JSONFormatter\nLogs get written in JSON format. Is the default format configured for sync / async loggers.  \nExample of log:\n```javascript\n{\"appName\":\"demo\",\"date\":\"2022-03-16T16:01:20Z\",\"env\":\"dev\",\"lvl\":\"DEBUG\",\"msg\":\"Hello World\",\"src\":\"/logger_async_test.go:43\",\"year\":2022}\n```\n\n##### LogfmtFormatter\nLogs get written in [logfmt](https://brandur.org/logfmt) format.  \nExample of configuring:  \n```go\nxLogger := xlog.NewSyncLogger(\n\tos.Stdout,\n\txlog.SyncLoggerWithOptions(xOpts),\n\txlog.SyncLoggerWithFormatter(xlog.LogfmtFormatter),\n)\n```\n\nExample of log:  \n```\ndate=2022-04-12T16:01:20Z lvl=INFO src=/formatter_logfmt_test.go:42 appName=demo env=dev msg=\"Hello World\" year=2022\n```\n\n##### TextFormatter\nLogs get written in custom, human friendly format: *TIME SOURCE LEVEL MESSAGE KEY1=VALUE1 KEY2=VALUE2 ...*  \nNote: this is not a structured logging format. It can be used for a \"dev\" logger, for example.  \nExample of configuring (see also `ExampleSyncLogger_devLogger` from doc reference):  \n```go\nxLogger := xlog.NewSyncLogger(\n\tos.Stdout,\n\txlog.SyncLoggerWithOptions(xOpts),\n\txlog.SyncLoggerWithFormatter(xlog.TextFormatter(xOpts)),\n)\n```\n\nExample of log:  \n```\n2022-03-14T16:01:20Z /formatter_text_test.go:40 DEBUG Hello World year=2022\n```\n\n##### SyslogFormatter\nLogs get written to system syslog.\nExample of configuring (see also `ExampleSyncLogger_withSyslog` from doc reference):\n```go\nxLogger := xlog.NewSyncLogger(\n\tsyslogWriter,\n\txlog.SyncLoggerWithFormatter(xlog.SyslogFormatter(\n\t\txlog.JSONFormatter,\n\t\txlog.NewDefaultSyslogLevelProvider(xOpts),\n\t\t\"\",\n\t)),\n\txlog.SyncLoggerWithOptions(xOpts),\n)\n```\n\n##### SentryFormatter\nLogs get written to [Sentry](https://docs.sentry.io/).\nExample of configuring (see also `ExampleSyncLogger_withSentry` from doc reference):\n```go\nxLogger := xlog.NewSyncLogger(\n\tio.Discard, // no need for other writer, SentryFormatter will override it with a buffered one in order to get original Formatter output.\n\txlog.SyncLoggerWithOptions(xOpts),\n\txlog.SyncLoggerWithFormatter(xlog.SentryFormatter(\n\t\txlog.JSONFormatter,\n\t\tsentry.CurrentHub().Clone(), // make a clone if you're not using sentry only in the logger.\n\t\txOpts,\n\t)),\n)\n```\n\n\n### Writers\n\n##### SyncWriter\n`SyncWriter` decorates an `io.Writer` so that each call to Write is synchronized with a mutex, making is safe for concurrent use by multiple goroutines.  \nIt should be used if writer's `Write` method is not thread safe.  \nFor example an `os.File` is safe, so it doesn't need this wrapper, on the other hand, a `bytes.Buffer` is not.  \n\n##### BufferedWriter\n`BufferedWriter` decorates an `io.Writer` so that written bytes are buffered.  \nIt is concurrent safe to use.  \nIt has the capability of auto-flushing the buffer, time interval based. This capability can also be disabled.\nIf an error occurs in the write process, at next log write, this error is not persisted, opposite using directly a `bufio.Writer` (see [this](https://github.com/golang/go/blob/go1.17.3/src/bufio/bufio.go#L633)).  \nExample of benchmarks between directly writes to a file, and writing to a \"buffered\" file:\n```\ngo test -run=^# -benchmem -benchtime=5s -bench \".*FileWriter\"\ngoos: darwin\ngoarch: amd64\npkg: github.com/actforgood/xlog\ncpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz\nBenchmarkAsyncLogger_json_withFileWriter_with256ChanSize_with1Worker-8                            \n666721               9007 ns/op            1704 B/op           32 allocs/op\nBenchmarkAsyncLogger_json_withBufferedFileWriter_with256ChanSize_with1Worker-8                   \n1597966              3696 ns/op            1704 B/op           32 allocs/op\n\nBenchmarkSyncLogger_json_withFileWriter-8                                                         \n507146             10856 ns/op            1696 B/op           32 allocs/op\nBenchmarkSyncLogger_json_withBufferedFileWriter-8                                                 \n920844              5928 ns/op            1696 B/op           32 allocs/op\n```\n\n\n### Misc \nFeel free to use this logger if you like it and fits your needs.  \nCheck also other popular, performant loggers like Uber Zap, Zerolog, Gokit...  \nHere stands some benchmarks made locally based on [this](https://github.com/imkira/go-loggers-bench) repo.  \n```\ngo test -run=^# -benchmem -benchtime=5s -bench \".*JSON\"\ngoos: darwin\ngoarch: amd64\npkg: github.com/imkira/go-loggers-bench\ncpu: Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz\nBenchmarkGokitJSONPositive-8          3904477       1466 ns/op      1544 B/op      24 allocs/op\nBenchmarkLog15JSONPositive-8           973974       5464 ns/op      2009 B/op      30 allocs/op\nBenchmarkLogrusJSONPositive-8         2950423       1986 ns/op      2212 B/op      34 allocs/op\nBenchmarkXlogSyncJSONPositive-8       3880016       1520 ns/op      1662 B/op      28 allocs/op\nBenchmarkZerologJSONPositive-8       28168381      202.8 ns/op         0 B/op       0 allocs/op\n\nBenchmarkGokitJSONNegative-8        180957508      32.65 ns/op       128 B/op       1 allocs/op\nBenchmarkLog15JSONNegative-8         12070347      466.3 ns/op       632 B/op       5 allocs/op\nBenchmarkLogrusJSONNegative-8        24485853      211.4 ns/op       496 B/op       4 allocs/op\nBenchmarkXlogSyncJSONNegative-8    1000000000      3.415 ns/op         0 B/op       0 allocs/op\nBenchmarkZerologJSONNegative-8     1000000000      3.288 ns/op         0 B/op       0 allocs/op\n```\n\n\n### License\nThis package is released under a MIT license. See [LICENSE](LICENSE).  \nOther 3rd party packages directly used by this package are released under their own licenses.  \n\n* github.com/getsentry/sentry-go - [BSD 2 Clause](https://github.com/getsentry/sentry-go/blob/master/LICENSE)  \n* github.com/go-logfmt/logfmt - [MIT License](https://github.com/go-logfmt/logfmt/blob/main/LICENSE)  \n* github.com/actforgood/xerr - [MIT License](https://github.com/actforgood/xerr/blob/main/LICENSE)  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Factforgood%2Fxlog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Factforgood%2Fxlog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Factforgood%2Fxlog/lists"}