{"id":20265390,"url":"https://github.com/telemachus/humane","last_synced_at":"2025-09-19T01:51:38.483Z","repository":{"id":149994901,"uuid":"622393559","full_name":"telemachus/humane","owner":"telemachus","description":"A human-friendly (but still largely structured) slog.Handler","archived":false,"fork":false,"pushed_at":"2024-12-20T18:46:46.000Z","size":41,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-24T23:51:31.076Z","etag":null,"topics":["go","golang","logging","slog"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/telemachus/humane","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/telemachus.png","metadata":{"files":{"readme":"docs/README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE.txt","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":"2023-04-02T01:09:13.000Z","updated_at":"2025-03-21T15:44:45.000Z","dependencies_parsed_at":"2024-08-24T18:10:18.328Z","dependency_job_id":null,"html_url":"https://github.com/telemachus/humane","commit_stats":null,"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/telemachus%2Fhumane","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/telemachus%2Fhumane/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/telemachus%2Fhumane/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/telemachus%2Fhumane/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/telemachus","download_url":"https://codeload.github.com/telemachus/humane/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248329668,"owners_count":21085580,"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","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","logging","slog"],"created_at":"2024-11-14T11:47:04.990Z","updated_at":"2025-09-19T01:51:33.437Z","avatar_url":"https://github.com/telemachus.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `humane`: a human-friendly (but still largely structured) `slog.Handler`\n\n`humane` provides a slog.Handler for a human-friendly version of logfmt.  The\nidea for this format comes from [Brandur Leach's original post about\nlogfmt][logfmt].  See the section [Human logfmt and best practices][details]\nfor details.  (To be very clear, Brandur Leach wrote that section in 2016, and\nhe has nothing to do with this project.  Any bad ideas are entirely my fault,\nnot his.)\n\n[logfmt]: https://brandur.org/logfmt\n[details]: https://brandur.org/logfmt#human\n\n## The Format\n\nBriefly, the format is as follows.\n\n```\nLEVEL | Message text | [foo=bar...] time=\"2023-04-02T10:50.09 EDT\"\n```\n\nThe level and message Attrs appear as is without `key=value` structure or\nquoting.  Then the rest of the Attrs appear as `key=value` pairs.  A time Attr\nwill be added by default to the third section.  (See below for how to change\nthe format of this Attr or omit it entirely.)  The three sections of the log\nline are separated by a pipe character (`|`).  The pipes should make it easy\nto parse out the sections of the message with (e.g.) `cut` or `awk`, but no\nattempt is made to check for that character anywhere else in the log.  Thus, if\npipes appear elsewhere, all bets are off.  (This seems like a reasonable\ntrade-off to me since the format is meant for humans to scan rather than for\nother programs to parse.  If you want something fully structured, you should\nprobably use JSON or another format.)\n\n## Installation\n\n```\ngo get github.com/telemachus/humane\n```\n\n## Usage\n\n```go\n// Create a logger with default options.  See below for more on available\n// options.\nlogger := slog.New(humane.NewHandler(os.Stdout, nil))\nlogger.Info(\"My informative message\", \"foo\", \"bar\", \"bizz\", \"buzz\")\nlogger.Error(\"Ooops\", slog.Any(\"error\", err))\n// Output:\n//  INFO | My informative message | foo=bar bizz=buzz time=\"2023-04-02T10:50.09 EDT\"\n// ERROR | Ooops | error=\"error message\" time=\"2023-04-02T10:50.09 EDT\"\n\n// You can also set options.  Again, see the next section for more details.\nopts := \u0026humane.Options{\n    Level: slog.LevelError,\n    TimeFormat: time.RFC3339,\n}\nlogger := slog.New(humane.NewHandler(os.Stderr, opts))\nlogger.Info(\"This message will not be written\")\n```\n\n## Options\n\n+ `Level slog.Leveler`: Level defaults to slog.Info.  You can use\n  a [slog.Level](https://pkg.go.dev/log/slog#Level) to change the default.  If\n  you want something more complex, you can also implement\n  a [slog.Leveler](https://pkg.go.dev/log/slog#Leveler).\n+ `ReplaceAttr func(groups []string, a slog.Attr)`: As in slog itself, this\n  function is applied to each Attr in a given Record during handling.  This\n  allows you to, e.g., omit or edit Attrs in creative ways.  See [slog's\n  documentation and tests for further examples](https://pkg.go.dev/log/slog).\n  Note that the ReplaceAttr function is **not** applied to the level or\n  message Attrs since they receive specific formatting by this handler.\n  (However, I am open to reconsidering that.  Please open an [issue][issue] to\n  discuss it.)  In order to make the time and source Attrs easier to test for,\n  they use constants defined by slog for their keys: `slog.TimeKey` and\n  `slog.SourceKey`.\n+ `TimeFormat string`: The time format defaults to \"2006-01-02T03:04.05 MST\".\n  You can use this option to set some other time format.  (You can also tweak\n  the time format via a ReplaceAttr function, but setting this option is\n  easier for simple format changes.)  The time Attr uses `slog.TimeKey` as its\n  key value by default.\n+ `AddSource bool`: This option defaults to false.  If you set it to true,\n  then an Attr containing `source=/path/to/source:line` will be added to each\n  record.  If a source Attr is present, it uses `slog.SourceKey` as its\n  default key value.\n\nA common need (e.g., for testing) is to remove the time Attr altogether.\nHere's a simple way to do that.\n\n```go\nfunc removeTime(_ []string, a slog.Attr) slog.Attr {\n    if a.Key == slog.TimeKey {\n        return slog.Attr{}\n    }\n    return a\n}\nopts := \u0026humane.Options{ReplaceAttr: removeTime}\nlogger := slog.New(humane.NewHandler(os.Stdout, opts))\n```\n\n[slog]: https://pkg.go.dev/log/slog\n[issue]: https://github.com/telemachus/humane/issues\n\n## Bugs and Limitations\n\nI'm not aware of any bugs yet, but I'm sure there in here.  Please [let me\nknow][issue] if you find any.\n\nOne limitation concerns the source Attr.  If you use the logger in a helper\nfunction or a wrapper, then the source information will likely be wrong.  See\n[slog's documentation][sourceproblem] for a discussion and workaround.\n\n[sourceproblem]: https://pkg.go.dev/log/slog#hdr-Wrapping_output_methods\n\n## Acknowledgments\n\nI'm using quite a lot of code from slog itself as well as from the [slog\nextras repository][slogextras].  The [guide to writing `slog` handlers][guide]\nwas also very useful.  Thanks to Jonathan Amsterdam for for all three of these.\nI've also taken ideas and code from sources on [Go's wiki][wiki] as well as\nseveral blog posts about slog.  See below for a list of resources.  (Note that\nsome of the resources are more or less out of date since slog and its API have\nchanged over time.)\n\n\n\n+ [A Guide to Writing `slog` Handlers][guide]\n+ [Proposal: Structured Logging][proposal]\n+ [`slog`: Golang's official structured logging package][sobyte]\n+ [Structured logging in Go][mrkaran]\n+ [A Comprehensive Guide to Structured Logging in Go][betterstack]\n\n[slogextras]: https://github.com/jba/slog\n[guide]: https://github.com/golang/example/tree/master/slog-handler-guide\n[wiki]: https://github.com/golang/go/wiki/Resources-for-slog\n[proposal]: https://go.googlesource.com/proposal/+/master/design/56345-structured-logging.md\n[sobyte]: https://www.sobyte.net/post/2022-10/go-slog/\n[mrkaran]: https://mrkaran.dev/posts/structured-logging-in-go-with-slog/\n[betterstack]: https://betterstack.com/community/guides/logging/logging-in-go/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftelemachus%2Fhumane","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftelemachus%2Fhumane","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftelemachus%2Fhumane/lists"}