{"id":28265565,"url":"https://github.com/cheesycoffee/jogger","last_synced_at":"2026-06-30T11:31:07.893Z","repository":{"id":293244600,"uuid":"981807042","full_name":"cheesycoffee/jogger","owner":"cheesycoffee","description":"a lightweight tracing-aware logging library for Go. It builds on top of uber-go/zap and uses concepts inspired by Jaeger and OpenTracing to trace spans and contextual logs across a request lifecycle.","archived":false,"fork":false,"pushed_at":"2025-05-12T01:43:30.000Z","size":6,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-18T23:36:19.455Z","etag":null,"topics":["jaeger","logger","logging","span","trace","tracing","zap"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cheesycoffee.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2025-05-11T23:11:38.000Z","updated_at":"2025-05-12T01:49:20.000Z","dependencies_parsed_at":"2025-05-14T11:51:32.396Z","dependency_job_id":"f723f73f-dbec-4827-9604-4bb120deeeb1","html_url":"https://github.com/cheesycoffee/jogger","commit_stats":null,"previous_names":["cheesycoffee/jogger"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/cheesycoffee/jogger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheesycoffee%2Fjogger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheesycoffee%2Fjogger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheesycoffee%2Fjogger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheesycoffee%2Fjogger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cheesycoffee","download_url":"https://codeload.github.com/cheesycoffee/jogger/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheesycoffee%2Fjogger/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34965641,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-30T02:00:05.919Z","response_time":92,"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":["jaeger","logger","logging","span","trace","tracing","zap"],"created_at":"2025-05-20T11:15:45.980Z","updated_at":"2026-06-30T11:31:07.879Z","avatar_url":"https://github.com/cheesycoffee.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Jogger\n\n**Jogger** is a lightweight tracing-aware logging library for Go. It builds on top of `uber-go/zap` and uses concepts inspired by `Jaeger` and `OpenTracing` to trace spans and contextual logs across a request lifecycle.\n\n---\n\n## ✨ Features\n\n* Zap-based structured logging\n* Span-based tracing abstraction\n* Context propagation with request IDs\n* Environment-specific encoders (production \u0026 development)\n\n---\n\n## 🧩 Compatibility\n\n- **Go 1.12 or later**  \n  This library is compatible with Go 1.12+ and does not rely on generics or Go modules features introduced after that version.\n\n---\n\n## 📦 Installation\n\n```bash\ngo get github.com/cheesycoffee/jogger\n```\n\n---\n\n## 🚀 Usage\n\n### Add manually request ID to context or via middlware\nmanually added :\n```go\nctx := context.Background()\nctx = jogger.WithRequestID(ctx, \"abc-123\")\n```\n\nREST middleware :\n```go\n    func LoggingMiddleware(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tstart := time.Now()\n\n\t\t// Extract or generate request ID\n\t\tvar incomingID string\n\n\t\t// check existing request id within header\n\t\trequestIDHeaders := []string{\"X-Request-ID\", \"X-Correlation-ID\", \"X-Amzn-Trace-Id\", \"uber-trace-id\"}\n\t\tfor _, k := range requestIDHeaders {\n\t\t\tincomingID = r.Header.Get(k)\n\t\t\tif incomingID != \"\" {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\t// generate new request id if does not exist in header\n\t\tif incomingID == \"\" {\n\t\t\tincomingID = uuid.New().String()\n\t\t}\n\n\t\tctx := jogger.WithRequestID(r.Context(), incomingID)\n\n\t\t// Read and restore body\n\t\tvar bodyMap map[string]interface{}\n\t\tif r.Body != nil {\n\t\t\tbodyBytes, _ := io.ReadAll(r.Body)\n\t\t\tr.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))\n\n\t\t\t_ = json.Unmarshal(bodyBytes, \u0026bodyMap)\n\n\t\t\t// remove sensitive data from loggin\n\t\t\tcredentials := []string{\"password\", \"creditCard\"}\n\t\t\tfor _, k := range credentials {\n\t\t\t\tdelete(bodyMap, k)\n\t\t\t}\n\t\t}\n\n\t\t// Sanitize Authorization header\n\t\theaders := make(map[string]interface{})\n\t\tauthHeader := r.Header.Get(\"Authorization\")\n\t\tif strings.HasPrefix(authHeader, \"Bearer \") {\n\t\t\theaders[\"Authorization\"] = \"Bearer ***redacted***\"\n\t\t} else if strings.HasPrefix(authHeader, \"Basic \") {\n\t\t\theaders[\"Authorization\"] = \"Basic ***redacted***\"\n\t\t}\n\n\t\tjogger.Info(ctx, \"Incoming Request\",\n\t\t\tzap.String(\"method\", r.Method),\n\t\t\tzap.String(\"path\", r.URL.Path),\n\t\t\tzap.Any(\"body\", bodyMap),\n\t\t\tzap.Any(\"headers\", headers),\n\t\t\tzap.String(\"requestID\", incomingID),\n\t\t)\n\n\t\t// Replace context in request\n\t\tr = r.WithContext(ctx)\n\n\t\t// Capture response status\n\t\trr := \u0026responseRecorder{ResponseWriter: w, statusCode: http.StatusOK}\n\t\terr := catchPanic(func() {\n\t\t\tnext.ServeHTTP(rr, r)\n\t\t})\n\n\t\tduration := time.Since(start).Milliseconds()\n\n\t\tjogger.Info(ctx, \"Completed Request\",\n\t\t\tzap.String(\"method\", r.Method),\n\t\t\tzap.String(\"path\", r.URL.Path),\n\t\t\tzap.Int(\"status\", rr.statusCode),\n\t\t\tzap.Int64(\"durationMs\", duration),\n\t\t\tzap.String(\"requestID\", incomingID),\n\t\t\tzap.Error(err),\n\t\t)\n\t})\n}\n\n// responseRecorder wraps http.ResponseWriter to capture status code\ntype responseRecorder struct {\n\thttp.ResponseWriter\n\tstatusCode int\n}\n\nfunc (rr *responseRecorder) WriteHeader(code int) {\n\trr.statusCode = code\n\trr.ResponseWriter.WriteHeader(code)\n}\n\n// catchPanic recovers from panics and returns an error\nfunc catchPanic(fn func()) (err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = fmt.Errorf(\"panic: %v\", e)\n\t\t}\n\t}()\n\tfn()\n\treturn nil\n}\n```\n\n### Start using span\n\n```go\nfunc (r *repository) GetAllUsers(ctx context.Context, userParam UserParam) (userList []model.User, err error) {\n    span, ctx := jogger.StartSpan(ctx, \"Repository:GetAllUsers\")\n    defer span.Finish(\u0026err) // pass error pointer to capture the last named var result\n    \n\n    query := fmt.Sprintf(`SELECT id, name, created_at, updated_at FROM users LIMIT %d OFFSET %d`, userParam.PageSize, userParam.Offset)\n    span.SetTag(\"query\", query)\n\n\trows, err := r.db.QueryContext(ctx, query)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar user model.User\n\t\tif err = rows.Scan(\u0026user.ID, \u0026user.Name, \u0026user.CreatedAt, \u0026user.UpdatedAt); err != nil {\n\t\t\treturn\n\t\t}\n\t\tuserList = append(userList, user)\n\t}\n\n\terr = rows.Err()\n\n    return\n}\n```\n\n### 3. Log messages with context\n\n```go\njogger.Info(ctx, \"retrieving users\", zap.Int(\"user_count\", 42))\njogger.Warn(ctx, \"slow response\")\njogger.Error(ctx, \"query failed\", zap.Error(err))\n```\n\n---\n\n## 📁 Example Console Log Output\n\n* Logs include `requestID`, `spanID`, `duration`, and any custom tags\n\n```log\n2025-05-12T08:19:25.126+0700    INFO    Incoming Request        {\"requestID\": \"39164279-e9ad-4312-9cc5-fb0002ec80cb\", \"method\": \"GET\", \"path\": \"/v1/users\", \"body\": null, \"headers\": {}, \"requestID\": \"39164279-e9ad-4312-9cc5-fb0002ec80cb\"}\n2025-05-12T08:19:25.126+0700    ERROR   span finished with error        {\"span\": \"Repository:GetAllUsers\", \"spanID\": \"7a888a3d-a94b-4a0c-b6f6-021a398b7de1\", \"requestID\": \"39164279-e9ad-4312-9cc5-fb0002ec80cb\", \"duration\": 0.000000201}\n2025-05-12T08:19:25.126+0700    ERROR   span finished with error        {\"span\": \"Usecase:GetAllUsers\", \"spanID\": \"1cde182f-d89f-4676-b0dc-bdf674b27f41\", \"requestID\": \"39164279-e9ad-4312-9cc5-fb0002ec80cb\", \"duration\": 0.000148596, \"error\": \"sql: Scan error on column index 2, name \\\"created_at\\\": unsupported Scan, storing driver.Value type int64 into type *time.Time\"}\n2025-05-12T08:19:25.126+0700    ERROR   span finished with error        {\"span\": \"handlerRest:GetAllUsers\", \"spanID\": \"19d75057-f909-443d-b8d6-35bb82816499\", \"requestID\": \"39164279-e9ad-4312-9cc5-fb0002ec80cb\", \"duration\": 0.000165527}\n2025-05-12T08:19:25.126+0700    INFO    Completed Request       {\"requestID\": \"39164279-e9ad-4312-9cc5-fb0002ec80cb\", \"method\": \"GET\", \"path\": \"/v1/users\", \"status\": 500, \"durationMs\": 0, \"requestID\": \"39164279-e9ad-4312-9cc5-fb0002ec80cb\"}\n```\n\n---\n\n## 🧪 Testing\n\nRun unit tests:\n\n```bash\ngo test -v\n```\n\n---","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcheesycoffee%2Fjogger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcheesycoffee%2Fjogger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcheesycoffee%2Fjogger/lists"}