This commit is contained in:
bel
2020-01-13 03:37:51 +00:00
commit c8eb52f9ba
2023 changed files with 702080 additions and 0 deletions

202
.rclone_repo/vendor/github.com/pengsrc/go-shared/LICENSE generated vendored Executable file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2017 Jingwen Peng
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -0,0 +1,102 @@
// Package buffer provides a thin wrapper around a byte slice. Unlike the
// standard library's bytes.BytesBuffer, it supports a portion of the strconv
// package's zero-allocation formatters.
package buffer
import (
"strconv"
"time"
)
// BytesBuffer is a thin wrapper around a byte slice. It's intended to be
// pooled, so the only way to construct one is via a BytesBufferPool.
type BytesBuffer struct {
bs []byte
pool BytesBufferPool
}
// Free returns the BytesBuffer to its BytesBufferPool.
// Callers must not retain references to the BytesBuffer after calling Free.
func (b *BytesBuffer) Free() {
b.pool.put(b)
}
// Len returns the length of the underlying byte slice.
func (b *BytesBuffer) Len() int {
return len(b.bs)
}
// Cap returns the capacity of the underlying byte slice.
func (b *BytesBuffer) Cap() int {
return cap(b.bs)
}
// Bytes returns a mutable reference to the underlying byte slice.
func (b *BytesBuffer) Bytes() []byte {
return b.bs
}
// String returns a string copy of the underlying byte slice.
func (b *BytesBuffer) String() string {
return string(b.bs)
}
// Reset resets the underlying byte slice. Subsequent writes re-use the slice's
// backing array.
func (b *BytesBuffer) Reset() {
b.bs = b.bs[:0]
}
// Write implements io.Writer.
func (b *BytesBuffer) Write(bs []byte) (int, error) {
b.bs = append(b.bs, bs...)
return len(bs), nil
}
// AppendByte writes a single byte to the BytesBuffer.
func (b *BytesBuffer) AppendByte(v byte) {
b.bs = append(b.bs, v)
}
// AppendBytes writes bytes to the BytesBuffer.
func (b *BytesBuffer) AppendBytes(bs []byte) {
b.bs = append(b.bs, bs...)
}
// AppendString writes a string to the BytesBuffer.
func (b *BytesBuffer) AppendString(s string) {
b.bs = append(b.bs, s...)
}
// AppendInt appends an integer to the underlying buffer (assuming base 10).
func (b *BytesBuffer) AppendInt(i int64) {
b.bs = strconv.AppendInt(b.bs, i, 10)
}
// AppendUint appends an unsigned integer to the underlying buffer (assuming
// base 10).
func (b *BytesBuffer) AppendUint(i uint64) {
b.bs = strconv.AppendUint(b.bs, i, 10)
}
// AppendFloat appends a float to the underlying buffer. It doesn't quote NaN
// or +/- Inf.
func (b *BytesBuffer) AppendFloat(f float64, bitSize int) {
b.bs = strconv.AppendFloat(b.bs, f, 'f', -1, bitSize)
}
// AppendBool appends a bool to the underlying buffer.
func (b *BytesBuffer) AppendBool(v bool) {
b.bs = strconv.AppendBool(b.bs, v)
}
// AppendTime appends a time to the underlying buffer.
func (b *BytesBuffer) AppendTime(t time.Time, format string) {
if format == "" {
b.bs = strconv.AppendInt(b.bs, t.Unix(), 10)
} else {
b.bs = t.AppendFormat(b.bs, format)
}
}
const defaultSize = 1024 // Create 1 KiB buffers by default

View File

@@ -0,0 +1,39 @@
package buffer
import "sync"
// A BytesBufferPool is a type-safe wrapper around a sync.BytesBufferPool.
type BytesBufferPool struct {
p *sync.Pool
}
// NewBytesPool constructs a new BytesBufferPool.
func NewBytesPool() BytesBufferPool {
return BytesBufferPool{
p: &sync.Pool{
New: func() interface{} {
return &BytesBuffer{bs: make([]byte, 0, defaultSize)}
},
},
}
}
// Get retrieves a BytesBuffer from the pool, creating one if necessary.
func (p BytesBufferPool) Get() *BytesBuffer {
buf := p.p.Get().(*BytesBuffer)
buf.Reset()
buf.pool = p
return buf
}
func (p BytesBufferPool) put(buf *BytesBuffer) {
p.p.Put(buf)
}
// GlobalBytesPool returns the global buffer pool.
func GlobalBytesPool() *BytesBufferPool {
return &bytesPool
}
// bytesPool is a pool of buffer bytes.
var bytesPool = NewBytesPool()

View File

@@ -0,0 +1,17 @@
package check
import (
"fmt"
"os"
)
// Dir checks the given path, will return error if path not exists or path
// is not directory.
func Dir(path string) error {
if info, err := os.Stat(path); err != nil {
return fmt.Errorf(`directory not exists: %s`, path)
} else if !info.IsDir() {
return fmt.Errorf(`path is not directory: %s`, path)
}
return nil
}

View File

@@ -0,0 +1,37 @@
package check
import (
"fmt"
"os"
)
// ReadableError is just a structure contains readable message that can be
// returned directly to end user.
type ReadableError struct {
Message string
}
// Error returns the description of ReadableError.
func (e ReadableError) Error() string {
return e.Message
}
// NewReadableError creates a ReadableError{} from given message.
func NewReadableError(message string) ReadableError {
return ReadableError{Message: message}
}
// ErrorForExit check the error.
// If error is not nil, print the error message and exit the application.
// If error is nil, do nothing.
func ErrorForExit(name string, err error, code ...int) {
if err != nil {
exitCode := 1
if len(code) > 0 {
exitCode = code[0]
}
fmt.Fprintf(os.Stderr, "%s: %s (%d)\n", name, err.Error(), exitCode)
fmt.Fprintf(os.Stderr, "See \"%s --help\".\n", name)
os.Exit(exitCode)
}
}

View File

@@ -0,0 +1,11 @@
package check
import (
"regexp"
)
// HostAndPort checks whether a string contains host and port.
// It returns true if matched.
func HostAndPort(hostAndPort string) bool {
return regexp.MustCompile(`^[^:]+:[0-9]+$`).MatchString(hostAndPort)
}

View File

@@ -0,0 +1,41 @@
package check
// StringSliceContains iterates over the slice to find the target.
func StringSliceContains(slice []string, target string) bool {
for _, v := range slice {
if v == target {
return true
}
}
return false
}
// IntSliceContains iterates over the slice to find the target.
func IntSliceContains(slice []int, target int) bool {
for _, v := range slice {
if v == target {
return true
}
}
return false
}
// Int32SliceContains iterates over the slice to find the target.
func Int32SliceContains(slice []int32, target int32) bool {
for _, v := range slice {
if v == target {
return true
}
}
return false
}
// Int64SliceContains iterates over the slice to find the target.
func Int64SliceContains(slice []int64, target int64) bool {
for _, v := range slice {
if v == target {
return true
}
}
return false
}

