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