diff --git a/cmd/admin-handlers.go b/cmd/admin-handlers.go index dd51d3621..6e98ea8b3 100644 --- a/cmd/admin-handlers.go +++ b/cmd/admin-handlers.go @@ -194,7 +194,7 @@ func (a adminAPIHandlers) ServiceActionHandler(w http.ResponseWriter, r *http.Re case madmin.ServiceActionStop: serviceSig = serviceStop default: - logger.LogIf(ctx, fmt.Errorf("Unrecognized service action %s requested", action)) + logger.LogIf(ctx, fmt.Errorf("Unrecognized service action %s requested", action), logger.Application) writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL) return } @@ -705,7 +705,7 @@ func extractHealInitParams(vars map[string]string, qParms url.Values, r io.Reade if hip.clientToken == "" { jerr := json.NewDecoder(r).Decode(&hip.hs) if jerr != nil { - logger.LogIf(context.Background(), jerr) + logger.LogIf(context.Background(), jerr, logger.Application) err = ErrRequestBodyParse return } @@ -1517,7 +1517,7 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques password := globalServerConfig.GetCredential().SecretKey configBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength)) if err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL) return } @@ -1525,7 +1525,7 @@ func (a adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http.Reques // Validate JSON provided in the request body: check the // client has not sent JSON objects with duplicate keys. if err = quick.CheckDuplicateKeys(string(configBytes)); err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrAdminConfigBadJSON), r.URL) return } @@ -1656,6 +1656,13 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque if err != nil { limitLines = 10 } + + logKind := r.URL.Query().Get("logType") + if logKind == "" { + logKind = string(logger.All) + } + logKind = strings.ToUpper(logKind) + // Avoid reusing tcp connection if read timeout is hit // This is needed to make r.Context().Done() work as // expected in case of read timeout @@ -1672,7 +1679,7 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque return } - globalConsoleSys.Subscribe(logCh, doneCh, node, limitLines, nil) + globalConsoleSys.Subscribe(logCh, doneCh, node, limitLines, logKind, nil) for _, peer := range peers { if node == "" || strings.EqualFold(peer.host.Name, node) { @@ -1689,7 +1696,7 @@ func (a adminAPIHandlers) ConsoleLogHandler(w http.ResponseWriter, r *http.Reque select { case entry := <-logCh: log := entry.(madmin.LogInfo) - if log.SendLog(node) { + if log.SendLog(node, logKind) { if err := enc.Encode(log); err != nil { return } diff --git a/cmd/auth-handler.go b/cmd/auth-handler.go index ef1cf8031..17cef185c 100644 --- a/cmd/auth-handler.go +++ b/cmd/auth-handler.go @@ -142,7 +142,7 @@ func checkAdminRequestAuthType(ctx context.Context, r *http.Request, region stri if s3Err != ErrNone { reqInfo := (&logger.ReqInfo{}).AppendTags("requestHeaders", dumpRequest(r)) ctx := logger.SetReqInfo(ctx, reqInfo) - logger.LogIf(ctx, errors.New(getAPIError(s3Err).Description)) + logger.LogIf(ctx, errors.New(getAPIError(s3Err).Description), logger.Application) } return s3Err } @@ -235,7 +235,7 @@ func getClaimsFromToken(r *http.Request) (map[string]interface{}, error) { if err != nil { // Base64 decoding fails, we should log to indicate // something is malforming the request sent by client. - logger.LogIf(context.Background(), err) + logger.LogIf(context.Background(), err, logger.Application) return nil, errAuthentication } claims[iampolicy.SessionPolicyName] = string(spBytes) @@ -312,7 +312,7 @@ func checkRequestAuthTypeToAccessKey(ctx context.Context, r *http.Request, actio // To extract region from XML in request body, get copy of request body. payload, err := ioutil.ReadAll(io.LimitReader(r.Body, maxLocationConstraintSize)) if err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) return accessKey, owner, ErrMalformedXML } diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 99aebe75c..ac9e60425 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -354,7 +354,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, // Read incoming body XML bytes. if _, err := io.ReadFull(r.Body, deleteXMLBytes); err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) writeErrorResponse(ctx, w, toAdminAPIErr(ctx, err), r.URL, guessIsBrowserReq(r)) return } @@ -362,7 +362,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, // Unmarshal list of keys to be deleted. deleteObjects := &DeleteObjectsRequest{} if err := xml.Unmarshal(deleteXMLBytes, deleteObjects); err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedXML), r.URL, guessIsBrowserReq(r)) return } @@ -596,7 +596,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h // Read multipart data and save in memory and in the disk if needed form, err := reader.ReadForm(maxFormMemory) if err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL, guessIsBrowserReq(r)) return } @@ -607,7 +607,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h // Extract all form fields fileBody, fileName, fileSize, formValues, err := extractPostPolicyFormValues(ctx, form) if err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMalformedPOSTRequest), r.URL, guessIsBrowserReq(r)) return } diff --git a/cmd/common-main.go b/cmd/common-main.go index bbb7fd5f3..8f8e375ad 100644 --- a/cmd/common-main.go +++ b/cmd/common-main.go @@ -254,7 +254,7 @@ func handleCommonEnvVars() { func logStartupMessage(msg string, data ...interface{}) { if globalConsoleSys != nil { - globalConsoleSys.Send(msg) + globalConsoleSys.Send(msg, string(logger.All)) } logger.StartupMessage(msg, data...) } diff --git a/cmd/config-current.go b/cmd/config-current.go index 03f1314ad..66bf1bcb4 100644 --- a/cmd/config-current.go +++ b/cmd/config-current.go @@ -336,14 +336,14 @@ func (s *serverConfig) lookupConfigs() { for _, l := range s.Logger.HTTP { if l.Enabled { // Enable http logging - logger.AddTarget(http.New(l.Endpoint, loggerUserAgent, NewCustomHTTPTransport())) + logger.AddTarget(http.New(l.Endpoint, loggerUserAgent, string(logger.All), NewCustomHTTPTransport())) } } for _, l := range s.Logger.Audit { if l.Enabled { // Enable http audit logging - logger.AddAuditTarget(http.New(l.Endpoint, loggerUserAgent, NewCustomHTTPTransport())) + logger.AddAuditTarget(http.New(l.Endpoint, loggerUserAgent, string(logger.All), NewCustomHTTPTransport())) } } diff --git a/cmd/consolelogger.go b/cmd/consolelogger.go index 12a12384f..8a09ec684 100644 --- a/cmd/consolelogger.go +++ b/cmd/consolelogger.go @@ -66,7 +66,7 @@ func (sys *HTTPConsoleLoggerSys) HasLogListeners() bool { } // Subscribe starts console logging for this node. -func (sys *HTTPConsoleLoggerSys) Subscribe(subCh chan interface{}, doneCh chan struct{}, node string, last int, filter func(entry interface{}) bool) { +func (sys *HTTPConsoleLoggerSys) Subscribe(subCh chan interface{}, doneCh chan struct{}, node string, last int, logKind string, filter func(entry interface{}) bool) { // Enable console logging for remote client even if local console logging is disabled in the config. if !globalServerConfig.Logger.Console.Enabled && !sys.pubsub.HasSubscribers() { logger.AddTarget(globalConsoleSys.Console()) @@ -83,7 +83,7 @@ func (sys *HTTPConsoleLoggerSys) Subscribe(subCh chan interface{}, doneCh chan s lastN = make([]madmin.LogInfo, last) sys.logBufLk.RLock() sys.logBuf.Do(func(p interface{}) { - if p != nil && (p.(madmin.LogInfo)).SendLog(node) { + if p != nil && (p.(madmin.LogInfo)).SendLog(node, logKind) { lastN[cnt%last] = p.(madmin.LogInfo) cnt++ } @@ -119,7 +119,7 @@ func (sys *HTTPConsoleLoggerSys) Console() *HTTPConsoleLoggerSys { // Send log message 'e' to console and publish to console // log pubsub system -func (sys *HTTPConsoleLoggerSys) Send(e interface{}) error { +func (sys *HTTPConsoleLoggerSys) Send(e interface{}, logKind string) error { var lg madmin.LogInfo switch e := e.(type) { case log.Entry: @@ -136,7 +136,7 @@ func (sys *HTTPConsoleLoggerSys) Send(e interface{}) error { sys.logBufLk.Unlock() if globalServerConfig.Logger.Console.Enabled { - return sys.console.Send(e) + return sys.console.Send(e, string(logger.All)) } return nil } diff --git a/cmd/endpoint.go b/cmd/endpoint.go index 09137c662..80e5537d7 100644 --- a/cmd/endpoint.go +++ b/cmd/endpoint.go @@ -260,7 +260,7 @@ func (endpoints EndpointList) UpdateIsLocal() error { reqInfo := (&logger.ReqInfo{}).AppendTags("host", endpoints[i].HostName) reqInfo.AppendTags("elapsedTime", humanize.RelTime(startTime, startTime.Add(timeElapsed), "elapsed", "")) ctx := logger.SetReqInfo(context.Background(), reqInfo) - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) } } else { resolvedList[i] = true diff --git a/cmd/format-disk-cache.go b/cmd/format-disk-cache.go index 07871a53f..9322dec59 100644 --- a/cmd/format-disk-cache.go +++ b/cmd/format-disk-cache.go @@ -138,12 +138,12 @@ func initFormatCache(ctx context.Context, drives []string) (formats []*formatCac } if !os.IsNotExist(err) { logger.GetReqInfo(ctx).AppendTags("drive", drive) - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) return nil, err } if err = os.Mkdir(drive, 0777); err != nil { logger.GetReqInfo(ctx).AppendTags("drive", drive) - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) return nil, err } } diff --git a/cmd/fs-v1-multipart.go b/cmd/fs-v1-multipart.go index a59412af6..1a893db0c 100644 --- a/cmd/fs-v1-multipart.go +++ b/cmd/fs-v1-multipart.go @@ -280,7 +280,7 @@ func (fs *FSObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID // Validate input data size and it can never be less than -1. if data.Size() < -1 { - logger.LogIf(ctx, errInvalidArgument) + logger.LogIf(ctx, errInvalidArgument, logger.Application) return pi, toObjectErr(errInvalidArgument) } diff --git a/cmd/fs-v1.go b/cmd/fs-v1.go index 7d5152d8c..0418e3fdb 100644 --- a/cmd/fs-v1.go +++ b/cmd/fs-v1.go @@ -548,7 +548,7 @@ func (fs *FSObjects) GetObjectNInfo(ctx context.Context, bucket, object string, // Check if range is valid if off > size || off+length > size { err = InvalidRange{off, length, size} - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) closeFn() rwPoolUnlocker() nsUnlocker() @@ -587,13 +587,13 @@ func (fs *FSObjects) getObject(ctx context.Context, bucket, object string, offse // Offset cannot be negative. if offset < 0 { - logger.LogIf(ctx, errUnexpected) + logger.LogIf(ctx, errUnexpected, logger.Application) return toObjectErr(errUnexpected, bucket, object) } // Writer cannot be nil. if writer == nil { - logger.LogIf(ctx, errUnexpected) + logger.LogIf(ctx, errUnexpected, logger.Application) return toObjectErr(errUnexpected, bucket, object) } @@ -622,7 +622,7 @@ func (fs *FSObjects) getObject(ctx context.Context, bucket, object string, offse return toObjectErr(perr, bucket, object) } if objEtag != etag { - logger.LogIf(ctx, InvalidETag{}) + logger.LogIf(ctx, InvalidETag{}, logger.Application) return toObjectErr(InvalidETag{}, bucket, object) } } @@ -648,7 +648,7 @@ func (fs *FSObjects) getObject(ctx context.Context, bucket, object string, offse // Reply back invalid range if the input offset and length fall out of range. if offset > size || offset+length > size { err = InvalidRange{offset, length, size} - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) return err } @@ -864,7 +864,7 @@ func (fs *FSObjects) putObject(ctx context.Context, bucket string, object string // Validate input data size and it can never be less than zero. if data.Size() < -1 { - logger.LogIf(ctx, errInvalidArgument) + logger.LogIf(ctx, errInvalidArgument, logger.Application) return ObjectInfo{}, errInvalidArgument } diff --git a/cmd/gateway/gcs/gateway-gcs.go b/cmd/gateway/gcs/gateway-gcs.go index baa6ba7f3..03d5c403a 100644 --- a/cmd/gateway/gcs/gateway-gcs.go +++ b/cmd/gateway/gcs/gateway-gcs.go @@ -27,6 +27,7 @@ import ( "io/ioutil" "math" "net/http" + "os" "path" "strconv" @@ -158,14 +159,14 @@ EXAMPLES: // Handler for 'minio gateway gcs' command line. func gcsGatewayMain(ctx *cli.Context) { projectID := ctx.Args().First() - if projectID == "" && env.Get("GOOGLE_APPLICATION_CREDENTIALS", "") == "" { - logger.LogIf(context.Background(), errGCSProjectIDNotFound) + if projectID == "" && os.Getenv("GOOGLE_APPLICATION_CREDENTIALS") == "" { + logger.LogIf(context.Background(), errGCSProjectIDNotFound, logger.Application) cli.ShowCommandHelpAndExit(ctx, "gcs", 1) } if projectID != "" && !isValidGCSProjectIDFormat(projectID) { reqInfo := (&logger.ReqInfo{}).AppendTags("projectID", ctx.Args().First()) contxt := logger.SetReqInfo(context.Background(), reqInfo) - logger.LogIf(contxt, errGCSInvalidProjectID) + logger.LogIf(contxt, errGCSInvalidProjectID, logger.Application) cli.ShowCommandHelpAndExit(ctx, "gcs", 1) } @@ -762,7 +763,7 @@ func (l *gcsGateway) GetObject(ctx context.Context, bucket string, key string, s // if we want to mimic S3 behavior exactly, we need to verify if bucket exists first, // otherwise gcs will just return object not exist in case of non-existing bucket if _, err := l.client.Bucket(bucket).Attrs(ctx); err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) return gcsToObjectError(err, bucket) } @@ -775,7 +776,7 @@ func (l *gcsGateway) GetObject(ctx context.Context, bucket string, key string, s r, err := object.NewRangeReader(ctx, startOffset, length) if err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) return gcsToObjectError(err, bucket, key) } defer r.Close() @@ -873,7 +874,7 @@ func (l *gcsGateway) GetObjectInfo(ctx context.Context, bucket string, object st // if we want to mimic S3 behavior exactly, we need to verify if bucket exists first, // otherwise gcs will just return object not exist in case of non-existing bucket if _, err := l.client.Bucket(bucket).Attrs(ctx); err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) return minio.ObjectInfo{}, gcsToObjectError(err, bucket) } @@ -893,7 +894,7 @@ func (l *gcsGateway) PutObject(ctx context.Context, bucket string, key string, r // if we want to mimic S3 behavior exactly, we need to verify if bucket exists first, // otherwise gcs will just return object not exist in case of non-existing bucket if _, err := l.client.Bucket(bucket).Attrs(ctx); err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) return minio.ObjectInfo{}, gcsToObjectError(err, bucket) } diff --git a/cmd/gateway/oss/gateway-oss.go b/cmd/gateway/oss/gateway-oss.go index 1ea0fa241..392991bf3 100644 --- a/cmd/gateway/oss/gateway-oss.go +++ b/cmd/gateway/oss/gateway-oss.go @@ -519,13 +519,13 @@ func (l *ossObjects) ListObjectsV2(ctx context.Context, bucket, prefix, continua // length indicates the total length of the object. func ossGetObject(ctx context.Context, client *oss.Client, bucket, key string, startOffset, length int64, writer io.Writer, etag string) error { if length < 0 && length != -1 { - logger.LogIf(ctx, fmt.Errorf("Invalid argument")) + logger.LogIf(ctx, fmt.Errorf("Invalid argument"), logger.Application) return ossToObjectError(fmt.Errorf("Invalid argument"), bucket, key) } bkt, err := client.Bucket(bucket) if err != nil { - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) return ossToObjectError(err, bucket, key) } diff --git a/cmd/logger/audit.go b/cmd/logger/audit.go index d27a64f03..13f1a7577 100644 --- a/cmd/logger/audit.go +++ b/cmd/logger/audit.go @@ -155,6 +155,6 @@ func AuditLog(w http.ResponseWriter, r *http.Request, api string, reqClaims map[ entry.API.StatusCode = statusCode entry.API.TimeToFirstByte = timeToFirstByte.String() entry.API.TimeToResponse = timeToResponse.String() - _ = t.Send(entry) + _ = t.Send(entry, string(All)) } } diff --git a/cmd/logger/logger.go b/cmd/logger/logger.go index 9db9ce2d0..8ecbbb6ea 100644 --- a/cmd/logger/logger.go +++ b/cmd/logger/logger.go @@ -273,36 +273,53 @@ func hashString(input string) string { return hex.EncodeToString(checksum) } +// Kind specifies the kind of error log +type Kind string + +const ( + // Minio errors + Minio Kind = "MINIO" + // Application errors + Application Kind = "APPLICATION" + // All errors + All Kind = "ALL" +) + // LogAlwaysIf prints a detailed error message during // the execution of the server. -func LogAlwaysIf(ctx context.Context, err error) { +func LogAlwaysIf(ctx context.Context, err error, errKind ...interface{}) { if err == nil { return } - logIf(ctx, err) + logIf(ctx, err, errKind...) } // LogIf prints a detailed error message during // the execution of the server, if it is not an // ignored error. -func LogIf(ctx context.Context, err error) { +func LogIf(ctx context.Context, err error, errKind ...interface{}) { if err == nil { return } if err.Error() != diskNotFoundError { - logIf(ctx, err) + logIf(ctx, err, errKind...) } } // logIf prints a detailed error message during // the execution of the server. -func logIf(ctx context.Context, err error) { +func logIf(ctx context.Context, err error, errKind ...interface{}) { if Disable { return } - + logKind := string(Minio) + if len(errKind) > 0 { + if ek, ok := errKind[0].(Kind); ok { + logKind = string(ek) + } + } req := GetReqInfo(ctx) if req == nil { @@ -330,6 +347,7 @@ func logIf(ctx context.Context, err error) { entry := log.Entry{ DeploymentID: req.DeploymentID, Level: ErrorLvl.String(), + LogKind: logKind, RemoteHost: req.RemoteHost, Host: req.Host, RequestID: req.RequestID, @@ -359,7 +377,7 @@ func logIf(ctx context.Context, err error) { // Iterate over all logger targets to send the log entry for _, t := range Targets { - t.Send(entry) + t.Send(entry, entry.LogKind) } } @@ -368,9 +386,9 @@ var ErrCritical struct{} // CriticalIf logs the provided error on the console. It fails the // current go-routine by causing a `panic(ErrCritical)`. -func CriticalIf(ctx context.Context, err error) { +func CriticalIf(ctx context.Context, err error, errKind ...interface{}) { if err != nil { - LogIf(ctx, err) + LogIf(ctx, err, errKind...) panic(ErrCritical) } } diff --git a/cmd/logger/logonce.go b/cmd/logger/logonce.go index 2201aae30..f6355c14a 100644 --- a/cmd/logger/logonce.go +++ b/cmd/logger/logonce.go @@ -30,7 +30,7 @@ type logOnceType struct { } // One log message per error. -func (l *logOnceType) logOnceIf(ctx context.Context, err error, id interface{}) { +func (l *logOnceType) logOnceIf(ctx context.Context, err error, id interface{}, errKind ...interface{}) { if err == nil { return } @@ -49,7 +49,7 @@ func (l *logOnceType) logOnceIf(ctx context.Context, err error, id interface{}) l.Unlock() if shouldLog { - LogIf(ctx, err) + LogIf(ctx, err, errKind...) } } @@ -76,6 +76,6 @@ var logOnce = newLogOnceType() // LogOnceIf - Logs notification errors - once per error. // id is a unique identifier for related log messages, refer to cmd/notification.go // on how it is used. -func LogOnceIf(ctx context.Context, err error, id interface{}) { - logOnce.logOnceIf(ctx, err, id) +func LogOnceIf(ctx context.Context, err error, id interface{}, errKind ...interface{}) { + logOnce.logOnceIf(ctx, err, id, errKind...) } diff --git a/cmd/logger/message/log/entry.go b/cmd/logger/message/log/entry.go index 95a65d658..becfe89d0 100644 --- a/cmd/logger/message/log/entry.go +++ b/cmd/logger/message/log/entry.go @@ -40,6 +40,7 @@ type API struct { type Entry struct { DeploymentID string `json:"deploymentid,omitempty"` Level string `json:"level"` + LogKind string `json:"errKind"` Time string `json:"time"` API *API `json:"api,omitempty"` RemoteHost string `json:"remotehost,omitempty"` diff --git a/cmd/logger/target/console/console.go b/cmd/logger/target/console/console.go index 3cd7be559..48634e902 100644 --- a/cmd/logger/target/console/console.go +++ b/cmd/logger/target/console/console.go @@ -32,7 +32,7 @@ import ( type Target struct{} // Send log message 'e' to console -func (c *Target) Send(e interface{}) error { +func (c *Target) Send(e interface{}, logKind string) error { entry, ok := e.(log.Entry) if !ok { return fmt.Errorf("Uexpected log entry structure %#v", e) diff --git a/cmd/logger/target/http/http.go b/cmd/logger/target/http/http.go index a61287fd1..e931054cb 100644 --- a/cmd/logger/target/http/http.go +++ b/cmd/logger/target/http/http.go @@ -22,6 +22,7 @@ import ( "errors" "net/http" gohttp "net/http" + "strings" xhttp "github.com/minio/minio/cmd/http" ) @@ -39,6 +40,7 @@ type Target struct { endpoint string // User-Agent to be set on each log request sent to the `endpoint` userAgent string + logKind string client gohttp.Client } @@ -75,10 +77,11 @@ func (h *Target) startHTTPLogger() { // New initializes a new logger target which // sends log over http to the specified endpoint -func New(endpoint, userAgent string, transport *gohttp.Transport) *Target { +func New(endpoint, userAgent, logKind string, transport *gohttp.Transport) *Target { h := Target{ endpoint: endpoint, userAgent: userAgent, + logKind: strings.ToUpper(logKind), client: gohttp.Client{ Transport: transport, }, @@ -90,7 +93,10 @@ func New(endpoint, userAgent string, transport *gohttp.Transport) *Target { } // Send log message 'e' to http target. -func (h *Target) Send(entry interface{}) error { +func (h *Target) Send(entry interface{}, errKind string) error { + if h.logKind != errKind && h.logKind != "ALL" { + return nil + } select { case h.logCh <- entry: default: diff --git a/cmd/logger/targets.go b/cmd/logger/targets.go index 7434accbd..dbc443937 100644 --- a/cmd/logger/targets.go +++ b/cmd/logger/targets.go @@ -20,7 +20,7 @@ package logger // a single log entry and Send it to the log target // e.g. Send the log to a http server type Target interface { - Send(entry interface{}) error + Send(entry interface{}, errKind string) error } // Targets is the set of enabled loggers diff --git a/cmd/object-handlers.go b/cmd/object-handlers.go index 5a5c4a7d9..ce87d0093 100644 --- a/cmd/object-handlers.go +++ b/cmd/object-handlers.go @@ -329,7 +329,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req return } - logger.LogIf(ctx, err) + logger.LogIf(ctx, err, logger.Application) } } diff --git a/cmd/peer-rest-server.go b/cmd/peer-rest-server.go index 1fdb394de..85bb7b1ee 100644 --- a/cmd/peer-rest-server.go +++ b/cmd/peer-rest-server.go @@ -948,7 +948,7 @@ func (s *peerRESTServer) ConsoleLogHandler(w http.ResponseWriter, r *http.Reques defer close(doneCh) ch := make(chan interface{}, 2000) - globalConsoleSys.Subscribe(ch, doneCh, "", 0, nil) + globalConsoleSys.Subscribe(ch, doneCh, "", 0, string(logger.All), nil) enc := gob.NewEncoder(w) for { diff --git a/cmd/sts-errors.go b/cmd/sts-errors.go index c35d2560c..d2291d467 100644 --- a/cmd/sts-errors.go +++ b/cmd/sts-errors.go @@ -37,7 +37,14 @@ func writeSTSErrorResponse(ctx context.Context, w http.ResponseWriter, errCode S if errCtxt != nil { stsErrorResponse.Error.Message = fmt.Sprintf("%v", errCtxt) } - logger.LogIf(ctx, errCtxt) + logKind := logger.All + switch errCode { + case ErrSTSInternalError, ErrSTSNotInitialized: + logKind = logger.Minio + default: + logKind = logger.Application + } + logger.LogIf(ctx, errCtxt, logKind) encodedErrorResponse := encodeResponse(stsErrorResponse) writeResponse(w, err.HTTPStatusCode, encodedErrorResponse, mimeXML) } diff --git a/cmd/xl-v1-multipart.go b/cmd/xl-v1-multipart.go index 571cd6811..1ad879761 100644 --- a/cmd/xl-v1-multipart.go +++ b/cmd/xl-v1-multipart.go @@ -297,7 +297,7 @@ func (xl xlObjects) PutObjectPart(ctx context.Context, bucket, object, uploadID // Validate input data size and it can never be less than zero. if data.Size() < -1 { - logger.LogIf(ctx, errInvalidArgument) + logger.LogIf(ctx, errInvalidArgument, logger.Application) return pi, toObjectErr(errInvalidArgument) } diff --git a/cmd/xl-v1-object.go b/cmd/xl-v1-object.go index 0316f9daa..197b8e64a 100644 --- a/cmd/xl-v1-object.go +++ b/cmd/xl-v1-object.go @@ -206,7 +206,7 @@ func (xl xlObjects) getObject(ctx context.Context, bucket, object string, startO // Start offset cannot be negative. if startOffset < 0 { - logger.LogIf(ctx, errUnexpected) + logger.LogIf(ctx, errUnexpected, logger.Application) return errUnexpected } @@ -258,7 +258,7 @@ func (xl xlObjects) getObject(ctx context.Context, bucket, object string, startO // Reply back invalid range if the input offset and length fall out of range. if startOffset > xlMeta.Stat.Size || startOffset+length > xlMeta.Stat.Size { - logger.LogIf(ctx, InvalidRange{startOffset, length, xlMeta.Stat.Size}) + logger.LogIf(ctx, InvalidRange{startOffset, length, xlMeta.Stat.Size}, logger.Application) return InvalidRange{startOffset, length, xlMeta.Stat.Size} } @@ -570,7 +570,7 @@ func (xl xlObjects) putObject(ctx context.Context, bucket string, object string, // Validate input data size and it can never be less than zero. if data.Size() < -1 { - logger.LogIf(ctx, errInvalidArgument) + logger.LogIf(ctx, errInvalidArgument, logger.Application) return ObjectInfo{}, toObjectErr(errInvalidArgument) } @@ -636,7 +636,7 @@ func (xl xlObjects) putObject(ctx context.Context, bucket string, object string, // Should return IncompleteBody{} error when reader has fewer bytes // than specified in request header. if n < data.Size() { - logger.LogIf(ctx, IncompleteBody{}) + logger.LogIf(ctx, IncompleteBody{}, logger.Application) return ObjectInfo{}, IncompleteBody{} } diff --git a/docs/config/config.sample.json b/docs/config/config.sample.json index 5ba269b9e..3d102cb07 100644 --- a/docs/config/config.sample.json +++ b/docs/config/config.sample.json @@ -186,6 +186,7 @@ "http": { "target1": { "enabled": false, + "type": "minio", "endpoint": "https://username:password@example.com/api" } } diff --git a/pkg/event/target/amqp.go b/pkg/event/target/amqp.go index b56525174..442a983e0 100644 --- a/pkg/event/target/amqp.go +++ b/pkg/event/target/amqp.go @@ -76,7 +76,7 @@ type AMQPTarget struct { conn *amqp.Connection connMutex sync.Mutex store Store - loggerOnce func(ctx context.Context, err error, id interface{}) + loggerOnce func(ctx context.Context, err error, id interface{}, errKind ...interface{}) } // ID - returns TargetID. @@ -215,7 +215,7 @@ func (target *AMQPTarget) Close() error { } // NewAMQPTarget - creates new AMQP target. -func NewAMQPTarget(id string, args AMQPArgs, doneCh <-chan struct{}, loggerOnce func(ctx context.Context, err error, id interface{})) (*AMQPTarget, error) { +func NewAMQPTarget(id string, args AMQPArgs, doneCh <-chan struct{}, loggerOnce func(ctx context.Context, err error, id interface{}, errKind ...interface{})) (*AMQPTarget, error) { var conn *amqp.Connection var err error diff --git a/pkg/event/target/redis.go b/pkg/event/target/redis.go index 69db7d9a5..cb653716e 100644 --- a/pkg/event/target/redis.go +++ b/pkg/event/target/redis.go @@ -99,7 +99,7 @@ type RedisTarget struct { pool *redis.Pool store Store firstPing bool - loggerOnce func(ctx context.Context, err error, id interface{}) + loggerOnce func(ctx context.Context, err error, id interface{}, errKind ...interface{}) } // ID - returns target ID. @@ -222,7 +222,7 @@ func (target *RedisTarget) Close() error { } // NewRedisTarget - creates new Redis target. -func NewRedisTarget(id string, args RedisArgs, doneCh <-chan struct{}, loggerOnce func(ctx context.Context, err error, id interface{})) (*RedisTarget, error) { +func NewRedisTarget(id string, args RedisArgs, doneCh <-chan struct{}, loggerOnce func(ctx context.Context, err error, id interface{}, errKind ...interface{})) (*RedisTarget, error) { pool := &redis.Pool{ MaxIdle: 3, IdleTimeout: 2 * 60 * time.Second, diff --git a/pkg/madmin/api-log.go b/pkg/madmin/api-log.go index 6448d7e15..c2b1648b9 100644 --- a/pkg/madmin/api-log.go +++ b/pkg/madmin/api-log.go @@ -35,12 +35,14 @@ type LogInfo struct { } // SendLog returns true if log pertains to node specified in args. -func (l LogInfo) SendLog(node string) bool { - return node == "" || strings.EqualFold(node, l.NodeName) +func (l LogInfo) SendLog(node, logKind string) bool { + nodeFltr := (node == "" || strings.EqualFold(node, l.NodeName)) + typeFltr := strings.EqualFold(logKind, "all") || strings.EqualFold(l.LogKind, logKind) + return nodeFltr && typeFltr } // GetLogs - listen on console log messages. -func (adm AdminClient) GetLogs(node string, lineCnt int, doneCh <-chan struct{}) <-chan LogInfo { +func (adm AdminClient) GetLogs(node string, lineCnt int, logKind string, doneCh <-chan struct{}) <-chan LogInfo { logCh := make(chan LogInfo, 1) // Only success, start a routine to start reading line by line. @@ -49,6 +51,7 @@ func (adm AdminClient) GetLogs(node string, lineCnt int, doneCh <-chan struct{}) urlValues := make(url.Values) urlValues.Set("node", node) urlValues.Set("limit", strconv.Itoa(lineCnt)) + urlValues.Set("logType", logKind) for { reqData := requestData{ relPath: "/v1/log",