View File

@@ -0,0 +1,10 @@
package convert
// StringSliceWithConverter converts a list of string using the passed converter function
func StringSliceWithConverter(s []string, c func(string) string) []string {
out := []string{}
for _, i := range s {
out = append(out, c(i))
}
return out
}

View File

@@ -0,0 +1,82 @@
package convert
import (
"fmt"
"time"
"github.com/pengsrc/go-shared/check"
)
// Supported time layouts
const (
RFC822 = "Mon, 02 Jan 2006 15:04:05 GMT"
ISO8601 = "2006-01-02T15:04:05Z"
ISO8601Milli = "2006-01-02T15:04:05.000Z"
NGINXTime = "02/Jan/2006:15:04:05 -0700"
)
// TimeToString transforms given time to string.
func TimeToString(timeValue time.Time, format string) string {
if check.StringSliceContains([]string{RFC822, ISO8601, ISO8601Milli}, format) {
timeValue = timeValue.UTC()
}
return timeValue.Format(format)
}
// StringToTime transforms given string to time.
func StringToTime(timeString string, format string) (time.Time, error) {
result, err := time.Parse(format, timeString)
if timeString != "0001-01-01T00:00:00Z" {
zero := time.Time{}
if result == zero {
err = fmt.Errorf(`failed to parse "%s" like "%s"`, timeString, format)
}
}
return result, err
}
// TimeToTimestamp transforms given time to unix time int.
func TimeToTimestamp(t time.Time) int64 {
zero := time.Time{}
if t == zero {
t = time.Unix(0, 0).UTC()
}
return t.Unix()
}
// TimestampToTime transforms given unix time int64 to time in UTC.
func TimestampToTime(unix int64) time.Time {
return time.Unix(unix, 0).UTC()
}
// TimestampToTimePointer transforms given unix time int64 to time pointer in UTC.
func TimestampToTimePointer(unix int64) *time.Time {
if unix == 0 {
return nil
}
t := time.Unix(unix, 0).UTC()
return &t
}
// TimePointerToTimestamp transforms given time pointer to unix time int64.
func TimePointerToTimestamp(t *time.Time) int64 {
if t == nil {
return 0
}
return t.Unix()
}
// StringToTimestamp transforms given string to unix time int64. It will
// return -1 when time string parse error.
func StringToTimestamp(timeString string, format string) int64 {
t, err := StringToTime(timeString, format)
if err != nil {
return -1
}
return t.Unix()
}
// TimestampToString converts unix timestamp to formatted string.
func TimestampToString(unix int64, format string) string {
return TimeToString(time.Unix(unix, 0).UTC(), format)
}

View File

