{"id":14868522,"url":"https://github.com/neilotoole/slogt","last_synced_at":"2025-07-11T21:33:21.610Z","repository":{"id":171454353,"uuid":"622192469","full_name":"neilotoole/slogt","owner":"neilotoole","description":"Bridge between Go testing.T and slog.","archived":false,"fork":false,"pushed_at":"2023-08-12T17:28:51.000Z","size":33,"stargazers_count":71,"open_issues_count":0,"forks_count":2,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-11T08:59:53.009Z","etag":null,"topics":["adapter","bridge","go","golang","handler","slog","testing"],"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/neilotoole.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-04-01T11:54:49.000Z","updated_at":"2025-04-09T10:56:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"3356197e-f1e4-49f0-9331-0c4d5b10d34c","html_url":"https://github.com/neilotoole/slogt","commit_stats":null,"previous_names":["neilotoole/slogt"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/neilotoole/slogt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neilotoole%2Fslogt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neilotoole%2Fslogt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neilotoole%2Fslogt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neilotoole%2Fslogt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neilotoole","download_url":"https://codeload.github.com/neilotoole/slogt/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neilotoole%2Fslogt/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264902235,"owners_count":23681025,"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":["adapter","bridge","go","golang","handler","slog","testing"],"created_at":"2024-09-20T06:00:58.246Z","updated_at":"2025-07-11T21:33:21.602Z","avatar_url":"https://github.com/neilotoole.png","language":"Go","funding_links":[],"categories":["Testing"],"sub_categories":[],"readme":"# slogt\n\n`slogt` is a bridge between Go stdlib [`testing`](https://pkg.go.dev/testing) pkg\nand [`log/slog`](https://pkg.go.dev/golang.org/log/slog).\n\n\nThe problem: when tests execute, your `slog` output goes directly to `stdout`,\nunlike a call to `t.Log`, which is correlated with your test's execution.\n\n```go\nfunc TestSlog_Ugly(t *testing.T) {\n   log := slog.New(slog.NewTextHandler(os.Stdout, nil))\n   t.Log(\"I am indented correctly\")\n   log.Info(\"But I am not\")\n}\n```\n\nProduces:\n\n```text\n=== RUN   TestSlog_Ugly\n    slogt_test.go:22: I am indented correctly\ntime=2023-04-01T11:29:27.236-06:00 level=INFO msg=\"But I am not\"\n```\n\nNote the second line (produced via `slog`).\n\n`slogt` bridges those packages.\n\n```go\nfunc TestSlogt_Pretty(t *testing.T) {\n\tlog := slogt.New(t)\n\tt.Log(\"I am indented correctly\")\n\tlog.Info(\"And so am I\")\n}\n```\n\nProduces:\n\n```text\n=== RUN   TestSlogt_Pretty\n    slogt_test.go:28: I am indented correctly\n    logger.go:230: time=2023-04-01T11:33:06.342-06:00 level=INFO msg=\"And so am I\"\n```\n\n\n## Usage\n\nRun `go get` as per procedure:\n\n```shell\ngo get -u github.com/neilotoole/slogt\n```\n\nThen, use `slogt.New` to get a `*slog.Logger` that you can\nuse as you normally would.\n\n```go\nfunc TestText(t *testing.T) {\n   log := slogt.New(t)\n   log.Info(\"hello world\")\n}\n```\n\nProduces:\n\n```text\n=== RUN   TestText\n    logger.go:230: time=2023-04-01T11:14:53.073-06:00 level=INFO msg=\"hello world\"\n```\n\nIn practice, you would pass the `*slog.Logger` returned from `slogt.New` to\nthe component under test. For example:\n\n```go\nfunc TestApp(t *testing.T) {\n  log := slogt.New(t)\n  \n  app := app.New(log, ...) // other dependencies\n\n  result, err := app.DepositMoney(100)\n  require.NoError(t, err)\n  require.Equal(t, 100, result.Balance)\n}\n```\n\nIf the `app.DepositMoney` method logs anything, its output will be piped\nto `t.Log` as desired. \n\n### Options\n\nThe default output is text, i.e. a `slog.TextHandler.` You can\nspecify JSON using the `slogt.JSON()` option.\n\n```go\nfunc TestJSON(t *testing.T) {\n   log := slogt.New(t, slogt.JSON())\n   log.Info(\"hello world\")\n}\n```\n\nProduces:\n\n```text\n=== RUN   TestJSON\n    logger.go:230: {\"time\":\"2023-04-01T11:14:12.164085-06:00\",\"level\":\"INFO\",\"msg\":\"hello world\"}\n```\n\nTo switch the default handler:\n\n```go\nfunc init() {\n    slogt.DefaultHandler = slogt.JSON\t\n}\n```\n\nYou can exercise full control over the handler using `slogt.Factory()`.\n\n```go\nfunc TestSomething(T *testing.T) {\n    // This factory returns a slog.Handler using slog.LevelError.\n    f := slogt.Factory(func(w io.Writer) slog.Handler {\n       opts := \u0026slog.HandlerOptions{\n           Level: slog.LevelError,\n       }\n       return slog.NewTextHandler(w, opts)\n    })\n\n    log := slogt.New(t, f)\n}\n```\n\n## Deficiency\n\nCalling `t.Log()` prints the callsite as the first element\nof each log statement (`logger.go:230` in the example below).\n\n```text\n=== RUN   TestText\n    logger.go:230: time=2023-04-01T11:14:53.073-06:00 level=INFO msg=\"hello world\"\n```\n\nBut `logger.go:230` is actually in the internals of `slog` package.\nWhat we really want to see is the location of the caller of, say, `log.Info()`.\n\nAlas, given the available functionality\non `testing.T` (i.e. the `Helper` method), and how `slog` is implemented,\nthere's no way to have the correct callsite printed.\n\nThere are a number of ways this could be fixed:\n\n1. The Go team could implement a `testing.NewLogger(t)` function that effectively\n   does what this package does, but it would have access to the `testing.T`'s\n   internal state, and so could manipulate the calldepth.\n2. The `testing.T` type could expose a `HelperN(depth int)` method that allows\n   logging libraries and the like to manipulate the calldepth further. This would\n   be generically useful even independent of this particular case.\n3. The `slog` package could test if the handler implements an interface with\n   method `Helper()`, and if so, invoke that method. This would need to be\n   implemented in several spots in `slog` codebase, and would introduce a little\n   overhead.\n\nBeing that none of the above are available right now, we have to live\nwith the incorrect callsite always being printed. If you also want to\nsee the correct callsite alongside the incorrect one, you can do this:\n\n```go\nfunc TestCaller(t *testing.T) {\n\tf := slogt.Factory(func(w io.Writer) slog.Handler {\n\t\topts := \u0026slog.HandlerOptions{\n\t\t\tAddSource: true,\n\t\t}\n\n\t\treturn slog.NewTextHandler(w, opts)\n\t})\n\n\tlog := slogt.New(t, f)\n\tlog.Info(\"Show me the real callsite\")\n}\n```\n\nWhich produces (note the `source` attribute):\n\n```text\n=== RUN   TestCaller\n    logger.go:230: time=2023-04-01T11:21:49.896-06:00 level=INFO source=/Users/neilotoole/slogt/slogt_test.go:103 msg=\"Show me the real callsite\"\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneilotoole%2Fslogt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneilotoole%2Fslogt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneilotoole%2Fslogt/lists"}