diff --git a/.gitignore b/.gitignore index 3b735ec..c7a4bfd 100644 --- a/.gitignore +++ b/.gitignore @@ -15,7 +15,11 @@ *.out # Dependency directories (remove the comment below to include it) -# vendor/ +vendor/ +bin/ +_output/ +tools/ +tmp/ # Go workspace file go.work diff --git a/LICENSE b/LICENSE index bdd544d..6f148c5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Kubernetes Cub community +Copyright (c) 2023 KubeCub. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..341371c --- /dev/null +++ b/Makefile @@ -0,0 +1,140 @@ +# Copyright 2023 KubeCub. All rights reserved. +# Use of this source code is governed by a MIT style +# license that can be found in the LICENSE file. + +# ============================================================================== +# define the default goal +# +ROOT_PACKAGE=github.com/kubecub/log + +SHELL := /bin/bash +DIRS=$(shell ls) +GO=go + +.DEFAULT_GOAL := help + +# include the common makefile +COMMON_SELF_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +# ROOT_DIR: root directory of the code base +ifeq ($(origin ROOT_DIR),undefined) +ROOT_DIR := $(abspath $(shell cd $(COMMON_SELF_DIR)/. && pwd -P)) +endif +# OUTPUT_DIR: The directory where the build output is stored. +ifeq ($(origin OUTPUT_DIR),undefined) +OUTPUT_DIR := $(ROOT_DIR)/bin +$(shell mkdir -p $(OUTPUT_DIR)) +endif + +ifeq ($(origin VERSION), undefined) +VERSION := $(shell git describe --abbrev=0 --dirty --always --tags | sed 's/-/./g') +endif + +# Check if the tree is dirty. default to dirty(maybe u should commit?) +GIT_TREE_STATE:="dirty" +ifeq (, $(shell git status --porcelain 2>/dev/null)) + GIT_TREE_STATE="clean" +endif +GIT_COMMIT:=$(shell git rev-parse HEAD) + +IMG ?= ghcr.io/kubecub/log:latest + +BUILDFILE = "./main.go" +BUILDAPP = "$(OUTPUT_DIR)/" + +# Define the directory you want to copyright +CODE_DIRS := $(ROOT_DIR) #$(ROOT_DIR)/pkg $(ROOT_DIR)/core $(ROOT_DIR)/integrationtest $(ROOT_DIR)/lib $(ROOT_DIR)/mock $(ROOT_DIR)/db $(ROOT_DIR)/openapi +FINDS := find $(CODE_DIRS) + +ifndef V +MAKEFLAGS += --no-print-directory +endif + +# Linux command settings +FIND := find . ! -path './image/*' ! -path './vendor/*' ! -path './bin/*' +XARGS := xargs -r +LICENSE_TEMPLATE ?= $(ROOT_DIR)/scripts/boilerplate.txt + +# ============================================================================== +# Targets + +## all: Build all the necessary targets. +.PHONY: all +all: tidy add-copyright lint cover build + +## build: Build binaries by default. +.PHONY: build +build: + @echo "$(shell go version)" + @echo "===========> Building binary $(BUILDAPP) *[Git Info]: $(VERSION)-$(GIT_COMMIT)" + @export CGO_ENABLED=0 && go build -o $(BUILDAPP) -ldflags '-s -w' $(BUILDFILE) + +## build.%: Builds a binary of the specified directory. +.PHONY: build.% +build.%: + @echo "$(shell go version)" + @echo "===========> Building binary $(BUILDAPP) *[Git Info]: $(VERSION)-$(GIT_COMMIT)" + @export CGO_ENABLED=0 && GOOS=linux go build -o $(BUILDAPP)/$*/ -ldflags '-s -w' $*/example/$(BUILDFILE) + @export CGO_ENABLED=0 && GOOS=linux go build -o $(BUILDAPP)/$*/ -ldflags '-s -w' $*/example/$(BUILDFILE) + + +## tidy: tidy go.mod +.PHONY: tidy +tidy: + @$(GO) mod tidy + +## fmt: Run go fmt against code. +.PHONY: fmt +fmt: + @$(GO) fmt ./... + +## vet: Run go vet against code. +.PHONY: vet +vet: + @$(GO) vet ./... + +## lint: Run go lint against code. +.PHONY: lint +lint: + @golangci-lint run -v ./... + +## style: Code style -> fmt,vet,lint +.PHONY: style +style: fmt vet lint + +## test: Run unit test +.PHONY: test +test: + @echo "===========> Run unit test" + @$(GO) test ./... + +## cover: Run unit test with coverage. +.PHONY: cover +cover: test + @$(GO) test -cover + +## copyright.verify: Validate boilerplate headers for assign files. +.PHONY: copyright-verify +copyright-verify: + @echo "===========> Validate boilerplate headers for assign files starting in the $(ROOT_DIR) directory" + @addlicense -v -check -ignore **/test/** -f $(LICENSE_TEMPLATE) $(CODE_DIRS) + @echo "===========> End of boilerplate headers check..." + +## copyright-add: Add the boilerplate headers for all files. +.PHONY: copyright-add +copyright-add: + @echo "===========> Adding $(LICENSE_TEMPLATE) the boilerplate headers for all files" + @addlicense -y $(shell date +"%Y") -v -c "KubeCub." -f $(LICENSE_TEMPLATE) $(CODE_DIRS) + @echo "===========> End the copyright is added..." + +## go.clean: Clean all builds. +.PHONY: clean +clean: + @echo "===========> Cleaning all builds OUTPUT_DIR($(OUTPUT_DIR))" + @-rm -vrf $(OUTPUT_DIR) + @echo "===========> End clean..." + +## help: Show this help info. +.PHONY: help +help: Makefile + @printf "\n\033[1mUsage: make ...\033[0m\n\n\\033[1mTargets:\\033[0m\n\n" + @sed -n 's/^##//p' $< | awk -F':' '{printf "\033[36m%-28s\033[0m %s\n", $$1, $$2}' | sed -e 's/^/ /' \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..96575e6 --- /dev/null +++ b/README.md @@ -0,0 +1,41 @@ +# Project myproject + + + +## Features + + + +## Getting Started + +### Prerequisites + + + +### Building + + + +### Running + + + +## Using + + + +## Contributing + + + +## Community(optional) + + + +## Authors + + + +## License + + diff --git a/cuslog/entry.go b/cuslog/entry.go new file mode 100644 index 0000000..5c878fd --- /dev/null +++ b/cuslog/entry.go @@ -0,0 +1,68 @@ +// Copyright © 2023 KubeCub. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. + +package cuslog + +import ( + "bytes" + "runtime" + "strings" + "time" +) + +type Entry struct { + logger *logger + Buffer *bytes.Buffer + Map map[string]interface{} + Level Level + Time time.Time + File string + Line int + Func string + Format string + Args []interface{} +} + +func entry(logger *logger) *Entry { + return &Entry{logger: logger, Buffer: new(bytes.Buffer), Map: make(map[string]interface{}, 5)} +} + +func (e *Entry) write(level Level, format string, args ...interface{}) { + if e.logger.opt.level > level { + return + } + e.Time = time.Now() + e.Level = level + e.Format = format + e.Args = args + if !e.logger.opt.disableCaller { + if pc, file, line, ok := runtime.Caller(2); !ok { + e.File = "???" + e.Func = "???" + } else { + e.File, e.Line, e.Func = file, line, runtime.FuncForPC(pc).Name() + e.Func = e.Func[strings.LastIndex(e.Func, "/")+1:] + } + } + e.format() + e.writer() + e.release() +} + +func (e *Entry) format() { + _ = e.logger.opt.formatter.Format(e) +} + +func (e *Entry) writer() { + e.logger.mu.Lock() + _, _ = e.logger.opt.output.Write(e.Buffer.Bytes()) + e.logger.mu.Unlock() +} + +func (e *Entry) release() { + e.Args, e.Line, e.File, e.Format, e.Func = nil, 0, "", "", "" + e.Buffer.Reset() + e.logger.entryPool.Put(e) +} \ No newline at end of file diff --git a/cuslog/example/example.go b/cuslog/example/example.go new file mode 100644 index 0000000..9382242 --- /dev/null +++ b/cuslog/example/example.go @@ -0,0 +1,35 @@ +// Copyright © 2023 KubeCub. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. + +package main + +import ( + "log" + "os" + + "github.com/kubecub/log/cuslog" +) + +func main() { + cuslog.Info("std log") + cuslog.SetOptions(cuslog.WithLevel(cuslog.DebugLevel)) + cuslog.Debug("change std log to debug level") + cuslog.SetOptions(cuslog.WithFormatter(&cuslog.JsonFormatter{IgnoreBasicFields: false})) + cuslog.Debug("log in json format") + cuslog.Info("another log in json format") + + // 输出到文件 + fd, err := os.OpenFile("test.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Fatalln("create file test.log failed") + } + defer fd.Close() + + l := cuslog.New(cuslog.WithLevel(cuslog.InfoLevel), + cuslog.WithOutput(fd), + cuslog.WithFormatter(&cuslog.JsonFormatter{IgnoreBasicFields: false}), + ) + l.Info("custom log with json formatter") +} diff --git a/cuslog/example/main.go b/cuslog/example/main.go new file mode 100644 index 0000000..b7b4f9a --- /dev/null +++ b/cuslog/example/main.go @@ -0,0 +1,32 @@ +// Copyright © 2023 KubeCub. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. + +package main + +import ( + "log" + "os" + + "github.com/kubecub/log/cuslog" +) + +func main() { + cuslog.Info("std log") + cuslog.SetOptions(cuslog.WithLevel(cuslog.DebugLevel)) + cuslog.Debug("change std log to debug level") + + // 输出到文件 + fd, err := os.OpenFile("test.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Fatalln("create file test.log failed") + } + defer fd.Close() + + l := cuslog.New(cuslog.WithLevel(cuslog.InfoLevel), + cuslog.WithOutput(fd), + cuslog.WithFormatter(&cuslog.JsonFormatter{IgnoreBasicFields: false}), + ) + l.Info("custom log with json formatter") +} diff --git a/cuslog/example/test.log b/cuslog/example/test.log new file mode 100644 index 0000000..cd1de45 --- /dev/null +++ b/cuslog/example/test.log @@ -0,0 +1,11 @@ +{"level":"INFO","time":"2020-11-30T21:49:16+08:00","message":"custom log with json formatter"} +{"level":"INFO","time":"2020-11-30T21:49:27+08:00","file":"/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/log/cuslog/example/main.go:27","func":"main.main","message":"custom log with json formatter"} +{"level":"INFO","time":"2020-11-30T21:49:34+08:00","file":"/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/log/cuslog/example/main.go:26","func":"main.main","message":"custom log with json formatter"} +{"level":"INFO","time":"2020-11-30T21:49:35+08:00","file":"/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/log/cuslog/example/main.go:26","func":"main.main","message":"custom log with json formatter"} +{"time":"2020-12-01T06:57:52+08:00","file":"/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/log/cuslog/example/main.go:26","func":"main.main","message":"custom log with json formatter","level":"INFO"} +{"file":"/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/log/cuslog/example/main2.go:28","func":"main.main","message":"custom log with json formatter","level":"INFO","time":"2020-12-02T01:13:28+08:00"} +{"level":"INFO","time":"2020-12-02T01:15:26+08:00","file":"/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/log/cuslog/example/example.go:28","func":"main.main","message":"custom log with json formatter"} +{"level":"INFO","time":"2020-12-02T01:16:18+08:00","file":"/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/log/cuslog/example/example.go:29","func":"main.main","message":"custom log with json formatter"} +{"time":"2020-12-04T09:10:54+08:00","file":"/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/log/cuslog/example/main.go:26","func":"main.main","message":"custom log with json formatter","level":"INFO"} +{"func":"main.main","message":"custom log with json formatter","level":"INFO","time":"2020-12-04T09:10:56+08:00","file":"/home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/log/cuslog/example/example.go:29"}{"func":"main.main","message":"custom log with json formatter","level":"INFO","time":"2023-04-22T21:25:22+08:00","file":"/root/workspaces/log/cuslog/example/example.go:29"} +{"level":"INFO","time":"2023-04-22T21:25:24+08:00","file":"/root/workspaces/log/cuslog/example/main.go:26","func":"main.main","message":"custom log with json formatter"} diff --git a/cuslog/formatter.go b/cuslog/formatter.go new file mode 100644 index 0000000..47b5f49 --- /dev/null +++ b/cuslog/formatter.go @@ -0,0 +1,12 @@ +// Copyright © 2023 KubeCub. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. + +package cuslog + +type Formatter interface { + // Maybe in async goroutine + // Please write the result to buffer + Format(entry *Entry) error +} \ No newline at end of file diff --git a/cuslog/formatter_json.go b/cuslog/formatter_json.go new file mode 100644 index 0000000..208b5ad --- /dev/null +++ b/cuslog/formatter_json.go @@ -0,0 +1,51 @@ +// Copyright © 2023 KubeCub. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. + +package cuslog + +import ( + "fmt" + "strconv" + "time" + + jsoniter "github.com/json-iterator/go" +) + +type JsonFormatter struct { + IgnoreBasicFields bool +} + +func (f *JsonFormatter) Format(e *Entry) error { + if !f.IgnoreBasicFields { + e.Map["level"] = LevelNameMapping[e.Level] + e.Map["time"] = e.Time.Format(time.RFC3339) + if e.File != "" { + e.Map["file"] = e.File + ":" + strconv.Itoa(e.Line) + e.Map["func"] = e.Func + } + + switch e.Format { + case FmtEmptySeparate: + e.Map["message"] = fmt.Sprint(e.Args...) + default: + e.Map["message"] = fmt.Sprintf(e.Format, e.Args...) + } + + return jsoniter.NewEncoder(e.Buffer).Encode(e.Map) + } + + switch e.Format { + case FmtEmptySeparate: + for _, arg := range e.Args { + if err := jsoniter.NewEncoder(e.Buffer).Encode(arg); err != nil { + return err + } + } + default: + e.Buffer.WriteString(fmt.Sprintf(e.Format, e.Args...)) + } + + return nil +} \ No newline at end of file diff --git a/cuslog/formatter_text.go b/cuslog/formatter_text.go new file mode 100644 index 0000000..62ec368 --- /dev/null +++ b/cuslog/formatter_text.go @@ -0,0 +1,42 @@ +// Copyright © 2023 KubeCub. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. + +package cuslog + +import ( + "fmt" + "time" +) + +type TextFormatter struct { + IgnoreBasicFields bool +} + +func (f *TextFormatter) Format(e *Entry) error { + if !f.IgnoreBasicFields { + e.Buffer.WriteString(fmt.Sprintf("%s %s", e.Time.Format(time.RFC3339), LevelNameMapping[e.Level])) // allocs + if e.File != "" { + short := e.File + for i := len(e.File) - 1; i > 0; i-- { + if e.File[i] == '/' { + short = e.File[i+1:] + break + } + } + e.Buffer.WriteString(fmt.Sprintf(" %s:%d", short, e.Line)) + } + e.Buffer.WriteString(" ") + } + + switch e.Format { + case FmtEmptySeparate: + e.Buffer.WriteString(fmt.Sprint(e.Args...)) + default: + e.Buffer.WriteString(fmt.Sprintf(e.Format, e.Args...)) + } + e.Buffer.WriteString("\n") + + return nil +} \ No newline at end of file diff --git a/cuslog/go.mod b/cuslog/go.mod new file mode 100644 index 0000000..438d252 --- /dev/null +++ b/cuslog/go.mod @@ -0,0 +1,10 @@ +module github.com/kubecub/log/cuslog + +go 1.20 + +require github.com/json-iterator/go v1.1.12 + +require ( + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect +) diff --git a/cuslog/go.sum b/cuslog/go.sum new file mode 100644 index 0000000..ea794b2 --- /dev/null +++ b/cuslog/go.sum @@ -0,0 +1,12 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/cuslog/logger.go b/cuslog/logger.go new file mode 100644 index 0000000..72a238d --- /dev/null +++ b/cuslog/logger.go @@ -0,0 +1,170 @@ +// Copyright © 2023 KubeCub. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. + +// To print logs, you need to create a Logger according to the log configuration. +// Then, you need to call the log printing method of the Logger to output logs of different levels. + +package cuslog + +import ( + "fmt" + "io" + "os" + "sync" + "unsafe" +) + +var std = New() + +type logger struct { + opt *options + mu sync.Mutex + entryPool *sync.Pool +} + +func New(opts ...Option) *logger { + logger := &logger{opt: initOptions(opts...)} + logger.entryPool = &sync.Pool{New: func() interface{} { return entry(logger) }} + return logger +} + +func StdLogger() *logger { + return std +} + +func SetOptions(opts ...Option) { + std.SetOptions(opts...) +} + +func (l *logger) SetOptions(opts ...Option) { + l.mu.Lock() + defer l.mu.Unlock() + + for _, opt := range opts { + opt(l.opt) + } +} + +func Writer() io.Writer { + return std +} + +func (l *logger) Writer() io.Writer { + return l +} + +func (l *logger) Write(data []byte) (int, error) { + l.entry().write(l.opt.stdLevel, FmtEmptySeparate, *(*string)(unsafe.Pointer(&data))) + return 0, nil +} + +func (l *logger) entry() *Entry { + return l.entryPool.Get().(*Entry) +} + +func (l *logger) Debug(args ...interface{}) { + l.entry().write(DebugLevel, FmtEmptySeparate, args...) +} + +func (l *logger) Info(args ...interface{}) { + l.entry().write(InfoLevel, FmtEmptySeparate, args...) +} + +func (l *logger) Warn(args ...interface{}) { + l.entry().write(WarnLevel, FmtEmptySeparate, args...) +} + +func (l *logger) Error(args ...interface{}) { + l.entry().write(ErrorLevel, FmtEmptySeparate, args...) +} + +func (l *logger) Panic(args ...interface{}) { + l.entry().write(PanicLevel, FmtEmptySeparate, args...) + panic(fmt.Sprint(args...)) +} + +func (l *logger) Fatal(args ...interface{}) { + l.entry().write(FatalLevel, FmtEmptySeparate, args...) + os.Exit(1) +} + +func (l *logger) Debugf(format string, args ...interface{}) { + l.entry().write(DebugLevel, format, args...) +} + +func (l *logger) Infof(format string, args ...interface{}) { + l.entry().write(InfoLevel, format, args...) +} + +func (l *logger) Warnf(format string, args ...interface{}) { + l.entry().write(WarnLevel, format, args...) +} + +func (l *logger) Errorf(format string, args ...interface{}) { + l.entry().write(ErrorLevel, format, args...) +} + +func (l *logger) Panicf(format string, args ...interface{}) { + l.entry().write(PanicLevel, format, args...) + panic(fmt.Sprintf(format, args...)) +} + +func (l *logger) Fatalf(format string, args ...interface{}) { + l.entry().write(FatalLevel, format, args...) + os.Exit(1) +} + +// std logger +func Debug(args ...interface{}) { + std.entry().write(DebugLevel, FmtEmptySeparate, args...) +} + +func Info(args ...interface{}) { + std.entry().write(InfoLevel, FmtEmptySeparate, args...) +} + +func Warn(args ...interface{}) { + std.entry().write(WarnLevel, FmtEmptySeparate, args...) +} + +func Error(args ...interface{}) { + std.entry().write(ErrorLevel, FmtEmptySeparate, args...) +} + +func Panic(args ...interface{}) { + std.entry().write(PanicLevel, FmtEmptySeparate, args...) + panic(fmt.Sprint(args...)) +} + +func Fatal(args ...interface{}) { + std.entry().write(FatalLevel, FmtEmptySeparate, args...) + os.Exit(1) +} + +func Debugf(format string, args ...interface{}) { + std.entry().write(DebugLevel, format, args...) +} + +func Infof(format string, args ...interface{}) { + std.entry().write(InfoLevel, format, args...) +} + +func Warnf(format string, args ...interface{}) { + std.entry().write(WarnLevel, format, args...) +} + +func Errorf(format string, args ...interface{}) { + std.entry().write(ErrorLevel, format, args...) +} + +func Panicf(format string, args ...interface{}) { + std.entry().write(PanicLevel, format, args...) + panic(fmt.Sprintf(format, args...)) +} + +func Fatalf(format string, args ...interface{}) { + std.entry().write(FatalLevel, format, args...) + os.Exit(1) +} diff --git a/cuslog/options.go b/cuslog/options.go new file mode 100644 index 0000000..a1dd767 --- /dev/null +++ b/cuslog/options.go @@ -0,0 +1,156 @@ +// Copyright © 2023 KubeCub. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. + +package cuslog + +import ( + "bytes" + "errors" + "fmt" + "io" + "os" +) + +const ( + FmtEmptySeparate = "" +) + +// log level +type Level uint8 + +/* + During log output, by comparing the size of the switch level and the output level, + Therefore, the log Level must be defined as a numeric type for easy comparison. + Almost all log packages define log levels in terms of the constant counter iota. + Also, because you want to output a readable log level in the log output (for example, INFO instead of 1), + So you need to map Level to LevelName. LevelNameMapping, LevelNameMapping, is used for formatting. +*/ + +// const log level +const ( + // DebugLevel logs are typically voluminous, and are usually disabled in + // production. + DebugLevel Level = iota + // InfoLevel is the default logging priority. + InfoLevel + // WarnLevel logs are more important than Info, but don't need individual + // human review. + WarnLevel + // ErrorLevel logs are high-priority. If an application is running smoothly, + // it shouldn't generate any error-level logs. + ErrorLevel + // PanicLevel logs a message, then panics. + PanicLevel + // FatalLevel logs a message, then calls os.Exit(1). + FatalLevel +) + +// log level string name mapping +var LevelNameMapping = map[Level]string{ + DebugLevel: "DEBUG", + InfoLevel: "INFO", + WarnLevel: "WARN", + ErrorLevel: "ERROR", + PanicLevel: "PANIC", + FatalLevel: "FATAL", +} + +// errUnmarshalNilLevel is returned by UnmarshalText when unmarshaling a nil *Level. +var errUnmarshalNilLevel = errors.New("can't unmarshal a nil *Level") + +// unmarshalText unmarshals text to a level. +func (l *Level) unmarshalText(text []byte) bool { + switch string(text) { + case "debug", "DEBUG": + *l = DebugLevel + case "info", "INFO", "": // make the zero value useful + *l = InfoLevel + case "warn", "WARN": + *l = WarnLevel + case "error", "ERROR": + *l = ErrorLevel + case "panic", "PANIC": + *l = PanicLevel + case "fatal", "FATAL": + *l = FatalLevel + default: + return false + } + return true +} + +// UnmarshalText unmarshals text to a level. +func (l *Level) UnmarshalText(text []byte) error { + if l == nil { + return errUnmarshalNilLevel + } + if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) { + return fmt.Errorf("unrecognized level: %q", text) + } + return nil +} + +// log options +type options struct { + output io.Writer + level Level + stdLevel Level + formatter Formatter + disableCaller bool +} + +type Option func(*options) + +func initOptions(opts ...Option) (o *options) { + o = &options{} + for _, opt := range opts { + opt(o) + } + + if o.output == nil { + o.output = os.Stderr + } + + if o.formatter == nil { + o.formatter = &TextFormatter{} + } + + return +} + +// Set output location. +func WithOutput(output io.Writer) Option { + return func(o *options) { + o.output = output + } +} + +// Set log output level. +func WithLevel(level Level) Option { + return func(o *options) { + o.level = level + } +} + +// Set log output level. +func WithStdLevel(level Level) Option { + return func(o *options) { + o.stdLevel = level + } +} + +// Set log formatter. +func WithFormatter(formatter Formatter) Option { + return func(o *options) { + o.formatter = formatter + } +} + +// Sets whether to print the file name and line number. +func WithDisableCaller(caller bool) Option { + return func(o *options) { + o.disableCaller = caller + } +} diff --git a/example/go.mod b/example/go.mod new file mode 100644 index 0000000..72d28fc --- /dev/null +++ b/example/go.mod @@ -0,0 +1,3 @@ +module github.com/kubecub/log/example + +go 1.20 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b0fcc94 --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module github.com/kubecub/log + +go 1.20 + +require go.uber.org/zap v1.24.0 + +require ( + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..017cf3a --- /dev/null +++ b/go.sum @@ -0,0 +1,18 @@ +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/golangci.yaml b/golangci.yaml new file mode 100644 index 0000000..86425ec --- /dev/null +++ b/golangci.yaml @@ -0,0 +1,331 @@ +# Copyright © 2023 KubeCub. +# +# Licensed under the MIT License (the "License"); +# you may not use this file except in compliance with the License. + +# options for analysis running +run: + # default concurrency is a available CPU number + concurrency: 4 + + # timeout for analysis, e.g. 30s, 5m, default is 1m + timeout: 1m + + # exit code when at least one issue was found, default is 1 + issues-exit-code: 1 + + # include test files or not, default is true + tests: true + + # default is true. Enables skipping of directories: + # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ + skip-dirs-use-default: true + + # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": + # If invoked with -mod=readonly, the go command is disallowed from the implicit + # automatic updating of go.mod described above. Instead, it fails when any changes + # to go.mod are needed. This setting is most useful to check that go.mod does + # not need updates, such as in a continuous integration and testing system. + # If invoked with -mod=vendor, the go command assumes that the vendor + # directory holds the correct copies of dependencies and ignores + # the dependency descriptions in go.mod. + #modules-download-mode: release|readonly|vendor + + # Allow multiple parallel golangci-lint instances running. + # If false (default) - golangci-lint acquires file lock on start. + allow-parallel-runners: true + +# output configuration options +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" + format: colored-line-number + + # print lines of code with issue, default is true + print-issued-lines: true + + # print linter name in the end of issue text, default is true + print-linter-name: true + + # make issues output unique by line, default is true + uniq-by-line: true + + +# all available settings of specific linters +linters-settings: + dogsled: + # checks assignments with too many blank identifiers; default is 2 + max-blank-identifiers: 2 + dupl: + # tokens count to trigger issue, 150 by default + threshold: 100 + errcheck: + # report about not checking of errors in type assertions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + + # path to a file containing a list of functions to exclude from checking + # see https://github.com/kisielk/errcheck#excluding-functions for details + #exclude: errcheck.txt + exhaustive: + # indicates that switch statements are to be considered exhaustive if a + # 'default' case is present, even if all enum members aren't listed in the + # switch + default-signifies-exhaustive: false + funlen: + lines: 150 + statements: 40 + gocognit: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 30 + nestif: + # minimal complexity of if statements to report, 5 by default + min-complexity: 4 + goconst: + # minimal length of string constant, 3 by default + min-len: 5 + # minimal occurrences count to trigger, 3 by default + min-occurrences: 5 + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 30 + godot: + # check all top-level comments, not only declarations + check-all: false + godox: + # report any comments starting with keywords, this is useful for TODO or FIXME comments that + # might be left in the code accidentally and should be resolved before merging + keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting + - OPTIMIZE # marks code that should be optimized before merging + - HACK # marks hack-arounds that should be removed before merging + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + goimports: + # put imports beginning with prefix after 3rd-party packages; + # it's a comma-separated list of prefixes + local-prefixes: github.com/org/project + golint: + # minimal confidence for issues, default is 0.8 + min-confidence: 0.9 + gomnd: + settings: + mnd: + # the list of enabled checks, see https://github.com/tommy-muehle/go-mnd/#checks for description. + checks: argument,case,condition,operation,return,assign + gomodguard: + allowed: + modules: # List of allowed modules + # - gopkg.in/yaml.v2 + domains: # List of allowed module domains + # - golang.org + govet: + # report about shadowed variables + check-shadowing: true + + # enable or disable analyzers by name + enable: + - atomicalign + enable-all: false + disable: + - shadow + disable-all: false + depguard: + list-type: blacklist + include-go-root: false + packages: + packages-with-error-message: + lll: + # max line length, lines longer will be reported. Default is 120. + # '\t' is counted as 1 character by default, and can be changed with the tab-width option + line-length: 120 + # tab width in spaces. Default to 1. + tab-width: 1 + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + misspell: + # Correct spellings using locale preferences for US or UK. + # Default is to use a neutral variety of English. + # Setting locale to US will correct the British spelling of 'colour' to 'color'. + locale: US + ignore-words: + - someword + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 30 + prealloc: + # XXX: we don't recommend using this linter before doing performance profiling. + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + nolintlint: + # Enable to ensure that nolint directives are all used. Default is true. + allow-unused: false + # Disable to ensure that nolint directives don't have a leading space. Default is true. + allow-leading-space: true + # Exclude following linters from requiring an explanation. Default is []. + allow-no-explanation: [] + # Enable to require an explanation of nonzero length after each nolint directive. Default is false. + require-explanation: true + # Enable to require nolint directives to mention the specific linter being suppressed. Default is false. + require-specific: true + unparam: + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + unused: + # treat code as a program (not a library) and report unused exported identifiers; default is false. + # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find funcs usages. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + wsl: + # If true append is only allowed to be cuddled if appending value is + # matching variables, fields or types on line above. Default is true. + strict-append: true + # Allow calls and assignments to be cuddled as long as the lines have any + # matching variables, fields or types. Default is true. + allow-assign-and-call: true + # Allow multiline assignments to be cuddled. Default is true. + allow-multiline-assign: true + # Allow declarations (var) to be cuddled. + allow-cuddle-declarations: false + # Allow trailing comments in ending of blocks + allow-trailing-comment: false + # Force newlines in end of case at this limit (0 = never). + force-case-trailing-whitespace: 0 + # Force cuddling of err checks with err var assignment + force-err-cuddling: false + # Allow leading comments to be separated with empty liens + allow-separated-leading-comment: false + +linters: + # please, do not use `enable-all`: it's deprecated and will be removed soon. + # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint + disable-all: true + enable: + - bodyclose + - deadcode + - depguard + - dogsled + - dupl + - errcheck + - funlen + - gochecknoinits + - goconst + - gocritic + - gocyclo + - gofmt + - goimports + - golint + - goprintffuncname + - gosec + - gosimple + - govet + - ineffassign + - interfacer + - lll + - misspell + - nakedret + - nolintlint + - rowserrcheck + - scopelint + - staticcheck + - structcheck + - stylecheck + - typecheck + - unconvert + - unparam + - unused + - varcheck + - whitespace + - asciicheck + - gocognit + - godot + - godox + - maligned + - nestif + - prealloc + - gomodguard + # don't enable: + #- goerr113 + #- wsl + #- testpackage + #- exhaustive (TODO: enable after next release; current release at time of writing is v1.27) + #- gochecknoglobals + #- gomnd + +issues: + # Excluding configuration per-path, per-linter, per-text and per-source + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: true + + # The default value is false. If set to true exclude and exclude-rules + # regular expressions become case sensitive. + exclude-case-sensitive: false + + # The list of ids of default excludes to include or disable. By default it's empty. + include: + - EXC0002 # disable excluding of issues about comments from golint + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-issues-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 + + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false + + # Show only new issues created after git revision `REV` + # new-from-rev: REV + + # Show only new issues created in git patch with set file path. + #new-from-patch: path/to/patch/file + +severity: + # Default value is empty string. + # Set the default severity for issues. If severity rules are defined and the issues + # do not match or no severity is provided to the rule this will be the default + # severity applied. Severities should match the supported severity names of the + # selected out format. + # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity + # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity + # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message + default-severity: error + + # The default value is false. + # If set to true severity-rules regular expressions become case sensitive. + case-sensitive: false + + # Default value is empty list. + # When a list of severity rules are provided, severity information will be added to lint + # issues. Severity rules have the same filtering capability as exclude rules except you + # are allowed to specify one matcher per severity rule. + # Only affects out formats that support setting severity information. + rules: + - linters: + - dupl + severity: info \ No newline at end of file diff --git a/logrus/go.mod b/logrus/go.mod new file mode 100644 index 0000000..471f3ef --- /dev/null +++ b/logrus/go.mod @@ -0,0 +1,3 @@ +module github.com/kubecub/log/logrus + +go 1.20 diff --git a/scripts/boilerplate.txt b/scripts/boilerplate.txt new file mode 100644 index 0000000..3947fe1 --- /dev/null +++ b/scripts/boilerplate.txt @@ -0,0 +1,4 @@ +Copyright © {{.Year}} {{.Holder}} All rights reserved. + +Licensed under the MIT License (the "License"); +you may not use this file except in compliance with the License. diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100644 index 0000000..edd23ef --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright © 2023 KubeCub. All rights reserved. +# +# Licensed under the MIT License (the "License"); +# you may not use this file except in compliance with the License. + + +BIN_DIR="/bin" + +if [[ ! -d "$BIN_DIR" ]]; then + mkdir "$BIN_DIR" +fi + +go build -o "$BIN_DIR/binary1" ./cmd/binary1 +go build -o "$BIN_DIR/binary2" ./cmd/binary2 +go build -o "$BIN_DIR/binary3" ./cmd/binary3 \ No newline at end of file diff --git a/type.go b/type.go new file mode 100644 index 0000000..f79c5cb --- /dev/null +++ b/type.go @@ -0,0 +1,126 @@ +// Copyright © 2023 KubeCub. All rights reserved. +// +// Licensed under the MIT License (the "License"); +// you may not use this file except in compliance with the License. + +package log + +import ( + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +type keylogger string + +type ( + // Field is an alias for the field structure in the underlying log frame. + // The Field type is used to add key-value pairs to a logger's context in Go, + // with most fields being lazily marshaled to make it inexpensive to add fields to disabled debug-level log statements. + Field = zapcore.Field + // Level is an alias for the level structure in the underlying log frame. + // A Level is a logging priority. Higher levels are more important. + Level = zapcore.Level +) + +// Defines common log fields. +const ( + KeyRequestID keylogger = "requestID" + KeyUsername keylogger = "username" + KeyWatcherName keylogger = "watcher" +) + +// Define the basic log level. +// Alias for zap level type const. +var ( + // DebugLevel logs are typically voluminous, and are usually disabled in + // production. + DebugLevel = zapcore.DebugLevel // iota - 1 + // InfoLevel is the default logging priority. + InfoLevel = zapcore.InfoLevel + // WarnLevel logs are more important than Info, but don't need individual + // human review. + WarnLevel = zapcore.WarnLevel + // ErrorLevel logs are high-priority. If an application is running smoothly, + // it shouldn't generate any error-level logs. + ErrorLevel = zapcore.ErrorLevel + // DPanicLevel logs are particularly important errors. In development the + // logger panics after writing the message. + DPanicLevel = zapcore.DPanicLevel + // PanicLevel logs a message, then panics. + PanicLevel = zapcore.PanicLevel + // FatalLevel logs a message, then calls os.Exit(1). + FatalLevel = zapcore.FatalLevel +) + +// Alias for zap type functions. +var ( + Any = zap.Any // accepts a field key and an arbitrary value + Array = zap.Array // accepts a field key and an ArrayMarshaler + Bool = zap.Bool // accepts a field key and a bool + Boolp = zap.Boolp // accepts a field key and a *bool + Bools = zap.Bools // accepts a field key and a []bool + ByteString = zap.ByteString // accepts a field key and a ByteString + ByteStrings = zap.ByteStrings // accepts a field key and a []ByteString + Binary = zap.Binary // accepts a field key and []byte + Complex128 = zap.Complex128 // accepts a field key and a complex128 + Complex128p = zap.Complex128p // accepts a field key and a *complex128 + Complex128s = zap.Complex128s // accepts a field key and a []complex128 + Complex64 = zap.Complex64 // accepts a field key and a complex64 + Complex64p = zap.Complex64p // accepts a field key and a *complex64 + Complex64s = zap.Complex64s // accepts a field key and a []complex64 + Duration = zap.Duration // accepts a field key and a time.Duration + Durationp = zap.Durationp // accepts a field key and a *time.Duration + Durations = zap.Durations // accepts a field key and a []time.Duration + Err = zap.Error // accepts a field key and an error + Errors = zap.Errors // accepts a field key and a []error + Float32 = zap.Float32 // accepts a field key and a float32 + Float32p = zap.Float32p // accepts a field key and a *float32 + Float32s = zap.Float32s // accepts a field key and a []float32 + Float64 = zap.Float64 // accepts a field key and a float64 + Float64p = zap.Float64p // accepts a field key and a *float64 + Float64s = zap.Float64s // accepts a field key and a []float64 + Int = zap.Int // accepts a field key and an int + Intp = zap.Intp // accepts a field key and a *int + Ints = zap.Ints // accepts a field key and a []int + Int64 = zap.Int64 // accepts a field key and an int64 + Int64p = zap.Int64p // accepts a field key and a *int64 + Int64s = zap.Int64s // accepts a field key and a []int64 + Int32 = zap.Int32 // accepts a field key and an int32 + Int32p = zap.Int32p // accepts a field key and a *int32 + Int32s = zap.Int32s // accepts a field key and a []int32 + Int16 = zap.Int16 // accepts a field key and an int16 + Int16p = zap.Int16p // accepts a field key and a *int16 + Int16s = zap.Int16s // accepts a field key and a []int16 + Int8 = zap.Int8 // accepts a field key and an int8 + Int8p = zap.Int8p // accepts a field key and a *int8 + Int8s = zap.Int8s // accepts a field key and a []int8 + Namespace = zap.Namespace // accepts a field key and returns a new logger that is a child of the current logger and adds a namespace to the field key + Object = zap.Object // accepts a field key and an ObjectMarshaler + Reflect = zap.Reflect // accepts a field key and an arbitrary value, and reflects on the value to create more fields + Skip = zap.Skip // accepts no arguments and is a no-op + String = zap.String // accepts a field key and a string + Stringp = zap.Stringp // accepts a field key and a *string + Strings = zap.Strings // accepts a field key and a []string + Stringer = zap.Stringer // accepts a field key and a fmt.Stringer + Time = zap.Time // accepts a field key and a time.Time + Timep = zap.Timep // accepts a field key and a *time.Time + Times = zap.Times // accepts a field key and a []time.Time + Uint = zap.Uint // accepts a field key and a uint + Uintp = zap.Uintp // accepts a field key and a *uint + Uints = zap.Uints // accepts a field key and a []uint + Uint64 = zap.Uint64 // accepts a field key and a uint64 + Uint64p = zap.Uint64p // accepts a field key and a *uint64 + Uint64s = zap.Uint64s // accepts a field key and a []uint64 + Uint32 = zap.Uint32 // accepts a field key and a uint32 + Uint32p = zap.Uint32p // accepts a field key and a *uint32 + Uint32s = zap.Uint32s // accepts a field key and a []uint32 + Uint16 = zap.Uint16 // accepts a field key and a uint16 + Uint16p = zap.Uint16p // accepts a field key and a *uint16 + Uint16s = zap.Uint16s // accepts a field key and a []uint16 + Uint8 = zap.Uint8 // accepts a field key and a uint8 + Uint8p = zap.Uint8p // accepts a field key and a *uint8 + Uint8s = zap.Uint8s // accepts a field key and a []uint8 + Uintptr = zap.Uintptr // accepts a field key and a uintptr + Uintptrp = zap.Uintptrp // accepts a field key and a *uintptr + Uintptrs = zap.Uintptrs // accepts a field key and a []uintptr +)