@@ -0,0 +1,550 @@
package convert
import (
"time"
)
// String returns a pointer to the given string value.
func String(v string) *string {
return &v
}
// StringValue returns the value of the given string pointer or
// "" if the pointer is nil.
func StringValue(v *string) string {
if v != nil {
return *v
}
return ""
}
// StringSlice converts a slice of string values into a slice of
// string pointers.
func StringSlice(src []string) []*string {
dst := make([]*string, len(src))
for i := 0; i < len(src); i++ {
dst[i] = &(src[i])
}
return dst
}
// StringValueSlice converts a slice of string pointers into a slice of
// string values.
func StringValueSlice(src []*string) []string {
dst := make([]string, len(src))
for i := 0; i < len(src); i++ {
if src[i] != nil {
dst[i] = *(src[i])
}
}
return dst
}
// StringMap converts a string map of string values into a string
// map of string pointers.
func StringMap(src map[string]string) map[string]*string {
dst := make(map[string]*string)
for k, val := range src {
v := val
dst[k] = &v
}
return dst
}
// StringValueMap converts a string map of string pointers into a string
// map of string values.
func StringValueMap(src map[string]*string) map[string]string {
dst := make(map[string]string)
for k, val := range src {
if val != nil {
dst[k] = *val
}
}
return dst
}
// Bool returns a pointer to the given bool value.
func Bool(v bool) *bool {
return &v
}
// BoolValue returns the value of the given bool pointer or
// false if the pointer is nil.
func BoolValue(v *bool) bool {
if v != nil {
return *v
}
return false
}
// BoolSlice converts a slice of bool values into a slice of
// bool pointers.
func BoolSlice(src []bool) []*bool {
dst := make([]*bool, len(src))
for i := 0; i < len(src); i++ {
dst[i] = &(src[i])
}
return dst
}
// BoolValueSlice converts a slice of bool pointers into a slice of
// bool values.
func BoolValueSlice(src []*bool) []bool {
dst := make([]bool, len(src))
for i := 0; i < len(src); i++ {
if src[i] != nil {
dst[i] = *(src[i])
}
}
return dst
}
// BoolMap converts a string map of bool values into a string
// map of bool pointers.
func BoolMap(src map[string]bool) map[string]*bool {
dst := make(map[string]*bool)
for k, val := range src {
v := val
dst[k] = &v
}
return dst
}
// BoolValueMap converts a string map of bool pointers into a string
// map of bool values.
func BoolValueMap(src map[string]*bool) map[string]bool {
dst := make(map[string]bool)
for k, val := range src {
if val != nil {
dst[k] = *val
}
}
return dst
}
// Int returns a pointer to the given int value.
func Int(v int) *int {
return &v
}
// IntValue returns the value of the given int pointer or
// 0 if the pointer is nil.
func IntValue(v *int) int {
if v != nil {
return *v
}
return 0
}
// IntSlice converts a slice of int values into a slice of
// int pointers.
func IntSlice(src []int) []*int {
dst := make([]*int, len(src))
for i := 0; i < len(src); i++ {
dst[i] = &(src[i])
}
return dst
}
// IntValueSlice converts a slice of int pointers into a slice of
// int values.
func IntValueSlice(src []*int) []int {
dst := make([]int, len(src))
for i := 0; i < len(src); i++ {
if src[i] != nil {
dst[i] = *(src[i])
}
}
return dst
}
// IntMap converts a string map of int values into a string
// map of int pointers.
func IntMap(src map[string]int) map[string]*int {
dst := make(map[string]*int)
for k, val := range src {
v := val
dst[k] = &v
}
return dst
}
// IntValueMap converts a string map of int pointers into a string
// map of int values.
func IntValueMap(src map[string]*int) map[string]int {
dst := make(map[string]int)
for k, val := range src {
if val != nil {
dst[k] = *val
}
}
return dst
}
// Int32 returns a pointer to the given int32 value.
func Int32(v int32) *int32 {
return &v
}
// Int32Value returns the value of the given int32 pointer or
// 0 if the pointer is nil.
func Int32Value(v *int32) int32 {
if v != nil {
return *v
}
return 0
}
// Int32Slice converts a slice of int32 values into a slice of
// int32 pointers.
func Int32Slice(src []int32) []*int32 {
dst := make([]*int32, len(src))
for i := 0; i < len(src); i++ {
dst[i] = &(src[i])
}
return dst
}
// Int32ValueSlice converts a slice of int32 pointers into a slice of
// int32 values.
func Int32ValueSlice(src []*int32) []int32 {
dst := make([]int32, len(src))
for i := 0; i < len(src); i++ {
if src[i] != nil {
dst[i] = *(src[i])
}
}
return dst
}
// Int32Map converts a string map of int32 values into a string
// map of int32 pointers.
func Int32Map(src map[string]int32) map[string]*int32 {
dst := make(map[string]*int32)
for k, val := range src {
v := val
dst[k] = &v
}
return dst
}
// Int32ValueMap converts a string map of int32 pointers into a string
// map of int32 values.
func Int32ValueMap(src map[string]*int32) map[string]int32 {
dst := make(map[string]int32)
for k, val := range src {
if val != nil {
dst[k] = *val
}
}
return dst
}
// Int64 returns a pointer to the given int64 value.
func Int64(v int64) *int64 {
return &v
}
// Int64Value returns the value of the given int64 pointer or
// 0 if the pointer is nil.
func Int64Value(v *int64) int64 {
if v != nil {
return *v
}
return 0
}
// Int64Slice converts a slice of int64 values into a slice of
// int64 pointers.
func Int64Slice(src []int64) []*int64 {
dst := make([]*int64, len(src))
for i := 0; i < len(src); i++ {
dst[i] = &(src[i])
}
return dst
}
// Int64ValueSlice converts a slice of int64 pointers into a slice of
// int64 values.
func Int64ValueSlice(src []*int64) []int64 {
dst := make([]int64, len(src))
for i := 0; i < len(src); i++ {
if src[i] != nil {
dst[i] = *(src[i])
}
}
return dst
}
// Int64Map converts a string map of int64 values into a string
// map of int64 pointers.
func Int64Map(src map[string]int64) map[string]*int64 {
dst := make(map[string]*int64)
for k, val := range src {
v := val
dst[k] = &v
}
return dst
}
// Int64ValueMap converts a string map of int64 pointers into a string
// map of int64 values.
func Int64ValueMap(src map[string]*int64) map[string]int64 {
dst := make(map[string]int64)
for k, val := range src {
if val != nil {
dst[k] = *val
}
}
return dst
}
// Int64Uint returns a uint pointer to the given int64 value.
func Int64Uint(src int64) *uint {
dst := uint(src)
return &dst
}
// Uint8 return a uint8 pointer to the given uint8 value.
func Uint8(src uint8) *uint8 {
dst := uint8(src)
return &dst
}
// Uint8Value returns the value of the given uint8 pointer or
// 0 if the pointer is nil.
func Uint8Value(v *uint8) uint8 {
if v != nil {
return *v
}
return 0
}
// Uint32 return a uint32 pointer to the given uint32 value.
func Uint32(src uint32) *uint32 {
dst := uint32(src)
return &dst
}
// Uint32Value returns the value of the given uint32 pointer or
// 0 if the pointer is nil.
func Uint32Value(v *uint32) uint32 {
if v != nil {
return *v
}
return 0
}
// Uint64 return a uint64 pointer to the given uint64 value.
func Uint64(src uint64) *uint64 {
dst := uint64(src)
return &dst
}
// Uint64Value returns the value of the given uint64 pointer or
// 0 if the pointer is nil.
func Uint64Value(v *uint64) uint64 {
if v != nil {
return *v
}
return 0
}
// Uint64Slice converts a slice of uint64 values into a slice of
// uint64 pointers.
func Uint64Slice(src []uint64) []*uint64 {
dst := make([]*uint64, len(src))
for i := 0; i < len(src); i++ {
dst[i] = &(src[i])
}
return dst
}
// Uint64ValueSlice converts a slice of uint64 pointers into a slice of
// uint64 values.
func Uint64ValueSlice(src []*uint64) []uint64 {
dst := make([]uint64, len(src))
for i := 0; i < len(src); i++ {
if src[i] != nil {
dst[i] = *(src[i])
}
}
return dst
}
// Float32 returns a pointer to the given float32 value.
func Float32(v float32) *float32 {
return &v
}
// Float32Value returns the value of the given float32 pointer or
// 0 if the pointer is nil.
func Float32Value(v *float32) float32 {
if v != nil {
return *v
}
return 0
}
// Float32Slice converts a slice of float32 values into a slice of
// float32 pointers.
func Float32Slice(src []float32) []*float32 {
dst := make([]*float32, len(src))
for i := 0; i < len(src); i++ {
dst[i] = &(src[i])
}
return dst
}
// Float32ValueSlice converts a slice of float32 pointers into a slice of
// float32 values.
func Float32ValueSlice(src []*float32) []float32 {
dst := make([]float32, len(src))
for i := 0; i < len(src); i++ {
if src[i] != nil {
dst[i] = *(src[i])
}
}
return dst
}
// Float32Map converts a string map of float32 values into a string
// map of float32 pointers.
func Float32Map(src map[string]float32) map[string]*float32 {
dst := make(map[string]*float32)
for k, val := range src {
v := val
dst[k] = &v
}
return dst
}
// Float32ValueMap converts a string map of float32 pointers into a string
// map of float32 values.
func Float32ValueMap(src map[string]*float32) map[string]float32 {
dst := make(map[string]float32)
for k, val := range src {
if val != nil {
dst[k] = *val
}
}
return dst
}
// Float64 returns a pointer to the given float64 value.
func Float64(v float64) *float64 {
return &v
}
// Float64Value returns the value of the given float64 pointer or
// 0 if the pointer is nil.
func Float64Value(v *float64) float64 {
if v != nil {
return *v
}
return 0
}
// Float64Slice converts a slice of float64 values into a slice of
// float64 pointers.
func Float64Slice(src []float64) []*float64 {
dst := make([]*float64, len(src))
for i := 0; i < len(src); i++ {
dst[i] = &(src[i])
}
return dst
}
// Float64ValueSlice converts a slice of float64 pointers into a slice of
// float64 values.
func Float64ValueSlice(src []*float64) []float64 {
dst := make([]float64, len(src))
for i := 0; i < len(src); i++ {
if src[i] != nil {
dst[i] = *(src[i])
}
}
return dst
}
// Float64Map converts a string map of float64 values into a string
// map of float64 pointers.
func Float64Map(src map[string]float64) map[string]*float64 {
dst := make(map[string]*float64)
for k, val := range src {
v := val
dst[k] = &v
}
return dst
}
// Float64ValueMap converts a string map of float64 pointers into a string
// map of float64 values.
func Float64ValueMap(src map[string]*float64) map[string]float64 {
dst := make(map[string]float64)
for k, val := range src {
if val != nil {
dst[k] = *val
}
}
return dst
}
// Time returns a pointer to the given time.Time value.
func Time(v time.Time) *time.Time {
return &v
}
// TimeValue returns the value of the given time.Time pointer or
// time.Time{} if the pointer is nil.
func TimeValue(v *time.Time) time.Time {
if v != nil {
return *v
}
return time.Time{}
}
// TimeSlice converts a slice of time.Time values into a slice of
// time.Time pointers.
func TimeSlice(src []time.Time) []*time.Time {
dst := make([]*time.Time, len(src))
for i := 0; i < len(src); i++ {
dst[i] = &(src[i])
}
return dst
}
// TimeValueSlice converts a slice of time.Time pointers into a slice of
// time.Time values.
func TimeValueSlice(src []*time.Time) []time.Time {
dst := make([]time.Time, len(src))
for i := 0; i < len(src); i++ {
if src[i] != nil {
dst[i] = *(src[i])
}
}
return dst
}
// TimeMap converts a string map of time.Time values into a string
// map of time.Time pointers.
func TimeMap(src map[string]time.Time) map[string]*time.Time {
dst := make(map[string]*time.Time)
for k, val := range src {
v := val
dst[k] = &v
}
return dst
}
// TimeValueMap converts a string map of time.Time pointers into a string
// map of time.Time values.
func TimeValueMap(src map[string]*time.Time) map[string]time.Time {
dst := make(map[string]time.Time)
for k, val := range src {
if val != nil {
dst[k] = *val
}
}
return dst
}

