{"id":13413320,"url":"https://github.com/phuslu/log","last_synced_at":"2026-01-13T18:21:56.764Z","repository":{"id":37525101,"uuid":"195634128","full_name":"phuslu/log","owner":"phuslu","description":"Fastest structured logging","archived":false,"fork":false,"pushed_at":"2025-09-04T03:42:35.000Z","size":776,"stargazers_count":793,"open_issues_count":7,"forks_count":52,"subscribers_count":25,"default_branch":"master","last_synced_at":"2025-09-04T05:46:58.065Z","etag":null,"topics":["golang","json","log","logger","logging","slog","structured-logging"],"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/phuslu.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2019-07-07T09:40:38.000Z","updated_at":"2025-09-04T03:42:06.000Z","dependencies_parsed_at":"2024-06-06T14:55:34.231Z","dependency_job_id":"b1650a27-0e7e-497a-ab5c-a36df4c8a1bd","html_url":"https://github.com/phuslu/log","commit_stats":{"total_commits":705,"total_committers":13,"mean_commits":54.23076923076923,"dds":0.2056737588652482,"last_synced_commit":"1817cec14de141c7958a8ef37786018af4c40050"},"previous_names":[],"tags_count":119,"template":false,"template_full_name":null,"purl":"pkg:github/phuslu/log","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phuslu%2Flog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phuslu%2Flog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phuslu%2Flog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phuslu%2Flog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/phuslu","download_url":"https://codeload.github.com/phuslu/log/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/phuslu%2Flog/sbom","scorecard":{"id":732766,"data":{"date":"2025-08-11","repo":{"name":"github.com/phuslu/log","commit":"f8ba7b4ac762102b5e666e4bec87c9325482422b"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":1,"reason":"Found 3/29 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":4,"reason":"3 commit(s) and 2 issue activity found in the last 90 days -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/benchmark.yml:1","Warn: no topLevel permission defined: .github/workflows/build.yml:1","Warn: no topLevel permission defined: .github/workflows/go-slog.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/benchmark.yml:35: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/benchmark.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/benchmark.yml:36: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/benchmark.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/benchmark.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/benchmark.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/benchmark.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/benchmark.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:41: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/build.yml:42: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/build.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/build.yml:46: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/build.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-slog.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/go-slog.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-slog.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/phuslu/log/go-slog.yml/master?enable=pin","Info:   0 out of  10 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 4 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-22T14:53:57.080Z","repository_id":37525101,"created_at":"2025-08-22T14:53:57.080Z","updated_at":"2025-08-22T14:53:57.080Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28395861,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["golang","json","log","logger","logging","slog","structured-logging"],"created_at":"2024-07-30T20:01:37.638Z","updated_at":"2026-01-13T18:21:56.728Z","avatar_url":"https://github.com/phuslu.png","language":"Go","readme":"# phuslog - Fastest structured logging\n\n[![godoc][godoc-img]][godoc]\n[![goreport][report-img]][report]\n[![build][build-img]][build]\n![stability-stable][stability-img]\n\n## Features\n\n* Dependency Free\n* Clean API\n* Comprehensive Writers\n    - `IOWriter`, *io.Writer wrapper*\n    - `ConsoleWriter`, *colorful \u0026 formatting*\n    - `FileWriter`, *rotating \u0026 effective*\n    - `AsyncWriter`, *asynchronously \u0026 performant*\n    - `MultiLevelWriter`, *multiple level dispatch*\n    - `SyslogWriter`, *memory efficient syslog*\n    - `JournalWriter`, *linux journal logging*\n    - `EventlogWriter`, *windows eventlog logging*\n* Stdlib Interoperability\n    - `Logger.Std`, *transform to std log instances*\n    - `Logger.Slog`, *transform to slog instances*\n    - `SlogNewJSONHandler`, *drop-in replacement of slog.NewJSONHandler*\n* Utility Functions\n    - `Goid()`, *the goroutine id matches stack trace*\n    - `NewXID()`, *create a tracing id*\n    - `Fastrandn(n uint32)`, *fast pseudorandom uint32 in [0,n)*\n    - `IsTerminal(fd uintptr)`, *isatty for golang*\n    - `Printf(fmt string, a ...any)`, *printf logging*\n* Extreme Performance\n    - [Significantly faster][high-performance] than all other json loggers.\n\n## Interfaces\n\n### Logger\n```go\n// Logger represents an active logging object that generates lines of JSON output to an io.Writer.\ntype Logger struct {\n\t// Level defines log levels.\n\tLevel Level\n\n\t// Caller determines if adds the file:line of the \"caller\" key.\n\t// If Caller is negative, adds the full /path/to/file:line of the \"caller\" key.\n\tCaller int\n\n\t// TimeField defines the time field name in output.  It uses \"time\" in if empty.\n\tTimeField string\n\n\t// TimeFormat specifies the time format in output. Uses RFC3339 with millisecond if empty.\n\t// Strongly recommended to leave TimeFormat empty for optimal built-in log formatting performance.\n\t// If set to `TimeFormatUnix/TimeFormatUnixMs`, timestamps will be formatted.\n\tTimeFormat string\n\n\t// TimeLocation specifices that the location of TimeFormat used. Uses time.Local if empty.\n\tTimeLocation *time.Location\n\n\t// Writer specifies the writer of output. It uses a wrapped os.Stderr Writer in if empty.\n\tWriter log.Writer\n}\n\n// DefaultLogger is the global logger.\nvar DefaultLogger = Logger{\n\tLevel:      DebugLevel,\n\tCaller:     0,\n\tTimeField:  \"\",\n\tTimeFormat: \"\",\n\tWriter:     \u0026log.IOWriter{os.Stderr},\n}\n```\n\n### ConsoleWriter\n```go\n// ConsoleWriter parses the JSON input and writes it in a colorized, human-friendly format to Writer.\n// IMPORTANT: Don't use ConsoleWriter on critical path of a high concurrency and low latency application.\n//\n// Default output format:\n//     {Time} {Level} {Goid} {Caller} \u003e {Message} {Key}={Value} {Key}={Value}\ntype ConsoleWriter struct {\n\t// ColorOutput determines if used colorized output.\n\tColorOutput bool\n\n\t// QuoteString determines if quoting string values.\n\tQuoteString bool\n\n\t// EndWithMessage determines if output message in the end of line.\n\tEndWithMessage bool\n\n\t// Writer is the output destination. using os.Stderr if empty.\n\tWriter io.Writer\n\n\t// Formatter specifies an optional text formatter for creating a customized output,\n\t// If it is set, ColorOutput, QuoteString and EndWithMessage will be ignored.\n\tFormatter func(w io.Writer, args *FormatterArgs) (n int, err error)\n}\n\n// FormatterArgs is a parsed sturct from json input\ntype FormatterArgs struct {\n\tTime       string // \"2019-07-10T05:35:54.277Z\"\n\tLevel      string // \"info\"\n\tCaller     string // \"prog.go:42\"\n\tCallerFunc string // \"main.main\"\n\tGoid       string // \"123\"\n\tStack      string // \"\u003cstack string\u003e\"\n\tMessage    string // \"a structure message\"\n\tKeyValues  []struct {\n\t\tKey       string // \"foo\"\n\t\tValue     string // \"bar\"\n\t\tValueType byte   // 's'\n\t}\n}\n```\n\n### FileWriter\n```go\n// FileWriter is an Writer that writes to the specified filename.\ntype FileWriter struct {\n\t// Filename is the file to write logs to.  Backup log files will be retained\n\t// in the same directory.\n\tFilename string\n\n\t// FileMode represents the file's mode and permission bits.  The default\n\t// mode is 0644\n\tFileMode os.FileMode\n\n\t// MaxSize is the maximum size in bytes of the log file before it gets rotated.\n\tMaxSize int64\n\n\t// MaxBackups is the maximum number of old log files to retain.  The default\n\t// is to retain all old log files\n\tMaxBackups int\n\n\t// TimeFormat specifies the time format of filename, uses `2006-01-02T15-04-05` as default format.\n\t// If set with `TimeFormatUnix`, `TimeFormatUnixMs`, times are formated as UNIX timestamp.\n\tTimeFormat string\n\n\t// LocalTime determines if the time used for formatting the timestamps in\n\t// log files is the computer's local time.  The default is to use UTC time.\n\tLocalTime bool\n\n\t// HostName determines if the hostname used for formatting in log files.\n\tHostName bool\n\n\t// ProcessID determines if the pid used for formatting in log files.\n\tProcessID bool\n\n\t// EnsureFolder ensures the file directory creation before writing.\n\tEnsureFolder bool\n\n\t// Header specifies an optional header function of log file after rotation,\n\tHeader func(fileinfo os.FileInfo) []byte\n\n\t// Cleaner specifies an optional cleanup function of log backups after rotation,\n\t// if not set, the default behavior is to delete more than MaxBackups log files.\n\tCleaner func(filename string, maxBackups int, matches []os.FileInfo)\n}\n```\n*Highlights*:\n- FileWriter uses a symlink to point to the current log file with a timestamp, instead of renaming for rotation. On Windows, this may require administrator privileges.\n- FileWriter `.Rotate()` method does not rotate logs based on broad TimeFormat values (e.g., daily or monthly) until the file reaches its `MaxSize`.\n- FileWriter combined with `AsyncWriter` can maximize performance and throughput on Linux, see [AsyncWriter](https://github.com/phuslu/log?tab=readme-ov-file#async-file-writer) section.\n\n## Getting Started\n\n### Simple Logging Example\n\nAn out of box example. [![playground][play-simple-img]][play-simple]\n```go\npackage main\n\nimport (\n\t\"github.com/phuslu/log\"\n)\n\nfunc main() {\n\tlog.Info().Str(\"foo\", \"bar\").Int(\"number\", 42).Msg(\"hi, phuslog\")\n\tlog.Info().Msgf(\"foo=%s number=%d error=%+v\", \"bar\", 42, \"an error\")\n}\n\n// Output:\n//   {\"time\":\"2020-03-22T09:58:41.828Z\",\"level\":\"info\",\"foo\":\"bar\",\"number\":42,\"message\":\"hi, phuslog\"}\n//   {\"time\":\"2020-03-22T09:58:41.828Z\",\"level\":\"info\",\"message\":\"foo=bar number=42 error=an error\"}\n```\n\u003e Note: By default log writes to `os.Stderr`\n\n### Customize the logger fields:\n\nTo customize logger filed name and format. [![playground][play-customize-img]][play-customize]\n```go\npackage main\n\nimport (\n\t\"github.com/phuslu/log\"\n)\n\nfunc main() {\n\tlog.DefaultLogger = log.Logger{\n\t\tLevel:      log.InfoLevel,\n\t\tCaller:     1,\n\t\tTimeField:  \"date\",\n\t\tTimeFormat: \"2006-01-02\",\n\t\tWriter:     \u0026log.IOWriter{os.Stdout},\n\t}\n\n\tlog.Info().Str(\"foo\", \"bar\").Msgf(\"hello %s\", \"world\")\n\n\tlogger := log.Logger{\n\t\tLevel:      log.InfoLevel,\n\t\tTimeField:  \"ts\",\n\t\tTimeFormat: log.TimeFormatUnixMs,\n\t}\n\n\tlogger.Log().Str(\"foo\", \"bar\").Msg(\"\")\n}\n\n// Output:\n//    {\"date\":\"2019-07-04\",\"level\":\"info\",\"caller\":\"prog.go:16\",\"foo\":\"bar\",\"message\":\"hello world\"}\n//    {\"ts\":1257894000000,\"foo\":\"bar\"}\n```\n\n### Customize the log writer\n\nTo allow the use of ordinary functions as log writers, use `WriterFunc`.\n\n```go\nlogger := log.Logger{\n\tWriter: log.WriterFunc(func(e *log.Entry) (int, error) {\n\t\tif e.Level \u003e= log.ErrorLevel {\n\t\t\treturn os.Stderr.Write(e.Value())\n\t\t} else {\n\t\t\treturn os.Stdout.Write(e.Value())\n\t\t}\n\t}),\n}\n\nlogger.Info().Msg(\"a stdout entry\")\nlogger.Error().Msg(\"a stderr entry\")\n```\n\n### Pretty Console Writer\n\nTo log a human-friendly, colorized output, use `ConsoleWriter`. [![playground][play-pretty-img]][play-pretty]\n\n```go\nif log.IsTerminal(os.Stderr.Fd()) {\n\tlog.DefaultLogger = log.Logger{\n\t\tTimeFormat: \"15:04:05\",\n\t\tCaller:     1,\n\t\tWriter: \u0026log.ConsoleWriter{\n\t\t\tColorOutput:    true,\n\t\t\tQuoteString:    true,\n\t\t\tEndWithMessage: true,\n\t\t},\n\t}\n}\n\nlog.Debug().Int(\"everything\", 42).Str(\"foo\", \"bar\").Msg(\"hello world\")\nlog.Info().Int(\"everything\", 42).Str(\"foo\", \"bar\").Msg(\"hello world\")\nlog.Warn().Int(\"everything\", 42).Str(\"foo\", \"bar\").Msg(\"hello world\")\nlog.Error().Err(errors.New(\"an error\")).Msg(\"hello world\")\n```\n![Pretty logging][pretty-img]\n\u003e Note: pretty logging also works on windows console\n\n### Formatting Console Writer\n\nTo log with user-defined format(e.g. glog), using `ConsoleWriter.Formatter`. [![playground][play-glog-img]][play-glog]\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/phuslu/log\"\n)\n\ntype Glog struct {\n\tLogger log.Logger\n}\n\nfunc (l *Glog) Infof(fmt string, a ...any) { l.Logger.Info().Msgf(fmt, a...) }\n\nfunc (l *Glog) Warnf(fmt string, a ...any) { l.Logger.Warn().Msgf(fmt, a...) }\n\nfunc (l *Glog) Errorf(fmt string, a ...any) { l.Logger.Error().Msgf(fmt, a...) }\n\nvar glog = \u0026Glog{log.Logger{\n\tLevel:      log.InfoLevel,\n\tCaller:     2,\n\tTimeFormat: \"0102 15:04:05.999999\",\n\tWriter: \u0026log.ConsoleWriter{Formatter: func(w io.Writer, a *log.FormatterArgs) (int, error) {\n\t\treturn fmt.Fprintf(w, \"%c%s %s %s] %s\\n%s\", a.Level[0]-32, a.Time, a.Goid, a.Caller, a.Message, a.Stack)\n\t}},\n}}\n\nfunc main() {\n\tglog.Infof(\"hello glog %s\", \"Info\")\n\tglog.Warnf(\"hello glog %s\", \"Warn\")\n\tglog.Errorf(\"hello glog %s\", \"Error\")\n}\n\n// Output:\n// I0725 09:59:57.503246 19 console_test.go:183] hello glog Info\n// W0725 09:59:57.504247 19 console_test.go:184] hello glog Warn\n// E0725 09:59:57.504247 19 console_test.go:185] hello glog Error\n```\n\n### Formatting Logfmt output\n\nTo log with logfmt format, also using `ConsoleWriter.Formatter`. [![playground][play-logfmt-img]][play-logfmt]\n\n```go\npackage main\n\nimport (\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/phuslu/log\"\n)\n\nfunc main() {\n\tlog.DefaultLogger = log.Logger{\n\t\tLevel:      log.InfoLevel,\n\t\tCaller:     1,\n\t\tTimeField:  \"ts\",\n\t\tTimeFormat: log.TimeFormatUnixWithMs,\n\t\tWriter: \u0026log.ConsoleWriter{\n\t\t\tFormatter: log.LogfmtFormatter{\"ts\"}.Formatter,\n\t\t\tWriter:    io.MultiWriter(os.Stdout, os.Stderr),\n\t\t},\n\t}\n\n\tlog.Info().Str(\"foo\", \"bar\").Int(\"no\", 42).Msgf(\"a logfmt %s\", \"info\")\n}\n\n// Output:\n// ts=1257894000.000 level=info goid=1 caller=\"prog.go:20\" foo=\"bar\" no=42 \"a logfmt info\"\n// ts=1257894000.000 level=info goid=1 caller=\"prog.go:20\" foo=\"bar\" no=42 \"a logfmt info\"\n```\n\n### Rotating File Writer\n\nTo log to a daily-rotating file, use `FileWriter`. [![playground][play-file-img]][play-file]\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/phuslu/log\"\n\t\"github.com/robfig/cron/v3\"\n)\n\nfunc main() {\n\tlogger := log.Logger{\n\t\tLevel: log.ParseLevel(\"info\"),\n\t\tWriter: \u0026log.FileWriter{\n\t\t\tFilename:     \"logs/main.log\",\n\t\t\tFileMode:     0600,\n\t\t\tMaxSize:      100 * 1024 * 1024,\n\t\t\tMaxBackups:   7,\n\t\t\tEnsureFolder: true,\n\t\t\tLocalTime:    true,\n\t\t},\n\t}\n\n\trunner := cron.New(cron.WithLocation(time.Local))\n\trunner.AddFunc(\"0 0 * * *\", func() { logger.Writer.(*log.FileWriter).Rotate() })\n\tgo runner.Run()\n\n\tfor {\n\t\ttime.Sleep(time.Second)\n\t\tlogger.Info().Msg(\"hello world\")\n\t}\n}\n```\n\n### Rotating File Writer within a total size\n\nTo rotating log file hourly and keep in a total size, use `FileWriter.Cleaner`.\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/phuslu/log\"\n\t\"github.com/robfig/cron/v3\"\n)\n\nfunc main() {\n\tlogger := log.Logger{\n\t\tLevel: log.ParseLevel(\"info\"),\n\t\tWriter: \u0026log.FileWriter{\n\t\t\tFilename: \"main.log\",\n\t\t\tMaxSize:  500 * 1024 * 1024,\n\t\t\tCleaner:  func(filename string, maxBackups int, matches []os.FileInfo) {\n\t\t\t\tvar dir = filepath.Dir(filename)\n\t\t\t\tvar total int64\n\t\t\t\tfor i := len(matches) - 1; i \u003e= 0; i-- {\n\t\t\t\t\ttotal += matches[i].Size()\n\t\t\t\t\tif total \u003e 5*1024*1024*1024 {\n\t\t\t\t\t\tos.Remove(filepath.Join(dir, matches[i].Name()))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t}\n\n\trunner := cron.New(cron.WithLocation(time.UTC))\n\trunner.AddFunc(\"0 * * * *\", func() { logger.Writer.(*log.FileWriter).Rotate() })\n\tgo runner.Run()\n\n\tfor {\n\t\ttime.Sleep(time.Second)\n\t\tlogger.Info().Msg(\"hello world\")\n\t}\n}\n```\n\n### Rotating File Writer with compression\n\nTo rotating log file hourly and compressing after rotation, use `FileWriter.Cleaner`.\n```go\npackage main\n\nimport (\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/phuslu/log\"\n\t\"github.com/robfig/cron/v3\"\n)\n\nfunc main() {\n\tlogger := log.Logger{\n\t\tLevel: log.ParseLevel(\"info\"),\n\t\tWriter: \u0026log.FileWriter{\n\t\t\tFilename: \"main.log\",\n\t\t\tMaxSize:  500 * 1024 * 1024,\n\t\t\tCleaner:  func(filename string, maxBackups int, matches []os.FileInfo) {\n\t\t\t\tvar dir = filepath.Dir(filename)\n\t\t\t\tfor i, fi := range matches {\n\t\t\t\t\tfilename := filepath.Join(dir, fi.Name())\n\t\t\t\t\tswitch {\n\t\t\t\t\tcase i \u003e maxBackups:\n\t\t\t\t\t\tos.Remove(filename)\n\t\t\t\t\tcase !strings.HasSuffix(filename, \".gz\"):\n\t\t\t\t\t\tgo exec.Command(\"nice\", \"gzip\", filename).Run()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t}\n\n\trunner := cron.New(cron.WithLocation(time.UTC))\n\trunner.AddFunc(\"0 * * * *\", func() { logger.Writer.(*log.FileWriter).Rotate() })\n\tgo runner.Run()\n\n\tfor {\n\t\ttime.Sleep(time.Second)\n\t\tlogger.Info().Msg(\"hello world\")\n\t}\n}\n```\n\n### Async File Writer\n\nFor maximum write performance with asynchronous file logging, use `AsyncWriter`.\n\n```go\nlogger := log.Logger{\n\tLevel: log.InfoLevel,\n\tWriter: \u0026log.AsyncWriter{\n\t\tChannelSize:   4096,\n\t\tDiscardOnFull: false,\n\t\tWriter:        \u0026log.FileWriter{\n\t\t\tFilename:   \"main.log\",\n\t\t\tFileMode:   0600,\n\t\t\tMaxSize:    50 * 1024 * 1024,\n\t\t\tMaxBackups: 7,\n\t\t\tLocalTime:  false,\n\t\t},\n\t},\n}\n\nlogger.Info().Int(\"number\", 42).Str(\"foo\", \"bar\").Msg(\"a async info log\")\nlogger.Warn().Int(\"number\", 42).Str(\"foo\", \"bar\").Msg(\"a async warn log\")\nlogger.Writer.(io.Closer).Close()\n```\n*Highlights*:\n- To flush data and shut down safely, explicitly call the .Close() method.\n- The automatic `writev` enabling can boost write performance by up to 10x under high load.\n\n### Random Sample Logger:\n\nTo logging only 5% logs, use below idiom.\n```go\nif log.Fastrandn(100) \u003c 5 {\n\tlog.Log().Msg(\"hello world\")\n}\n```\n\n### Multiple Dispatching Writer\n\nTo log to different writers by different levels, use `MultiLevelWriter`.\n\n```go\nlog.DefaultLogger.Writer = \u0026log.MultiLevelWriter{\n\tInfoWriter:    \u0026log.FileWriter{Filename: \"main.INFO\", MaxSize: 100\u003c\u003c20},\n\tWarnWriter:    \u0026log.FileWriter{Filename: \"main.WARNING\", MaxSize: 100\u003c\u003c20},\n\tErrorWriter:   \u0026log.FileWriter{Filename: \"main.ERROR\", MaxSize: 100\u003c\u003c20},\n\tConsoleWriter: \u0026log.ConsoleWriter{ColorOutput: true},\n\tConsoleLevel:  log.ErrorLevel,\n}\n\nlog.Info().Int(\"number\", 42).Str(\"foo\", \"bar\").Msg(\"a info log\")\nlog.Warn().Int(\"number\", 42).Str(\"foo\", \"bar\").Msg(\"a warn log\")\nlog.Error().Int(\"number\", 42).Str(\"foo\", \"bar\").Msg(\"a error log\")\n```\n\n### Multiple Entry Writer\nTo log to different writers, use `MultiEntryWriter`.\n\n```go\nlog.DefaultLogger.Writer = \u0026log.MultiEntryWriter{\n\t\u0026log.ConsoleWriter{ColorOutput: true},\n\t\u0026log.FileWriter{Filename: \"main.log\", MaxSize: 100\u003c\u003c20},\n\t\u0026log.EventlogWriter{Source: \".NET Runtime\", ID: 1000},\n}\n\nlog.Info().Int(\"number\", 42).Str(\"foo\", \"bar\").Msg(\"a info log\")\n```\n\n### Multiple IO Writer\n\nTo log to multiple io writers like `io.MultiWriter`, use below idiom. [![playground][play-multiio-img]][play-multiio]\n\n```go\nlog.DefaultLogger.Writer = \u0026log.MultiIOWriter{\n\tos.Stdout,\n\t\u0026log.FileWriter{Filename: \"main.log\", MaxSize: 100\u003c\u003c20},\n}\n\nlog.Info().Int(\"number\", 42).Str(\"foo\", \"bar\").Msg(\"a info log\")\n```\n\n### Multiple Combined Logger:\n\nTo logging to different logger as you want, use below idiom. [![playground][play-combined-img]][play-combined]\n```go\npackage main\n\nimport (\n\t\"github.com/phuslu/log\"\n)\n\nvar logger = struct {\n\tConsole log.Logger\n\tAccess  log.Logger\n\tData    log.Logger\n}{\n\tConsole: log.Logger{\n\t\tTimeFormat: \"15:04:05\",\n\t\tCaller:     1,\n\t\tWriter: \u0026log.ConsoleWriter{\n\t\t\tColorOutput:    true,\n\t\t\tEndWithMessage: true,\n\t\t},\n\t},\n\tAccess: log.Logger{\n\t\tLevel: log.InfoLevel,\n\t\tWriter: \u0026log.FileWriter{\n\t\t\tFilename:   \"access.log\",\n\t\t\tMaxSize:    50 * 1024 * 1024,\n\t\t\tMaxBackups: 7,\n\t\t\tLocalTime:  false,\n\t\t},\n\t},\n\tData: log.Logger{\n\t\tLevel: log.InfoLevel,\n\t\tWriter: \u0026log.FileWriter{\n\t\t\tFilename:   \"data.log\",\n\t\t\tMaxSize:    50 * 1024 * 1024,\n\t\t\tMaxBackups: 7,\n\t\t\tLocalTime:  false,\n\t\t},\n\t},\n}\n\nfunc main() {\n\tlogger.Console.Info().Msgf(\"hello world\")\n\tlogger.Access.Log().Msgf(\"handle request\")\n\tlogger.Data.Log().Msgf(\"some data\")\n}\n```\n\n### SyslogWriter\n\n`SyslogWriter` is a memory-efficient, cross-platform, dependency-free syslog writer, outperforms all other structured logging libraries.\n\n```go\npackage main\n\nimport (\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/phuslu/log\"\n)\n\nfunc main() {\n\tgo func() {\n\t\tln, _ := net.Listen(\"tcp\", \"127.0.0.1:1601\")\n\t\tfor {\n\t\t\tconn, _ := ln.Accept()\n\t\t\tgo func(c net.Conn) {\n\t\t\t\tb := make([]byte, 8192)\n\t\t\t\tn, _ := conn.Read(b)\n\t\t\t\tprintln(string(b[:n]))\n\t\t\t}(conn)\n\t\t}\n\t}()\n\n\tsyslog := log.Logger{\n\t\tLevel:      log.InfoLevel,\n\t\tTimeField:  \"ts\",\n\t\tTimeFormat: log.TimeFormatUnixMs,\n\t\tWriter: \u0026log.SyslogWriter{\n\t\t\tNetwork: \"tcp\",            // \"unixgram\",\n\t\t\tAddress: \"127.0.0.1:1601\", // \"/run/systemd/journal/syslog\",\n\t\t\tTag:     \"\",\n\t\t\tMarker:  \"@cee:\",\n\t\t\tDial:    net.Dial,\n\t\t},\n\t}\n\n\tsyslog.Info().Str(\"foo\", \"bar\").Int(\"an\", 42).Msg(\"a syslog info\")\n\tsyslog.Warn().Str(\"foo\", \"bar\").Int(\"an\", 42).Msg(\"a syslog warn\")\n\ttime.Sleep(2)\n}\n\n// Output:\n// \u003c6\u003e2022-07-24T18:48:15+08:00 127.0.0.1:59277 [11516]: @cee:{\"ts\":1658659695428,\"level\":\"info\",\"foo\":\"bar\",\"an\":42,\"message\":\"a syslog info\"}\n// \u003c4\u003e2022-07-24T18:48:15+08:00 127.0.0.1:59277 [11516]: @cee:{\"ts\":1658659695429,\"level\":\"warn\",\"foo\":\"bar\",\"an\":42,\"message\":\"a syslog warn\"}\n```\n\n### JournalWriter\n\nTo log to linux systemd journald, using `JournalWriter`.\n\n```go\nlog.DefaultLogger.Writer = \u0026log.JournalWriter{\n\tJournalSocket: \"/run/systemd/journal/socket\",\n}\n\nlog.Info().Int(\"number\", 42).Str(\"foo\", \"bar\").Msg(\"hello world\")\n```\n\n### EventlogWriter\n\nTo log to windows system event, using `EventlogWriter`.\n\n```go\nlog.DefaultLogger.Writer = \u0026log.EventlogWriter{\n\tSource: \".NET Runtime\",\n\tID:     1000,\n}\n\nlog.Info().Int(\"number\", 42).Str(\"foo\", \"bar\").Msg(\"hello world\")\n```\n\n### Stdlib Log Adapter\n\nUsing wrapped loggers for stdlog. [![playground][play-stdlog-img]][play-stdlog]\n\n```go\npackage main\n\nimport (\n\tstdlog \"log\"\n\t\"os\"\n\n\t\"github.com/phuslu/log\"\n)\n\nfunc main() {\n\tvar logger *stdlog.Logger = (\u0026log.Logger{\n\t\tLevel:      log.InfoLevel,\n\t\tTimeField:  \"date\",\n\t\tTimeFormat: \"2006-01-02\",\n\t\tCaller:     1,\n\t\tContext:    log.NewContext(nil).Str(\"logger\", \"mystdlog\").Int(\"myid\", 42).Value(),\n\t\tWriter:     \u0026log.IOWriter{os.Stdout},\n\t}).Std(\"\", 0)\n\n\tlogger.Print(\"hello from stdlog Print\")\n\tlogger.Println(\"hello from stdlog Println\")\n\tlogger.Printf(\"hello from stdlog %s\", \"Printf\")\n}\n```\n\n### slog Adapter\n\nUsing wrapped loggers for slog. [![playground][play-slog-img]][play-slog]\n\n```go\npackage main\n\nimport (\n\t\"log/slog\"\n\n\t\"github.com/phuslu/log\"\n)\n\nfunc main() {\n\tvar logger *slog.Logger = (\u0026log.Logger{\n\t\tLevel:      log.InfoLevel,\n\t\tTimeField:  \"date\",\n\t\tTimeFormat: \"2006-01-02\",\n\t\tCaller:     1,\n\t}).Slog()\n\n\tlogger = logger.With(\"logger\", \"a_test_slog\").With(\"everything\", 42)\n\n\tlogger.Info(\"hello from slog Info\")\n\tlogger.Warn(\"hello from slog Warn\")\n\tlogger.Error(\"hello from slog Error\")\n}\n```\n\n### slog.JSONHandler replacement\n\nUsing as a high performance version of slog.JSONHandler. [![playground][play-phusluslog-img]][play-phusluslog]\n\n```go\npackage main\n\nimport (\n\t\"log/slog\"\n\t\"os\"\n\n\tphuslog \"github.com/phuslu/log\"\n)\n\nfunc main() {\n\tslog.SetDefault(slog.New(phuslog.SlogNewJSONHandler(os.Stderr, \u0026slog.HandlerOptions{AddSource: true})))\n\n\tslog.Info(\"hello from phuslog\", \"a\", 1, \"b\", 2)\n}\n```\n\n### User-defined Data Structure\n\nTo log with user-defined struct effectively, implements `MarshalObject`. [![playground][play-marshal-img]][play-marshal]\n\n```go\npackage main\n\nimport (\n\t\"github.com/phuslu/log\"\n)\n\ntype User struct {\n\tID   int\n\tName string\n\tPass string\n}\n\nfunc (u *User) MarshalObject(e *log.Entry) {\n\te.Int(\"id\", u.ID).Str(\"name\", u.Name).Str(\"password\", \"***\")\n}\n\nfunc main() {\n\tlog.Info().Object(\"user\", \u0026User{1, \"neo\", \"123456\"}).Msg(\"\")\n\tlog.Info().EmbedObject(\u0026User{2, \"john\", \"abc\"}).Msg(\"\")\n}\n\n// Output:\n//   {\"time\":\"2020-07-12T05:03:43.949Z\",\"level\":\"info\",\"user\":{\"id\":1,\"name\":\"neo\",\"password\":\"***\"}}\n//   {\"time\":\"2020-07-12T05:03:43.949Z\",\"level\":\"info\",\"id\":2,\"name\":\"john\",\"password\":\"***\"}\n```\n\n### Contextual Fields\n\nTo add preserved `key:value` pairs to each entry, use `NewContext`. [![playground][play-context-img]][play-context]\n\n```go\nlogger := log.Logger{\n\tLevel:   log.InfoLevel,\n\tContext: log.NewContext(nil).Str(\"ctx\", \"some_ctx\").Value(),\n}\n\nlogger.Debug().Int(\"no0\", 0).Msg(\"zero\")\nlogger.Info().Int(\"no1\", 1).Msg(\"first\")\nlogger.Info().Int(\"no2\", 2).Msg(\"second\")\n\n// Output:\n//   {\"time\":\"2020-07-12T05:03:43.949Z\",\"level\":\"info\",\"ctx\":\"some_ctx\",\"no1\":1,\"message\":\"first\"}\n//   {\"time\":\"2020-07-12T05:03:43.949Z\",\"level\":\"info\",\"ctx\":\"some_ctx\",\"no2\":2,\"message\":\"second\"}\n```\n\nYou can make a copy of log and add contextual fields. [![playground][play-context-add-img]][play-context-add]\n\n```go\npackage main\n\nimport (\n\t\"github.com/phuslu/log\"\n)\n\nfunc main() {\n\tsublogger := log.DefaultLogger\n\tsublogger.Level = log.InfoLevel\n\tsublogger.Context = log.NewContext(nil).Str(\"ctx\", \"some_ctx\").Value()\n\n\tsublogger.Debug().Int(\"no0\", 0).Msg(\"zero\")\n\tsublogger.Info().Int(\"no1\", 1).Msg(\"first\")\n\tsublogger.Info().Int(\"no2\", 2).Msg(\"second\")\n\tlog.Debug().Int(\"no3\", 3).Msg(\"no context\")\n}\n\n// Output:\n//   {\"time\":\"2021-06-14T06:36:42.904+02:00\",\"level\":\"info\",\"ctx\":\"some_ctx\",\"no1\":1,\"message\":\"first\"}\n//   {\"time\":\"2021-06-14T06:36:42.905+02:00\",\"level\":\"info\",\"ctx\":\"some_ctx\",\"no2\":2,\"message\":\"second\"}\n//   {\"time\":\"2021-06-14T06:36:42.906+02:00\",\"level\":\"debug\",\"no3\":3,\"message\":\"no context\"}\n```\n\n### Third-party Logger Interceptor\n\n| Logger | Interceptor |\n|---|---|\n| logr |  https://github.com/phuslu/log-contrib/tree/master/logr |\n| gin |  https://github.com/phuslu/log-contrib/tree/master/gin |\n| fiber |  https://github.com/phuslu/log-contrib/tree/master/fiber |\n| gorm |  https://github.com/phuslu/log-contrib/tree/master/gorm |\n| grpc |  https://github.com/phuslu/log-contrib/tree/master/grpc |\n| grpcgateway |  https://github.com/phuslu/log-contrib/tree/master/grpcgateway |\n\n### High Performance\n\n\u003cdetails\u003e\n  \u003csummary\u003eThe most common benchmarks(disabled/simple/caller/printf/any) against slog/zap/zerolog\u003c/summary\u003e\n\n```go\n// go test -v -cpu=4 -run=none -bench=. -benchtime=10s -benchmem bench_test.go\npackage main\n\nimport (\n\t\"io\"\n\t\"log\"\n\t\"log/slog\"\n\t\"testing\"\n\n\tphuslog \"github.com/phuslu/log\"\n\t\"github.com/rs/zerolog\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst msg = \"The quick brown fox jumps over the lazy dog\"\nvar obj = struct {Rate string; Low int; High float32}{\"15\", 16, 123.2}\n\nfunc BenchmarkSlogDisabled(b *testing.B) {\n\tlogger := slog.New(slog.NewJSONHandler(io.Discard, nil))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Debug(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogSimple(b *testing.B) {\n\tlogger := slog.New(slog.NewJSONHandler(io.Discard, nil))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogPrintf(b *testing.B) {\n\tslog.SetDefault(slog.New(slog.NewJSONHandler(io.Discard, nil)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlog.Printf(\"rate=%s low=%d high=%f msg=%s\", \"15\", 16, 123.2, msg)\n\t}\n}\n\nfunc BenchmarkSlogCaller(b *testing.B) {\n\tlogger := slog.New(slog.NewJSONHandler(io.Discard, \u0026slog.HandlerOptions{AddSource: true}))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogAny(b *testing.B) {\n\tlogger := slog.New(slog.NewJSONHandler(io.Discard, nil))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"object\", \u0026obj)\n\t}\n}\n\nfunc BenchmarkSlogPhusDisabled(b *testing.B) {\n\tlogger := slog.New(phuslog.SlogNewJSONHandler(io.Discard, nil))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Debug(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogPhusSimple(b *testing.B) {\n\tlogger := slog.New(phuslog.SlogNewJSONHandler(io.Discard, nil))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogPhusPrintf(b *testing.B) {\n\tslog.SetDefault(slog.New(phuslog.SlogNewJSONHandler(io.Discard, nil)))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlog.Printf(\"rate=%s low=%d high=%f msg=%s\", \"15\", 16, 123.2, msg)\n\t}\n}\n\nfunc BenchmarkSlogPhusCaller(b *testing.B) {\n\tlogger := slog.New(phuslog.SlogNewJSONHandler(io.Discard, \u0026slog.HandlerOptions{AddSource: true}))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogPhusAny(b *testing.B) {\n\tlogger := slog.New(phuslog.SlogNewJSONHandler(io.Discard, nil))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"object\", \u0026obj)\n\t}\n}\n\nfunc BenchmarkZapDisabled(b *testing.B) {\n\tlogger := zap.New(zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),\n\t\tzapcore.AddSync(io.Discard),\n\t\tzapcore.InfoLevel,\n\t)).Sugar()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Debugw(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkZapSimple(b *testing.B) {\n\tlogger := zap.New(zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),\n\t\tzapcore.AddSync(io.Discard),\n\t\tzapcore.InfoLevel,\n\t)).Sugar()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Infow(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkZapPrintf(b *testing.B) {\n\tlogger := zap.New(zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),\n\t\tzapcore.AddSync(io.Discard),\n\t\tzapcore.InfoLevel,\n\t)).Sugar()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Infof(\"rate=%s low=%d high=%f msg=%s\", \"15\", 16, 123.2, msg)\n\t}\n}\n\nfunc BenchmarkZapCaller(b *testing.B) {\n\tlogger := zap.New(zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),\n\t\tzapcore.AddSync(io.Discard),\n\t\tzapcore.InfoLevel),\n\t\tzap.AddCaller(),\n\t).Sugar()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Infow(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkZapAny(b *testing.B) {\n\tlogger := zap.New(zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),\n\t\tzapcore.AddSync(io.Discard),\n\t\tzapcore.InfoLevel,\n\t)).Sugar()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Infow(msg, \"rate\", \"15\", \"low\", 16, \"object\", \u0026obj)\n\t}\n}\n\nfunc BenchmarkZeroLogDisabled(b *testing.B) {\n\tzerolog.SetGlobalLevel(zerolog.InfoLevel)\n\tlogger := zerolog.New(io.Discard).With().Timestamp().Logger()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Debug().Str(\"rate\", \"15\").Int(\"low\", 16).Float32(\"high\", 123.2).Msg(msg)\n\t}\n}\n\nfunc BenchmarkZeroLogSimple(b *testing.B) {\n\tlogger := zerolog.New(io.Discard).With().Timestamp().Logger()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info().Str(\"rate\", \"15\").Int(\"low\", 16).Float32(\"high\", 123.2).Msg(msg)\n\t}\n}\n\nfunc BenchmarkZeroLogPrintf(b *testing.B) {\n\tlogger := zerolog.New(io.Discard).With().Timestamp().Logger()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info().Msgf(\"rate=%s low=%d high=%f msg=%s\", \"15\", 16, 123.2, msg)\n\t}\n}\n\nfunc BenchmarkZeroLogCaller(b *testing.B) {\n\tlogger := zerolog.New(io.Discard).With().Caller().Timestamp().Logger()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info().Str(\"rate\", \"15\").Int(\"low\", 16).Float32(\"high\", 123.2).Msg(msg)\n\t}\n}\n\nfunc BenchmarkZeroLogAny(b *testing.B) {\n\tlogger := zerolog.New(io.Discard).With().Timestamp().Logger()\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info().Any(\"rate\", \"15\").Any(\"low\", 16).Any(\"object\", \u0026obj).Msg(msg)\n\t}\n}\n\nfunc BenchmarkPhusLogDisabled(b *testing.B) {\n\tlogger := phuslog.Logger{Level: phuslog.InfoLevel, Writer: phuslog.IOWriter{io.Discard}}\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Debug().Str(\"rate\", \"15\").Int(\"low\", 16).Float32(\"high\", 123.2).Msg(msg)\n\t}\n}\n\nfunc BenchmarkPhusLogSimple(b *testing.B) {\n\tlogger := phuslog.Logger{Writer: phuslog.IOWriter{io.Discard}}\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info().Str(\"rate\", \"15\").Int(\"low\", 16).Float32(\"high\", 123.2).Msg(msg)\n\t}\n}\n\nfunc BenchmarkPhusLogPrintf(b *testing.B) {\n\tlogger := phuslog.Logger{Writer: phuslog.IOWriter{io.Discard}}\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info().Msgf(\"rate=%s low=%d high=%f msg=%s\", \"15\", 16, 123.2, msg)\n\t}\n}\n\nfunc BenchmarkPhusLogCaller(b *testing.B) {\n\tlogger := phuslog.Logger{Caller: 1, Writer: phuslog.IOWriter{io.Discard}}\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info().Str(\"rate\", \"15\").Int(\"low\", 16).Float32(\"high\", 123.2).Msg(msg)\n\t}\n}\n\nfunc BenchmarkPhusLogAny(b *testing.B) {\n\tlogger := phuslog.Logger{Writer: phuslog.IOWriter{io.Discard}}\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info().Any(\"rate\", \"15\").Any(\"low\", 16).Any(\"object\", \u0026obj).Msg(msg)\n\t}\n}\n```\n\n\u003c/details\u003e\n\nA Performance result as below, for daily benchmark results see [github actions][benchmark]\n```\ngoos: linux\ngoarch: amd64\ncpu: AMD EPYC 7763 64-Core Processor\n\nBenchmarkSlogDisabled-4      \t715096197\t         8.452 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkSlogSimple-4        \t 4394904\t      1367 ns/op\t     120 B/op\t       3 allocs/op\nBenchmarkSlogPrintf-4        \t 5546492\t      1053 ns/op\t      80 B/op\t       1 allocs/op\nBenchmarkSlogCaller-4        \t 2708773\t      2203 ns/op\t     688 B/op\t       9 allocs/op\nBenchmarkSlogAny-4           \t 3936673\t      1516 ns/op\t     112 B/op\t       2 allocs/op\n\nBenchmarkZapDisabled-4       \t662012907\t         9.076 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkZapSimple-4         \t 6586341\t       926.9 ns/op\t     384 B/op\t       1 allocs/op\nBenchmarkZapPrintf-4         \t 6375831\t       951.9 ns/op\t      80 B/op\t       1 allocs/op\nBenchmarkZapCaller-4         \t 3601339\t      1673 ns/op\t     632 B/op\t       3 allocs/op\nBenchmarkZapAny-4            \t 4649176\t      1288 ns/op\t     480 B/op\t       2 allocs/op\n\nBenchmarkZeroLogDisabled-4   \t606002878\t         9.908 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkZeroLogSimple-4     \t18342879\t       328.7 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkZeroLogPrintf-4     \t 8904566\t       669.6 ns/op\t      80 B/op\t       1 allocs/op\nBenchmarkZeroLogCaller-4     \t 4687348\t      1280 ns/op\t     304 B/op\t       4 allocs/op\nBenchmarkZeroLogAny-4        \t 7031146\t       851.2 ns/op\t      64 B/op\t       3 allocs/op\n\nBenchmarkPhusLogDisabled-4   \t624706401\t         9.599 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkPhusLogSimple-4     \t22249552\t       243.1 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkPhusLogPrintf-4     \t11471342\t       524.8 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkPhusLogCaller-4     \t12550828\t       480.9 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkPhusLogAny-4        \t11623692\t       516.4 ns/op\t       0 B/op\t       0 allocs/op\n\nPASS\nok  \tbench\t139.331s\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eAs slog handlers, comparing with stdlib/zap/zerolog implementations\u003c/summary\u003e\n\n```go\n// go test -v -cpu=1 -run=none -bench=. -benchtime=10s -benchmem bench_test.go\npackage bench\n\nimport (\n\t\"io\"\n\t\"log/slog\"\n\t\"testing\"\n\n\t\"github.com/phsym/zeroslog\"\n\tphuslog \"github.com/phuslu/log\"\n\tseankhliao \"go.seankhliao.com/svcrunner/v3/jsonlog\"\n\t\"go.uber.org/zap\"\n\t\"go.uber.org/zap/exp/zapslog\"\n\t\"go.uber.org/zap/zapcore\"\n)\n\nconst msg = \"The quick brown fox jumps over the lazy dog\"\n\nfunc BenchmarkSlogSimpleStd(b *testing.B) {\n\tlogger := slog.New(slog.NewJSONHandler(io.Discard, nil))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogGroupsStd(b *testing.B) {\n\tlogger := slog.New(slog.NewJSONHandler(io.Discard, nil)).With(\"a\", 1).WithGroup(\"g\").With(\"b\", 2)\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogSimpleZap(b *testing.B) {\n\tlogcore := zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),\n\t\tzapcore.AddSync(io.Discard),\n\t\tzapcore.InfoLevel,\n\t)\n\tlogger := slog.New(zapslog.NewHandler(logcore))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogGroupsZap(b *testing.B) {\n\tlogcore := zapcore.NewCore(\n\t\tzapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),\n\t\tzapcore.AddSync(io.Discard),\n\t\tzapcore.InfoLevel,\n\t)\n\tlogger := slog.New(zapslog.NewHandler(logcore)).With(\"a\", 1).WithGroup(\"g\").With(\"b\", 2)\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogSimpleZerolog(b *testing.B) {\n\tlogger := slog.New(zeroslog.NewJsonHandler(io.Discard, \u0026zeroslog.HandlerOptions{Level: slog.LevelInfo}))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogGroupsZerolog(b *testing.B) {\n\tlogger := slog.New(zeroslog.NewJsonHandler(io.Discard, \u0026zeroslog.HandlerOptions{Level: slog.LevelInfo})).With(\"a\", 1).WithGroup(\"g\").With(\"b\", 2)\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogSimpleSeankhliao(b *testing.B) {\n\tlogger := slog.New(seankhliao.New(slog.LevelInfo, io.Discard))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogGroupsSeankhliao(b *testing.B) {\n\tlogger := slog.New(seankhliao.New(slog.LevelInfo, io.Discard)).With(\"a\", 1).WithGroup(\"g\").With(\"b\", 2)\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogSimplePhuslog(b *testing.B) {\n\tlogger := slog.New((\u0026phuslog.Logger{Writer: phuslog.IOWriter{io.Discard}}).Slog().Handler())\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogGroupsPhuslog(b *testing.B) {\n\tlogger := slog.New((\u0026phuslog.Logger{Writer: phuslog.IOWriter{io.Discard}}).Slog().Handler()).With(\"a\", 1).WithGroup(\"g\").With(\"b\", 2)\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogSimplePhuslogStd(b *testing.B) {\n\tlogger := slog.New(phuslog.SlogNewJSONHandler(io.Discard, nil))\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n\nfunc BenchmarkSlogGroupsPhuslogStd(b *testing.B) {\n\tlogger := slog.New(phuslog.SlogNewJSONHandler(io.Discard, nil)).With(\"a\", 1).WithGroup(\"g\").With(\"b\", 2)\n\tfor i := 0; i \u003c b.N; i++ {\n\t\tlogger.Info(msg, \"rate\", \"15\", \"low\", 16, \"high\", 123.2)\n\t}\n}\n```\n\n\u003c/details\u003e\n\nA Performance result as below, for daily benchmark results see [github actions][benchmark]\n```\ngoos: linux\ngoarch: amd64\ncpu: AMD EPYC 7763 64-Core Processor                \n\nBenchmarkSlogSimpleStd        \t 4314817\t      1413 ns/op\t     120 B/op\t       3 allocs/op\nBenchmarkSlogGroupsStd        \t 4167734\t      1462 ns/op\t     120 B/op\t       3 allocs/op\n\nBenchmarkSlogSimpleZap        \t 4824007\t      1245 ns/op\t     192 B/op\t       1 allocs/op\nBenchmarkSlogGroupsZap        \t 4800220\t      1256 ns/op\t     192 B/op\t       1 allocs/op\n\nBenchmarkSlogSimpleZerolog    \t 7713812\t       783.7 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkSlogGroupsZerolog    \t 5506782\t      1089 ns/op\t     288 B/op\t       1 allocs/op\n\nBenchmarkSlogSimplePhuslog    \t 8858504\t       683.0 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkSlogGroupsPhuslog    \t 8615334\t       694.0 ns/op\t       0 B/op\t       0 allocs/op\n\nBenchmarkSlogSimplePhuslogStd \t 8889276\t       666.7 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkSlogGroupsPhuslogStd \t 8849634\t       683.3 ns/op\t       0 B/op\t       0 allocs/op\n\nPASS\nok  \tbench\t84.415s\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003eAs a drop-in replacement for slog.JSONHandler, it provides 50% to 100% speedup and full compatibility.\u003c/summary\u003e\n\n```go\n// go test -v -args -useWarnings \u0026\u0026 go test -v -run=none -bench=. -args -useWarnings\n// a special thanks to @madkins23 for the help, with reference to https://github.com/phuslu/log/pull/70\npackage bench\n\nimport (\n\t\"io\"\n\t\"log/slog\"\n\t\"testing\"\n\n\tbenchtests \"github.com/madkins23/go-slog/bench/tests\"\n\t\"github.com/madkins23/go-slog/infra\"\n\t\"github.com/madkins23/go-slog/infra/warning\"\n\tverifytests \"github.com/madkins23/go-slog/verify/tests\"\n\t\"github.com/phuslu/log\"\n\t\"github.com/stretchr/testify/suite\"\n)\n\nfunc BenchmarkSlogJSON(b *testing.B) {\n\tslogNewJSONHandler := func(w io.Writer, options *slog.HandlerOptions) slog.Handler {\n\t\treturn slog.NewJSONHandler(w, options)\n\t}\n\tcreator := infra.NewCreator(\"slog/JSONHandler\", slogNewJSONHandler, nil,\n\t\t`^slog/JSONHandler^ is the JSON handler provided with the ^slog^ library.\n\t\tIt is fast and as a part of the Go distribution it is used\n\t\talong with published documentation as a model for ^slog.Handler^ behavior.`,\n\t\tmap[string]string{\n\t\t\t\"slog/JSONHandler\": \"https://pkg.go.dev/log/slog#JSONHandler\",\n\t\t})\n\tslogSuite := benchtests.NewSlogBenchmarkSuite(creator)\n\tbenchtests.Run(b, slogSuite)\n}\n\nfunc BenchmarkPhusluSlog(b *testing.B) {\n\tcreator := infra.NewCreator(\"phuslu/slog\", log.SlogNewJSONHandler, nil,\n\t\t`^phuslu/slog^ is a wrapper around the pre-existing ^phuslu/log^ logging library.`,\n\t\tmap[string]string{\n\t\t\t\"phuslu/log\": \"https://github.com/phuslu/log\",\n\t\t})\n\tslogSuite := benchtests.NewSlogBenchmarkSuite(creator)\n\tbenchtests.Run(b, slogSuite)\n}\n\nfunc TestVerifyPhusluSlog(t *testing.T) {\n\tcreator := infra.NewCreator(\"phuslu/slog\", log.SlogNewJSONHandler, nil,\n\t\t`^phuslu/slog^ is a wrapper around the pre-existing ^phuslu/log^ logging library.`,\n\t\tmap[string]string{\n\t\t\t\"phuslu/log\": \"https://github.com/phuslu/log\",\n\t\t})\n\tslogSuite := verifytests.NewSlogTestSuite(creator)\n\tslogSuite.WarnOnly(warning.Duplicates)\n\tsuite.Run(t, slogSuite)\n}\n\nfunc TestMain(m *testing.M) {\n\twarning.WithWarnings(m)\n}\n```\n\n\u003c/details\u003e\n\nA Performance result as below, for daily go-slog results see [github actions][go-slog]\n```\ngoos: linux\ngoarch: amd64\ncpu: AMD EPYC 7763 64-Core Processor                \n\nBenchmarkSlogJSON/BenchmarkAttributes-4         \t  870120\t      1441 ns/op\t 290.15 MB/s\t     472 B/op\t       6 allocs/op\nBenchmarkSlogJSON/BenchmarkBigGroup-4           \t   10000\t    102796 ns/op\t 225.45 MB/s\t  112990 B/op\t      14 allocs/op\nBenchmarkSlogJSON/BenchmarkDisabled-4           \t309658138\t         3.876 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkSlogJSON/BenchmarkKeyValues-4          \t  793542\t      1509 ns/op\t 277.01 MB/s\t     472 B/op\t       6 allocs/op\nBenchmarkSlogJSON/BenchmarkLogging-4            \t   42348\t     27256 ns/op\t 322.61 MB/s\t       0 B/op\t       0 allocs/op\nBenchmarkSlogJSON/BenchmarkSimple-4             \t 4208820\t       286.2 ns/op\t 289.98 MB/s\t       0 B/op\t       0 allocs/op\nBenchmarkSlogJSON/BenchmarkSimpleSource-4       \t 1388956\t       867.6 ns/op\t 359.61 MB/s\t     568 B/op\t       6 allocs/op\nBenchmarkSlogJSON/BenchmarkWithAttrsAttributes-4         \t  789448\t      1468 ns/op\t 534.76 MB/s\t     472 B/op\t       6 allocs/op\nBenchmarkSlogJSON/BenchmarkWithAttrsKeyValues-4          \t  746674\t      1603 ns/op\t 489.56 MB/s\t     472 B/op\t       6 allocs/op\nBenchmarkSlogJSON/BenchmarkWithAttrsSimple-4             \t 3369094\t       315.5 ns/op\t1426.53 MB/s\t       0 B/op\t       0 allocs/op\nBenchmarkSlogJSON/BenchmarkWithGroupAttributes-4         \t  653076\t      1536 ns/op\t 281.32 MB/s\t     472 B/op\t       6 allocs/op\nBenchmarkSlogJSON/BenchmarkWithGroupKeyValues-4          \t  806590\t      1529 ns/op\t 282.48 MB/s\t     472 B/op\t       6 allocs/op\n\nBenchmarkPhusluSlog/BenchmarkAttributes-4                \t 1358455\t       901.1 ns/op\t 480.53 MB/s\t     240 B/op\t       1 allocs/op\nBenchmarkPhusluSlog/BenchmarkBigGroup-4                  \t   50872\t     23419 ns/op\t 989.60 MB/s\t      48 B/op\t       1 allocs/op\nBenchmarkPhusluSlog/BenchmarkDisabled-4                  \t406019344\t         2.947 ns/op\t       0 B/op\t       0 allocs/op\nBenchmarkPhusluSlog/BenchmarkKeyValues-4                 \t 1292756\t       960.2 ns/op\t 450.93 MB/s\t     240 B/op\t       1 allocs/op\nBenchmarkPhusluSlog/BenchmarkLogging-4                   \t   84048\t     14283 ns/op\t 616.25 MB/s\t       0 B/op\t       0 allocs/op\nBenchmarkPhusluSlog/BenchmarkSimple-4                    \t 7523289\t       159.0 ns/op\t 521.87 MB/s\t       0 B/op\t       0 allocs/op\nBenchmarkPhusluSlog/BenchmarkSimpleSource-4              \t 5962424\t       201.8 ns/op\t1546.16 MB/s\t       0 B/op\t       0 allocs/op\nBenchmarkPhusluSlog/BenchmarkWithAttrsAttributes-4       \t 1300897\t       910.9 ns/op\t 894.68 MB/s\t     240 B/op\t       1 allocs/op\nBenchmarkPhusluSlog/BenchmarkWithAttrsKeyValues-4        \t 1269901\t       948.3 ns/op\t 859.42 MB/s\t     240 B/op\t       1 allocs/op\nBenchmarkPhusluSlog/BenchmarkWithAttrsSimple-4           \t 7303563\t       166.9 ns/op\t2786.27 MB/s\t       0 B/op\t       0 allocs/op\nBenchmarkPhusluSlog/BenchmarkWithGroupAttributes-4       \t 1328126\t       896.8 ns/op\t 498.45 MB/s\t     240 B/op\t       1 allocs/op\nBenchmarkPhusluSlog/BenchmarkWithGroupKeyValues-4        \t 1294560\t       951.7 ns/op\t 469.70 MB/s\t     240 B/op\t       1 allocs/op\n\nPASS\nok  \tbench\t37.548s\n```\n\nIn summary, phuslog offers a blend of low latency, minimal memory usage, and efficient logging across various scenarios, making it an excellent option for high-performance logging in Go applications.\n\n## Acknowledgment\nThis log is heavily inspired by [zerolog][zerolog], [glog][glog], [gjson][gjson] and [lumberjack][lumberjack].\n\n[godoc-img]: http://img.shields.io/badge/godoc-reference-5272B4.svg\n[godoc]: https://pkg.go.dev/github.com/phuslu/log\n[report-img]: https://goreportcard.com/badge/github.com/phuslu/log\n[report]: https://goreportcard.com/report/github.com/phuslu/log\n[build-img]: https://github.com/phuslu/log/workflows/build/badge.svg\n[build]: https://github.com/phuslu/log/actions\n[stability-img]: https://img.shields.io/badge/stability-stable-green.svg\n[high-performance]: https://github.com/phuslu/log?tab=readme-ov-file#high-performance\n[play-simple-img]: https://img.shields.io/badge/playground-NGV25aBKmYH-29BEB0?style=flat\u0026logo=go\n[play-simple]: https://go.dev/play/p/NGV25aBKmYH\n[play-customize-img]: https://img.shields.io/badge/playground-p9ZSSL4--IaK-29BEB0?style=flat\u0026logo=go\n[play-customize]: https://go.dev/play/p/p9ZSSL4-IaK\n[play-multiio-img]: https://img.shields.io/badge/playground-MH--J3Je--KEq-29BEB0?style=flat\u0026logo=go\n[play-multiio]: https://go.dev/play/p/MH-J3Je-KEq\n[play-combined-img]: https://img.shields.io/badge/playground-24d4eDIpDeR-29BEB0?style=flat\u0026logo=go\n[play-combined]: https://go.dev/play/p/24d4eDIpDeR\n[play-file-img]: https://img.shields.io/badge/playground-tjMc97E2EpW-29BEB0?style=flat\u0026logo=go\n[play-file]: https://go.dev/play/p/tjMc97E2EpW\n[play-pretty-img]: https://img.shields.io/badge/playground-SCcXG33esvI-29BEB0?style=flat\u0026logo=go\n[play-pretty]: https://go.dev/play/p/SCcXG33esvI\n[pretty-img]: https://user-images.githubusercontent.com/195836/101993218-cda82380-3cf3-11eb-9aa2-b8b1c832a72e.png\n[play-glog-img]: https://img.shields.io/badge/playground-oxSyv3ra5W5-29BEB0?style=flat\u0026logo=go\n[play-glog]: https://go.dev/play/p/oxSyv3ra5W5\n[play-logfmt-img]: https://img.shields.io/badge/playground-8ZsrWnsWBep-29BEB0?style=flat\u0026logo=go\n[play-logfmt]: https://go.dev/play/p/8ZsrWnsWBep\n[play-context-img]: https://img.shields.io/badge/playground-oAVAo302faf-29BEB0?style=flat\u0026logo=go\n[play-context]: https://go.dev/play/p/oAVAo302faf\n[play-context-add-img]: https://img.shields.io/badge/playground-LuCghJxMPHI-29BEB0?style=flat\u0026logo=go\n[play-context-add]: https://go.dev/play/p/LuCghJxMPHI\n[play-marshal-img]: https://img.shields.io/badge/playground-SoQdwQOaQR2-29BEB0?style=flat\u0026logo=go\n[play-marshal]: https://go.dev/play/p/SoQdwQOaQR2\n[play-stdlog]: https://go.dev/play/p/LU8vQruS7-S\n[play-stdlog-img]: https://img.shields.io/badge/playground-LU8vQruS7--S-29BEB0?style=flat\u0026logo=go\n[play-slog]: https://go.dev/play/p/JW3Ts6FcB40\n[play-slog-img]: https://img.shields.io/badge/playground-JW3Ts6FcB40-29BEB0?style=flat\u0026logo=go\n[play-phusluslog]: https://go.dev/play/p/KzGInrdzByD\n[play-phusluslog-img]: https://img.shields.io/badge/playground-KzGInrdzByD-29BEB0?style=flat\u0026logo=go\n[benchmark]: https://github.com/phuslu/log/actions?query=workflow%3Abenchmark\n[go-slog]: https://github.com/phuslu/log/actions?query=workflow%3Ago-slog\n[zerolog]: https://github.com/rs/zerolog\n[glog]: https://github.com/golang/glog\n[gjson]: https://github.com/tidwall/gjson\n[lumberjack]: https://github.com/natefinch/lumberjack\n","funding_links":[],"categories":["Logging","日志记录","Go","Relational Databases","Logging 日志库"],"sub_categories":["Search and Analytic Databases","检索及分析资料库","SQL 查询语句构建库","Advanced Console UIs"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphuslu%2Flog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fphuslu%2Flog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fphuslu%2Flog/lists"}