{"id":21275693,"url":"https://github.com/halimath/kvlog","last_synced_at":"2025-10-20T08:20:35.848Z","repository":{"id":47600212,"uuid":"330420483","full_name":"halimath/kvlog","owner":"halimath","description":"A structured logging facility for go based on key-value-pairs","archived":false,"fork":false,"pushed_at":"2023-05-12T17:15:00.000Z","size":84,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-15T13:13:38.777Z","etag":null,"topics":["go","golang","log","logger","logging"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/halimath.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":"2021-01-17T15:19:05.000Z","updated_at":"2022-09-02T07:24:55.000Z","dependencies_parsed_at":"2024-11-21T09:46:19.147Z","dependency_job_id":null,"html_url":"https://github.com/halimath/kvlog","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/halimath/kvlog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/halimath%2Fkvlog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/halimath%2Fkvlog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/halimath%2Fkvlog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/halimath%2Fkvlog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/halimath","download_url":"https://codeload.github.com/halimath/kvlog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/halimath%2Fkvlog/sbom","scorecard":{"id":453497,"data":{"date":"2025-08-11","repo":{"name":"github.com/halimath/kvlog","commit":"5e17b1e6887d70e605b30db876c75f70c2918dd0"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/halimath/kvlog/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/halimath/kvlog/ci.yml/main?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 6 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-19T08:51:39.865Z","repository_id":47600212,"created_at":"2025-08-19T08:51:39.865Z","updated_at":"2025-08-19T08:51:39.865Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":280052761,"owners_count":26264246,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-20T02:00:06.978Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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","golang","log","logger","logging"],"created_at":"2024-11-21T09:36:12.690Z","updated_at":"2025-10-20T08:20:35.832Z","avatar_url":"https://github.com/halimath.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kvlog\n\n![CI Status][ci-img-url] [![Go Report Card][go-report-card-img-url]][go-report-card-url]\n[![Package Doc][package-doc-img-url]][package-doc-url] [![Releases][release-img-url]][release-url]\n\n[ci-img-url]: https://github.com/halimath/kvlog/workflows/CI/badge.svg\n[go-report-card-img-url]: https://goreportcard.com/badge/github.com/halimath/kvlog\n[go-report-card-url]: https://goreportcard.com/report/github.com/halimath/kvlog\n[package-doc-img-url]: https://img.shields.io/badge/GoDoc-Reference-blue.svg\n[package-doc-url]: https://pkg.go.dev/github.com/halimath/kvlog\n[release-img-url]: https://img.shields.io/github/v/release/halimath/kvlog.svg\n[release-url]: https://github.com/halimath/kvlog/releases\n\n`kvlog` provides a structured logging facility. The underlying structure is based on key-value pairs.\nkey-value pairs are rendered as [JSON lines] but other Formatters can be used to provide different outputs\nincluding custom ones.\n\n[JSON lines]: https://jsonlines.org/\n\n# Why another logging lib?\n\n`kvlog` tries to find a balance between highly performance optimized libs such as `zap` or `zerolog` and those\nthat define a \"dead simple\" API (such as `go-kit` or `logrus`). It does not perform as well as the first two\nbut significantly better than the last two. `kvlog` tries to provide an easy-to-use API for producing\nstructured log events while keeping up a good performance.\n\n# Usage\n\n## Installation\n\n`kvlog` uses go modules and requires Go 1.16 or greater.\n\n```\n$ go get -u github.com/halimath/kvlog\n```\n## Creating a Logger\n\nTo emit log events, you need a `Logger`. `kvlog` provides a ready-to-use `Logger` via the `L` variable.\nYou can create a custom logger giving you more flexibility on the logger's target and format. \nCreating a new `Logger` is done via the `kvlog.New` function. \nIt accepts any number of `Handler`s. Each `Handler` pairs an `io.Writer` as well as a `Formatter`.\n\n```go\nlogger := kvlog.New(kvlog.NewSyncHandler(os.Stdout, kvlog.JSONLFormatter())).\n\tAddHook(kvlog.TimeHook)\n```\n\n`Handler`s can be synchronous as well as asynchronous. \nSynchronous Handlers execute the Formatter as well as writing the output in the same goroutine that invoked\nthe `Logger`. \nAsynchronous `Handler`s dispatch the log event to a different goroutine via a channel. \nThus, asynchronous Handlers must be closed before shutdown in order to flush the channel and emit all log \nevents.\n\n## Emitting Events\n\nThe easiest way to emit a simple log message is to use a `Logger`'s `Log`, `Log` or `Logf` method.\n\n```go\nkvlog.L.Logs(\"hello, world\")\nkvlog.L.Logf(\"hello, %s\", \"world)\n```\n\n`Log` will log all given key-value pairs while `Logs` and `Logf` will format a message with additional \nkey-value pairs. With the default JSONL formatter, this produces:\n\n```json\n{\"msg\":\"hello\"}\n{\"msg\":\"hello, world\"}\n```\n\nIf you want to add more key-value pairs - which is the primary use case for a structured logger - you can pass\nadditional arguments to any of the log methods. key-value pairs are best created using one of the `With...`\nfunctions from `kvlog`.\n\n```go\nkvlog.L.Logs(\"hello, world\",\n\tkvlog.WithKV(\"tracing_id\", 1234),\n\tkvlog.WithDur(time.Second),\n\tkvlog.WithErr(fmt.Errorf(\"some error\")),\n)\n```\n\n## Deriving Loggers\n\nLogger's can be derived from another Logger. This enables to configure a set of key-value-pairs to be added\nto every event emmitted via the deriverd logger. The syntax works similar to emitting log messages this time\nonly invoking the `Sub` method instead of `Log`.\n\n```go\ndl := l.Sub(\n\tkvlog.WithKV(\"tracing_id\", \"1234\"),\n)\n```\n\n## Hooks\n\nIn addition to deriving loggers, any number of `Hook`s may be added to a logger. The hook's callback function\nis invoked everytime an `Event` is emitted via this logger or any of its derived loggers. Hooks are useful\nto add dynamic values, such as timestamps or anything else read from the surrounding context. Adding a\ntimestamp to every log event is realized via the `TimeHook`.\n\n```go\nl := kvlog.New(kvlog.NewSyncHandler(\u0026buf, kvlog.JSONLFormatter())).\n\tAddHook(kvlog.TimeHook)\n```\n\nYou can write your own hook by implement the `kvlog.Hook` interface or using the `kvlog.HookFunc` convenience\ntype for a simple function. \n\n```go\n// This is an example for some function that determines a dynamic value.\nextractTracingID := func() string {\n\t// some real implementation here\n\treturn \"1234\"\n}\n\n// Create a logger and add the hook\nlogger := kvlog.New(kvlog.NewSyncHandler(os.Stdout, kvlog.JSONLFormatter())).\n\tAddHook(kvlog.HookFunc(func(e *kvlog.Event) {\n\t\te.AddPair(kvlog.WithKV(\"tracing_id\", extractTracingID()))\n\t}))\n\n// Emit some event\nlogger.Logs(\"request\")\n```\n\nThis example produces:\n\n```json\n{\"tracing_id\":\"1234\",\"msg\":\"request\"}\n```\n\n## Passing a logger by `Context`\n\nThe go standard library provides package `context` to pass contextual values\nto operations being called downstream. `kvlog` provides functions that hook into\na context and add a `Logger` which can later be retrieved. As a sub logger can\nbe configured with context keys, you can pass that logger down via the context.\n\nUse `ContextWithLogger` to create a derived context holding a logger. Calling\n`FromContext` restores the logger. If no logger is contained in the context a\ndefault NoOp logger is returned, so code can run without panic.\n\n```go\nctx = ContextWithLogger(ctx, subLogger)\n\n// ...\nl = FromContext(ctx)\nl.Logs(\"my message\")\n```\n\n## Formatters\n\nThe kvlog package comes with three Formatters out of the box:\n- `JSONLFormatter` formats events as JSON line values\n- `ConsoleFormatter` formats events for output on a terminal which includes colorizing the event\n- `KVFormatter` formats events in the legacy KV-Format\n\nThe `JSONLFormatter` features a lot of optimizations to improve time and memory behavior. The other two have a\nless optimized performance. While the `ConsoleFormatter` is intended for dev use the `KVFormatter` is only\nprovided for compatibility reasons and should be considered deprecated. Use `JSONLFormatter` for production\nsystems.\n\nCustom formatters may be created by implementing the `kvlog.Formatter` interface or using the \n`kvlog.FormatterFunc` convenience type.\n\n## HTTP Middleware\n\n`kvlog` contains a HTTP middleware that generates an access log and supports adding a logger to the request's\n`Context`. The middleware is compatible with frameworks such as Chi that support `Use` but you can also\nuse the middleware with bare `net/http`. \n\n```go\npackage main\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/halimath/kvlog\"\n)\n\nfunc main() {\n    mux := http.NewServeMux()\n    // ...\n\tkvlog.L.Log(\"started\")\n\thttp.ListenAndServe(\":8000\", kvlog.Middleware(kvlog.L, true)(mux))\n}\n```\n\n## Default Keys\n\nThe following table lists the default keys used by `kvlog`. You can customize these by setting a module-level\nvariable. These are also given in the table below.\n\nKey | Used with | Variable to change | Description\n-- | -- | -- | --\n`time` | `TimeHook` | `KeyTime` | The default key used to identify an event's time stamp.\n`err` | `Event.Err` | `KeyError` | The default key used to identify an event's error.\n`msg` | `Event.Log` or `Event.Logf` | `KeyMessage` | The default key used to identify an event's message.\n`dur` | `Event.Dur` | `KeyDuration` | The default key used to identify an event's duration value.\n\n## Customizing memory behavior\n\n`kvlog` includes a set of performance optimizations. Most of them work by pre-allocating memory for data\nstructures to be reused in order to avoid allocations for each log event. These pre-allocations can be tuned\nin case an application has a specific load profile. This tuning can be performed by setting module-global\nvariables. \n\nThere are two primary areas, where pre-allocation is used.\n\n1. New `Event`s are not created every time a logger's `With` method is called. Instead, most of the time a\n   pre-existing `Event` is pulled from a `sync.Pool` and put back after the event has been formatted. Such a\n   pool exists for _each root_ logger - that is _every_ logger created with `kvlog.New`. The pool is \n   pre-filled with a number of events. All the events pre-filled into the pool also have a pre-allocated\n   number of key-value-pair slots which are re-used by overwriting them whenever a `KV` method is called.\n   Both numbers - initial pool size and pre-allocated number of pairs - can be changed.\n1. When using an asynchronous handler, the handler's formatter is invoked synchronously. The output is written\n   to a `bytes.Buffer`. This buffer comes from a `sync.Pool` and has a pre-allocated bytes slice. After the\n   event has been formatted, the buffer is sent over a bufferd channel. The channel is consumed by another\n   goroutine, which copies the buffer's bytes on the output writer. After that, the buffer is put back into\n   the pool. Pool size, buffer size and channel buffer size can be customized.\n\nChanges to these variables only take effect for loggers/handlers created after the variable have been \nassigned. Use at your own risk.\n\nVariable | Default Value | Description\n:-- | --: | --\n`DefaultEventSize` | 16 | The default size Events created from an Event pool.\n`InitialEventPoolSize` | 128 | Number of events to allocate for a new Event pool.\n`AsyncHandlerBufferSize` | 2048 | Defines the size of an async handler's buffer that is preallocated.\n`AsyncHandlerPoolSize` | 64 | Defines the number of preallocated buffers in a pool of buffers.\n`AsyncHandlerChannelSize` | 1024 | Number of log events to buffer in an async handler's channel.\n\n# Benchmarks\n\nThe [`benchmarks`](./benchmarks) directory contains some benchmarking tests which compare different logging\nlibs with a structured log event consisting of several key-value-pairs of different types. These are the\nresults run on a developers laptop.\n\nLibrary | ns/op | B/op | allocs/op\n-- | --: | --: | --:\nkvlog (sync handler) | 1527 | 152 | 8\nkvlog (async handler) | 1392 | 153 | 8\nzerolog | 352.9 | 0 | 0\nlogrus | 4430 | 2192 |34\ngo-kit/log  | 2201 | 970 | 18\n\n\n# Changelog\n\n## 0.11.0\n\n__:warning: breaking change:__ This version changes the API of the HTTP middleware function\n\n* New HTTP middleware function compatible with other frameworks (such as Chi)\n* Middleware can add logger to `Context`\n\n## 0.10.0\n\n* Context API\n* NoOp Logger\n\n## 0.9.0\n\n__:warning: breaking change:__ This version provides a new API which is _not compatible_ to the API exposed\nbefore. This involves the way loggers and other components are configured (which normally only affects a small\nportion of the using code) as well as the way log events are emitted. The interface emitting log messages\nremains similar.\n\n* New API\n* Performance improvements\n\n## 0.8.1 - retracted\n\n* Fix: add `sync.Mutex` to lock `Handler`\n\n## 0.8.0 - retracted\n\n* added `NoOpHandler` to easily silence logging output\n\n## 0.7.0 - retracted\n\n__:warning: breaking change:__ This version provides a new API which is _not compatible_ to the API exposed\nbefore. This involves the way loggers and other components are configured as well as how log events are\nemitted.\n\n* New chaining API to create messages\n* Performance optimization\n\n## 0.6.0\n\n__:warning: breaking change:__ This version provides a new API which is in parts\n_not compatible_ to the API exposed before. This basically involves the way loggers\nand other components are configured (which normally only affects a small portion of\nthe using code). The interface to creating and emitting log messages stays the same.\n\n* Nested loggers allow adding default key value pairs to add to all logger (i.e. for use with a _category_)\n* Reorganization into several packages; root package `kvlog` acts as a facade\n* Renamed some types (mostly interfaces) to better match the new package name (i.e. `handler.Interface` instead of `handler.Handler`)\n* Added `jsonl` formatter which creates [JSON lines](https://jsonlines.org/) output\n\n## 0.5.0\n* `KVFormatter` sorts pairs based on key\n* New `TerminalFormatter` providing colored output on terminals\n* Moved to github\n\n## 0.4.0\n* Export package level logger instance `L`\n\n## 0.3.0\n__:warning: breaking changes:__ This version provides a new API which is _not compatible_ to the \nAPI exposed before.\n* Introduction of new component structure (see description above)\n\n## 0.2.0\n* Improve log message rendering\n\n## 0.1.0\n* Initial release\n\n# License\n\n```\nCopyright 2019, 2020 Alexander Metzner.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhalimath%2Fkvlog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhalimath%2Fkvlog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhalimath%2Fkvlog/lists"}