460
.rclone_repo/vendor/github.com/pengsrc/go-shared/log/event.go generated vendored Executable file
View File

@@ -0,0 +1,460 @@
package log
import (
"context"
"fmt"
"os"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/pengsrc/go-shared/buffer"
"github.com/pengsrc/go-shared/convert"
)
// A EventCallerPool is a type-safe wrapper around a sync.BytesBufferPool.
type EventCallerPool struct {
p *sync.Pool
}
// NewEventCallerPool constructs a new BytesBufferPool.
func NewEventCallerPool() EventCallerPool {
return EventCallerPool{
p: &sync.Pool{
New: func() interface{} {
return &EventCaller{}
},
},
}
}
// Get retrieves a EventCaller from the pool, creating one if necessary.
func (p EventCallerPool) Get() *EventCaller {
e := p.p.Get().(*EventCaller)
e.pool = p
e.Defined = false
e.PC = 0
e.File = ""
e.Line = 0
return e
}
func (p EventCallerPool) put(caller *EventCaller) {
p.p.Put(caller)
}
// EventCaller represents the caller of a logging function.
type EventCaller struct {
pool EventCallerPool
Defined bool
PC uintptr
File string
Line int
}
// Free returns the EventCaller to its EventCallerPool.
// Callers must not retain references to the EventCaller after calling Free.
func (ec *EventCaller) Free() {
ec.pool.put(ec)
}
// String returns the full path and line number of the caller.
func (ec EventCaller) String() string {
return ec.FullPath()
}
// FullPath returns a /full/path/to/package/file:line description of the
// caller.
func (ec EventCaller) FullPath() string {
if !ec.Defined {
return "undefined"
}
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
buf.AppendString(ec.File)
buf.AppendByte(':')
buf.AppendInt(int64(ec.Line))
return buf.String()
}
// TrimmedPath returns a package/file:line description of the caller,
// preserving only the leaf directory name and file name.
func (ec EventCaller) TrimmedPath() string {
if !ec.Defined {
return "undefined"
}
// nb. To make sure we trim the path correctly on Windows too, we
// counter-intuitively need to use '/' and *not* os.PathSeparator here,
// because the path given originates from Go stdlib, specifically
// runtime.Caller() which (as of Mar/17) returns forward slashes even on
// Windows.
//
// See https://github.com/golang/go/issues/3335
// and https://github.com/golang/go/issues/18151
//
// for discussion on the issue on Go side.
//
// Find the last separator.
//
idx := strings.LastIndexByte(ec.File, '/')
if idx == -1 {
return ec.FullPath()
}
// Find the penultimate separator.
idx = strings.LastIndexByte(ec.File[:idx], '/')
if idx == -1 {
return ec.FullPath()
}
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
// Keep everything after the penultimate separator.
buf.AppendString(ec.File[idx+1:])
buf.AppendByte(':')
buf.AppendInt(int64(ec.Line))
return buf.String()
}
// A EventPool is a type-safe wrapper around a sync.BytesBufferPool.
type EventPool struct {
p *sync.Pool
}
// NewEventPool constructs a new BytesBufferPool.
func NewEventPool() EventPool {
return EventPool{
p: &sync.Pool{
New: func() interface{} {
return &Event{}
},
},
}
}
// Get retrieves a Event from the pool, creating one if necessary.
func (p EventPool) Get() *Event {
e := p.p.Get().(*Event)
e.buffer = buffer.GlobalBytesPool().Get()
e.pool = p
e.level = MuteLevel
e.lw = nil
e.ctx = nil
e.ctxKeys = nil
e.isEnabled = false
e.isCallerEnabled = false
return e
}
func (p EventPool) put(event *Event) {
event.buffer.Free()
event.ctx = nil
event.ctxKeys = nil
p.p.Put(event)
}
// Event represents a log event. It is instanced by one of the with method of
// logger and finalized by the log method such as Debug().
type Event struct {
buffer *buffer.BytesBuffer
pool EventPool
level Level
lw LevelWriter
ctx context.Context
ctxKeys *[]interface{}
ctxKeysMap *map[interface{}]string
isEnabled bool
isCallerEnabled bool
}
// Free returns the Event to its EventPool.
// Callers must not retain references to the Event after calling Free.
func (e *Event) Free() {
e.pool.put(e)
}
// Message writes the *Event to level writer.
//
// NOTICE: Once this method is called, the *Event should be disposed.
// Calling twice can have unexpected result.
func (e *Event) Message(message string) {
if !e.isEnabled {
return
}
e.write(message)
}
// Messagef writes the *Event to level writer.
//
// NOTICE: Once this method is called, the *Event should be disposed.
// Calling twice can have unexpected result.
func (e *Event) Messagef(format string, v ...interface{}) {
if !e.isEnabled {
return
}
e.write(format, v...)
}
// Byte appends string key and byte value to event.
func (e *Event) Byte(key string, value byte) *Event {
return e.appendField(key, func() { e.buffer.AppendByte(value) })
}
// Bytes appends string key and bytes value to event.
func (e *Event) Bytes(key string, value []byte) *Event {
return e.appendField(key, func() {
if needsQuote(string(value)) {
e.buffer.AppendString(strconv.Quote(string(value)))
} else {
e.buffer.AppendBytes(value)
}
})
}
// String appends string key and string value to event.
func (e *Event) String(key string, value string) *Event {
return e.appendField(key, func() {
if needsQuote(string(value)) {
e.buffer.AppendString(strconv.Quote(value))
} else {
e.buffer.AppendString(value)
}
})
}
// Int appends string key and int value to event.
func (e *Event) Int(key string, value int) *Event {
return e.appendField(key, func() { e.buffer.AppendInt(int64(value)) })
}
// Int32 appends string key and int32 value to event.
func (e *Event) Int32(key string, value int32) *Event {
return e.appendField(key, func() { e.buffer.AppendInt(int64(value)) })
}
// Int64 appends string key and int64 value to event.
func (e *Event) Int64(key string, value int64) *Event {
return e.appendField(key, func() { e.buffer.AppendInt(value) })
}
// Uint appends string key and uint value to event.
func (e *Event) Uint(key string, value uint) *Event {
return e.appendField(key, func() { e.buffer.AppendUint(uint64(value)) })
}
// Uint32 appends string key and uint32 value to event.
func (e *Event) Uint32(key string, value uint32) *Event {
return e.appendField(key, func() { e.buffer.AppendUint(uint64(value)) })
}
// Uint64 appends string key and uint64 value to event.
func (e *Event) Uint64(key string, value uint64) *Event {
return e.appendField(key, func() { e.buffer.AppendUint(value) })
}
// Float32 appends string key and float32 value to event.
func (e *Event) Float32(key string, value float32) *Event {
return e.appendField(key, func() { e.buffer.AppendFloat(float64(value), 32) })
}
// Float64 appends string key and float value to event.
func (e *Event) Float64(key string, value float64) *Event {
return e.appendField(key, func() { e.buffer.AppendFloat(value, 64) })
}
// Bool appends string key and bool value to event.
func (e *Event) Bool(key string, value bool) *Event {
return e.appendField(key, func() { e.buffer.AppendBool(value) })
}
// Time appends string key and time value to event.
func (e *Event) Time(key string, value time.Time, format string) *Event {
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
buf.AppendTime(value, format)
return e.Bytes(key, buf.Bytes())
}
// Error appends string key and error value to event.
func (e *Event) Error(key string, err error) *Event {
return e.String(key, err.Error())
}
// Interface appends string key and interface value to event.
func (e *Event) Interface(key string, value interface{}) *Event {
switch v := value.(type) {
case byte:
e.Byte(key, v)
case []byte:
e.Bytes(key, v)
case string:
e.String(key, v)
case int:
e.Int(key, v)
case int32:
e.Int32(key, v)
case int64:
e.Int64(key, v)
case uint:
e.Uint(key, v)
case uint32:
e.Uint32(key, v)
case uint64:
e.Uint64(key, v)
case float32:
e.Float32(key, v)
case float64:
e.Float64(key, v)
case bool:
e.Bool(key, v)
case time.Time:
e.Time(key, v, convert.ISO8601Milli)
case error:
e.Error(key, v)
case nil:
e.String(key, "nil")
default:
panic(fmt.Sprintf("unknown field type: %v", value))
}
return e
}
func (e *Event) appendField(key string, appendFunc func()) *Event {
if !e.isEnabled {
return e
}
// Ignore field with empty key.
if len(key) <= 0 {
return e
}
// Append space if event field not empty.
if e.buffer.Len() != 0 {
e.buffer.AppendByte(' ')
}
e.buffer.AppendString(key)
e.buffer.AppendString("=")
appendFunc()
return e
}
func (e *Event) write(format string, v ...interface{}) {
defer e.Free()
if !e.isEnabled {
return
}
// Append interested contexts.
if e.ctx != nil && e.ctxKeys != nil && e.ctxKeysMap != nil {
for _, key := range *e.ctxKeys {
if value := e.ctx.Value(key); value != nil {
e.Interface((*e.ctxKeysMap)[key], e.ctx.Value(key))
}
}
}
// Append caller.
if e.isCallerEnabled {
ec := newEventCaller(runtime.Caller(callerSkipOffset))
e.String("source", ec.TrimmedPath())
}
// Compose and store current log.
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
// Format print message.
if format != "" {
if len(v) == 0 {
fmt.Fprint(buf, format)
} else {
fmt.Fprintf(buf, format, v...)
}
} else {
fmt.Fprint(buf, v...)
}
// Append filed.
buf.AppendByte(' ')
buf.AppendBytes(e.buffer.Bytes())
// Finally write.
if _, err := e.lw.WriteLevel(e.level, buf.Bytes()); err != nil {
fmt.Fprintf(os.Stderr, "log: could not write event: %v", err)
}
switch e.level {
case PanicLevel:
panic(buf.String())
case FatalLevel:
os.Exit(1)
}
}
func newEventCaller(pc uintptr, file string, line int, ok bool) (ec *EventCaller) {
ec = eventCallerPool.Get()
if ok {
ec.PC = pc
ec.File = file
ec.Line = line
ec.Defined = true
}
return
}
func newEvent(
ctx context.Context,
ctxKeys *[]interface{}, ctxKeysMap *map[interface{}]string,
level Level, lw LevelWriter,
isEnabled bool, isCallerEnabled bool,
) (e *Event) {
e = eventPool.Get()
e.level = level
e.lw = lw
e.ctx = ctx
e.ctxKeys = ctxKeys
e.ctxKeysMap = ctxKeysMap
e.isEnabled = isEnabled
e.isCallerEnabled = isCallerEnabled
return
}
func needsQuote(s string) bool {
for i := range s {
if s[i] < 0x20 || s[i] > 0x7e || s[i] == ' ' || s[i] == '\\' || s[i] == '"' {
return true
}
}
return false
}
const callerSkipOffset = 2
// eventCallerPool is a pool of newEvent callers.
var eventCallerPool = NewEventCallerPool()
// eventPool is a pool of events.
var eventPool = NewEventPool()

