{"id":34808247,"url":"https://github.com/elgopher/yala","last_synced_at":"2025-12-25T12:11:05.630Z","repository":{"id":43089192,"uuid":"448347507","full_name":"elgopher/yala","owner":"elgopher","description":"Tiny structured logging abstraction or facade for various logging libraries, allowing the end user to plug in the desired logging library in main.go.","archived":false,"fork":false,"pushed_at":"2025-12-20T12:26:33.000Z","size":253,"stargazers_count":14,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-22T15:59:56.944Z","etag":null,"topics":["abstraction","bridge","context","facade","fmt","glog","go","golang","log","log15","logfmt","logger","logging","logrus","structured-logging","zap","zerolog"],"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/elgopher.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null}},"created_at":"2022-01-15T17:37:16.000Z","updated_at":"2025-12-20T11:21:52.000Z","dependencies_parsed_at":"2023-07-15T14:25:32.845Z","dependency_job_id":null,"html_url":"https://github.com/elgopher/yala","commit_stats":null,"previous_names":["jacekolszak/yala"],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/elgopher/yala","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elgopher%2Fyala","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elgopher%2Fyala/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elgopher%2Fyala/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elgopher%2Fyala/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elgopher","download_url":"https://codeload.github.com/elgopher/yala/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elgopher%2Fyala/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27991564,"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-12-23T02:00:07.087Z","response_time":69,"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":["abstraction","bridge","context","facade","fmt","glog","go","golang","log","log15","logfmt","logger","logging","logrus","structured-logging","zap","zerolog"],"created_at":"2025-12-25T12:11:04.898Z","updated_at":"2025-12-25T12:11:05.620Z","avatar_url":"https://github.com/elgopher.png","language":"Go","readme":"# YALA - Yet Another Logging Abstraction for Go\n\n[![Build](https://github.com/elgopher/yala/actions/workflows/build.yml/badge.svg)](https://github.com/elgopher/yala/actions/workflows/build.yml)\n[![Go Reference](https://pkg.go.dev/badge/github.com/elgopher/yala.svg)](https://pkg.go.dev/github.com/elgopher/yala)\n[![Go Report Card](https://goreportcard.com/badge/github.com/elgopher/yala)](https://goreportcard.com/report/github.com/elgopher/yala)\n[![codecov](https://codecov.io/gh/elgopher/yala/branch/master/graph/badge.svg)](https://codecov.io/gh/elgopher/yala)\n[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)\n\u003cimg src=\"docs/logo.png\" align=\"right\" width=\"30%\"\u003e\n\nTiny **structured logging** abstraction or facade for various logging libraries, allowing the end user to plug in the desired logging library in `main.go`.\n\n## Supported logging libraries (via adapters)\n\n[logrus](adapter/logrusadapter), [zap](adapter/zapadapter), [zerolog](adapter/zerologadapter), [glog](adapter/glogadapter), [log15](adapter/log15adapter), [standard log](adapter/logadapter) and [console](adapter/console)\n\n## When to use?\n\n* If you are a package/module/library author\n* And you want to participate in the end user logging system (log messages using the logger provided by the end user)\n* You don't want to add dependency to any specific logging library to your code\n* You don't want to manually inject logger to every possible place where you want to log something (such as function, struct etc.)\n* If you need a nice and elegant API with a bunch of useful functions, but at the same time you don't want your end users to spend hours on writing their own logging adapter.\n\n## Installation\n\n```shell\n# Add yala to your Go module:\ngo get github.com/elgopher/yala        \n```\n\nPlease note that at least Go `1.19` is required.\n\n## How to use\n\n### Choose logger - global or normal?\n\nGlobal logger can be accessed from everywhere in your package and can be reconfigured anytime. Normal logger is an\nimmutable logger, initialized only once. \n\n### Use global logger\n\n```go\npackage lib // this is your package, part of module/library etc.\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/elgopher/yala/logger\"\n)\n\n// define global logger, no need to initialize it (by default nothing is logged)\nvar log logger.Global\n\n// Provide a public function for setting adapter. It will be called in main.go\nfunc SetLoggerAdapter(adapter logger.Adapter) {\n\tlog.SetAdapter(adapter)\n}\n\nfunc Function(ctx context.Context) {\n\tlog.Debug(ctx, \"Debug message\")\n\t\n\tlog.InfoFields(ctx, \"Message with field\", logger.Fields{\n\t\t\"field_name\": \"value\", \n\t\t\"other_name\": \"value\",\n\t})\n\t\n\tlog.ErrorCause(ctx, \"Message with error\", errors.New(\"some\"))\n}\n```\n\n#### Specify adapter - a real logger implementation.\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\n\t\"github.com/elgopher/yala/adapter/console\"\n\t\"lib\"\n)\n\n// End user decides what library to plug in.\nfunc main() {\n\tadapter := console.StdoutAdapter() // will print messages to console\n\tlib.SetLoggerAdapter(adapter)\n\n\tctx := context.Background()\n\tlib.Function(ctx)\n}\n```\n\n### Why context.Context is a parameter?\n\n`context.Context` can very useful in transiting request-scoped tags or even entire logger. A `logger.Adapter` implementation might use them\nmaking possible to log messages instrumented with tags. Thanks to that your library can trully participate in the incoming request. \n\n### Use normal logger\n\nLogging is a special kind of dependency. It is used all over the place. Adding it as an explicit dependency to every\nfunction, struct etc. can be cumbersome. Still though, you have an option to use **normal** logger by injecting\nlogger.Adapter into your library:\n\n```go\n// your library code:\nfunc NewLibrary(adapter logger.Adapter) YourLib {\n\t// create a new normal logger which provides similar API to the global logger\n\tl := logger.WithAdapter(adapter)     \n\treturn YourLib{log: l}\n}\n\ntype YourLib struct {\n\tlog logger.Logger\n}\n\nfunc (l YourLib) Method(ctx context.Context) {\n\tl.log.Debug(ctx, \"message from normal logger\")\n}\n\n\n// end user code\nadapter := console.StdoutAdapter()\nlib := NewLibrary(adapter)\n```\n\n### How to use existing adapters\n\n* [Logrus](adapter/logrusadapter/_example/main.go)\n* [standard log package](adapter/logadapter/_example/main.go)\n* [print logs to console using simplified adapter](adapter/console/_example/main.go)\n* [Zap](adapter/zapadapter/_example/main.go)\n* [Zerolog](adapter/zerologadapter/_example/main.go)\n* [glog](adapter/glogadapter/_example/main.go)\n* [Log15](adapter/log15adapter/_example/main.go)\n\n### Writing your own adapter\n\nJust implement `logger.Adapter` interface:\n\n```go\ntype MyAdapter struct{}\n\nfunc (MyAdapter) Log(context.Context, logger.Entry) {\n    // here you can do whatever you want with the log entry \n}\n```\n\n### Difference between Logger and Adapter\n\n* Logger is used by package/module/library author\n* Adapter is an interface to be implemented by adapters. They use real logging libraries under the hood.\n* So, why two abstractions? Simply because the smaller the Adapter interface, the easier it is to implement it. On the other hand, from library perspective, more methods means API which is easier to use. \n* Here is the architecture from the package perspective:\n\n\u003cimg src=\"docs/architecture.svg\" width=\"100%\"\u003e\n\n\n### More examples\n\n* [How to reuse logger](logger/_examples/reuse/main.go)\n\n### Advanced recipes\n\n* [Filter out messages starting with given prefix](logger/_examples/filter/main.go)\n* [Filter messages by level](logger/_examples/levelfilter/main.go)\n* [Add field to each message taken from context.Context](logger/_examples/tags/main.go)\n* [Rename fields](logger/_examples/rename/main.go)\n* [Report caller information in each message](logger/_examples/caller/main.go)\n* [Zap logger passed over context.Context](logger/_examples/contextlogger/main.go)\n\n## Why just don't create my own abstraction instead of using yala?\n\nYes, you can also create your own. Very often it is just an interface with a single method, like this:\n\n```go\ntype ImaginaryLogger interface {\n    Log(context.Context, Entry)\n}\n```\n\nBut there are limitations for such solution:\n\n* such interface alone is not very easy to use in your package/module/library. You just have to write way too much boilerplate code.\n* someone who is using your package is supposed to write implementation of this interface (or you can provide prebuilt implementation for various logging libraries). In both cases this cost time and effort.\n* it is not obvious how logging API should look like. Someone would argue that is better to have a much more complicated interface like this:\n```go\ntype AnotherImaginaryLogger interface {\n\tWith(field string, value interface{}) AnotherImaginaryLogger\n\tWithError(err error) AnotherImaginaryLogger\n\tInfo(context.Context, string)\n\tDebug(context.Context, string)\n\tWarn(context.Context, string)\n\tError(context.Context, string)\n}\n```\nUnfortunately such interface is much harder to implement, than interface with a single method.\n\n## But yala is just another API. Why is it unique?\n\n* yala is designed for the ease of use. And by that I mean ease of use for everyone - developer logging messages, developer writing adapter and end user configuring the adapter:\n  * two types of concurrency-safe loggers\n  * easy to implement, one-method adapter interface\n  * full control over what is logged, and how\n* yala is using `context.Context` in each method call, making possible to use sophisticated request-scoped logging\n\n## YALA limitations\n\n* even though your package will be independent of any specific logging implementation, you still have to import \n  `github.com/elgopher/yala/logger`. This package is relatively small though, compared to real logging libraries\n  (about ~250 lines of production code) and **it does not import any external libraries**.\n* yala is not optimized for **extreme** high performance, because this would hurt the developer experience and readability of the created code. Any intermediary API ads overhead - global synchronized variables, wrapper code and even polymorphism slow down the execution a bit. The overhead varies, but it is usually a matter of tens of nanoseconds per call. \n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felgopher%2Fyala","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felgopher%2Fyala","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felgopher%2Fyala/lists"}