2021-04-18 15:41:13 -04:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
|
|
|
//
|
|
|
|
// This file is part of MinIO Object Storage stack
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2015-09-18 02:49:06 -04:00
|
|
|
|
2018-04-05 18:04:40 -04:00
|
|
|
package logger
|
2015-09-18 02:49:06 -04:00
|
|
|
|
|
|
|
import (
|
2018-04-05 18:04:40 -04:00
|
|
|
"context"
|
2018-12-18 19:08:11 -05:00
|
|
|
"encoding/hex"
|
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
|
|
|
"errors"
|
2016-10-11 03:50:27 -04:00
|
|
|
"fmt"
|
2018-03-30 22:13:25 -04:00
|
|
|
"go/build"
|
2018-01-17 10:24:46 -05:00
|
|
|
"path/filepath"
|
2018-12-18 19:08:11 -05:00
|
|
|
"reflect"
|
2016-10-11 03:50:27 -04:00
|
|
|
"runtime"
|
2024-03-06 06:43:16 -05:00
|
|
|
"sort"
|
2016-05-25 05:32:35 -04:00
|
|
|
"strings"
|
2018-01-17 10:24:46 -05:00
|
|
|
"time"
|
2015-09-18 02:49:06 -04:00
|
|
|
|
2018-12-18 19:08:11 -05:00
|
|
|
"github.com/minio/highwayhash"
|
2023-06-19 20:53:08 -04:00
|
|
|
"github.com/minio/madmin-go/v3"
|
2022-02-23 16:36:01 -05:00
|
|
|
xhttp "github.com/minio/minio/internal/http"
|
2023-09-04 15:57:37 -04:00
|
|
|
"github.com/minio/pkg/v2/logger/message/log"
|
2018-04-05 18:04:40 -04:00
|
|
|
)
|
|
|
|
|
2022-01-02 12:15:06 -05:00
|
|
|
// HighwayHash key for logging in anonymous mode
|
|
|
|
var magicHighwayHash256Key = []byte("\x4b\xe7\x34\xfa\x8e\x23\x8a\xcd\x26\x3e\x83\xe6\xbb\x96\x85\x52\x04\x0f\x93\x5d\xa3\x9f\x44\x14\x97\xe0\x9d\x13\x22\xde\x36\xa0")
|
2018-12-18 19:08:11 -05:00
|
|
|
|
2018-01-17 10:24:46 -05:00
|
|
|
// Enumerated level types
|
|
|
|
const (
|
2024-02-01 19:13:57 -05:00
|
|
|
// Log types errors
|
|
|
|
FatalKind = madmin.LogKindFatal
|
|
|
|
WarningKind = madmin.LogKindWarning
|
|
|
|
ErrorKind = madmin.LogKindError
|
|
|
|
EventKind = madmin.LogKindEvent
|
|
|
|
InfoKind = madmin.LogKindInfo
|
2018-01-17 10:24:46 -05:00
|
|
|
)
|
2017-03-23 19:36:00 -04:00
|
|
|
|
2024-02-01 19:13:57 -05:00
|
|
|
// DisableErrorLog avoids printing error/event/info kind of logs
|
|
|
|
var DisableErrorLog = false
|
2022-08-30 11:23:40 -04:00
|
|
|
|
2018-11-19 17:47:03 -05:00
|
|
|
var trimStrings []string
|
|
|
|
|
|
|
|
// TimeFormat - logging time format.
|
|
|
|
const TimeFormat string = "15:04:05 MST 01/02/2006"
|
2018-04-05 18:04:40 -04:00
|
|
|
|
|
|
|
var matchingFuncNames = [...]string{
|
|
|
|
"http.HandlerFunc.ServeHTTP",
|
|
|
|
"cmd.serverMain",
|
|
|
|
// add more here ..
|
|
|
|
}
|
|
|
|
|
2018-11-19 17:47:03 -05:00
|
|
|
// quietFlag: Hide startup messages if enabled
|
2018-04-05 18:04:40 -04:00
|
|
|
// jsonFlag: Display in JSON format, if enabled
|
|
|
|
var (
|
2018-12-18 19:08:11 -05:00
|
|
|
quietFlag, jsonFlag, anonFlag bool
|
2018-05-08 22:04:36 -04:00
|
|
|
// Custom function to format error
|
|
|
|
errorFmtFunc func(string, error, bool) string
|
2018-04-05 18:04:40 -04:00
|
|
|
)
|
|
|
|
|
2018-01-17 10:24:46 -05:00
|
|
|
// EnableQuiet - turns quiet option on.
|
2018-04-05 18:04:40 -04:00
|
|
|
func EnableQuiet() {
|
2018-11-19 17:47:03 -05:00
|
|
|
quietFlag = true
|
2017-03-23 19:36:00 -04:00
|
|
|
}
|
|
|
|
|
2018-01-17 10:24:46 -05:00
|
|
|
// EnableJSON - outputs logs in json format.
|
2018-04-05 18:04:40 -04:00
|
|
|
func EnableJSON() {
|
|
|
|
jsonFlag = true
|
2018-11-19 17:47:03 -05:00
|
|
|
quietFlag = true
|
|
|
|
}
|
|
|
|
|
2018-12-18 19:08:11 -05:00
|
|
|
// EnableAnonymous - turns anonymous flag
|
|
|
|
// to avoid printing sensitive information.
|
|
|
|
func EnableAnonymous() {
|
|
|
|
anonFlag = true
|
|
|
|
}
|
|
|
|
|
2018-11-19 17:47:03 -05:00
|
|
|
// IsJSON - returns true if jsonFlag is true
|
|
|
|
func IsJSON() bool {
|
|
|
|
return jsonFlag
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsQuiet - returns true if quietFlag is true
|
|
|
|
func IsQuiet() bool {
|
|
|
|
return quietFlag
|
2017-03-23 19:36:00 -04:00
|
|
|
}
|
|
|
|
|
2019-10-04 13:35:33 -04:00
|
|
|
// RegisterError registers the specified rendering function. This latter
|
2018-05-08 22:04:36 -04:00
|
|
|
// will be called for a pretty rendering of fatal errors.
|
2019-10-04 13:35:33 -04:00
|
|
|
func RegisterError(f func(string, error, bool) string) {
|
2018-05-08 22:04:36 -04:00
|
|
|
errorFmtFunc = f
|
|
|
|
}
|
|
|
|
|
2024-03-06 06:43:16 -05:00
|
|
|
// uniq swaps away duplicate elements in data, returning the size of the
|
|
|
|
// unique set. data is expected to be pre-sorted, and the resulting set in
|
|
|
|
// the range [0:size] will remain in sorted order. Uniq, following a
|
|
|
|
// sort.Sort call, can be used to prepare arbitrary inputs for use as sets.
|
|
|
|
func uniq(data sort.Interface) (size int) {
|
|
|
|
p, l := 0, data.Len()
|
|
|
|
if l <= 1 {
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
for i := 1; i < l; i++ {
|
|
|
|
if !data.Less(p, i) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
p++
|
|
|
|
if p < i {
|
|
|
|
data.Swap(p, i)
|
2018-11-19 17:47:03 -05:00
|
|
|
}
|
|
|
|
}
|
2024-03-06 06:43:16 -05:00
|
|
|
return p + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove any duplicates and return unique entries.
|
|
|
|
func uniqueEntries(paths []string) []string {
|
|
|
|
sort.Strings(paths)
|
|
|
|
n := uniq(sort.StringSlice(paths))
|
|
|
|
return paths[:n]
|
2018-11-19 17:47:03 -05:00
|
|
|
}
|
|
|
|
|
2018-04-05 18:04:40 -04:00
|
|
|
// Init sets the trimStrings to possible GOPATHs
|
|
|
|
// and GOROOT directories. Also append github.com/minio/minio
|
|
|
|
// This is done to clean up the filename, when stack trace is
|
|
|
|
// displayed when an error happens.
|
2018-06-18 15:04:46 -04:00
|
|
|
func Init(goPath string, goRoot string) {
|
2018-01-17 10:24:46 -05:00
|
|
|
var goPathList []string
|
2018-06-18 15:04:46 -04:00
|
|
|
var goRootList []string
|
2018-03-30 22:13:25 -04:00
|
|
|
var defaultgoPathList []string
|
2018-06-18 15:04:46 -04:00
|
|
|
var defaultgoRootList []string
|
2024-02-22 01:26:06 -05:00
|
|
|
pathSeparator := ":"
|
2018-01-17 10:24:46 -05:00
|
|
|
// Add all possible GOPATH paths into trimStrings
|
|
|
|
// Split GOPATH depending on the OS type
|
|
|
|
if runtime.GOOS == "windows" {
|
2024-02-22 01:26:06 -05:00
|
|
|
pathSeparator = ";"
|
2016-11-23 14:35:04 -05:00
|
|
|
}
|
2017-03-23 19:36:00 -04:00
|
|
|
|
2024-02-22 01:26:06 -05:00
|
|
|
goPathList = strings.Split(goPath, pathSeparator)
|
|
|
|
goRootList = strings.Split(goRoot, pathSeparator)
|
|
|
|
defaultgoPathList = strings.Split(build.Default.GOPATH, pathSeparator)
|
|
|
|
defaultgoRootList = strings.Split(build.Default.GOROOT, pathSeparator)
|
2018-06-18 15:04:46 -04:00
|
|
|
|
2018-01-17 10:24:46 -05:00
|
|
|
// Add trim string "{GOROOT}/src/" into trimStrings
|
|
|
|
trimStrings = []string{filepath.Join(runtime.GOROOT(), "src") + string(filepath.Separator)}
|
2017-03-23 19:36:00 -04:00
|
|
|
|
2018-01-17 10:24:46 -05:00
|
|
|
// Add all possible path from GOPATH=path1:path2...:pathN
|
|
|
|
// as "{path#}/src/" into trimStrings
|
|
|
|
for _, goPathString := range goPathList {
|
|
|
|
trimStrings = append(trimStrings, filepath.Join(goPathString, "src")+string(filepath.Separator))
|
|
|
|
}
|
2018-03-30 22:13:25 -04:00
|
|
|
|
2018-06-18 15:04:46 -04:00
|
|
|
for _, goRootString := range goRootList {
|
|
|
|
trimStrings = append(trimStrings, filepath.Join(goRootString, "src")+string(filepath.Separator))
|
|
|
|
}
|
|
|
|
|
2018-03-30 22:13:25 -04:00
|
|
|
for _, defaultgoPathString := range defaultgoPathList {
|
|
|
|
trimStrings = append(trimStrings, filepath.Join(defaultgoPathString, "src")+string(filepath.Separator))
|
|
|
|
}
|
|
|
|
|
2018-06-18 15:04:46 -04:00
|
|
|
for _, defaultgoRootString := range defaultgoRootList {
|
|
|
|
trimStrings = append(trimStrings, filepath.Join(defaultgoRootString, "src")+string(filepath.Separator))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove duplicate entries.
|
|
|
|
trimStrings = uniqueEntries(trimStrings)
|
|
|
|
|
2018-01-17 10:24:46 -05:00
|
|
|
// Add "github.com/minio/minio" as the last to cover
|
|
|
|
// paths like "{GOROOT}/src/github.com/minio/minio"
|
|
|
|
// and "{GOPATH}/src/github.com/minio/minio"
|
|
|
|
trimStrings = append(trimStrings, filepath.Join("github.com", "minio", "minio")+string(filepath.Separator))
|
2015-09-19 05:36:50 -04:00
|
|
|
}
|
|
|
|
|
2018-01-17 10:24:46 -05:00
|
|
|
func trimTrace(f string) string {
|
|
|
|
for _, trimString := range trimStrings {
|
|
|
|
f = strings.TrimPrefix(filepath.ToSlash(f), filepath.ToSlash(trimString))
|
2017-03-23 19:36:00 -04:00
|
|
|
}
|
2018-01-17 10:24:46 -05:00
|
|
|
return filepath.FromSlash(f)
|
|
|
|
}
|
2017-03-23 19:36:00 -04:00
|
|
|
|
2018-05-08 22:04:36 -04:00
|
|
|
func getSource(level int) string {
|
|
|
|
pc, file, lineNumber, ok := runtime.Caller(level)
|
2018-04-10 12:37:14 -04:00
|
|
|
if ok {
|
|
|
|
// Clean up the common prefixes
|
|
|
|
file = trimTrace(file)
|
|
|
|
_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
|
|
|
|
return fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName)
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2018-01-17 10:24:46 -05:00
|
|
|
// getTrace method - creates and returns stack trace
|
|
|
|
func getTrace(traceLevel int) []string {
|
|
|
|
var trace []string
|
|
|
|
pc, file, lineNumber, ok := runtime.Caller(traceLevel)
|
|
|
|
|
2018-06-18 15:04:46 -04:00
|
|
|
for ok && file != "" {
|
2018-01-17 10:24:46 -05:00
|
|
|
// Clean up the common prefixes
|
|
|
|
file = trimTrace(file)
|
|
|
|
// Get the function name
|
|
|
|
_, funcName := filepath.Split(runtime.FuncForPC(pc).Name())
|
|
|
|
// Skip duplicate traces that start with file name, "<autogenerated>"
|
|
|
|
// and also skip traces with function name that starts with "runtime."
|
|
|
|
if !strings.HasPrefix(file, "<autogenerated>") &&
|
|
|
|
!strings.HasPrefix(funcName, "runtime.") {
|
|
|
|
// Form and append a line of stack trace into a
|
|
|
|
// collection, 'trace', to build full stack trace
|
|
|
|
trace = append(trace, fmt.Sprintf("%v:%v:%v()", file, lineNumber, funcName))
|
2018-04-05 18:04:40 -04:00
|
|
|
|
|
|
|
// Ignore trace logs beyond the following conditions
|
|
|
|
for _, name := range matchingFuncNames {
|
|
|
|
if funcName == name {
|
|
|
|
return trace
|
|
|
|
}
|
|
|
|
}
|
2018-01-17 10:24:46 -05:00
|
|
|
}
|
|
|
|
traceLevel++
|
|
|
|
// Read stack trace information from PC
|
|
|
|
pc, file, lineNumber, ok = runtime.Caller(traceLevel)
|
|
|
|
}
|
|
|
|
return trace
|
2017-03-23 19:36:00 -04:00
|
|
|
}
|
|
|
|
|
2023-03-20 04:40:24 -04:00
|
|
|
// HashString - return the highway hash of the passed string
|
|
|
|
func HashString(input string) string {
|
2021-11-09 18:28:08 -05:00
|
|
|
hh, _ := highwayhash.New(magicHighwayHash256Key)
|
|
|
|
hh.Write([]byte(input))
|
|
|
|
return hex.EncodeToString(hh.Sum(nil))
|
2018-12-18 19:08:11 -05:00
|
|
|
}
|
|
|
|
|
2018-08-14 16:58:48 -04:00
|
|
|
// LogAlwaysIf prints a detailed error message during
|
2018-05-08 22:04:36 -04:00
|
|
|
// the execution of the server.
|
2019-10-11 21:50:54 -04:00
|
|
|
func LogAlwaysIf(ctx context.Context, err error, errKind ...interface{}) {
|
2018-08-14 16:58:48 -04:00
|
|
|
if err == nil {
|
2018-04-27 18:03:19 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-10-11 21:50:54 -04:00
|
|
|
logIf(ctx, err, errKind...)
|
2018-08-14 16:58:48 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// LogIf prints a detailed error message during
|
|
|
|
// the execution of the server, if it is not an
|
|
|
|
// ignored error.
|
2019-10-11 21:50:54 -04:00
|
|
|
func LogIf(ctx context.Context, err error, errKind ...interface{}) {
|
2022-07-25 20:53:03 -04:00
|
|
|
if logIgnoreError(err) {
|
2020-10-30 17:55:50 -04:00
|
|
|
return
|
|
|
|
}
|
|
|
|
logIf(ctx, err, errKind...)
|
2018-08-14 16:58:48 -04:00
|
|
|
}
|
|
|
|
|
perf: websocket grid connectivity for all internode communication (#18461)
This PR adds a WebSocket grid feature that allows servers to communicate via
a single two-way connection.
There are two request types:
* Single requests, which are `[]byte => ([]byte, error)`. This is for efficient small
roundtrips with small payloads.
* Streaming requests which are `[]byte, chan []byte => chan []byte (and error)`,
which allows for different combinations of full two-way streams with an initial payload.
Only a single stream is created between two machines - and there is, as such, no
server/client relation since both sides can initiate and handle requests. Which server
initiates the request is decided deterministically on the server names.
Requests are made through a mux client and server, which handles message
passing, congestion, cancelation, timeouts, etc.
If a connection is lost, all requests are canceled, and the calling server will try
to reconnect. Registered handlers can operate directly on byte
slices or use a higher-level generics abstraction.
There is no versioning of handlers/clients, and incompatible changes should
be handled by adding new handlers.
The request path can be changed to a new one for any protocol changes.
First, all servers create a "Manager." The manager must know its address
as well as all remote addresses. This will manage all connections.
To get a connection to any remote, ask the manager to provide it given
the remote address using.
```
func (m *Manager) Connection(host string) *Connection
```
All serverside handlers must also be registered on the manager. This will
make sure that all incoming requests are served. The number of in-flight
requests and responses must also be given for streaming requests.
The "Connection" returned manages the mux-clients. Requests issued
to the connection will be sent to the remote.
* `func (c *Connection) Request(ctx context.Context, h HandlerID, req []byte) ([]byte, error)`
performs a single request and returns the result. Any deadline provided on the request is
forwarded to the server, and canceling the context will make the function return at once.
* `func (c *Connection) NewStream(ctx context.Context, h HandlerID, payload []byte) (st *Stream, err error)`
will initiate a remote call and send the initial payload.
```Go
// A Stream is a two-way stream.
// All responses *must* be read by the caller.
// If the call is canceled through the context,
//The appropriate error will be returned.
type Stream struct {
// Responses from the remote server.
// Channel will be closed after an error or when the remote closes.
// All responses *must* be read by the caller until either an error is returned or the channel is closed.
// Canceling the context will cause the context cancellation error to be returned.
Responses <-chan Response
// Requests sent to the server.
// If the handler is defined with 0 incoming capacity this will be nil.
// Channel *must* be closed to signal the end of the stream.
// If the request context is canceled, the stream will no longer process requests.
Requests chan<- []byte
}
type Response struct {
Msg []byte
Err error
}
```
There are generic versions of the server/client handlers that allow the use of type
safe implementations for data types that support msgpack marshal/unmarshal.
2023-11-20 20:09:35 -05:00
|
|
|
// LogIfNot prints a detailed error message during
|
|
|
|
// the execution of the server, if it is not an ignored error (either internal or given).
|
|
|
|
func LogIfNot(ctx context.Context, err error, ignored ...error) {
|
|
|
|
if logIgnoreError(err) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
for _, ignore := range ignored {
|
|
|
|
if errors.Is(err, ignore) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logIf(ctx, err)
|
|
|
|
}
|
|
|
|
|
2022-05-12 10:20:58 -04:00
|
|
|
func errToEntry(ctx context.Context, err error, errKind ...interface{}) log.Entry {
|
2024-02-15 18:13:30 -05:00
|
|
|
var l string
|
|
|
|
if anonFlag {
|
|
|
|
l = reflect.TypeOf(err).String()
|
|
|
|
} else {
|
|
|
|
l = fmt.Sprintf("%v (%T)", err, err)
|
|
|
|
}
|
|
|
|
return buildLogEntry(ctx, l, getTrace(3), errKind...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func logToEntry(ctx context.Context, message string, errKind ...interface{}) log.Entry {
|
|
|
|
return buildLogEntry(ctx, message, nil, errKind...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func buildLogEntry(ctx context.Context, message string, trace []string, errKind ...interface{}) log.Entry {
|
2024-02-01 19:13:57 -05:00
|
|
|
logKind := madmin.LogKindError
|
2019-10-11 21:50:54 -04:00
|
|
|
if len(errKind) > 0 {
|
2022-07-05 17:45:49 -04:00
|
|
|
if ek, ok := errKind[0].(madmin.LogKind); ok {
|
|
|
|
logKind = ek
|
2019-10-11 21:50:54 -04:00
|
|
|
}
|
|
|
|
}
|
2024-02-01 19:13:57 -05:00
|
|
|
|
2018-04-05 18:04:40 -04:00
|
|
|
req := GetReqInfo(ctx)
|
|
|
|
if req == nil {
|
2023-08-21 17:25:24 -04:00
|
|
|
req = &ReqInfo{
|
|
|
|
API: "SYSTEM",
|
|
|
|
RequestID: fmt.Sprintf("%X", time.Now().UTC().UnixNano()),
|
|
|
|
}
|
2018-04-05 18:04:40 -04:00
|
|
|
}
|
2022-06-30 13:48:50 -04:00
|
|
|
req.RLock()
|
|
|
|
defer req.RUnlock()
|
2018-04-05 18:04:40 -04:00
|
|
|
|
|
|
|
API := "SYSTEM"
|
|
|
|
if req.API != "" {
|
|
|
|
API = req.API
|
|
|
|
}
|
|
|
|
|
2023-05-09 11:11:32 -04:00
|
|
|
// Copy tags. We hold read lock already.
|
|
|
|
tags := make(map[string]interface{}, len(req.tags))
|
|
|
|
for _, entry := range req.tags {
|
2018-04-05 18:04:40 -04:00
|
|
|
tags[entry.Key] = entry.Val
|
|
|
|
}
|
|
|
|
|
2018-05-08 22:04:36 -04:00
|
|
|
// Get the cause for the Error
|
2022-11-11 15:42:15 -05:00
|
|
|
deploymentID := req.DeploymentID
|
2019-07-01 15:22:01 -04:00
|
|
|
if req.DeploymentID == "" {
|
2022-11-11 15:42:15 -05:00
|
|
|
deploymentID = xhttp.GlobalDeploymentID
|
2019-07-01 15:22:01 -04:00
|
|
|
}
|
2022-01-03 04:28:52 -05:00
|
|
|
|
|
|
|
objects := make([]log.ObjectVersion, 0, len(req.Objects))
|
|
|
|
for _, ov := range req.Objects {
|
|
|
|
objects = append(objects, log.ObjectVersion{
|
|
|
|
ObjectName: ov.ObjectName,
|
|
|
|
VersionID: ov.VersionID,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-11-19 17:47:03 -05:00
|
|
|
entry := log.Entry{
|
2022-11-11 15:42:15 -05:00
|
|
|
DeploymentID: deploymentID,
|
2024-02-01 19:13:57 -05:00
|
|
|
Level: logKind,
|
2018-07-19 18:55:06 -04:00
|
|
|
RemoteHost: req.RemoteHost,
|
2019-07-18 12:58:37 -04:00
|
|
|
Host: req.Host,
|
2018-07-19 18:55:06 -04:00
|
|
|
RequestID: req.RequestID,
|
|
|
|
UserAgent: req.UserAgent,
|
2021-12-23 18:33:54 -05:00
|
|
|
Time: time.Now().UTC(),
|
2018-11-19 17:47:03 -05:00
|
|
|
API: &log.API{
|
|
|
|
Name: API,
|
|
|
|
Args: &log.Args{
|
2022-01-03 04:28:52 -05:00
|
|
|
Bucket: req.BucketName,
|
|
|
|
Object: req.ObjectName,
|
|
|
|
VersionID: req.VersionID,
|
|
|
|
Objects: objects,
|
2018-11-19 17:47:03 -05:00
|
|
|
},
|
|
|
|
},
|
2024-02-15 18:13:30 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if trace != nil {
|
|
|
|
entry.Trace = &log.Trace{
|
2018-11-19 17:47:03 -05:00
|
|
|
Message: message,
|
|
|
|
Source: trace,
|
|
|
|
Variables: tags,
|
2024-02-15 18:13:30 -05:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
entry.Message = message
|
2018-07-19 18:55:06 -04:00
|
|
|
}
|
2018-04-05 18:04:40 -04:00
|
|
|
|
2018-12-18 19:08:11 -05:00
|
|
|
if anonFlag {
|
2023-03-20 04:40:24 -04:00
|
|
|
entry.API.Args.Bucket = HashString(entry.API.Args.Bucket)
|
|
|
|
entry.API.Args.Object = HashString(entry.API.Args.Object)
|
|
|
|
entry.RemoteHost = HashString(entry.RemoteHost)
|
2024-03-21 13:19:36 -04:00
|
|
|
if entry.Trace != nil {
|
|
|
|
entry.Trace.Variables = make(map[string]interface{})
|
|
|
|
}
|
2018-12-18 19:08:11 -05:00
|
|
|
}
|
|
|
|
|
2022-05-12 10:20:58 -04:00
|
|
|
return entry
|
|
|
|
}
|
|
|
|
|
|
|
|
// consoleLogIf prints a detailed error message during
|
|
|
|
// the execution of the server.
|
|
|
|
func consoleLogIf(ctx context.Context, err error, errKind ...interface{}) {
|
2024-02-01 19:13:57 -05:00
|
|
|
if DisableErrorLog {
|
2022-05-12 10:20:58 -04:00
|
|
|
return
|
|
|
|
}
|
2024-03-06 06:43:16 -05:00
|
|
|
if err == nil {
|
|
|
|
return
|
|
|
|
}
|
2022-05-12 10:20:58 -04:00
|
|
|
if consoleTgt != nil {
|
2024-03-06 06:43:16 -05:00
|
|
|
consoleTgt.Send(ctx, errToEntry(ctx, err, errKind...))
|
2022-05-12 10:20:58 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// logIf prints a detailed error message during
|
|
|
|
// the execution of the server.
|
|
|
|
func logIf(ctx context.Context, err error, errKind ...interface{}) {
|
2024-02-01 19:13:57 -05:00
|
|
|
if DisableErrorLog {
|
2022-05-12 10:20:58 -04:00
|
|
|
return
|
|
|
|
}
|
2024-02-15 18:13:30 -05:00
|
|
|
if err == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
entry := errToEntry(ctx, err, errKind...)
|
|
|
|
sendLog(ctx, entry)
|
|
|
|
}
|
2022-05-12 10:20:58 -04:00
|
|
|
|
2024-02-15 18:13:30 -05:00
|
|
|
func sendLog(ctx context.Context, entry log.Entry) {
|
2022-05-12 10:20:58 -04:00
|
|
|
systemTgts := SystemTargets()
|
|
|
|
if len(systemTgts) == 0 {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-07-19 18:55:06 -04:00
|
|
|
// Iterate over all logger targets to send the log entry
|
2022-05-12 10:20:58 -04:00
|
|
|
for _, t := range systemTgts {
|
2023-05-09 00:20:31 -04:00
|
|
|
if err := t.Send(ctx, entry); err != nil {
|
2024-02-15 18:13:30 -05:00
|
|
|
if consoleTgt != nil { // Sending to the console never fails
|
2022-03-15 20:45:51 -04:00
|
|
|
entry.Trace.Message = fmt.Sprintf("event(%#v) was not sent to Logger target (%#v): %#v", entry, t, err)
|
2023-05-09 00:20:31 -04:00
|
|
|
consoleTgt.Send(ctx, entry)
|
2022-03-15 20:45:51 -04:00
|
|
|
}
|
2021-12-20 16:16:53 -05:00
|
|
|
}
|
2018-04-05 18:04:40 -04:00
|
|
|
}
|
2016-11-23 19:36:26 -05:00
|
|
|
}
|
2018-04-10 12:37:14 -04:00
|
|
|
|
2024-02-15 18:13:30 -05:00
|
|
|
// Event sends a event log to log targets
|
|
|
|
func Event(ctx context.Context, msg string, args ...interface{}) {
|
|
|
|
if DisableErrorLog {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
entry := logToEntry(ctx, fmt.Sprintf(msg, args...), EventKind)
|
|
|
|
sendLog(ctx, entry)
|
|
|
|
}
|
|
|
|
|
2018-06-25 16:51:49 -04:00
|
|
|
// ErrCritical is the value panic'd whenever CriticalIf is called.
|
|
|
|
var ErrCritical struct{}
|
|
|
|
|
|
|
|
// CriticalIf logs the provided error on the console. It fails the
|
|
|
|
// current go-routine by causing a `panic(ErrCritical)`.
|
2019-10-11 21:50:54 -04:00
|
|
|
func CriticalIf(ctx context.Context, err error, errKind ...interface{}) {
|
2018-04-19 20:24:43 -04:00
|
|
|
if err != nil {
|
2019-10-11 21:50:54 -04:00
|
|
|
LogIf(ctx, err, errKind...)
|
2018-06-25 16:51:49 -04:00
|
|
|
panic(ErrCritical)
|
2018-04-19 20:24:43 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-08 22:04:36 -04:00
|
|
|
// FatalIf is similar to Fatal() but it ignores passed nil error
|
2018-04-10 12:37:14 -04:00
|
|
|
func FatalIf(err error, msg string, data ...interface{}) {
|
2018-05-08 22:04:36 -04:00
|
|
|
if err == nil {
|
|
|
|
return
|
2018-04-10 12:37:14 -04:00
|
|
|
}
|
2018-05-08 22:04:36 -04:00
|
|
|
fatal(err, msg, data...)
|
|
|
|
}
|