View File

@@ -0,0 +1,151 @@
package log
import (
"context"
)
// Fatal logs a message with severity FATAL followed by a call to os.Exit(1).
func Fatal(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, FatalLevel).write("", v...)
}
}
// Panic logs a message with severity PANIC followed by a call to panic().
func Panic(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, PanicLevel).write("", v...)
}
}
// Error logs a message with severity ERROR.
func Error(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, ErrorLevel).write("", v...)
}
}
// Warn logs a message with severity WARN.
func Warn(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, WarnLevel).write("", v...)
}
}
// Info logs a message with severity INFO.
func Info(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, InfoLevel).write("", v...)
}
}
// Debug logs a message with severity DEBUG.
func Debug(ctx context.Context, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, DebugLevel).write("", v...)
}
}
// Fatalf logs a message with severity FATAL in format followed by a call to
// os.Exit(1).
func Fatalf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, FatalLevel).write(format, v...)
}
}
// Panicf logs a message with severity PANIC in format followed by a call to
// panic().
func Panicf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, PanicLevel).write(format, v...)
}
}
// Errorf logs a message with severity ERROR in format.
func Errorf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, ErrorLevel).write(format, v...)
}
}
// Warnf logs a message with severity WARN in format.
func Warnf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, WarnLevel).write(format, v...)
}
}
// Infof logs a message with severity INFO in format.
func Infof(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, InfoLevel).write(format, v...)
}
}
// Debugf logs a message with severity DEBUG in format.
func Debugf(ctx context.Context, format string, v ...interface{}) {
if globalLogger != nil {
globalLogger.event(ctx, DebugLevel).write(format, v...)
}
}
// FatalEvent returns a log event with severity FATAL.
func FatalEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, FatalLevel)
}
return nil
}
// PanicEvent returns a log event with severity PANIC.
func PanicEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, PanicLevel)
}
return nil
}
// ErrorEvent returns a log event with severity ERROR.
func ErrorEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, ErrorLevel)
}
return nil
}
// WarnEvent returns a log event with severity WARN.
func WarnEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, WarnLevel)
}
return nil
}
// InfoEvent returns a log event with severity INFO.
func InfoEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, InfoLevel)
}
return nil
}
// DebugEvent returns a log event with severity DEBUG.
func DebugEvent(ctx context.Context) *Event {
if globalLogger != nil {
return globalLogger.event(ctx, DebugLevel)
}
return nil
}
// SetGlobalLogger sets a logger as global logger.
func SetGlobalLogger(l *Logger) {
globalLogger = l
}
// GlobalLogger returns the global logger.
func GlobalLogger() *Logger {
return globalLogger
}
var globalLogger *Logger

