parent
0772438125
commit
8a71b0ec5a
@ -0,0 +1,128 @@ |
|||||||
|
/* |
||||||
|
* MinIO Cloud Storage, (C) 2019 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 cmd |
||||||
|
|
||||||
|
import ( |
||||||
|
ring "container/ring" |
||||||
|
"context" |
||||||
|
|
||||||
|
"github.com/minio/minio/cmd/logger" |
||||||
|
"github.com/minio/minio/cmd/logger/message/log" |
||||||
|
"github.com/minio/minio/cmd/logger/target/console" |
||||||
|
"github.com/minio/minio/pkg/madmin" |
||||||
|
xnet "github.com/minio/minio/pkg/net" |
||||||
|
"github.com/minio/minio/pkg/pubsub" |
||||||
|
) |
||||||
|
|
||||||
|
// number of log messages to buffer
|
||||||
|
const defaultLogBufferCount = 10000 |
||||||
|
|
||||||
|
//HTTPConsoleLoggerSys holds global console logger state
|
||||||
|
type HTTPConsoleLoggerSys struct { |
||||||
|
pubsub *pubsub.PubSub |
||||||
|
console *console.Target |
||||||
|
nodeName string |
||||||
|
logBuf *ring.Ring |
||||||
|
} |
||||||
|
|
||||||
|
// NewConsoleLogger - creates new HTTPConsoleLoggerSys with all nodes subscribed to
|
||||||
|
// the console logging pub sub system
|
||||||
|
func NewConsoleLogger(ctx context.Context, endpoints EndpointList) *HTTPConsoleLoggerSys { |
||||||
|
host, err := xnet.ParseHost(GetLocalPeer(globalEndpoints)) |
||||||
|
if err != nil { |
||||||
|
logger.FatalIf(err, "Unable to start console logging subsystem") |
||||||
|
} |
||||||
|
var nodeName string |
||||||
|
if globalIsDistXL { |
||||||
|
nodeName = host.Name |
||||||
|
} |
||||||
|
ps := pubsub.New() |
||||||
|
return &HTTPConsoleLoggerSys{ |
||||||
|
ps, nil, nodeName, ring.New(defaultLogBufferCount), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// HasLogListeners returns true if console log listeners are registered
|
||||||
|
// for this node or peers
|
||||||
|
func (sys *HTTPConsoleLoggerSys) HasLogListeners() bool { |
||||||
|
return sys != nil && sys.pubsub.HasSubscribers() |
||||||
|
} |
||||||
|
|
||||||
|
// 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) { |
||||||
|
// 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()) |
||||||
|
} |
||||||
|
|
||||||
|
cnt := 0 |
||||||
|
// by default send all console logs in the ring buffer unless node or limit query parameters
|
||||||
|
// are set.
|
||||||
|
var lastN []madmin.LogInfo |
||||||
|
if last > defaultLogBufferCount || last <= 0 { |
||||||
|
last = defaultLogBufferCount |
||||||
|
} |
||||||
|
|
||||||
|
lastN = make([]madmin.LogInfo, last) |
||||||
|
r := sys.logBuf |
||||||
|
r.Do(func(p interface{}) { |
||||||
|
if p != nil && (p.(madmin.LogInfo)).SendLog(node) { |
||||||
|
lastN[cnt%last] = p.(madmin.LogInfo) |
||||||
|
cnt++ |
||||||
|
} |
||||||
|
}) |
||||||
|
// send last n console log messages in order filtered by node
|
||||||
|
if cnt > 0 { |
||||||
|
for i := 0; i < last; i++ { |
||||||
|
entry := lastN[(cnt+i)%last] |
||||||
|
if (entry == madmin.LogInfo{}) { |
||||||
|
continue |
||||||
|
} |
||||||
|
select { |
||||||
|
case subCh <- entry: |
||||||
|
case <-doneCh: |
||||||
|
return |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
sys.pubsub.Subscribe(subCh, doneCh, filter) |
||||||
|
} |
||||||
|
|
||||||
|
// Console returns a console target
|
||||||
|
func (sys *HTTPConsoleLoggerSys) Console() *HTTPConsoleLoggerSys { |
||||||
|
if sys.console == nil { |
||||||
|
sys.console = console.New() |
||||||
|
} |
||||||
|
return sys |
||||||
|
} |
||||||
|
|
||||||
|
// Send log message 'e' to console and publish to console
|
||||||
|
// log pubsub system
|
||||||
|
func (sys *HTTPConsoleLoggerSys) Send(e interface{}) error { |
||||||
|
lg := madmin.LogInfo{} |
||||||
|
lg.Entry = e.(log.Entry) |
||||||
|
lg.NodeName = sys.nodeName |
||||||
|
sys.pubsub.Publish(lg) |
||||||
|
// add log to ring buffer
|
||||||
|
sys.logBuf.Value = lg |
||||||
|
sys.logBuf = sys.logBuf.Next() |
||||||
|
|
||||||
|
if globalServerConfig.Logger.Console.Enabled { |
||||||
|
return sys.console.Send(e) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
/* |
||||||
|
* MinIO Cloud Storage, (C) 2019 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 madmin |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"net/http" |
||||||
|
"net/url" |
||||||
|
"strconv" |
||||||
|
"strings" |
||||||
|
|
||||||
|
"github.com/minio/minio/cmd/logger/message/log" |
||||||
|
) |
||||||
|
|
||||||
|
// LogInfo holds console log messages
|
||||||
|
type LogInfo struct { |
||||||
|
log.Entry |
||||||
|
NodeName string `json:"node"` |
||||||
|
Err error `json:"-"` |
||||||
|
} |
||||||
|
|
||||||
|
// SendLog returns true if log pertains to node specified in args.
|
||||||
|
func (l LogInfo) SendLog(node string) bool { |
||||||
|
return node == "" || strings.ToLower(node) == strings.ToLower(l.NodeName) |
||||||
|
} |
||||||
|
|
||||||
|
// GetLogs - listen on console log messages.
|
||||||
|
func (adm AdminClient) GetLogs(node string, lineCnt int, doneCh <-chan struct{}) <-chan LogInfo { |
||||||
|
logCh := make(chan LogInfo, 1) |
||||||
|
|
||||||
|
// Only success, start a routine to start reading line by line.
|
||||||
|
go func(logCh chan<- LogInfo) { |
||||||
|
defer close(logCh) |
||||||
|
urlValues := make(url.Values) |
||||||
|
urlValues.Set("node", node) |
||||||
|
urlValues.Set("limit", strconv.Itoa(lineCnt)) |
||||||
|
for { |
||||||
|
reqData := requestData{ |
||||||
|
relPath: "/v1/log", |
||||||
|
queryValues: urlValues, |
||||||
|
} |
||||||
|
// Execute GET to call log handler
|
||||||
|
resp, err := adm.executeMethod("GET", reqData) |
||||||
|
if err != nil { |
||||||
|
closeResponse(resp) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK { |
||||||
|
logCh <- LogInfo{Err: httpRespToErrorResponse(resp)} |
||||||
|
return |
||||||
|
} |
||||||
|
dec := json.NewDecoder(resp.Body) |
||||||
|
for { |
||||||
|
var info LogInfo |
||||||
|
if err = dec.Decode(&info); err != nil { |
||||||
|
break |
||||||
|
} |
||||||
|
select { |
||||||
|
case <-doneCh: |
||||||
|
return |
||||||
|
case logCh <- info: |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
}(logCh) |
||||||
|
|
||||||
|
// Returns the log info channel, for caller to start reading from.
|
||||||
|
return logCh |
||||||
|
} |
Loading…
Reference in new issue