{"id":35672324,"url":"https://github.com/bborbe/log","last_synced_at":"2026-01-05T19:03:34.646Z","repository":{"id":226336839,"uuid":"768008728","full_name":"bborbe/log","owner":"bborbe","description":null,"archived":false,"fork":false,"pushed_at":"2025-12-05T22:32:14.000Z","size":3911,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-12-09T12:27:20.749Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bborbe.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-03-06T09:55:30.000Z","updated_at":"2025-12-05T22:32:18.000Z","dependencies_parsed_at":"2024-03-07T04:30:33.949Z","dependency_job_id":"0748e6a3-665d-47ef-a7ed-c7185ccaff16","html_url":"https://github.com/bborbe/log","commit_stats":null,"previous_names":["bborbe/log"],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/bborbe/log","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bborbe","download_url":"https://codeload.github.com/bborbe/log/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bborbe%2Flog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28218052,"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":"2026-01-05T02:00:06.358Z","response_time":57,"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":[],"created_at":"2026-01-05T19:01:55.434Z","updated_at":"2026-01-05T19:03:34.634Z","avatar_url":"https://github.com/bborbe.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Go Log Utilities\n\n[![CI](https://github.com/bborbe/log/actions/workflows/ci.yml/badge.svg)](https://github.com/bborbe/log/actions/workflows/ci.yml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/bborbe/log)](https://goreportcard.com/report/github.com/bborbe/log)\n[![Go Reference](https://pkg.go.dev/badge/github.com/bborbe/log.svg)](https://pkg.go.dev/github.com/bborbe/log)\n[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause)\n\nA Go library providing advanced logging utilities focused on log sampling and dynamic log level management, designed to integrate seamlessly with Google's `glog` library.\n\n## Features\n\n- **Log Sampling**: Reduce log volume with intelligent sampling mechanisms\n- **Dynamic Log Level Management**: Change log levels at runtime via HTTP endpoints\n- **Multiple Sampler Types**: Counter-based, time-based, glog-level-based, and custom samplers\n- **Thread-Safe**: All components are designed for concurrent use\n- **Extensible**: Factory pattern and interface-based design for easy customization\n\n---\n\n## Installation\n\n```bash\ngo get github.com/bborbe/log\n```\n\n## Quick Start\n\n### Basic Log Sampling\n\n```go\npackage main\n\nimport (\n    \"time\"\n    \"github.com/bborbe/log\"\n    \"github.com/golang/glog\"\n)\n\nfunc main() {\n    // Sample every 10th log entry\n    modSampler := log.NewSampleMod(10)\n    \n    // Sample once every 10 seconds\n    timeSampler := log.NewSampleTime(10 * time.Second)\n    \n    // Use in your logging code\n    if modSampler.IsSample() {\n        glog.V(2).Infof(\"This will be logged every 10th time\")\n    }\n    \n    if timeSampler.IsSample() {\n        glog.V(2).Infof(\"This will be logged at most once every 10 seconds\")\n    }\n}\n```\n\n### Dynamic Log Level Management\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"net/http\"\n    \"time\"\n    \n    \"github.com/bborbe/log\"\n    \"github.com/golang/glog\"\n    \"github.com/gorilla/mux\"\n)\n\nfunc main() {\n    ctx := context.Background()\n    \n    // Create log level setter that auto-resets after 5 minutes\n    logLevelSetter := log.NewLogLevelSetter(\n        glog.Level(1), // default level\n        5*time.Minute, // auto-reset duration\n    )\n    \n    // Set up HTTP handler for dynamic log level changes\n    router := mux.NewRouter()\n    router.Handle(\"/debug/loglevel/{level}\", \n        log.NewSetLoglevelHandler(ctx, logLevelSetter))\n    \n    http.ListenAndServe(\":8080\", router)\n}\n```\n\nNow you can change log levels at runtime:\n```bash\ncurl http://localhost:8080/debug/loglevel/4\n```\n\n## Sampler Types\n\n### ModSampler\nSamples every Nth log entry based on a counter:\n```go\nsampler := log.NewSampleMod(100) // Sample every 100th log\n```\n\n### TimeSampler\nSamples based on time intervals:\n```go\nsampler := log.NewSampleTime(30 * time.Second) // Sample at most once per 30 seconds\n```\n\n### GlogLevelSampler\nSamples based on glog verbosity levels:\n```go\nsampler := log.NewSamplerGlogLevel(3) // Sample when glog level \u003e= 3\n```\n\n### ListSampler\nCombines multiple samplers with OR logic:\n```go\nsampler := log.SamplerList{\n    log.NewSampleTime(10 * time.Second),\n    log.NewSamplerGlogLevel(4),\n}\n```\n\n### FuncSampler\nCreate custom sampling logic:\n```go\nsampler := log.SamplerFunc(func() bool {\n    // Your custom sampling logic\n    return shouldSample()\n})\n```\n\n### TrueSampler\nAlways samples (useful for testing or special cases):\n```go\nsampler := log.SamplerTrue{}\n```\n\n## Factory Pattern\n\nUse the factory pattern for dependency injection:\n\n```go\n// Use the default factory\nfactory := log.DefaultSamplerFactory\nsampler := factory.Sampler()\n\n// Or create a custom factory\ncustomFactory := log.SamplerFactoryFunc(func() log.Sampler {\n    return log.NewSampleMod(50)\n})\n```\n\n---\n\n## Full Example\n\nHere's a complete, runnable example demonstrating multiple features working together in a production-like scenario:\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"net/http\"\n    \"time\"\n\n    \"github.com/bborbe/log\"\n    \"github.com/golang/glog\"\n    \"github.com/gorilla/mux\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    // Combine time-based and level-based sampling\n    // This will sample if EITHER condition is met (OR logic):\n    // - At most once every 10 seconds, OR\n    // - When glog verbosity is \u003e= 4\n    sampler := log.SamplerList{\n        log.NewSampleTime(10 * time.Second),\n        log.NewSamplerGlogLevel(4),\n    }\n\n    // Set up dynamic log level management\n    // Default level: 1, auto-resets after 5 minutes\n    logLevelSetter := log.NewLogLevelSetter(glog.Level(1), 5*time.Minute)\n\n    // Create HTTP server with debug endpoint\n    router := mux.NewRouter()\n    router.Handle(\"/debug/loglevel/{level}\",\n        log.NewSetLoglevelHandler(ctx, logLevelSetter))\n\n    // Start HTTP server in background\n    go func() {\n        if err := http.ListenAndServe(\":8080\", router); err != nil {\n            glog.Fatalf(\"HTTP server failed: %v\", err)\n        }\n    }()\n\n    // Example application loop with sampled logging\n    for i := 0; i \u003c 1000; i++ {\n        // High-frequency operation\n        processItem(i)\n\n        // Sampled logging to avoid log spam\n        if sampler.IsSample() {\n            glog.V(2).Infof(\"Processed item %d\", i)\n        }\n\n        time.Sleep(100 * time.Millisecond)\n    }\n}\n\nfunc processItem(i int) {\n    // Your application logic here\n    _ = i\n}\n```\n\nYou can change log levels at runtime:\n```bash\n# Increase verbosity to see more logs\ncurl http://localhost:8080/debug/loglevel/4\n\n# Response: set loglevel to 4 completed\n# Log level will auto-reset to 1 after 5 minutes\n```\n\n---\n\n## HTTP Log Level Management\n\nThe library provides built-in HTTP handlers for runtime log level changes:\n\n- **Endpoint**: `GET/POST /debug/loglevel/{level}`\n- **Auto-reset**: Automatically reverts to default level after specified duration\n- **Thread-safe**: Safe for concurrent access\n\nExample integration with gorilla/mux:\n```go\nrouter := mux.NewRouter()\nlogLevelSetter := log.NewLogLevelSetter(glog.Level(1), 5*time.Minute)\nrouter.Handle(\"/debug/loglevel/{level}\",\n    log.NewSetLoglevelHandler(context.Background(), logLevelSetter))\n```\n\n---\n\n## Development\n\n### Running Tests\n```bash\nmake test\n```\n\n### Code Generation (Mocks)\n```bash\nmake generate\n```\n\n### Full Development Workflow\n```bash\nmake precommit  # Format, test, lint, and check\n```\n\n## Testing Framework\n\nThe library uses:\n- **Ginkgo v2** for BDD-style testing\n- **Gomega** for assertions\n- **Counterfeiter** for mock generation\n\n---\n\n## Testing\n\n### Testing Code That Uses Log Samplers\n\nWhen testing code that uses this library, you have several options for controlling sampling behavior:\n\n#### Option 1: Use TrueSampler for Always Sampling\n\n```go\nimport (\n    \"testing\"\n    \"github.com/bborbe/log\"\n    \"github.com/golang/glog\"\n)\n\nfunc TestYourCode(t *testing.T) {\n    // Use TrueSampler to ensure logs always sample during tests\n    sampler := log.NewSamplerTrue()\n\n    // Your test code here\n    if sampler.IsSample() {\n        glog.V(2).Infof(\"This will always log in tests\")\n    }\n}\n```\n\n#### Option 2: Use SamplerFunc for Controlled Testing\n\n```go\nfunc TestWithControlledSampling(t *testing.T) {\n    shouldSample := true\n    sampler := log.SamplerFunc(func() bool {\n        return shouldSample\n    })\n\n    // Test when sampling is enabled\n    if sampler.IsSample() {\n        glog.V(2).Infof(\"Sampled log\")\n    }\n\n    // Test when sampling is disabled\n    shouldSample = false\n    if sampler.IsSample() {\n        t.Error(\"Should not sample\")\n    }\n}\n```\n\n#### Option 3: Use Mock Samplers (Counterfeiter)\n\n```go\nimport (\n    \"testing\"\n    \"github.com/bborbe/log/mocks\"\n)\n\nfunc TestWithMockSampler(t *testing.T) {\n    mockSampler := \u0026mocks.LogSampler{}\n\n    // Configure mock behavior\n    mockSampler.IsSampleReturns(true)\n\n    // Your test code using the mock\n    result := mockSampler.IsSample()\n    if !result {\n        t.Error(\"Expected sampling to be enabled\")\n    }\n\n    // Verify mock was called\n    if mockSampler.IsSampleCallCount() != 1 {\n        t.Error(\"Expected IsSample to be called once\")\n    }\n}\n```\n\n#### Testing with Ginkgo/Gomega\n\n```go\nimport (\n    . \"github.com/onsi/ginkgo/v2\"\n    . \"github.com/onsi/gomega\"\n    \"github.com/bborbe/log\"\n)\n\nvar _ = Describe(\"YourComponent\", func() {\n    var sampler log.Sampler\n\n    BeforeEach(func() {\n        sampler = log.NewSamplerTrue()\n    })\n\n    It(\"should sample logs\", func() {\n        Expect(sampler.IsSample()).To(BeTrue())\n    })\n})\n```\n\n---\n\n## License\n\nThis project is licensed under the BSD 3-Clause License - see the [LICENSE](LICENSE) file for details.\n\n## Contributing\n\n1. Fork the repository\n2. Create your feature branch\n3. Add tests for your changes\n4. Run `make precommit` to ensure code quality\n5. Submit a pull request\n\n## Dependencies\n\n- [glog](https://github.com/golang/glog) - Core logging functionality\n- [gorilla/mux](https://github.com/gorilla/mux) - HTTP routing for log level endpoints\n- [github.com/bborbe/time](https://github.com/bborbe/time) - Time utilities\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbborbe%2Flog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbborbe%2Flog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbborbe%2Flog/lists"}