View File

@@ -0,0 +1,67 @@
package log
import (
"fmt"
"strings"
)
// Level defines log levels.
type Level uint8
// String returns name of the level.
func (l Level) String() string {
switch l {
case MuteLevel:
return "MUTE"
case FatalLevel:
return "FATAL"
case PanicLevel:
return "PANIC"
case ErrorLevel:
return "ERROR"
case WarnLevel:
return "WARN"
case InfoLevel:
return "INFO"
case DebugLevel:
return "DEBUG"
}
return ""
}
const (
// MuteLevel disables the logger.
MuteLevel Level = iota
// FatalLevel defines fatal log level.
FatalLevel
// PanicLevel defines panic log level.
PanicLevel
// ErrorLevel defines error log level.
ErrorLevel
// WarnLevel defines warn log level.
WarnLevel
// InfoLevel defines info log level.
InfoLevel
// DebugLevel defines debug log level.
DebugLevel
)
// ParseLevel takes a string level and returns the log level constant.
func ParseLevel(level string) (Level, error) {
switch strings.ToUpper(level) {
case "FATAL":
return FatalLevel, nil
case "PANIC":
return PanicLevel, nil
case "ERROR":
return ErrorLevel, nil
case "WARN":
return WarnLevel, nil
case "INFO":
return InfoLevel, nil
case "DEBUG":
return DebugLevel, nil
}
return MuteLevel, fmt.Errorf(`"%q" is not a valid log Level`, level)
}

View File

