mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
Refactor logging in more Go idiomatic style (#6816)
This refactor brings a change which allows targets to be added in a cleaner way and also audit is now moved out. This PR also simplifies logger dependency for auditing
This commit is contained in:
parent
d732b1ff9d
commit
bfb505aa8e
@ -57,7 +57,7 @@ type accessControlPolicy struct {
|
|||||||
func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "GetBucketACL")
|
ctx := newContext(r, w, "GetBucketACL")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "GetBucketACL")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -106,7 +106,7 @@ func (api objectAPIHandlers) GetBucketACLHandler(w http.ResponseWriter, r *http.
|
|||||||
func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "GetObjectACL")
|
ctx := newContext(r, w, "GetObjectACL")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "GetObjectACL")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
|
@ -23,6 +23,8 @@ import (
|
|||||||
const (
|
const (
|
||||||
// Response request id.
|
// Response request id.
|
||||||
responseRequestIDKey = "x-amz-request-id"
|
responseRequestIDKey = "x-amz-request-id"
|
||||||
|
// Deployment id.
|
||||||
|
responseDeploymentIDKey = "x-minio-deployment-id"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ObjectIdentifier carries key name for the object to delete.
|
// ObjectIdentifier carries key name for the object to delete.
|
||||||
|
@ -59,7 +59,7 @@ func validateListObjectsArgs(prefix, marker, delimiter string, maxKeys int) APIE
|
|||||||
func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "ListObjectsV2")
|
ctx := newContext(r, w, "ListObjectsV2")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "ListObjectsV2")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -141,7 +141,7 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
|
|||||||
func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "ListObjectsV1")
|
ctx := newContext(r, w, "ListObjectsV1")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "ListObjectsV1")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
|
@ -89,7 +89,7 @@ func initFederatorBackend(objLayer ObjectLayer) {
|
|||||||
func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "GetBucketLocation")
|
ctx := newContext(r, w, "GetBucketLocation")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "GetBucketLocation")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -139,7 +139,7 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *
|
|||||||
func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "ListMultipartUploads")
|
ctx := newContext(r, w, "ListMultipartUploads")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "ListMultipartUploads")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -192,7 +192,7 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
|
|||||||
func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "ListBuckets")
|
ctx := newContext(r, w, "ListBuckets")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "ListBuckets")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -250,7 +250,7 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
|
|||||||
func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "DeleteMultipleObjects")
|
ctx := newContext(r, w, "DeleteMultipleObjects")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "DeleteMultipleObjects")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -397,7 +397,7 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
|
|||||||
func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "PutBucket")
|
ctx := newContext(r, w, "PutBucket")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "PutBucket")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -475,7 +475,7 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req
|
|||||||
func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "PostPolicyBucket")
|
ctx := newContext(r, w, "PostPolicyBucket")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "PostPolicyBucket")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -703,7 +703,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
|||||||
func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "HeadBucket")
|
ctx := newContext(r, w, "HeadBucket")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "HeadBucket")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -735,7 +735,7 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re
|
|||||||
func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) DeleteBucketHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "DeleteBucket")
|
ctx := newContext(r, w, "DeleteBucket")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "DeleteBucket")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
|
@ -44,7 +44,7 @@ var errNoSuchNotifications = errors.New("The specified bucket does not have buck
|
|||||||
func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "GetBucketNotification")
|
ctx := newContext(r, w, "GetBucketNotification")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "GetBucketNotification")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucketName := vars["bucket"]
|
bucketName := vars["bucket"]
|
||||||
@ -102,7 +102,7 @@ func (api objectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter,
|
|||||||
func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "PutBucketNotification")
|
ctx := newContext(r, w, "PutBucketNotification")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "PutBucketNotification")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -164,7 +164,7 @@ func (api objectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter,
|
|||||||
func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "ListenBucketNotification")
|
ctx := newContext(r, w, "ListenBucketNotification")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "ListenBucketNotification")
|
||||||
|
|
||||||
// Validate if bucket exists.
|
// Validate if bucket exists.
|
||||||
objAPI := api.ObjectAPI()
|
objAPI := api.ObjectAPI()
|
||||||
|
@ -40,7 +40,7 @@ const (
|
|||||||
func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "PutBucketPolicy")
|
ctx := newContext(r, w, "PutBucketPolicy")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "PutBucketPolicy")
|
||||||
|
|
||||||
objAPI := api.ObjectAPI()
|
objAPI := api.ObjectAPI()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
@ -103,7 +103,7 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
|
|||||||
func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "DeleteBucketPolicy")
|
ctx := newContext(r, w, "DeleteBucketPolicy")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "DeleteBucketPolicy")
|
||||||
|
|
||||||
objAPI := api.ObjectAPI()
|
objAPI := api.ObjectAPI()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
@ -141,7 +141,7 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
|
|||||||
func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "GetBucketPolicy")
|
ctx := newContext(r, w, "GetBucketPolicy")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "GetBucketPolicy")
|
||||||
|
|
||||||
objAPI := api.ObjectAPI()
|
objAPI := api.ObjectAPI()
|
||||||
if objAPI == nil {
|
if objAPI == nil {
|
||||||
|
@ -32,6 +32,8 @@ import (
|
|||||||
"github.com/minio/minio-go/pkg/set"
|
"github.com/minio/minio-go/pkg/set"
|
||||||
"github.com/minio/minio/cmd/crypto"
|
"github.com/minio/minio/cmd/crypto"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
"github.com/minio/minio/cmd/logger/target/console"
|
||||||
|
"github.com/minio/minio/cmd/logger/target/http"
|
||||||
"github.com/minio/minio/pkg/auth"
|
"github.com/minio/minio/pkg/auth"
|
||||||
"github.com/minio/minio/pkg/dns"
|
"github.com/minio/minio/pkg/dns"
|
||||||
xnet "github.com/minio/minio/pkg/net"
|
xnet "github.com/minio/minio/pkg/net"
|
||||||
@ -54,25 +56,25 @@ func loadLoggers() {
|
|||||||
auditEndpoint, ok := os.LookupEnv("MINIO_AUDIT_LOGGER_HTTP_ENDPOINT")
|
auditEndpoint, ok := os.LookupEnv("MINIO_AUDIT_LOGGER_HTTP_ENDPOINT")
|
||||||
if ok {
|
if ok {
|
||||||
// Enable audit HTTP logging through ENV.
|
// Enable audit HTTP logging through ENV.
|
||||||
logger.AddAuditTarget(logger.NewHTTP(auditEndpoint, NewCustomHTTPTransport()))
|
logger.AddAuditTarget(http.New(auditEndpoint, NewCustomHTTPTransport()))
|
||||||
}
|
}
|
||||||
|
|
||||||
loggerEndpoint, ok := os.LookupEnv("MINIO_LOGGER_HTTP_ENDPOINT")
|
loggerEndpoint, ok := os.LookupEnv("MINIO_LOGGER_HTTP_ENDPOINT")
|
||||||
if ok {
|
if ok {
|
||||||
// Enable HTTP logging through ENV.
|
// Enable HTTP logging through ENV.
|
||||||
logger.AddTarget(logger.NewHTTP(loggerEndpoint, NewCustomHTTPTransport()))
|
logger.AddTarget(http.New(loggerEndpoint, NewCustomHTTPTransport()))
|
||||||
} else {
|
} else {
|
||||||
for _, l := range globalServerConfig.Logger.HTTP {
|
for _, l := range globalServerConfig.Logger.HTTP {
|
||||||
if l.Enabled {
|
if l.Enabled {
|
||||||
// Enable http logging
|
// Enable http logging
|
||||||
logger.AddTarget(logger.NewHTTP(l.Endpoint, NewCustomHTTPTransport()))
|
logger.AddTarget(http.New(l.Endpoint, NewCustomHTTPTransport()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if globalServerConfig.Logger.Console.Enabled {
|
if globalServerConfig.Logger.Console.Enabled {
|
||||||
// Enable console logging
|
// Enable console logging
|
||||||
logger.AddTarget(logger.NewConsole())
|
logger.AddTarget(console.New())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/minio/minio/cmd/logger"
|
|
||||||
"github.com/minio/minio/pkg/lock"
|
"github.com/minio/minio/pkg/lock"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -273,7 +272,7 @@ func initFormatFS(ctx context.Context, fsPath string) (rlk *lock.RLockedFile, er
|
|||||||
rlk.Close()
|
rlk.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
logger.SetDeploymentID(id)
|
globalDeploymentID = id
|
||||||
return rlk, nil
|
return rlk, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,7 +239,7 @@ func StartGateway(ctx *cli.Context, gw Gateway) {
|
|||||||
loadLoggers()
|
loadLoggers()
|
||||||
|
|
||||||
// This is only to uniquely identify each gateway deployments.
|
// This is only to uniquely identify each gateway deployments.
|
||||||
logger.SetDeploymentID(os.Getenv("MINIO_GATEWAY_DEPLOYMENT_ID"))
|
globalDeploymentID = os.Getenv("MINIO_GATEWAY_DEPLOYMENT_ID")
|
||||||
|
|
||||||
var cacheConfig = globalServerConfig.GetCacheConfig()
|
var cacheConfig = globalServerConfig.GetCacheConfig()
|
||||||
if len(cacheConfig.Drives) > 0 {
|
if len(cacheConfig.Drives) > 0 {
|
||||||
|
@ -723,24 +723,25 @@ func (l rateLimit) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
l.handler.ServeHTTP(w, r)
|
l.handler.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
// requestIDHeaderHandler sets x-amz-request-id header.
|
// customHeaderHandler sets x-amz-request-id, x-minio-deployment-id header.
|
||||||
// Previously, this value was set right before a response
|
// Previously, this value was set right before a response was sent to
|
||||||
// was sent to the client.So, logger and Error response XML
|
// the client. So, logger and Error response XML were not using this
|
||||||
// were not using this value.
|
// value. This is set here so that this header can be logged as
|
||||||
// This is set here so that this header can be logged as
|
// part of the log entry, Error response XML and auditing.
|
||||||
// part of the log entry and Error response XML.
|
type customHeaderHandler struct {
|
||||||
type requestIDHeaderHandler struct {
|
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func addrequestIDHeader(h http.Handler) http.Handler {
|
func addCustomHeaders(h http.Handler) http.Handler {
|
||||||
return requestIDHeaderHandler{handler: h}
|
return customHeaderHandler{handler: h}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s requestIDHeaderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
func (s customHeaderHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
// Set unique request ID for each response.
|
// Set custom headers such as x-amz-request-id and x-minio-deployment-id
|
||||||
|
// for each request.
|
||||||
w.Header().Set(responseRequestIDKey, mustGetRequestID(UTCNow()))
|
w.Header().Set(responseRequestIDKey, mustGetRequestID(UTCNow()))
|
||||||
s.handler.ServeHTTP(w, r)
|
w.Header().Set(responseDeploymentIDKey, globalDeploymentID)
|
||||||
|
s.handler.ServeHTTP(logger.NewResponseWriter(w), r)
|
||||||
}
|
}
|
||||||
|
|
||||||
type securityHeaderHandler struct {
|
type securityHeaderHandler struct {
|
||||||
|
@ -255,6 +255,9 @@ var (
|
|||||||
// OPA policy system.
|
// OPA policy system.
|
||||||
globalPolicyOPA *iampolicy.Opa
|
globalPolicyOPA *iampolicy.Opa
|
||||||
|
|
||||||
|
// Deployment ID - unique per deployment
|
||||||
|
globalDeploymentID string
|
||||||
|
|
||||||
// Add new variable global values here.
|
// Add new variable global values here.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package logger
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Represents the current version of audit log structure.
|
|
||||||
const auditLogVersion = "1"
|
|
||||||
|
|
||||||
// AuditEntry - audit entry logs.
|
|
||||||
type AuditEntry struct {
|
|
||||||
Version string `json:"version"`
|
|
||||||
DeploymentID string `json:"deploymentid,omitempty"`
|
|
||||||
Time string `json:"time"`
|
|
||||||
API *api `json:"api,omitempty"`
|
|
||||||
RemoteHost string `json:"remotehost,omitempty"`
|
|
||||||
RequestID string `json:"requestID,omitempty"`
|
|
||||||
UserAgent string `json:"userAgent,omitempty"`
|
|
||||||
ReqQuery map[string]string `json:"requestQuery,omitempty"`
|
|
||||||
ReqHeader map[string]string `json:"requestHeader,omitempty"`
|
|
||||||
RespHeader map[string]string `json:"responseHeader,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuditTargets is the list of enabled audit loggers
|
|
||||||
var AuditTargets = []LoggingTarget{}
|
|
||||||
|
|
||||||
// AddAuditTarget adds a new audit logger target to the
|
|
||||||
// list of enabled loggers
|
|
||||||
func AddAuditTarget(t LoggingTarget) {
|
|
||||||
AuditTargets = append(AuditTargets, t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AuditLog - logs audit logs to all targets.
|
|
||||||
func AuditLog(ctx context.Context, w http.ResponseWriter, r *http.Request) {
|
|
||||||
if Disable {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
req := GetReqInfo(ctx)
|
|
||||||
if req == nil {
|
|
||||||
req = &ReqInfo{API: "SYSTEM"}
|
|
||||||
}
|
|
||||||
|
|
||||||
reqQuery := make(map[string]string)
|
|
||||||
for k, v := range r.URL.Query() {
|
|
||||||
reqQuery[k] = strings.Join(v, ",")
|
|
||||||
}
|
|
||||||
reqHeader := make(map[string]string)
|
|
||||||
for k, v := range r.Header {
|
|
||||||
reqHeader[k] = strings.Join(v, ",")
|
|
||||||
}
|
|
||||||
respHeader := make(map[string]string)
|
|
||||||
for k, v := range w.Header() {
|
|
||||||
respHeader[k] = strings.Join(v, ",")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send audit logs only to http targets.
|
|
||||||
for _, t := range AuditTargets {
|
|
||||||
t.send(AuditEntry{
|
|
||||||
Version: auditLogVersion,
|
|
||||||
DeploymentID: deploymentID,
|
|
||||||
RemoteHost: req.RemoteHost,
|
|
||||||
RequestID: req.RequestID,
|
|
||||||
UserAgent: req.UserAgent,
|
|
||||||
Time: time.Now().UTC().Format(time.RFC3339Nano),
|
|
||||||
API: &api{
|
|
||||||
Name: req.API,
|
|
||||||
Args: &args{
|
|
||||||
Bucket: req.BucketName,
|
|
||||||
Object: req.ObjectName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ReqQuery: reqQuery,
|
|
||||||
ReqHeader: reqHeader,
|
|
||||||
RespHeader: respHeader,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
68
cmd/logger/audit.go
Normal file
68
cmd/logger/audit.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package logger
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/minio/minio/cmd/logger/message/audit"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResponseWriter - is a wrapper to trap the http response status code.
|
||||||
|
type ResponseWriter struct {
|
||||||
|
http.ResponseWriter
|
||||||
|
statusCode int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewResponseWriter - returns a wrapped response writer to trap
|
||||||
|
// http status codes for auditiing purposes.
|
||||||
|
func NewResponseWriter(w http.ResponseWriter) *ResponseWriter {
|
||||||
|
return &ResponseWriter{w, http.StatusOK}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteHeader - writes http status code
|
||||||
|
func (lrw *ResponseWriter) WriteHeader(code int) {
|
||||||
|
lrw.statusCode = code
|
||||||
|
lrw.ResponseWriter.WriteHeader(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush - Calls the underlying Flush.
|
||||||
|
func (lrw *ResponseWriter) Flush() {
|
||||||
|
lrw.ResponseWriter.(http.Flusher).Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuditTargets is the list of enabled audit loggers
|
||||||
|
var AuditTargets = []Target{}
|
||||||
|
|
||||||
|
// AddAuditTarget adds a new audit logger target to the
|
||||||
|
// list of enabled loggers
|
||||||
|
func AddAuditTarget(t Target) {
|
||||||
|
AuditTargets = append(AuditTargets, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuditLog - logs audit logs to all audit targets.
|
||||||
|
func AuditLog(w http.ResponseWriter, r *http.Request, api string) {
|
||||||
|
var statusCode int
|
||||||
|
lrw, ok := w.(*ResponseWriter)
|
||||||
|
if ok {
|
||||||
|
statusCode = lrw.statusCode
|
||||||
|
}
|
||||||
|
// Send audit logs only to http targets.
|
||||||
|
for _, t := range AuditTargets {
|
||||||
|
t.Send(audit.ToEntry(w, r, api, statusCode))
|
||||||
|
}
|
||||||
|
}
|
@ -19,87 +19,168 @@ package logger
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
c "github.com/minio/mc/pkg/console"
|
||||||
|
"github.com/minio/minio/cmd/logger/message/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConsoleTarget implements loggerTarget to send log
|
// Console interface describes the methods that need to be implemented to satisfy the interface requirements.
|
||||||
// in plain or json format to the standard output.
|
type Console interface {
|
||||||
type ConsoleTarget struct{}
|
json(msg string, args ...interface{})
|
||||||
|
quiet(msg string, args ...interface{})
|
||||||
|
pretty(msg string, args ...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
func (c *ConsoleTarget) send(e interface{}) error {
|
func consoleLog(console Console, msg string, args ...interface{}) {
|
||||||
entry, ok := e.(logEntry)
|
switch {
|
||||||
if !ok {
|
case jsonFlag:
|
||||||
return fmt.Errorf("Uexpected log entry structure %#v", e)
|
// Strip escape control characters from json message
|
||||||
|
msg = ansiRE.ReplaceAllLiteralString(msg, "")
|
||||||
|
console.json(msg, args...)
|
||||||
|
case quietFlag:
|
||||||
|
console.quiet(msg, args...)
|
||||||
|
default:
|
||||||
|
console.pretty(msg, args...)
|
||||||
}
|
}
|
||||||
if jsonFlag {
|
}
|
||||||
logJSON, err := json.Marshal(&entry)
|
|
||||||
if err != nil {
|
// Fatal prints only fatal error message with no stack trace
|
||||||
return err
|
// it will be called for input validation failures
|
||||||
|
func Fatal(err error, msg string, data ...interface{}) {
|
||||||
|
fatal(err, msg, data...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fatal(err error, msg string, data ...interface{}) {
|
||||||
|
var errMsg string
|
||||||
|
if msg != "" {
|
||||||
|
errMsg = errorFmtFunc(fmt.Sprintf(msg, data...), err, jsonFlag)
|
||||||
|
} else {
|
||||||
|
errMsg = err.Error()
|
||||||
|
}
|
||||||
|
consoleLog(fatalMessage, errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
var fatalMessage fatalMsg
|
||||||
|
|
||||||
|
type fatalMsg struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fatalMsg) json(msg string, args ...interface{}) {
|
||||||
|
logJSON, err := json.Marshal(&log.Entry{
|
||||||
|
Level: FatalLvl.String(),
|
||||||
|
Time: time.Now().UTC().Format(time.RFC3339Nano),
|
||||||
|
Trace: &log.Trace{Message: fmt.Sprintf(msg, args...), Source: []string{getSource(6)}},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(logJSON))
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fatalMsg) quiet(msg string, args ...interface{}) {
|
||||||
|
f.pretty(msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
logTag = "ERROR"
|
||||||
|
logBanner = ColorBgRed(ColorFgWhite(ColorBold(logTag))) + " "
|
||||||
|
emptyBanner = ColorBgRed(strings.Repeat(" ", len(logTag))) + " "
|
||||||
|
bannerWidth = len(logTag) + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f fatalMsg) pretty(msg string, args ...interface{}) {
|
||||||
|
// Build the passed error message
|
||||||
|
errMsg := fmt.Sprintf(msg, args...)
|
||||||
|
|
||||||
|
tagPrinted := false
|
||||||
|
|
||||||
|
// Print the error message: the following code takes care
|
||||||
|
// of splitting error text and always pretty printing the
|
||||||
|
// red banner along with the error message. Since the error
|
||||||
|
// message itself contains some colored text, we needed
|
||||||
|
// to use some ANSI control escapes to cursor color state
|
||||||
|
// and freely move in the screen.
|
||||||
|
for _, line := range strings.Split(errMsg, "\n") {
|
||||||
|
if len(line) == 0 {
|
||||||
|
// No more text to print, just quit.
|
||||||
|
break
|
||||||
}
|
}
|
||||||
fmt.Println(string(logJSON))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
trace := make([]string, len(entry.Trace.Source))
|
for {
|
||||||
|
// Save the attributes of the current cursor helps
|
||||||
// Add a sequence number and formatting for each stack trace
|
// us save the text color of the passed error message
|
||||||
// No formatting is required for the first entry
|
ansiSaveAttributes()
|
||||||
for i, element := range entry.Trace.Source {
|
// Print banner with or without the log tag
|
||||||
trace[i] = fmt.Sprintf("%8v: %s", i+1, element)
|
if !tagPrinted {
|
||||||
}
|
fmt.Print(logBanner)
|
||||||
|
tagPrinted = true
|
||||||
tagString := ""
|
} else {
|
||||||
for key, value := range entry.Trace.Variables {
|
fmt.Print(emptyBanner)
|
||||||
if value != "" {
|
|
||||||
if tagString != "" {
|
|
||||||
tagString += ", "
|
|
||||||
}
|
}
|
||||||
tagString += key + "=" + value
|
// Restore the text color of the error message
|
||||||
|
ansiRestoreAttributes()
|
||||||
|
ansiMoveRight(bannerWidth)
|
||||||
|
// Continue error message printing
|
||||||
|
fmt.Println(line)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apiString := "API: " + entry.API.Name + "("
|
// Exit because this is a fatal error message
|
||||||
if entry.API.Args != nil && entry.API.Args.Bucket != "" {
|
os.Exit(1)
|
||||||
apiString = apiString + "bucket=" + entry.API.Args.Bucket
|
|
||||||
}
|
|
||||||
if entry.API.Args != nil && entry.API.Args.Object != "" {
|
|
||||||
apiString = apiString + ", object=" + entry.API.Args.Object
|
|
||||||
}
|
|
||||||
apiString += ")"
|
|
||||||
timeString := "Time: " + time.Now().Format(loggerTimeFormat)
|
|
||||||
|
|
||||||
var requestID string
|
|
||||||
if entry.RequestID != "" {
|
|
||||||
requestID = "\nRequestID: " + entry.RequestID
|
|
||||||
}
|
|
||||||
|
|
||||||
var remoteHost string
|
|
||||||
if entry.RemoteHost != "" {
|
|
||||||
remoteHost = "\nRemoteHost: " + entry.RemoteHost
|
|
||||||
}
|
|
||||||
|
|
||||||
var userAgent string
|
|
||||||
if entry.UserAgent != "" {
|
|
||||||
userAgent = "\nUserAgent: " + entry.UserAgent
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(entry.Trace.Variables) > 0 {
|
|
||||||
tagString = "\n " + tagString
|
|
||||||
}
|
|
||||||
|
|
||||||
var msg = colorFgRed(colorBold(entry.Trace.Message))
|
|
||||||
var output = fmt.Sprintf("\n%s\n%s%s%s%s\nError: %s%s\n%s",
|
|
||||||
apiString, timeString, requestID, remoteHost, userAgent,
|
|
||||||
msg, tagString, strings.Join(trace, "\n"))
|
|
||||||
|
|
||||||
fmt.Println(output)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConsole initializes a new logger target
|
type infoMsg struct{}
|
||||||
// which prints log directly in the standard
|
|
||||||
// output.
|
var info infoMsg
|
||||||
func NewConsole() LoggingTarget {
|
|
||||||
return &ConsoleTarget{}
|
func (i infoMsg) json(msg string, args ...interface{}) {
|
||||||
|
logJSON, err := json.Marshal(&log.Entry{
|
||||||
|
Level: InformationLvl.String(),
|
||||||
|
Message: fmt.Sprintf(msg, args...),
|
||||||
|
Time: time.Now().UTC().Format(time.RFC3339Nano),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(logJSON))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i infoMsg) quiet(msg string, args ...interface{}) {
|
||||||
|
i.pretty(msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i infoMsg) pretty(msg string, args ...interface{}) {
|
||||||
|
c.Printf(msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info :
|
||||||
|
func Info(msg string, data ...interface{}) {
|
||||||
|
consoleLog(info, msg+"\n", data...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var startupMessage startUpMsg
|
||||||
|
|
||||||
|
type startUpMsg struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s startUpMsg) json(msg string, args ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s startUpMsg) quiet(msg string, args ...interface{}) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s startUpMsg) pretty(msg string, args ...interface{}) {
|
||||||
|
c.Printf(msg, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StartupMessage :
|
||||||
|
func StartupMessage(msg string, data ...interface{}) {
|
||||||
|
consoleLog(startupMessage, msg+"\n", data...)
|
||||||
}
|
}
|
||||||
|
@ -18,23 +18,20 @@ package logger
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/build"
|
"go/build"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
c "github.com/minio/mc/pkg/console"
|
"github.com/minio/minio-go/pkg/set"
|
||||||
|
"github.com/minio/minio/cmd/logger/message/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Disable disables all logging, false by default. (used for "go test")
|
// Disable disables all logging, false by default. (used for "go test")
|
||||||
var Disable = false
|
var Disable = false
|
||||||
|
|
||||||
var trimStrings []string
|
|
||||||
|
|
||||||
// Level type
|
// Level type
|
||||||
type Level int8
|
type Level int8
|
||||||
|
|
||||||
@ -45,7 +42,10 @@ const (
|
|||||||
FatalLvl
|
FatalLvl
|
||||||
)
|
)
|
||||||
|
|
||||||
const loggerTimeFormat string = "15:04:05 MST 01/02/2006"
|
var trimStrings []string
|
||||||
|
|
||||||
|
// TimeFormat - logging time format.
|
||||||
|
const TimeFormat string = "15:04:05 MST 01/02/2006"
|
||||||
|
|
||||||
// List of error strings to be ignored by LogIf
|
// List of error strings to be ignored by LogIf
|
||||||
const (
|
const (
|
||||||
@ -91,79 +91,33 @@ func (level Level) String() string {
|
|||||||
return lvlStr
|
return lvlStr
|
||||||
}
|
}
|
||||||
|
|
||||||
// Console interface describes the methods that needs to be implemented to satisfy the interface requirements.
|
// quietFlag: Hide startup messages if enabled
|
||||||
type Console interface {
|
|
||||||
json(msg string, args ...interface{})
|
|
||||||
quiet(msg string, args ...interface{})
|
|
||||||
pretty(msg string, args ...interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func consoleLog(console Console, msg string, args ...interface{}) {
|
|
||||||
switch {
|
|
||||||
case jsonFlag:
|
|
||||||
// Strip escape control characters from json message
|
|
||||||
msg = ansiRE.ReplaceAllLiteralString(msg, "")
|
|
||||||
console.json(msg, args...)
|
|
||||||
case quiet:
|
|
||||||
console.quiet(msg, args...)
|
|
||||||
default:
|
|
||||||
console.pretty(msg, args...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type traceEntry struct {
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
Source []string `json:"source,omitempty"`
|
|
||||||
Variables map[string]string `json:"variables,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type args struct {
|
|
||||||
Bucket string `json:"bucket,omitempty"`
|
|
||||||
Object string `json:"object,omitempty"`
|
|
||||||
Metadata map[string]string `json:"metadata,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type api struct {
|
|
||||||
Name string `json:"name,omitempty"`
|
|
||||||
Args *args `json:"args,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type logEntry struct {
|
|
||||||
DeploymentID string `json:"deploymentid,omitempty"`
|
|
||||||
Level string `json:"level"`
|
|
||||||
Time string `json:"time"`
|
|
||||||
API *api `json:"api,omitempty"`
|
|
||||||
RemoteHost string `json:"remotehost,omitempty"`
|
|
||||||
RequestID string `json:"requestID,omitempty"`
|
|
||||||
UserAgent string `json:"userAgent,omitempty"`
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
Trace *traceEntry `json:"error,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// quiet: Hide startup messages if enabled
|
|
||||||
// jsonFlag: Display in JSON format, if enabled
|
// jsonFlag: Display in JSON format, if enabled
|
||||||
var (
|
var (
|
||||||
quiet, jsonFlag bool
|
quietFlag, jsonFlag bool
|
||||||
// Custom function to format error
|
// Custom function to format error
|
||||||
errorFmtFunc func(string, error, bool) string
|
errorFmtFunc func(string, error, bool) string
|
||||||
|
|
||||||
deploymentID string
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SetDeploymentID - Used to set the deployment ID, in XL and FS mode
|
|
||||||
func SetDeploymentID(id string) {
|
|
||||||
deploymentID = id
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableQuiet - turns quiet option on.
|
// EnableQuiet - turns quiet option on.
|
||||||
func EnableQuiet() {
|
func EnableQuiet() {
|
||||||
quiet = true
|
quietFlag = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// EnableJSON - outputs logs in json format.
|
// EnableJSON - outputs logs in json format.
|
||||||
func EnableJSON() {
|
func EnableJSON() {
|
||||||
jsonFlag = true
|
jsonFlag = true
|
||||||
quiet = true
|
quietFlag = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsJSON - returns true if jsonFlag is true
|
||||||
|
func IsJSON() bool {
|
||||||
|
return jsonFlag
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsQuiet - returns true if quietFlag is true
|
||||||
|
func IsQuiet() bool {
|
||||||
|
return quietFlag
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterUIError registers the specified rendering function. This latter
|
// RegisterUIError registers the specified rendering function. This latter
|
||||||
@ -172,6 +126,17 @@ func RegisterUIError(f func(string, error, bool) string) {
|
|||||||
errorFmtFunc = f
|
errorFmtFunc = f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove any duplicates and return unique entries.
|
||||||
|
func uniqueEntries(paths []string) []string {
|
||||||
|
m := make(set.StringSet)
|
||||||
|
for _, p := range paths {
|
||||||
|
if !m.Contains(p) {
|
||||||
|
m.Add(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m.ToSlice()
|
||||||
|
}
|
||||||
|
|
||||||
// Init sets the trimStrings to possible GOPATHs
|
// Init sets the trimStrings to possible GOPATHs
|
||||||
// and GOROOT directories. Also append github.com/minio/minio
|
// and GOROOT directories. Also append github.com/minio/minio
|
||||||
// This is done to clean up the filename, when stack trace is
|
// This is done to clean up the filename, when stack trace is
|
||||||
@ -326,20 +291,30 @@ func logIf(ctx context.Context, err error) {
|
|||||||
// Get the cause for the Error
|
// Get the cause for the Error
|
||||||
message := err.Error()
|
message := err.Error()
|
||||||
|
|
||||||
entry := logEntry{
|
entry := log.Entry{
|
||||||
DeploymentID: deploymentID,
|
DeploymentID: req.DeploymentID,
|
||||||
Level: ErrorLvl.String(),
|
Level: ErrorLvl.String(),
|
||||||
RemoteHost: req.RemoteHost,
|
RemoteHost: req.RemoteHost,
|
||||||
RequestID: req.RequestID,
|
RequestID: req.RequestID,
|
||||||
UserAgent: req.UserAgent,
|
UserAgent: req.UserAgent,
|
||||||
Time: time.Now().UTC().Format(time.RFC3339Nano),
|
Time: time.Now().UTC().Format(time.RFC3339Nano),
|
||||||
API: &api{Name: API, Args: &args{Bucket: req.BucketName, Object: req.ObjectName}},
|
API: &log.API{
|
||||||
Trace: &traceEntry{Message: message, Source: trace, Variables: tags},
|
Name: API,
|
||||||
|
Args: &log.Args{
|
||||||
|
Bucket: req.BucketName,
|
||||||
|
Object: req.ObjectName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Trace: &log.Trace{
|
||||||
|
Message: message,
|
||||||
|
Source: trace,
|
||||||
|
Variables: tags,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over all logger targets to send the log entry
|
// Iterate over all logger targets to send the log entry
|
||||||
for _, t := range Targets {
|
for _, t := range Targets {
|
||||||
t.send(entry)
|
t.Send(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,141 +337,3 @@ func FatalIf(err error, msg string, data ...interface{}) {
|
|||||||
}
|
}
|
||||||
fatal(err, msg, data...)
|
fatal(err, msg, data...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal prints only fatal error message without no stack trace
|
|
||||||
// it will be called for input validation failures
|
|
||||||
func Fatal(err error, msg string, data ...interface{}) {
|
|
||||||
fatal(err, msg, data...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fatal(err error, msg string, data ...interface{}) {
|
|
||||||
var errMsg string
|
|
||||||
if msg != "" {
|
|
||||||
errMsg = errorFmtFunc(fmt.Sprintf(msg, data...), err, jsonFlag)
|
|
||||||
} else {
|
|
||||||
errMsg = err.Error()
|
|
||||||
}
|
|
||||||
consoleLog(fatalMessage, errMsg)
|
|
||||||
}
|
|
||||||
|
|
||||||
var fatalMessage fatalMsg
|
|
||||||
|
|
||||||
type fatalMsg struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fatalMsg) json(msg string, args ...interface{}) {
|
|
||||||
logJSON, err := json.Marshal(&logEntry{
|
|
||||||
Level: FatalLvl.String(),
|
|
||||||
Time: time.Now().UTC().Format(time.RFC3339Nano),
|
|
||||||
Trace: &traceEntry{Message: fmt.Sprintf(msg, args...), Source: []string{getSource(6)}},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Println(string(logJSON))
|
|
||||||
|
|
||||||
os.Exit(1)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f fatalMsg) quiet(msg string, args ...interface{}) {
|
|
||||||
f.pretty(msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
logTag = "ERROR"
|
|
||||||
logBanner = colorBgRed(colorFgWhite(colorBold(logTag))) + " "
|
|
||||||
emptyBanner = colorBgRed(strings.Repeat(" ", len(logTag))) + " "
|
|
||||||
bannerWidth = len(logTag) + 1
|
|
||||||
)
|
|
||||||
|
|
||||||
func (f fatalMsg) pretty(msg string, args ...interface{}) {
|
|
||||||
// Build the passed error message
|
|
||||||
errMsg := fmt.Sprintf(msg, args...)
|
|
||||||
|
|
||||||
tagPrinted := false
|
|
||||||
|
|
||||||
// Print the error message: the following code takes care
|
|
||||||
// of splitting error text and always pretty printing the
|
|
||||||
// red banner along with the error message. Since the error
|
|
||||||
// message itself contains some colored text, we needed
|
|
||||||
// to use some ANSI control escapes to cursor color state
|
|
||||||
// and freely move in the screen.
|
|
||||||
for _, line := range strings.Split(errMsg, "\n") {
|
|
||||||
if len(line) == 0 {
|
|
||||||
// No more text to print, just quit.
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
|
||||||
// Save the attributes of the current cursor helps
|
|
||||||
// us save the text color of the passed error message
|
|
||||||
ansiSaveAttributes()
|
|
||||||
// Print banner with or without the log tag
|
|
||||||
if !tagPrinted {
|
|
||||||
fmt.Print(logBanner)
|
|
||||||
tagPrinted = true
|
|
||||||
} else {
|
|
||||||
fmt.Print(emptyBanner)
|
|
||||||
}
|
|
||||||
// Restore the text color of the error message
|
|
||||||
ansiRestoreAttributes()
|
|
||||||
ansiMoveRight(bannerWidth)
|
|
||||||
// Continue error message printing
|
|
||||||
fmt.Println(line)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit because this is a fatal error message
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
type infoMsg struct{}
|
|
||||||
|
|
||||||
var info infoMsg
|
|
||||||
|
|
||||||
func (i infoMsg) json(msg string, args ...interface{}) {
|
|
||||||
logJSON, err := json.Marshal(&logEntry{
|
|
||||||
Level: InformationLvl.String(),
|
|
||||||
Message: fmt.Sprintf(msg, args...),
|
|
||||||
Time: time.Now().UTC().Format(time.RFC3339Nano),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Println(string(logJSON))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i infoMsg) quiet(msg string, args ...interface{}) {
|
|
||||||
i.pretty(msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (i infoMsg) pretty(msg string, args ...interface{}) {
|
|
||||||
c.Printf(msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info :
|
|
||||||
func Info(msg string, data ...interface{}) {
|
|
||||||
consoleLog(info, msg+"\n", data...)
|
|
||||||
}
|
|
||||||
|
|
||||||
var startupMessage startUpMsg
|
|
||||||
|
|
||||||
type startUpMsg struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s startUpMsg) json(msg string, args ...interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s startUpMsg) quiet(msg string, args ...interface{}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s startUpMsg) pretty(msg string, args ...interface{}) {
|
|
||||||
c.Printf(msg, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// StartupMessage :
|
|
||||||
func StartupMessage(msg string, data ...interface{}) {
|
|
||||||
consoleLog(startupMessage, msg+"\n", data...)
|
|
||||||
}
|
|
||||||
|
90
cmd/logger/message/audit/entry.go
Normal file
90
cmd/logger/message/audit/entry.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package audit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/minio/minio/pkg/handlers"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Version - represents the current version of audit log structure.
|
||||||
|
const Version = "1"
|
||||||
|
|
||||||
|
// Entry - audit entry logs.
|
||||||
|
type Entry struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
DeploymentID string `json:"deploymentid,omitempty"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
API struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Bucket string `json:"bucket,omitempty"`
|
||||||
|
Object string `json:"object,omitempty"`
|
||||||
|
Status string `json:"status,omitempty"`
|
||||||
|
StatusCode int `json:"statusCode,omitempty"`
|
||||||
|
} `json:"api"`
|
||||||
|
RemoteHost string `json:"remotehost,omitempty"`
|
||||||
|
RequestID string `json:"requestID,omitempty"`
|
||||||
|
UserAgent string `json:"userAgent,omitempty"`
|
||||||
|
ReqQuery map[string]string `json:"requestQuery,omitempty"`
|
||||||
|
ReqHeader map[string]string `json:"requestHeader,omitempty"`
|
||||||
|
RespHeader map[string]string `json:"responseHeader,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToEntry - constructs an audit entry object.
|
||||||
|
func ToEntry(w http.ResponseWriter, r *http.Request, api string, statusCode int) Entry {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
object := vars["object"]
|
||||||
|
|
||||||
|
reqQuery := make(map[string]string)
|
||||||
|
for k, v := range r.URL.Query() {
|
||||||
|
reqQuery[k] = strings.Join(v, ",")
|
||||||
|
}
|
||||||
|
reqHeader := make(map[string]string)
|
||||||
|
for k, v := range r.Header {
|
||||||
|
reqHeader[k] = strings.Join(v, ",")
|
||||||
|
}
|
||||||
|
respHeader := make(map[string]string)
|
||||||
|
for k, v := range w.Header() {
|
||||||
|
respHeader[k] = strings.Join(v, ",")
|
||||||
|
}
|
||||||
|
respHeader["Etag"] = strings.Trim(respHeader["Etag"], `"`)
|
||||||
|
|
||||||
|
entry := Entry{
|
||||||
|
Version: Version,
|
||||||
|
DeploymentID: w.Header().Get("x-minio-deployment-id"),
|
||||||
|
RemoteHost: handlers.GetSourceIP(r),
|
||||||
|
RequestID: w.Header().Get("x-amz-request-id"),
|
||||||
|
UserAgent: r.UserAgent(),
|
||||||
|
Time: time.Now().UTC().Format(time.RFC3339Nano),
|
||||||
|
ReqQuery: reqQuery,
|
||||||
|
ReqHeader: reqHeader,
|
||||||
|
RespHeader: respHeader,
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.API.Name = api
|
||||||
|
entry.API.Bucket = bucket
|
||||||
|
entry.API.Object = object
|
||||||
|
entry.API.Status = http.StatusText(statusCode)
|
||||||
|
entry.API.StatusCode = statusCode
|
||||||
|
|
||||||
|
return entry
|
||||||
|
}
|
50
cmd/logger/message/log/entry.go
Normal file
50
cmd/logger/message/log/entry.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package log
|
||||||
|
|
||||||
|
// Args - defines the arguments for the API.
|
||||||
|
type Args struct {
|
||||||
|
Bucket string `json:"bucket,omitempty"`
|
||||||
|
Object string `json:"object,omitempty"`
|
||||||
|
Metadata map[string]string `json:"metadata,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace - defines the trace.
|
||||||
|
type Trace struct {
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
Source []string `json:"source,omitempty"`
|
||||||
|
Variables map[string]string `json:"variables,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// API - defines the api type and its args.
|
||||||
|
type API struct {
|
||||||
|
Name string `json:"name,omitempty"`
|
||||||
|
Args *Args `json:"args,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry - defines fields and values of each log entry.
|
||||||
|
type Entry struct {
|
||||||
|
DeploymentID string `json:"deploymentid,omitempty"`
|
||||||
|
Level string `json:"level"`
|
||||||
|
Time string `json:"time"`
|
||||||
|
API *API `json:"api,omitempty"`
|
||||||
|
RemoteHost string `json:"remotehost,omitempty"`
|
||||||
|
RequestID string `json:"requestID,omitempty"`
|
||||||
|
UserAgent string `json:"userAgent,omitempty"`
|
||||||
|
Message string `json:"message,omitempty"`
|
||||||
|
Trace *Trace `json:"error,omitempty"`
|
||||||
|
}
|
@ -35,22 +35,24 @@ type KeyVal struct {
|
|||||||
|
|
||||||
// ReqInfo stores the request info.
|
// ReqInfo stores the request info.
|
||||||
type ReqInfo struct {
|
type ReqInfo struct {
|
||||||
RemoteHost string // Client Host/IP
|
RemoteHost string // Client Host/IP
|
||||||
UserAgent string // User Agent
|
UserAgent string // User Agent
|
||||||
RequestID string // x-amz-request-id
|
DeploymentID string // x-minio-deployment-id
|
||||||
API string // API name - GetObject PutObject NewMultipartUpload etc.
|
RequestID string // x-amz-request-id
|
||||||
BucketName string // Bucket name
|
API string // API name - GetObject PutObject NewMultipartUpload etc.
|
||||||
ObjectName string // Object name
|
BucketName string // Bucket name
|
||||||
tags []KeyVal // Any additional info not accommodated by above fields
|
ObjectName string // Object name
|
||||||
|
tags []KeyVal // Any additional info not accommodated by above fields
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReqInfo :
|
// NewReqInfo :
|
||||||
func NewReqInfo(remoteHost, userAgent, requestID, api, bucket, object string) *ReqInfo {
|
func NewReqInfo(remoteHost, userAgent, deploymentID, requestID, api, bucket, object string) *ReqInfo {
|
||||||
req := ReqInfo{}
|
req := ReqInfo{}
|
||||||
req.RemoteHost = remoteHost
|
req.RemoteHost = remoteHost
|
||||||
req.UserAgent = userAgent
|
req.UserAgent = userAgent
|
||||||
req.API = api
|
req.API = api
|
||||||
|
req.DeploymentID = deploymentID
|
||||||
req.RequestID = requestID
|
req.RequestID = requestID
|
||||||
req.BucketName = bucket
|
req.BucketName = bucket
|
||||||
req.ObjectName = object
|
req.ObjectName = object
|
||||||
|
109
cmd/logger/target/console/console.go
Normal file
109
cmd/logger/target/console/console.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2018 Minio, Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package console
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/minio/minio/cmd/logger"
|
||||||
|
"github.com/minio/minio/cmd/logger/message/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Target implements loggerTarget to send log
|
||||||
|
// in plain or json format to the standard output.
|
||||||
|
type Target struct{}
|
||||||
|
|
||||||
|
// Send log message 'e' to console
|
||||||
|
func (c *Target) Send(e interface{}) error {
|
||||||
|
entry, ok := e.(log.Entry)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Uexpected log entry structure %#v", e)
|
||||||
|
}
|
||||||
|
if logger.IsJSON() {
|
||||||
|
logJSON, err := json.Marshal(&entry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(string(logJSON))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
trace := make([]string, len(entry.Trace.Source))
|
||||||
|
|
||||||
|
// Add a sequence number and formatting for each stack trace
|
||||||
|
// No formatting is required for the first entry
|
||||||
|
for i, element := range entry.Trace.Source {
|
||||||
|
trace[i] = fmt.Sprintf("%8v: %s", i+1, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
tagString := ""
|
||||||
|
for key, value := range entry.Trace.Variables {
|
||||||
|
if value != "" {
|
||||||
|
if tagString != "" {
|
||||||
|
tagString += ", "
|
||||||
|
}
|
||||||
|
tagString += key + "=" + value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
apiString := "API: " + entry.API.Name + "("
|
||||||
|
if entry.API.Args != nil && entry.API.Args.Bucket != "" {
|
||||||
|
apiString = apiString + "bucket=" + entry.API.Args.Bucket
|
||||||
|
}
|
||||||
|
if entry.API.Args != nil && entry.API.Args.Object != "" {
|
||||||
|
apiString = apiString + ", object=" + entry.API.Args.Object
|
||||||
|
}
|
||||||
|
apiString += ")"
|
||||||
|
timeString := "Time: " + time.Now().Format(logger.TimeFormat)
|
||||||
|
|
||||||
|
var requestID string
|
||||||
|
if entry.RequestID != "" {
|
||||||
|
requestID = "\nRequestID: " + entry.RequestID
|
||||||
|
}
|
||||||
|
|
||||||
|
var remoteHost string
|
||||||
|
if entry.RemoteHost != "" {
|
||||||
|
remoteHost = "\nRemoteHost: " + entry.RemoteHost
|
||||||
|
}
|
||||||
|
|
||||||
|
var userAgent string
|
||||||
|
if entry.UserAgent != "" {
|
||||||
|
userAgent = "\nUserAgent: " + entry.UserAgent
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(entry.Trace.Variables) > 0 {
|
||||||
|
tagString = "\n " + tagString
|
||||||
|
}
|
||||||
|
|
||||||
|
var msg = logger.ColorFgRed(logger.ColorBold(entry.Trace.Message))
|
||||||
|
var output = fmt.Sprintf("\n%s\n%s%s%s%s\nError: %s%s\n%s",
|
||||||
|
apiString, timeString, requestID, remoteHost, userAgent,
|
||||||
|
msg, tagString, strings.Join(trace, "\n"))
|
||||||
|
|
||||||
|
fmt.Println(output)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// New initializes a new logger target
|
||||||
|
// which prints log directly in the standard
|
||||||
|
// output.
|
||||||
|
func New() *Target {
|
||||||
|
return &Target{}
|
||||||
|
}
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package logger
|
package http
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@ -22,24 +22,24 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
gohttp "net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTPTarget implements loggerTarget and sends the json
|
// Target implements logger.Target and sends the json
|
||||||
// format of a log entry to the configured http endpoint.
|
// format of a log entry to the configured http endpoint.
|
||||||
// An internal buffer of logs is maintained but when the
|
// An internal buffer of logs is maintained but when the
|
||||||
// buffer is full, new logs are just ignored and an error
|
// buffer is full, new logs are just ignored and an error
|
||||||
// is returned to the caller.
|
// is returned to the caller.
|
||||||
type HTTPTarget struct {
|
type Target struct {
|
||||||
// Channel of log entries
|
// Channel of log entries
|
||||||
logCh chan interface{}
|
logCh chan interface{}
|
||||||
|
|
||||||
// HTTP(s) endpoint
|
// HTTP(s) endpoint
|
||||||
endpoint string
|
endpoint string
|
||||||
client http.Client
|
client gohttp.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTPTarget) startHTTPLogger() {
|
func (h *Target) startHTTPLogger() {
|
||||||
// Create a routine which sends json logs received
|
// Create a routine which sends json logs received
|
||||||
// from an internal channel.
|
// from an internal channel.
|
||||||
go func() {
|
go func() {
|
||||||
@ -49,7 +49,7 @@ func (h *HTTPTarget) startHTTPLogger() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", h.endpoint, bytes.NewBuffer(logJSON))
|
req, err := gohttp.NewRequest("POST", h.endpoint, bytes.NewBuffer(logJSON))
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
resp, err := h.client.Do(req)
|
resp, err := h.client.Do(req)
|
||||||
@ -65,12 +65,12 @@ func (h *HTTPTarget) startHTTPLogger() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHTTP initializes a new logger target which
|
// New initializes a new logger target which
|
||||||
// sends log over http to the specified endpoint
|
// sends log over http to the specified endpoint
|
||||||
func NewHTTP(endpoint string, transport *http.Transport) LoggingTarget {
|
func New(endpoint string, transport *gohttp.Transport) *Target {
|
||||||
h := HTTPTarget{
|
h := Target{
|
||||||
endpoint: endpoint,
|
endpoint: endpoint,
|
||||||
client: http.Client{
|
client: gohttp.Client{
|
||||||
Transport: transport,
|
Transport: transport,
|
||||||
},
|
},
|
||||||
logCh: make(chan interface{}, 10000),
|
logCh: make(chan interface{}, 10000),
|
||||||
@ -80,7 +80,8 @@ func NewHTTP(endpoint string, transport *http.Transport) LoggingTarget {
|
|||||||
return &h
|
return &h
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *HTTPTarget) send(entry interface{}) error {
|
// Send log message 'e' to http target.
|
||||||
|
func (h *Target) Send(entry interface{}) error {
|
||||||
select {
|
select {
|
||||||
case h.logCh <- entry:
|
case h.logCh <- entry:
|
||||||
default:
|
default:
|
@ -16,18 +16,18 @@
|
|||||||
|
|
||||||
package logger
|
package logger
|
||||||
|
|
||||||
// LoggingTarget is the entity that we will receive
|
// Target is the entity that we will receive
|
||||||
// a single log entry and send it to the log target
|
// a single log entry and Send it to the log target
|
||||||
// e.g. send the log to a http server
|
// e.g. Send the log to a http server
|
||||||
type LoggingTarget interface {
|
type Target interface {
|
||||||
send(entry interface{}) error
|
Send(entry interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Targets is the set of enabled loggers
|
// Targets is the set of enabled loggers
|
||||||
var Targets = []LoggingTarget{}
|
var Targets = []Target{}
|
||||||
|
|
||||||
// AddTarget adds a new logger target to the
|
// AddTarget adds a new logger target to the
|
||||||
// list of enabled loggers
|
// list of enabled loggers
|
||||||
func AddTarget(t LoggingTarget) {
|
func AddTarget(t Target) {
|
||||||
Targets = append(Targets, t)
|
Targets = append(Targets, t)
|
||||||
}
|
}
|
||||||
|
@ -33,25 +33,25 @@ var (
|
|||||||
return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd())
|
return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd())
|
||||||
}
|
}
|
||||||
|
|
||||||
colorBold = func() func(a ...interface{}) string {
|
ColorBold = func() func(a ...interface{}) string {
|
||||||
if isTerminal() {
|
if isTerminal() {
|
||||||
return color.New(color.Bold).SprintFunc()
|
return color.New(color.Bold).SprintFunc()
|
||||||
}
|
}
|
||||||
return fmt.Sprint
|
return fmt.Sprint
|
||||||
}()
|
}()
|
||||||
colorFgRed = func() func(format string, a ...interface{}) string {
|
ColorFgRed = func() func(format string, a ...interface{}) string {
|
||||||
if isTerminal() {
|
if isTerminal() {
|
||||||
return color.New(color.FgRed).SprintfFunc()
|
return color.New(color.FgRed).SprintfFunc()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf
|
return fmt.Sprintf
|
||||||
}()
|
}()
|
||||||
colorBgRed = func() func(format string, a ...interface{}) string {
|
ColorBgRed = func() func(format string, a ...interface{}) string {
|
||||||
if isTerminal() {
|
if isTerminal() {
|
||||||
return color.New(color.BgRed).SprintfFunc()
|
return color.New(color.BgRed).SprintfFunc()
|
||||||
}
|
}
|
||||||
return fmt.Sprintf
|
return fmt.Sprintf
|
||||||
}()
|
}()
|
||||||
colorFgWhite = func() func(format string, a ...interface{}) string {
|
ColorFgWhite = func() func(format string, a ...interface{}) string {
|
||||||
if isTerminal() {
|
if isTerminal() {
|
||||||
return color.New(color.FgWhite).SprintfFunc()
|
return color.New(color.FgWhite).SprintfFunc()
|
||||||
}
|
}
|
||||||
@ -83,17 +83,5 @@ func ansiRestoreAttributes() {
|
|||||||
if isTerminal() {
|
if isTerminal() {
|
||||||
ansiEscape("8")
|
ansiEscape("8")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func uniqueEntries(paths []string) []string {
|
|
||||||
found := map[string]bool{}
|
|
||||||
unqiue := []string{}
|
|
||||||
|
|
||||||
for v := range paths {
|
|
||||||
if _, ok := found[paths[v]]; !ok {
|
|
||||||
found[paths[v]] = true
|
|
||||||
unqiue = append(unqiue, paths[v])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return unqiue
|
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ func setHeadGetRespHeaders(w http.ResponseWriter, reqParams url.Values) {
|
|||||||
func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "SelectObject")
|
ctx := newContext(r, w, "SelectObject")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "SelectObject")
|
||||||
|
|
||||||
// Fetch object stat info.
|
// Fetch object stat info.
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
@ -305,7 +305,7 @@ func (api objectAPIHandlers) SelectObjectContentHandler(w http.ResponseWriter, r
|
|||||||
func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "GetObject")
|
ctx := newContext(r, w, "GetObject")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "GetObject")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -473,7 +473,7 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "HeadObject")
|
ctx := newContext(r, w, "HeadObject")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "HeadObject")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -656,7 +656,7 @@ func getCpObjMetadataFromHeader(ctx context.Context, r *http.Request, userMeta m
|
|||||||
func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "CopyObject")
|
ctx := newContext(r, w, "CopyObject")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "CopyObject")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -1028,7 +1028,7 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "PutObject")
|
ctx := newContext(r, w, "PutObject")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "PutObject")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -1291,7 +1291,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "NewMultipartUpload")
|
ctx := newContext(r, w, "NewMultipartUpload")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "NewMultipartUpload")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -1388,7 +1388,7 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
|||||||
func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "CopyObjectPart")
|
ctx := newContext(r, w, "CopyObjectPart")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "CopyObjectPart")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -1632,7 +1632,7 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
|||||||
func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "PutObjectPart")
|
ctx := newContext(r, w, "PutObjectPart")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "PutObjectPart")
|
||||||
|
|
||||||
objectAPI := api.ObjectAPI()
|
objectAPI := api.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -1883,7 +1883,7 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
|||||||
func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "AbortMultipartUpload")
|
ctx := newContext(r, w, "AbortMultipartUpload")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "AbortMultipartUpload")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -1929,7 +1929,7 @@ func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter,
|
|||||||
func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "ListObjectParts")
|
ctx := newContext(r, w, "ListObjectParts")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "ListObjectParts")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -2008,7 +2008,7 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
|
|||||||
func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "CompleteMultipartUpload")
|
ctx := newContext(r, w, "CompleteMultipartUpload")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "CompleteMultipartUpload")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
@ -2187,7 +2187,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
|
|||||||
func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
|
func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "DeleteObject")
|
ctx := newContext(r, w, "DeleteObject")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "DeleteObject")
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
|
@ -210,7 +210,7 @@ func connectLoadInitFormats(retryCount int, firstDisk bool, endpoints EndpointLi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.SetDeploymentID(format.ID)
|
globalDeploymentID = format.ID
|
||||||
|
|
||||||
if err = formatXLFixLocalDeploymentID(context.Background(), storageDisks, format); err != nil {
|
if err = formatXLFixLocalDeploymentID(context.Background(), storageDisks, format); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -47,8 +47,8 @@ func registerDistXLRouters(router *mux.Router, endpoints EndpointList) {
|
|||||||
|
|
||||||
// List of some generic handlers which are applied for all incoming requests.
|
// List of some generic handlers which are applied for all incoming requests.
|
||||||
var globalHandlers = []HandlerFunc{
|
var globalHandlers = []HandlerFunc{
|
||||||
// set x-amz-request-id header.
|
// set x-amz-request-id, x-minio-deployment-id header.
|
||||||
addrequestIDHeader,
|
addCustomHeaders,
|
||||||
// set HTTP security headers such as Content-Security-Policy.
|
// set HTTP security headers such as Content-Security-Policy.
|
||||||
addSecurityHeaders,
|
addSecurityHeaders,
|
||||||
// Forward path style requests to actual host in a bucket federated setup.
|
// Forward path style requests to actual host in a bucket federated setup.
|
||||||
|
13
cmd/utils.go
13
cmd/utils.go
@ -397,12 +397,13 @@ func newContext(r *http.Request, w http.ResponseWriter, api string) context.Cont
|
|||||||
object = prefix
|
object = prefix
|
||||||
}
|
}
|
||||||
reqInfo := &logger.ReqInfo{
|
reqInfo := &logger.ReqInfo{
|
||||||
RequestID: w.Header().Get(responseRequestIDKey),
|
DeploymentID: w.Header().Get(responseDeploymentIDKey),
|
||||||
RemoteHost: handlers.GetSourceIP(r),
|
RequestID: w.Header().Get(responseRequestIDKey),
|
||||||
UserAgent: r.UserAgent(),
|
RemoteHost: handlers.GetSourceIP(r),
|
||||||
API: api,
|
UserAgent: r.UserAgent(),
|
||||||
BucketName: bucket,
|
API: api,
|
||||||
ObjectName: object,
|
BucketName: bucket,
|
||||||
|
ObjectName: object,
|
||||||
}
|
}
|
||||||
return logger.SetReqInfo(context.Background(), reqInfo)
|
return logger.SetReqInfo(context.Background(), reqInfo)
|
||||||
}
|
}
|
||||||
|
@ -707,7 +707,7 @@ func (web *webAPIHandlers) CreateURLToken(r *http.Request, args *WebGenericArgs,
|
|||||||
func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "WebUpload")
|
ctx := newContext(r, w, "WebUpload")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "WebUpload")
|
||||||
|
|
||||||
objectAPI := web.ObjectAPI()
|
objectAPI := web.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -845,7 +845,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
|
||||||
ctx := newContext(r, w, "WebDownload")
|
ctx := newContext(r, w, "WebDownload")
|
||||||
|
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "WebDownload")
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
objectAPI := web.ObjectAPI()
|
objectAPI := web.ObjectAPI()
|
||||||
@ -1027,7 +1027,7 @@ func (web *webAPIHandlers) DownloadZip(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ctx := newContext(r, w, "WebDownloadZip")
|
ctx := newContext(r, w, "WebDownloadZip")
|
||||||
defer logger.AuditLog(ctx, w, r)
|
defer logger.AuditLog(w, r, "WebDownloadZip")
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
objectAPI := web.ObjectAPI()
|
objectAPI := web.ObjectAPI()
|
||||||
|
@ -636,9 +636,9 @@ func (xl xlObjects) HealObject(ctx context.Context, bucket, object string, dryRu
|
|||||||
reqInfo := logger.GetReqInfo(ctx)
|
reqInfo := logger.GetReqInfo(ctx)
|
||||||
var newReqInfo *logger.ReqInfo
|
var newReqInfo *logger.ReqInfo
|
||||||
if reqInfo != nil {
|
if reqInfo != nil {
|
||||||
newReqInfo = logger.NewReqInfo(reqInfo.RemoteHost, reqInfo.UserAgent, reqInfo.RequestID, reqInfo.API, bucket, object)
|
newReqInfo = logger.NewReqInfo(reqInfo.RemoteHost, reqInfo.UserAgent, reqInfo.DeploymentID, reqInfo.RequestID, reqInfo.API, bucket, object)
|
||||||
} else {
|
} else {
|
||||||
newReqInfo = logger.NewReqInfo("", "", "", "Heal", bucket, object)
|
newReqInfo = logger.NewReqInfo("", "", globalDeploymentID, "", "Heal", bucket, object)
|
||||||
}
|
}
|
||||||
healCtx := logger.SetReqInfo(context.Background(), newReqInfo)
|
healCtx := logger.SetReqInfo(context.Background(), newReqInfo)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user