{"id":13413312,"url":"https://github.com/logur/logur","last_synced_at":"2026-01-14T11:37:08.138Z","repository":{"id":57488274,"uuid":"161060081","full_name":"logur/logur","owner":"logur","description":"Logur is an opinionated collection of logging best practices","archived":false,"fork":false,"pushed_at":"2020-10-04T16:49:57.000Z","size":642,"stargazers_count":201,"open_issues_count":8,"forks_count":13,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-09-26T09:58:41.221Z","etag":null,"topics":["go-logging","logging","logur"],"latest_commit_sha":null,"homepage":"https://logur.dev/logur","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/logur.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-09T16:43:11.000Z","updated_at":"2024-08-13T00:32:45.000Z","dependencies_parsed_at":"2022-08-29T15:01:29.803Z","dependency_job_id":null,"html_url":"https://github.com/logur/logur","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/logur/logur","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logur%2Flogur","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logur%2Flogur/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logur%2Flogur/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logur%2Flogur/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/logur","download_url":"https://codeload.github.com/logur/logur/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logur%2Flogur/sbom","scorecard":{"id":597482,"data":{"date":"2025-08-11","repo":{"name":"github.com/logur/logur","commit":"271e0722d3feaca0065df83f772035872e14da9c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.7,"checks":[{"name":"Maintained","score":0,"reason":"project is archived","details":["Warn: Repository is archived."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 1/19 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":"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":"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":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Warn: no topLevel permission defined: .github/workflows/examples.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":"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/logur/logur/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/logur/logur/ci.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/logur/logur/ci.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:38: update your workflow using https://app.stepsecurity.io/secureworkflow/logur/logur/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:52: update your workflow using https://app.stepsecurity.io/secureworkflow/logur/logur/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:57: update your workflow using https://app.stepsecurity.io/secureworkflow/logur/logur/ci.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:60: update your workflow using https://app.stepsecurity.io/secureworkflow/logur/logur/ci.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/examples.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/logur/logur/examples.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/examples.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/logur/logur/examples.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/examples.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/logur/logur/examples.yml/master?enable=pin","Info:   0 out of   6 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   4 third-party 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":"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: 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":"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":"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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"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":"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":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 16 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":9,"reason":"1 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2023-2153 / GHSA-m425-mq94-257g / GHSA-qppj-fm5r-hxr3"],"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-20T23:30:41.574Z","repository_id":57488274,"created_at":"2025-08-20T23:30:41.574Z","updated_at":"2025-08-20T23:30:41.574Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28419213,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T10:47:48.104Z","status":"ssl_error","status_checked_at":"2026-01-14T10:46:19.031Z","response_time":107,"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-logging","logging","logur"],"created_at":"2024-07-30T20:01:37.451Z","updated_at":"2026-01-14T11:37:08.116Z","avatar_url":"https://github.com/logur.png","language":"Go","funding_links":[],"categories":["Logging","日志记录","Logging 日志库","Go","Relational Databases"],"sub_categories":["Search and Analytic Databases","检索及分析资料库","SQL 查询语句构建库","Advanced Console UIs"],"readme":"\u003e [!WARNING]\n\u003e This project is archived and no longer maintained. Consider using [`log/slog`](https://pkg.go.dev/log/slog) instead.\n\u003e\n\u003e Read more about why it was archived in [this post](https://sagikazarmark.com/blog/posts/less-is-more-archive-projects-for-a-better-open-source-ecosystem/).\n\n![Logur](.github/logo.png?raw=true)\n\n[![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#logging)\n\n[![GitHub Workflow Status](https://img.shields.io/github/workflow/status/logur/logur/CI?style=flat-square)](https://github.com/logur/logur/actions?query=workflow%3ACI)\n[![Codecov](https://img.shields.io/codecov/c/github/logur/logur?style=flat-square)](https://codecov.io/gh/logur/logur)\n[![Go Report Card](https://goreportcard.com/badge/logur.dev/logur?style=flat-square)](https://goreportcard.com/report/logur.dev/logur)\n![Go Version](https://img.shields.io/badge/go%20version-%3E=1.12-61CFDD.svg?style=flat-square)\n[![PkgGoDev](https://pkg.go.dev/badge/mod/logur.dev/logur)](https://pkg.go.dev/mod/logur.dev/logur)\n\n\n**Logur is an opinionated collection of logging best practices.**\n\n## Table of Contents\n\n- [Preface](#preface)\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n- [FAQ](#faq)\n    - [Why not just X logger?](#why-not-just-x-logger)\n    - [Why not go-kit logger?](#why-not-go-kit-logger)\n    - [Why not `logger.With(keyvals ...interface{})`?](#why-not-loggerwithkeyvals-interface)\n    - [Why no `*f` (format) functions?](#why-no-f-format-functions)\n    - [Why no `*ln` functions?](#why-no-ln-functions)\n- [Inspiration](#inspiration)\n\n\n## Preface\n\nLogur is an opinionated logging library targeted at producing (application) logs.\nIt does not try to solve every problem around logging,\nonly a few considered important by the developers, thus it's highly opinionated.\n\nThe main focus of the library:\n\n- provide a unified interface that does not require developers to import external dependencies\n- encourage leveled and structured logging\n- provide tools for easy integration of other logging libraries and components\n\nLogur does not care about log output, you can use whatever library/formatting/forwarder you want\n(ie. use an existing logging library with one of the [adapters](https://github.com/search?q=topic%3Aadapter+org%3Alogur)).\n\nDespite the opinionated nature, Logur encourages you to create custom logging interfaces for your needs\nand only use Logur as an integration layer/tool. Use the features you need/want and just omit the rest.\n\nAs mentioned above, Logur aims to cover only [95% of the use cases](https://dev.to/nickjj/optimize-your-programming-decisions-for-the-95-not-the-5-2n42),\nso Logur might not be for you. Read on for more details.\n\n\n## Features\n\n- Unified logger interface\n- Test logger for testing log event recording\n- Noop logger for discarding log events\n- `io.Writer` support\n- Standard library logger support\n- [Integrations](https://github.com/search?q=topic%3Aintegration+org%3Alogur) with well-known libraries:\n    - [gRPC log](https://godoc.org/google.golang.org/grpc/grpclog)\n    - [MySQL driver](https://github.com/go-sql-driver/mysql)\n    - [Watermill](https://watermill.io/)\n    - [InvisionApp logger](https://github.com/InVisionApp/go-logger) interface\n    - [Bugsnag](https://bugsnag.com) [SDK](https://godoc.org/github.com/bugsnag/bugsnag-go) (logger, for error handling see [github.com/emperror/emperror](https://github.com/emperror/emperror))\n    - [Rollbar](https://rollbar.com) [SDK](https://godoc.org/github.com/rollbar/rollbar-go) (logger, for error handling see [github.com/emperror/emperror](https://github.com/emperror/emperror))\n    - [logr](https://github.com/go-logr/logr) logger interface\n    - [go-kit](https://github.com/go-kit/kit) logger\n    - [zap](https://github.com/uber-go/zap) logger\n- [Adapters](https://github.com/search?q=topic%3Aadapter+org%3Alogur) for well-known logging libraries:\n    - [hclog](https://github.com/hashicorp/go-hclog)\n    - [go-kit log](https://github.com/go-kit/kit)\n    - [logrus](https://github.com/sirupsen/logrus)\n    - [zap](https://github.com/uber-go/zap)\n    - [zerolog](https://github.com/rs/zerolog)\n    - Next one contributed by You!\n\n\n## Installation\n\nLogur uses [Go Modules](https://github.com/golang/go/wiki/Modules) introduced in Go 1.11, so the recommended way is\nusing `go get`:\n\n```bash\n$ go get logur.dev/logur\n```\n\n\n## Usage\n\nAn opinionated library should come with some best practices for usage and so does this one.\n\n**TL;DR:** See example usage and best practices in [github.com/sagikazarmark/modern-go-application](https://github.com/sagikazarmark/modern-go-application).\nAlso, check out the [examples](examples) package in this repository.\n\n### Create a custom interface\n\nInterfaces should be defined by the consumer, so the `Logger` interface in this library should not be used directly.\nA custom interface should be defined instead:\n\n```go\ntype MyLogger interface {\n\tTrace(msg string, fields ...map[string]interface{})\n\tDebug(msg string, fields ...map[string]interface{})\n\tInfo(msg string, fields ...map[string]interface{})\n\tWarn(msg string, fields ...map[string]interface{})\n\tError(msg string, fields ...map[string]interface{})\n}\n```\n\nIn a lucky scenario all Logur loggers are compatible with the above interface, so you can just use them in your code:\n\n```go\nfunc main() {\n    logger := logur.NewNoopLogger()\n\n    myFunc(logger)\n}\n\nfunc myFunc(logger MyLogger) {\n\tlogger.Debug(\"myFunc ran\")\n\t// OR\n\tlogger.Debug(\"myFunc ran\", map[string]interface{}{\"key\": \"value\"})\n}\n```\n\nIn case you need to populate the logger with some common context, the interface becomes a bit more complicated:\n\n```go\ntype MyLogger interface {\n\tTrace(msg string, fields ...map[string]interface{})\n\tDebug(msg string, fields ...map[string]interface{})\n\t// ...\n\tWithFields(fields map[string]interface{}) MyLogger\n}\n```\n\nAs you can see `MyLogger` holds a reference to itself, which makes it incompatible with the Logur implementations.\nThe solution in this case is implementing a custom adapter:\n\n```go\ntype myLogger struct {\n\tlogger logur.Logger\n}\n\nfunc (l *myLogger) Debug(msg string, fields ...map[string]interface{}) { l.logger.Debug(msg, fields...) }\n// ...\nfunc (l *myLogger) WithFields(fields map[string]interface{}) MyLogger {\n\treturn myLogger{logur.WithFields(l.logger, fields)}\n}\n```\n\nNow you can easily use Logur provided loggers inside your code:\n\n```go\nfunc main() {\n    logger := \u0026myLogger{logur.NewNoopLogger()}\n\n    myFunc(logger)\n}\n\nfunc myFunc(logger MyLogger) {\n\tlogger.WithFields(map[string]interface{}{\"key\": \"value\"}).Debug(\"myFunc ran\", nil)\n}\n```\n\n### Wrap helper functions with custom ones\n\nIn many cases it is unavoidable to maintain a simple integration layer between third-party libraries and your\napplication. Logur is no exception. In the previous section you saw how the main interface works with adapters,\nbut that's not all Logur provides. It comes with a set of other tools (eg. a standard library logger compatible `io.Writer`)\nto make logging easier. It might be tempting to just use them in your application, but writing an integration\nlayer is recommended, even around functions.\n\nThe following example creates a simple standard library logger for using as an HTTP server error log:\n\n```go\nfunc newStandardErrorLogger() *log.Logger {\n\treturn logur.NewStandardLogger(logur.NewNoopLogger(), logur.ErrorLevel, \"\", 0)\n}\n\nfunc main() {\n\tserver := \u0026http.Server{\n\t\tHandler: nil,\n\t\tErrorLog: newStandardErrorLogger(),\n\t}\n}\n```\n\n\n## FAQ\n\n### Why not just X logger?\n\nTo be honest: mostly because I don't care. Logging libraries proliferated in the Go ecosystem in the past few years.\nEach tries to convince you it's the most performant or the easiest to use.\nBut the fact is your application doesn't care which you use.\nIn fact, it's happier if it doesn't know anything about it at all.\nLogging libraries (just like every third-party library) are external dependencies.\nIf you wire them into your application, it will be tied to the chosen libraries forever.\nThat's why using a custom interface is a highly recommended practice.\n\nLet's consider the following logger interface:\n\n```go\ntype Logger interface {\n\tTrace(msg string, fields ...map[string]interface{})\n\tDebug(msg string, fields ...map[string]interface{})\n\tInfo(msg string, fields ...map[string]interface{})\n\tWarn(msg string, fields ...map[string]interface{})\n\tError(msg string, fields ...map[string]interface{})\n}\n```\n\nYou can easily create an interface like this and implement an adapter for the logging library of your choice\nwithout wiring it into your application which makes the actual library a less important detail.\n\n\n### Why not Go kit logger?\n\nGo-kit deserves it's own entry because for quite some time I was really fond of the its logger interface\nand it was the closest thing to become an [official Go logging solution](https://docs.google.com/document/d/1shW9DZJXOeGbG9Mr9Us9MiaPqmlcVatD_D8lrOXRNMU).\nI still think it is great, because the interface is very simple, yet it's incredibly powerful.\nBut this simplicity is why I ultimately stopped using it as my primary logger\n(or I should say: stopped knowing that I actually use it).\n\nJust a short recap of the interface itself:\n\n```go\ntype Logger interface {\n\tLog(keyvals ...interface{}) error\n}\n```\n\nIt's really simple and easy to use in any application. Following Go's guidelines of using interfaces,\none can easily copy this interface and just use it to decouple the code from Go kit itself.\n\nThe problem with this interface appears when you try to do \"advanced stuff\"\n(like structured logging or adding a level to a log event):\n\n```go\nimport (\n\t\"github.com/go-kit/kit/log\"\n\t\"github.com/go-kit/kit/log/level\"\n)\n\n// ...\n\nlogger := log.With(logger, \"key\", \"value\")\nlevel.Info(logger).Log(\"msg\", \"message\")\n```\n\nAs you can see doing any kind of structured or leveled logging requires to import Go kit packages after all,\nwhich takes us back to [Why not just X logger?](#why-not-just-x-logger).\n\nIn short: Using Go kit directly - no matter how awesome its interface is - suffers from the same problem as\nusing any other logging library.\n\nOne could implement all those functions for a custom interface based on Go kit,\nbut it probably isn't worth the hassle. Defining a more verbose, custom interface is a lot easier to work with.\nThat being said, Go kit logger can very well serve as a perfect base for an implementation of that interface.\n\nThe [proposal](https://docs.google.com/document/d/1shW9DZJXOeGbG9Mr9Us9MiaPqmlcVatD_D8lrOXRNMU) linked above contains many examples\nwhy the authors ended up with an interface like this. Go check it out, you might just have the same use cases\nwhich could make the Go kit interface a better fit than the one in this library.\n\n\n### Why not `logger.With(keyvals ...interface{})`?\n\nThere is an increasing tendency of logging libraries implementing the following interface:\n\n```go\ntype Logger interface {\n\t// ...\n\tWith(keyvals ...interface{})\n}\n```\n\nThe arguments behind this interface are being simple and convenient, not as verbose as a map of fields.\nThere are also usual arguments *against* the alternative solutions, (eg. a `map[string]interface{}` endorsed by this library)\nlike not being able to order fields, being forced to import a concrete type, like `logur.Fields`\nor (quite often) performance questions.\n\nUltimately, this is not completely independent from personal taste, so one will always prefer one or the other.\n(You can read more in the above linked [Go kit proposal](https://docs.google.com/document/d/1shW9DZJXOeGbG9Mr9Us9MiaPqmlcVatD_D8lrOXRNMU)).\n\nLet's take a look at these arguments one by one:\n\n**1. Simplicity and verbosity**\n\nThis is something that's hard to argue with, but also a little bit subjective.\nHere is a comparison of a single line context logging:\n\n```go\nlogger = log.With(logger, \"key\", \"value\", \"key2\", \"value\")\nlogger = logur.WithFields(logger, map[string]interface{}{\"key\": \"value\", \"key2\": \"value\"})\n```\n\nObviously the second one is more verbose, takes a bit more efforts to write, but it is rather a question of habits.\n\nLet's take a look at a multiline example as well:\n\n```go\nlogger = log.With(logger,\n\t\"key\", \"value\",\n\t\"key2\", \"value\",\n)\n\nlogger = logur.WithFields(logger, map[string]interface{}{\n\t\"key\": \"value\",\n\t\"key2\": \"value\",\n})\n```\n\nThe difference is less visible in this case and harder to argue that one is better than the other.\n\nAlso, defining a custom type is relatively easy which makes the difference even smaller:\n\n```go\nlogger = log.With(logger,\n\t\"key\", \"value\",\n\t\"key2\", \"value\",\n)\n\ntype LogFields map[string]interface{}\n\nlogger = logur.WithFields(logger, LogFields{\n\t\"key\": \"value\",\n\t\"key2\": \"value\",\n})\n```\n\n\n**2. Ordering fields**\n\nThis is one of the less known arguments against maps in the context of logging, you can read about it in the\n[Go kit proposal](https://docs.google.com/document/d/1shW9DZJXOeGbG9Mr9Us9MiaPqmlcVatD_D8lrOXRNMU).\n\nSince maps are unordered in Go, fields added to a log line might not always look like the same on the output.\nVariadic (slice) arguments do not suffer from this issue. However, most implementations convert slices internally to maps,\nso if ordering matters, most logging libraries won't work anyway.\n\nAlso, this library is not a one size fits all proposal and doesn't try to solve [100% of the problems](https://dev.to/nickjj/optimize-your-programming-decisions-for-the-95-not-the-5-2n42)\n(unlike the official logger proposal), but rather aim for the most common use cases which doesn't include ordering of fields.\n\n\n**3. Performance**\n\nComparing the performance of different solutions is not easy and depends heavily on the interface.\n\nFor example, Uber's [Zap](https://github.com/uber-go/zap) comes with a rich interface\n(thus requires you to couple your code to Zap), but also promises zero-allocation in certain scenarios,\nmaking it an extremely fast logging library. If being *very fast* is a requirement for you,\neven at the expense tight coupling, then Zap is a great choice.\n\nMore generic interfaces often use ... well `interface{}` for structured context,\nbut [interface allocations](https://commaok.xyz/post/interface-allocs/) are much cheaper now.\n\nThe performance debate these days is often between two approaches:\n\n- variadic slices (`...interface{}`)\n- maps (`map[string]interface{}`)\n\nSpecifically, how the provided context can be *merged* with an existing, internal context of the logger.\nAdmittedly, `append`ing to a slice is much cheaper than merging a map, so if performance is crucial,\nusing an interface with variadic slices will always be **slightly** faster.\nBut that difference in performance is negligible in most of the cases, so you won't even notice it,\nunless you start logging with hundreds of structured context fields (which will have other problems anyway).\n\nThere is a problem with Logur adapters though: since the interface uses maps for structured context,\nlibraries like Zap that use variadic slices does not perform too well because of the map -\u003e slice conversion.\nIn most of the cases this should still be acceptable, but you should be aware of this fact when choosing an adapter.\n\nPartly because of this, I plan to add a `KVLogger` interface that uses variadic slices for structured context.\nObviously, map based libraries will suffer from the same performance penalty, but then the choice of the interface\nwill be up to the developer.\n\n\nComparing the slice and the map solution, there are also some arguments against using a variadic slice:\n\n**1. Odd number of arguments**\n\nThe variadic slice interface implementation has to deal with the case when an odd number of arguments are passed to\nthe function. While the [Go kit proposal](https://docs.google.com/document/d/1shW9DZJXOeGbG9Mr9Us9MiaPqmlcVatD_D8lrOXRNMU)\nargues that this is extremely hard mistake to make, the risk is still there that the logs will lack some information.\n\n**2. Converting the slice to key value pairs**\n\nIn order to display the context as key-value pairs the logging implementations has to convert the key parameters\nto string in most of the cases (while the value parameter can be handled by the marshaling protocol).\nThis adds an extra step to outputting the logs (an extra loop going through all the parameters).\nWhile there is no scientific evidence proving one to be slower than the other (yet), it seems to be an unnecessary\ncomplication at first.\n\n\n### Why no `*f` (format) functions?\n\nA previous version of this interface contained a set of functions that allowed messages to be formatted with arguments:\n\n```go\ntype Logger interface {\n\t// ...\n\tTracef(format string, args ...interface{})\n\tDebugf(format string, args ...interface{})\n\tInfof(format string, args ...interface{})\n\tWarnf(format string, args ...interface{})\n\tErrorf(format string, args ...interface{})\n}\n```\n\nThe reason why they were originally included in the interface is that most logging libraries implement these methods,\nbut experience showed that they are not used frequently. Also, nowadays structured logging is a better practice than\nformatting log messages with structured data, thus these methods were removed from the core interface.\n\n\n### Why no `*ln` functions?\n\nAnother common group of logging functions originally included in the interface is `*ln` function group:\n\n```go\ntype Logger interface {\n\t// ...\n\tTraceln(args ...interface{})\n\tDebugln(args ...interface{})\n\tInfoln(args ...interface{})\n\tWarnln(args ...interface{})\n\tErrorln(args ...interface{})\n}\n```\n\nUsually separate log events are represented on separate lines anyway, so the added value is not newlines in this case,\nbut the different semantics between `fmt.Print` and `fmt.Println`.\nSee [this](https://play.golang.org/p/32GnCpXttbH) example illustrating the difference.\n\nCommon logging libraries include these functions, but experience showed they are not used frequently,\nso they got removed.\n\n\n## Inspiration\n\nThis package is heavily inspired by a set of logging libraries:\n\n- [github.com/InVisionApp/go-logger](https://github.com/InVisionApp/go-logger)\n- [github.com/sirupsen/logrus](https://github.com/sirupsen/logrus)\n- [github.com/go-kit/kit](https://github.com/go-kit/kit)\n\n\n## Development\n\nContributions are welcome! :)\n\n1. Clone the repository\n1. Make changes on a new branch\n1. Run the test suite:\n    ```bash\n    ./pleasew build\n    ./pleasew test\n    ./pleasew gotest\n    ./pleasew lint\n    ```\n1. Commit, push and open a PR\n\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogur%2Flogur","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flogur%2Flogur","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogur%2Flogur/lists"}