@@ -0,0 +1,327 @@
// Package log provides support for logging to stdout, stderr and file.
package log
import (
"bufio"
"context"
"errors"
"fmt"
"io"
"os"
"os/signal"
"path"
"syscall"
"time"
"github.com/pengsrc/go-shared/check"
"github.com/pengsrc/go-shared/reopen"
)
// Logger presents a logger.
// The only way to initialize a logger is using the convention construct
// functions like NewLogger().
type Logger struct {
level Level
lw LevelWriter
// Interested context keys.
ctxKeys []interface{}
ctxKeysMap map[interface{}]string
// isCallerEnabled sets whether to annotating logs with the calling
// function's file name and line number. By default, all logs are annotated.
isCallerEnabled bool
}
// GetLevel get the log level string.
func (l *Logger) GetLevel() string {
return l.level.String()
}
// SetLevel sets the log level.
// Valid levels are "debug", "info", "warn", "error", and "fatal".
func (l *Logger) SetLevel(level string) (err error) {
levelFlag, err := ParseLevel(level)
if err == nil {
l.level = levelFlag
}
return err
}
// SetInterestContextKeys sets the contexts keys that the logger should be
// interested in. Value of the interested context key will extract and print as
// newEvent filed.
func (l *Logger) SetInterestContextKeys(keys []interface{}) {
l.ctxKeys = keys
l.ctxKeysMap = make(map[interface{}]string)
for _, key := range l.ctxKeys {
l.ctxKeysMap[key] = fmt.Sprintf("%v", key)
}
}
// SetCallerFlag sets whether to annotating logs with the caller.
func (l *Logger) SetCallerFlag(isEnabled bool) {
l.isCallerEnabled = isEnabled
}
// Flush writes buffered logs.
func (l *Logger) Flush() {
if flusher, ok := l.lw.(Flusher); ok {
flusher.Flush()
}
}
// Fatal logs a message with severity FATAL followed by a call to os.Exit(1).
func (l *Logger) Fatal(ctx context.Context, v ...interface{}) {
l.event(ctx, FatalLevel).write("", v...)
}
// Panic logs a message with severity PANIC followed by a call to panic().
func (l *Logger) Panic(ctx context.Context, v ...interface{}) {
l.event(ctx, PanicLevel).write("", v...)
}
// Error logs a message with severity ERROR.
func (l *Logger) Error(ctx context.Context, v ...interface{}) {
l.event(ctx, ErrorLevel).write("", v...)
}
// Warn logs a message with severity WARN.
func (l *Logger) Warn(ctx context.Context, v ...interface{}) {
l.event(ctx, WarnLevel).write("", v...)
}
// Info logs a message with severity INFO.
func (l *Logger) Info(ctx context.Context, v ...interface{}) {
l.event(ctx, InfoLevel).write("", v...)
}
// Debug logs a message with severity DEBUG.
func (l *Logger) Debug(ctx context.Context, v ...interface{}) {
l.event(ctx, DebugLevel).write("", v...)
}
// Fatalf logs a message with severity FATAL in format followed by a call to
// os.Exit(1).
func (l *Logger) Fatalf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, FatalLevel).write(format, v...)
}
// Panicf logs a message with severity PANIC in format followed by a call to
// panic().
func (l *Logger) Panicf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, PanicLevel).write(format, v...)
}
// Errorf logs a message with severity ERROR in format.
func (l *Logger) Errorf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, ErrorLevel).write(format, v...)
}
// Warnf logs a message with severity WARN in format.
func (l *Logger) Warnf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, WarnLevel).write(format, v...)
}
// Infof logs a message with severity INFO in format.
func (l *Logger) Infof(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, InfoLevel).write(format, v...)
}
// Debugf logs a message with severity DEBUG in format.
func (l *Logger) Debugf(ctx context.Context, format string, v ...interface{}) {
l.event(ctx, DebugLevel).write(format, v...)
}
// FatalEvent returns a log event with severity FATAL.
func (l *Logger) FatalEvent(ctx context.Context) *Event {
return l.event(ctx, FatalLevel)
}
// PanicEvent returns a log event with severity PANIC.
func (l *Logger) PanicEvent(ctx context.Context) *Event {
return l.event(ctx, PanicLevel)
}
// ErrorEvent returns a log event with severity ERROR.
func (l *Logger) ErrorEvent(ctx context.Context) *Event {
return l.event(ctx, ErrorLevel)
}
// WarnEvent returns a log event with severity WARN.
func (l *Logger) WarnEvent(ctx context.Context) *Event {
return l.event(ctx, WarnLevel)
}
// InfoEvent returns a log event with severity INFO.
func (l *Logger) InfoEvent(ctx context.Context) *Event {
return l.event(ctx, InfoLevel)
}
// DebugEvent returns a log event with severity DEBUG.
func (l *Logger) DebugEvent(ctx context.Context) *Event {
return l.event(ctx, DebugLevel)
}
func (l *Logger) event(ctx context.Context, level Level) (e *Event) {
var ctxKeys *[]interface{}
var ctxKeysMap *map[interface{}]string
if len(l.ctxKeys) > 0 {
ctxKeys = &l.ctxKeys
}
if len(l.ctxKeysMap) > 0 {
ctxKeysMap = &l.ctxKeysMap
}
return newEvent(ctx, ctxKeys, ctxKeysMap, level, l.lw, level <= l.level, l.isCallerEnabled)
}
// NewLogger creates a new logger for given out and level, and the level is
// optional.
func NewLogger(out io.Writer, level ...string) (*Logger, error) {
return NewLoggerWithError(out, nil, level...)
}
// NewLoggerWithError creates a new logger for given out, err out, level, and the
// err out can be nil, and the level is optional.
func NewLoggerWithError(out, errOut io.Writer, level ...string) (l *Logger, err error) {
if out == nil {
return nil, errors.New("logger output must specified")
}
sw := &StandardWriter{w: out, ew: errOut, pid: os.Getpid()}
l = &Logger{lw: sw}
if len(level) == 1 {
if err = l.SetLevel(level[0]); err != nil {
return nil, err
}
}
return l, nil
}
// NewTerminalLogger creates a logger that write into terminal.
func NewTerminalLogger(level ...string) (*Logger, error) {
return NewLogger(os.Stdout, level...)
}
// NewBufferedTerminalLogger creates a buffered logger that write into terminal.
func NewBufferedTerminalLogger(level ...string) (*Logger, error) {
return NewLogger(bufio.NewWriter(os.Stdout), level...)
}
// NewFileLogger creates a logger that write into file.
func NewFileLogger(filePath string, level ...string) (*Logger, error) {
return NewFileLoggerWithError(filePath, "", level...)
}
// NewFileLoggerWithError creates a logger that write into files.
func NewFileLoggerWithError(filePath, errFilePath string, level ...string) (*Logger, error) {
if err := check.Dir(path.Dir(filePath)); err != nil {
return nil, err
}
if errFilePath != "" {
if err := check.Dir(path.Dir(errFilePath)); err != nil {
return nil, err
}
}
out, err := reopen.NewFileWriter(filePath)
if err != nil {
return nil, err
}
var errOut *reopen.FileWriter
if errFilePath != "" {
errOut, err = reopen.NewFileWriter(errFilePath)
if err != nil {
return nil, err
}
}
c := make(chan os.Signal)
go func() {
for {
select {
case <-c:
out.Reopen()
if errOut != nil {
errOut.Reopen()
}
}
}
}()
signal.Notify(c, syscall.SIGHUP)
if errOut == nil {
return NewLoggerWithError(out, nil, level...)
}
return NewLoggerWithError(out, errOut, level...)
}
// NewBufferedFileLogger creates a logger that write into file with buffer.
// The flushSeconds's unit is second.
func NewBufferedFileLogger(filePath string, flushInterval int, level ...string) (*Logger, error) {
return NewBufferedFileLoggerWithError(filePath, "", flushInterval, level...)
}
// NewBufferedFileLoggerWithError creates a logger that write into files with buffer.
// The flushSeconds's unit is second.
func NewBufferedFileLoggerWithError(filePath, errFilePath string, flushInterval int, level ...string) (*Logger, error) {
if err := check.Dir(path.Dir(filePath)); err != nil {
return nil, err
}
if errFilePath != "" {
if err := check.Dir(path.Dir(errFilePath)); err != nil {
return nil, err
}
}
if flushInterval == 0 {
flushInterval = 10
}
out, err := reopen.NewFileWriter(filePath)
if err != nil {
return nil, err
}
var errOut *reopen.FileWriter
if errFilePath != "" {
errOut, err = reopen.NewFileWriter(errFilePath)
if err != nil {
return nil, err
}
}
bufferedOut := reopen.NewBufferedFileWriter(out)
var bufferedErrOut *reopen.BufferedFileWriter
if errOut != nil {
bufferedErrOut = reopen.NewBufferedFileWriter(errOut)
}
c := make(chan os.Signal)
go func() {
for {
select {
case <-c:
bufferedOut.Reopen()
if bufferedErrOut != nil {
bufferedErrOut.Reopen()
}
case <-time.After(time.Duration(flushInterval) * time.Second):
bufferedOut.Flush()
if bufferedErrOut != nil {
bufferedErrOut.Flush()
}
}
}
}()
signal.Notify(c, syscall.SIGHUP)
if bufferedErrOut == nil {
return NewLoggerWithError(bufferedOut, nil, level...)
}
return NewLoggerWithError(bufferedOut, bufferedErrOut, level...)
}

