blob: 92a758e84a50a7d4688c2c435021db5d7650aff9 [file] [log] [blame]
//go:build go1.9
// +build go1.9
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 accesslog
import (
"fmt"
"net/http"
"os"
"time"
"github.com/apache/servicecomb-service-center/pkg/chain"
"github.com/apache/servicecomb-service-center/pkg/log"
"github.com/apache/servicecomb-service-center/pkg/rest"
"github.com/apache/servicecomb-service-center/pkg/util"
"github.com/apache/servicecomb-service-center/server/config"
"github.com/go-chassis/openlog"
)
// Handler implements chain.Handler
// Handler records access log.
// Make sure to complete the initialization before handling the request.
type Handler struct {
logger openlog.Logger
whiteListAPIs map[string]struct{} // not record access log
}
// AddWhiteListAPIs adds APIs to white list, where the APIs will be ignored
// in access log.
// Not safe for concurrent use.
func (h *Handler) AddWhiteListAPIs(apis ...string) {
for _, api := range apis {
h.whiteListAPIs[api] = struct{}{}
}
}
// ShouldIgnoreAPI judges whether the API should be ignored in access log.
func (h *Handler) ShouldIgnoreAPI(api string) bool {
_, ok := h.whiteListAPIs[api]
return ok
}
// Handle handles the request
func (h *Handler) Handle(i *chain.Invocation) {
matchPattern := i.Context().Value(rest.CtxMatchPattern).(string)
if h.ShouldIgnoreAPI(matchPattern) {
i.Next()
return
}
startTimeStr := "unknown"
start, ok := i.Context().Value(rest.CtxStartTimestamp).(time.Time)
if ok {
startTimeStr = start.Format("2006-01-02T15:04:05.000Z07:00")
}
r := i.Context().Value(rest.CtxRequest).(*http.Request)
i.Next(chain.WithAsyncFunc(func(_ chain.Result) {
delayByMillisecond := "unknown"
if ok {
delayByMillisecond = fmt.Sprintf("%d", time.Since(start)/time.Millisecond)
}
statusCode := i.Context().Value(rest.CtxResponseStatus).(int)
// format: remoteIp requestReceiveTime "method requestUri proto" statusCode requestBodySize delay(ms)
// example: 127.0.0.1 2006-01-02T15:04:05.000Z07:00 "GET /v4/default/registry/microservices HTTP/1.1" 200 0 0
h.logger.Info(fmt.Sprintf("%s %s \"%s %s %s\" %d %d %s",
util.GetIPFromContext(i.Context()),
startTimeStr,
r.Method,
r.RequestURI,
r.Proto,
statusCode,
r.ContentLength,
delayByMillisecond))
}))
}
// NewAccessLogHandler creates a Handler
func NewAccessLogHandler(l openlog.Logger) *Handler {
return &Handler{
logger: l,
whiteListAPIs: make(map[string]struct{})}
}
// RegisterHandlers registers an access log handler to the handler chain
func RegisterHandlers() {
if !config.GetLog().EnableAccessLog {
return
}
logger := log.NewLogger(log.Config{
LoggerFile: os.ExpandEnv(config.GetLog().AccessLogFile),
LogFormatText: true,
LogRotateSize: int(config.GetLog().LogRotateSize),
LogBackupCount: int(config.GetLog().LogBackupCount),
NoCaller: true,
NoTime: true,
NoLevel: true,
})
h := NewAccessLogHandler(logger)
// no access log for heartbeat
h.AddWhiteListAPIs(
"/v4/:project/registry/microservices/:serviceId/instances/:instanceId/heartbeat",
"/v4/:project/registry/heartbeats",
"/registry/v3/microservices/:serviceId/instances/:instanceId/heartbeat",
"/registry/v3/heartbeats",
"")
chain.RegisterHandler(rest.ServerChainName, h)
}