{"id":13413318,"url":"https://github.com/francoispqt/onelog","last_synced_at":"2025-04-05T06:06:38.906Z","repository":{"id":57483837,"uuid":"132346526","full_name":"francoispqt/onelog","owner":"francoispqt","description":"Dead simple, super fast, zero allocation logger for Golang","archived":false,"fork":false,"pushed_at":"2019-03-06T04:37:07.000Z","size":80,"stargazers_count":413,"open_issues_count":2,"forks_count":17,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-29T05:05:46.939Z","etag":null,"topics":["allocation","fast","golang","logger","logging","modular","optimization"],"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/francoispqt.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}},"created_at":"2018-05-06T14:32:10.000Z","updated_at":"2025-03-04T10:14:41.000Z","dependencies_parsed_at":"2022-09-02T06:21:35.017Z","dependency_job_id":null,"html_url":"https://github.com/francoispqt/onelog","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoispqt%2Fonelog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoispqt%2Fonelog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoispqt%2Fonelog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoispqt%2Fonelog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/francoispqt","download_url":"https://codeload.github.com/francoispqt/onelog/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247294536,"owners_count":20915340,"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":["allocation","fast","golang","logger","logging","modular","optimization"],"created_at":"2024-07-30T20:01:37.587Z","updated_at":"2025-04-05T06:06:38.872Z","avatar_url":"https://github.com/francoispqt.png","language":"Go","readme":"\n[![Build Status](https://travis-ci.org/francoispqt/onelog.svg?branch=master)](https://travis-ci.org/francoispqt/onelog)\n[![codecov](https://codecov.io/gh/francoispqt/onelog/branch/master/graph/badge.svg)](https://codecov.io/gh/francoispqt/onelog)\n[![Go Report Card](https://goreportcard.com/badge/github.com/francoispqt/onelog)](https://goreportcard.com/report/github.com/francoispqt/onelog)\n[![Go doc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square\n)](https://godoc.org/github.com/francoispqt/onelog)\n![MIT License](https://img.shields.io/badge/license-mit-blue.svg?style=flat-square)\n\n# Onelog\nOnelog is a dead simple but very efficient JSON logger. \nIt is one of the fastest JSON logger out there. Also, it is one of the logger with the lowest allocation.\n\nIt gives more control over log levels enabled by using bitwise operation for setting levels on a logger.\n\nIt is also modular as you can add a custom hook, define level text values, level and message keys.\n\nGo 1.9 is required as it uses a type alias over gojay.Encoder.\n\nIt is named onelog as a reference to zerolog and because it sounds like `One Love` song from Bob Marley :)\n\n## Get Started\n\n```bash \ngo get github.com/francoispqt/onelog\n```\n\nBasic usage:\n\n```go\nimport \"github.com/francoispqt/onelog\"\n\nfunc main() {\n    // create a new Logger\n    // first argument is an io.Writer\n    // second argument is the level, which is an integer\n    logger := onelog.New(\n        os.Stdout, \n        onelog.ALL, // shortcut for onelog.DEBUG|onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL,\n    )\n    logger.Info(\"hello world !\") // {\"level\":\"info\",\"message\":\"hello world\"}\n}\n```\n\n\n## Levels\n\nLevels are ints mapped to a string. The logger will check if level is enabled with an efficient bitwise \u0026(AND), if disabled, it returns right away which makes onelog the fastest when running disabled logging with 0 allocs and less than 1ns/op. [See benchmarks](#benchmarks)\n\nWhen creating a logger you must use the `|` operator with different levels to toggle bytes. \n\nExample if you want levels INFO and WARN:\n```go\nlogger := onelog.New(\n    os.Stdout, \n    onelog.INFO|onelog.WARN,\n)\n```\n\nThis allows you to have a logger with different levels, for example you can do: \n```go\nvar logger *onelog.Logger\n\nfunc init() {\n    // if we are in debug mode, enable DEBUG lvl\n    if os.Getenv(\"DEBUG\") != \"\" {\n        logger = onelog.New(\n            os.Stdout, \n            onelog.ALL, // shortcut for onelog.DEBUG|onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL\n        )\n        return\n    }\n    logger = onelog.New(\n        os.Stdout, \n        onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL,\n    )\n}\n```\n\nAvailable levels:\n- onelog.DEBUG\n- onelog.INFO\n- onelog.WARN\n- onelog.ERROR\n- onelog.FATAL\n\nYou can change their textual values by doing, do this only once at runtime as it is not thread safe: \n```go\nonelog.LevelText(onelog.INFO, \"INFO\")\n```\n\n## Hook\n\nYou can define a hook which will be run for every log message. \n\nExample:\n```go \nlogger := onelog.New(\n    os.Stdout, \n    onelog.ALL,\n)\nlogger.Hook(func(e onelog.Entry) {\n    e.String(\"time\", time.Now().Format(time.RFC3339))\n})\nlogger.Info(\"hello world !\") // {\"level\":\"info\",\"message\":\"hello world\",\"time\":\"2018-05-06T02:21:01+08:00\"}\n```\n\n## Context\n\nContext allows enforcing a grouping format where all logs fields key-values pairs from all logging methods (With, Info, Debug, InfoWith, InfoWithEntry, ...etc) except\nfor values from using `logger.Hook`, will be enclosed in giving context name provided as it's key. For example using a context key \"params\" as below\n\n\n```go\nlogger := onelog.NewContext(\n    os.Stdout, \n    onelog.INFO|onelog.WARN,\n    \"params\"\n)\n\nlogger.InfoWithFields(\"breaking news !\", func(e onelog.Entry) {\n    e.String(\"userID\", \"123455\")\n}) \n\n// {\"level\":\"info\",\"message\":\"breaking news !\", \"params\":{\"userID\":\"123456\"}}\n```\n\nThis principle also applies when inheriting from a previous created logger as below\n\n```go\nparentLogger := onelog.New(\n    os.Stdout, \n    onelog.INFO|onelog.WARN,\n)\n\n\nlogger := parentLogger.WithContext(\"params\")\nlogger.InfoWithFields(\"breaking news !\", func(e onelog.Entry) {\n    e.String(\"userID\", \"123455\")\n}) \n\n// {\"level\":\"info\",\"message\":\"breaking news !\", \"params\":{\"userID\":\"123456\"}}\n```\n\n\nYou can always reset the context by calling `WithContext(\"\")` to create a no-context logger from a \ncontext logger parent.\n\n\n## Logging\n\n### Without extra fields\nLogging without extra fields is easy as:\n```go \nlogger := onelog.New(\n    os.Stdout, \n    onelog.ALL,\n)\nlogger.Debug(\"i'm not sure what's going on\") // {\"level\":\"debug\",\"message\":\"i'm not sure what's going on\"}\nlogger.Info(\"breaking news !\") // {\"level\":\"info\",\"message\":\"breaking news !\"}\nlogger.Warn(\"beware !\") // {\"level\":\"warn\",\"message\":\"beware !\"}\nlogger.Error(\"my printer is on fire\") // {\"level\":\"error\",\"message\":\"my printer is on fire\"}\nlogger.Fatal(\"oh my...\") // {\"level\":\"fatal\",\"message\":\"oh my...\"}\n```\n\n### With extra fields \nLogging with extra fields is quite simple, specially if you have used gojay:\n```go \nlogger := onelog.New(\n    os.Stdout, \n    onelog.ALL,\n)\n\nlogger.DebugWithFields(\"i'm not sure what's going on\", func(e onelog.Entry) {\n    e.String(\"string\", \"foobar\")\n    e.Int(\"int\", 12345)\n    e.Int64(\"int64\", 12345)\n    e.Float(\"float64\", 0.15)\n    e.Bool(\"bool\", true)\n    e.Err(\"err\", errors.New(\"someError\"))\n    e.ObjectFunc(\"user\", func(e Entry) {\n        e.String(\"name\", \"somename\")\n    })\n}) \n// {\"level\":\"debug\",\"message\":\"i'm not sure what's going on\",\"string\":\"foobar\",\"int\":12345,\"int64\":12345,\"float64\":0.15,\"bool\":true,\"err\":\"someError\",\"user\":{\"name\":\"somename\"}}\n\nlogger.InfoWithFields(\"breaking news !\", func(e onelog.Entry) {\n    e.String(\"userID\", \"123455\")\n}) \n// {\"level\":\"info\",\"message\":\"breaking news !\",\"userID\":\"123456\"}\n\nlogger.WarnWithFields(\"beware !\", func(e onelog.Entry) {\n    e.String(\"userID\", \"123455\")\n}) \n// {\"level\":\"warn\",\"message\":\"beware !\",\"userID\":\"123456\"}\n\nlogger.ErrorWithFields(\"my printer is on fire\", func(e onelog.Entry) {\n    e.String(\"userID\", \"123455\")\n}) \n// {\"level\":\"error\",\"message\":\"my printer is on fire\",\"userID\":\"123456\"}\n\nlogger.FatalWithFields(\"oh my...\", func(e onelog.Entry) {\n    e.String(\"userID\", \"123455\")\n}) \n// {\"level\":\"fatal\",\"message\":\"oh my...\",\"userID\":\"123456\"}\n```\n\nAlternatively, you can use the chain syntax: \n```go\nlogger.InfoWith(\"foo bar\").\n    Int(\"testInt\", 1).\n    Int64(\"testInt64\", 2).\n    Float(\"testFloat\", 1.15234).\n    String(\"testString\", \"string\").\n    Bool(\"testBool\", true).\n    ObjectFunc(\"testObj\", func(e Entry) {\n        e.Int(\"testInt\", 100)\n    }).\n    Object(\"testObj2\", testObj). // implementation of gojay.MarshalerJSONObject\n    Array(\"testArr\", testArr). // implementation of gojay.MarshalerJSONArray\n    Err(\"testErr\", errors.New(\"my printer is on fire !\")).\n    Write() // don't forget to call this method! \n```\n\n## Accumulate context\nYou can create get a logger with some accumulated context that will be included on all logs created by this logger.\n\nTo do that, you must call the `With` method on a logger.\nInternally it creates a copy of the current logger and returns it. \n\nExample: \n```go \nlogger := onelog.New(\n    os.Stdout, \n    onelog.ALL,\n).With(func(e onelog.Entry) {\n    e.String(\"userID\", \"123456\")\n})\n\nlogger.Info(\"user logged in\") // {\"level\":\"info\",\"message\":\"user logged in\",\"userID\":\"123456\"}\n\nlogger.Debug(\"wtf?\") // {\"level\":\"debug\",\"message\":\"wtf?\",\"userID\":\"123456\"}\n\nlogger.ErrorWithFields(\"Oops\", func(e onelog.Entry) {\n    e.String(\"error_code\", \"ROFL\")\n}) // {\"level\":\"error\",\"message\":\"oops\",\"userID\":\"123456\",\"error_code\":\"ROFL\"}\n```\n\n## Change levels txt values, message and/or level keys\nYou can change globally the levels values by calling the function: \n```go\nonelog.LevelText(onelog.INFO, \"INFO\")\n```\n\nYou can change the key of the message by calling the function: \n```go \nonelog.MsgKey(\"msg\")\n```\n\nYou can change the key of the level by calling the function: \n```go \nonelog.LevelKey(\"lvl\")\n```\n\nBeware, these changes are global (affects all instances of the logger). Also, these function should be called only once at runtime to avoid any data race issue.\n\n# Benchmarks\n\nFor thorough benchmarks please see the results in the bench suite created by the author of zerolog here: https://github.com/rs/logbench \n\nThe benchmarks data presented below is the one from Uber's benchmark suite where we added onelog. \n\nBenchmarks are here: https://github.com/francoispqt/zap/tree/onelog-bench/benchmarks\n\n## Disabled Logging\n|             | ns/op | bytes/op     | allocs/op |\n|-------------|-------|--------------|-----------|\n| Zap         | 8.73  | 0            | 0         |\n| zerolog     | 2.45  | 0            | 0         |\n| logrus      | 12.1  | 16           | 1         |\n| onelog      | 0.74  | 0            | 0         |\n\n## Disabled with fields\n|             | ns/op | bytes/op     | allocs/op |\n|-------------|-------|--------------|-----------|\n| Zap         | 208   | 768          | 5         |\n| zerolog     | 68.7  | 128          | 4         |\n| logrus      | 721   | 1493         | 12        |\n| onelog      | 1.31  | 0            | 0         |\n| onelog-chain| 68.2  | 0            | 0         |\n\n## Logging basic message\n|             | ns/op | bytes/op     | allocs/op |\n|-------------|-------|--------------|-----------|\n| Zap         | 205   | 0            | 0         |\n| zerolog     | 135   | 0            | 0         |\n| logrus      | 1256  | 1554         | 24        |\n| onelog      | 84.8  | 0            | 0         |\n\n## Logging basic message and accumulated context\n|             | ns/op | bytes/op     | allocs/op |\n|-------------|-------|--------------|-----------|\n| Zap         | 276   | 0            | 0         |\n| zerolog     | 141   | 0            | 0         |\n| logrus      | 1256  | 1554         | 24        |\n| onelog      | 82.4  | 0            | 0         |\n\n## Logging message with extra fields\n|             | ns/op | bytes/op     | allocs/op |\n|-------------|-------|--------------|-----------|\n| Zap         | 1764  | 770          | 5         |\n| zerolog     | 1210  | 128          | 4         |\n| logrus      | 13211 | 13584        | 129       |\n| onelog      | 971   | 128          | 4         |\n| onelog-chain| 1030  | 128          | 4         |\n","funding_links":[],"categories":["Logging 日志库","Go","Logging","日志记录","Relational Databases","日志"],"sub_categories":["SQL 查询语句构建库","Search and Analytic Databases","Advanced Console UIs","检索及分析资料库","交流"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrancoispqt%2Fonelog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrancoispqt%2Fonelog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrancoispqt%2Fonelog/lists"}