View File

@@ -0,0 +1,83 @@
package log
import (
"io"
"time"
"github.com/pengsrc/go-shared/buffer"
"github.com/pengsrc/go-shared/convert"
)
// LevelWriter defines as interface a writer may implement in order
// to receive level information with payload.
type LevelWriter interface {
io.Writer
WriteLevel(level Level, message []byte) (n int, err error)
}
// Flusher defines a interface with Flush() method.
type Flusher interface {
Flush() error
}
// StandardWriter implements io.Writer{} and LevelWriter{} interface.
type StandardWriter struct {
w io.Writer
ew io.Writer // Writer for WARN, ERROR, FATAL, PANIC
dl Level // Default level
pid int
}
// Write implements the io.Writer{} interface.
func (sw *StandardWriter) Write(p []byte) (n int, err error) {
return sw.WriteLevel(sw.dl, p)
}
// WriteLevel implements the LevelWriter{} interface.
func (sw *StandardWriter) WriteLevel(level Level, message []byte) (n int, err error) {
levelString := level.String()
if len(levelString) == 4 {
levelString = " " + levelString
}
buf := buffer.GlobalBytesPool().Get()
defer buf.Free()
buf.AppendString("[")
buf.AppendTime(time.Now().UTC(), convert.ISO8601Milli)
buf.AppendString(" #")
buf.AppendInt(int64(sw.pid))
buf.AppendString("] ")
buf.AppendString(levelString)
buf.AppendString(" -- : ")
buf.AppendBytes(message)
buf.AppendString("\n")
if sw.ew != nil {
if level > MuteLevel && level <= WarnLevel {
n, err = sw.ew.Write(buf.Bytes())
if err != nil {
return
}
}
}
return sw.w.Write(buf.Bytes())
}
// Flush implements the Flusher{} interface.
func (sw *StandardWriter) Flush() (err error) {
if flusher, ok := sw.w.(Flusher); ok {
err = flusher.Flush()
if err != nil {
return err
}
}
if sw.ew != nil {
if flusher, ok := sw.ew.(Flusher); ok {
err = flusher.Flush()
return err
}
}
return nil
}

View File

@@ -0,0 +1,182 @@
package reopen
import (
"bufio"
"io"
"os"
"sync"
"time"
)
// Reopener interface defines something that can be reopened.
type Reopener interface {
Reopen() error
}
// Writer is a writer that also can be reopened.
type Writer interface {
Reopener
io.Writer
}
// WriteCloser is a io.WriteCloser that can also be reopened.
type WriteCloser interface {
Reopener
io.WriteCloser
}
// FileWriter that can also be reopened.
type FileWriter struct {
// Ensures close/reopen/write are not called at the same time, protects f
mu sync.Mutex
f *os.File
mode os.FileMode
name string
}
// Close calls the under lying File.Close().
func (f *FileWriter) Close() error {
f.mu.Lock()
err := f.f.Close()
f.mu.Unlock()
return err
}
// Reopen the file.
func (f *FileWriter) Reopen() error {
f.mu.Lock()
err := f.reopen()
f.mu.Unlock()
return err
}
// Write implements the stander io.Writer interface.
func (f *FileWriter) Write(p []byte) (int, error) {
f.mu.Lock()
n, err := f.f.Write(p)
f.mu.Unlock()
return n, err
}
// reopen with mutex free.
func (f *FileWriter) reopen() error {
if f.f != nil {
f.f.Close()
f.f = nil
}
ff, err := os.OpenFile(f.name, os.O_WRONLY|os.O_APPEND|os.O_CREATE, f.mode)
if err != nil {
f.f = nil
return err
}
f.f = ff
return nil
}
// NewFileWriter opens a file for appending and writing and can be reopened.
// It is a ReopenWriteCloser...
func NewFileWriter(name string) (*FileWriter, error) {
// Standard default mode
return NewFileWriterMode(name, 0644)
}
// NewFileWriterMode opens a Reopener file with a specific permission.
func NewFileWriterMode(name string, mode os.FileMode) (*FileWriter, error) {
writer := FileWriter{
f: nil,
name: name,
mode: mode,
}
err := writer.reopen()
if err != nil {
return nil, err
}
return &writer, nil
}
// BufferedFileWriter is buffer writer than can be reopened.
type BufferedFileWriter struct {
mu sync.Mutex
OrigWriter *FileWriter
BufWriter *bufio.Writer
}
// Reopen implement Reopener.
func (bw *BufferedFileWriter) Reopen() error {
bw.mu.Lock()
bw.BufWriter.Flush()
// Use non-mutex version since we are using this one.
err := bw.OrigWriter.reopen()
bw.BufWriter.Reset(io.Writer(bw.OrigWriter))
bw.mu.Unlock()
return err
}
// Close flushes the internal buffer and closes the destination file.
func (bw *BufferedFileWriter) Close() error {
bw.mu.Lock()
bw.BufWriter.Flush()
bw.OrigWriter.f.Close()
bw.mu.Unlock()
return nil
}
// Write implements io.Writer (and reopen.Writer).
func (bw *BufferedFileWriter) Write(p []byte) (int, error) {
bw.mu.Lock()
n, err := bw.BufWriter.Write(p)
// Special Case... if the used space in the buffer is LESS than
// the input, then we did a flush in the middle of the line
// and the full log line was not sent on its way.
if bw.BufWriter.Buffered() < len(p) {
bw.BufWriter.Flush()
}
bw.mu.Unlock()
return n, err
}
// Flush flushes the buffer.
func (bw *BufferedFileWriter) Flush() (err error) {
bw.mu.Lock()
defer bw.mu.Unlock()
if err = bw.BufWriter.Flush(); err != nil {
return err
}
if err = bw.OrigWriter.f.Sync(); err != nil {
return err
}
return
}
// flushDaemon periodically flushes the log file buffers.
func (bw *BufferedFileWriter) flushDaemon(interval time.Duration) {
for range time.NewTicker(interval).C {
bw.Flush()
}
}
// NewBufferedFileWriter opens a buffered file that is periodically flushed.
func NewBufferedFileWriter(w *FileWriter) *BufferedFileWriter {
return NewBufferedFileWriterSize(w, bufferSize, flushInterval)
}
// NewBufferedFileWriterSize opens a buffered file with the given size that is periodically
// flushed on the given interval.
func NewBufferedFileWriterSize(w *FileWriter, size int, flush time.Duration) *BufferedFileWriter {
bw := BufferedFileWriter{
OrigWriter: w,
BufWriter: bufio.NewWriterSize(w, size),
}
go bw.flushDaemon(flush)
return &bw
}
const bufferSize = 256 * 1024
const flushInterval = 30 * time.Second