mirror of
https://github.com/kataras/iris.git
synced 2025-01-23 10:41:03 +01:00
190 lines
5.5 KiB
Go
190 lines
5.5 KiB
Go
package accesslog
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/kataras/iris/v12/context"
|
|
"github.com/kataras/iris/v12/core/memstore"
|
|
)
|
|
|
|
// Log represents the log data specifically for the accesslog middleware.
|
|
//
|
|
//easyjson:json
|
|
type Log struct {
|
|
// The AccessLog instance this Log was created of.
|
|
Logger *AccessLog `json:"-" yaml:"-" toml:"-"`
|
|
|
|
// The time the log is created.
|
|
Now time.Time `json:"-" yaml:"-" toml:"-"`
|
|
// TimeFormat selected to print the Time as string,
|
|
// useful on Template Formatter.
|
|
TimeFormat string `json:"-" yaml:"-" toml:"-"`
|
|
// Timestamp the Now's unix timestamp (milliseconds).
|
|
Timestamp int64 `json:"timestamp" csv:"timestamp"`
|
|
|
|
// Request-Response latency.
|
|
Latency time.Duration `json:"latency" csv:"latency"`
|
|
// The response status code.
|
|
Code int `json:"code" csv:"code"`
|
|
// Init request's Method and Path.
|
|
Method string `json:"method" csv:"method"`
|
|
Path string `json:"path" csv:"path"`
|
|
// The Remote Address.
|
|
IP string `json:"ip,omitempty" csv:"ip,omitempty"`
|
|
// Sorted URL Query arguments.
|
|
Query []memstore.StringEntry `json:"query,omitempty" csv:"query,omitempty"`
|
|
// Dynamic path parameters.
|
|
PathParams memstore.Store `json:"params,omitempty" csv:"params,omitempty"`
|
|
// Fields any data information useful to represent this Log.
|
|
Fields memstore.Store `json:"fields,omitempty" csv:"fields,omitempty"`
|
|
// The Request and Response raw bodies.
|
|
// If they are escaped (e.g. JSON),
|
|
// A third-party software can read it through:
|
|
// data, _ := strconv.Unquote(log.Request)
|
|
// err := json.Unmarshal([]byte(data), &customStruct)
|
|
Request string `json:"request,omitempty" csv:"request,omitempty"`
|
|
Response string `json:"response,omitempty" csv:"response,omitempty"`
|
|
// The actual number of bytes received and sent on the network (headers + body or body only).
|
|
BytesReceived int `json:"bytes_received,omitempty" csv:"bytes_received,omitempty"`
|
|
BytesSent int `json:"bytes_sent,omitempty" csv:"bytes_sent,omitempty"`
|
|
|
|
// A copy of the Request's Context when Async is true (safe to use concurrently),
|
|
// otherwise it's the current Context (not safe for concurrent access).
|
|
Ctx *context.Context `json:"-" yaml:"-" toml:"-"`
|
|
}
|
|
|
|
// Clone returns a raw copy value of this Log.
|
|
func (l *Log) Clone() Log {
|
|
return *l
|
|
}
|
|
|
|
// RequestValuesLine returns a string line which
|
|
// combines the path parameters, query and custom fields.
|
|
func (l *Log) RequestValuesLine() string {
|
|
buf := new(strings.Builder)
|
|
_, n := parseRequestValues(buf, l.Code, l.PathParams, l.Query, l.Fields)
|
|
if n == 0 {
|
|
return ""
|
|
}
|
|
|
|
requestValues := buf.String()
|
|
if n > 1 {
|
|
return requestValues[0 : n-1] // remove last space.
|
|
}
|
|
|
|
return requestValues
|
|
}
|
|
|
|
// BytesReceivedLine returns the formatted bytes received length.
|
|
func (l *Log) BytesReceivedLine() string {
|
|
if !l.Logger.BytesReceived && !l.Logger.BytesReceivedBody {
|
|
return ""
|
|
}
|
|
return formatBytes(l.BytesReceived)
|
|
}
|
|
|
|
// BytesSentLine returns the formatted bytes sent length.
|
|
func (l *Log) BytesSentLine() string {
|
|
if !l.Logger.BytesSent && !l.Logger.BytesSentBody {
|
|
return ""
|
|
}
|
|
return formatBytes(l.BytesSent)
|
|
}
|
|
|
|
func formatBytes(b int) string {
|
|
if b <= 0 {
|
|
return "0 B"
|
|
}
|
|
|
|
const unit = 1024
|
|
if b < unit {
|
|
return fmt.Sprintf("%d B", b)
|
|
}
|
|
div, exp := int64(unit), 0
|
|
for n := b / unit; n >= unit; n /= unit {
|
|
div *= unit
|
|
exp++
|
|
}
|
|
return fmt.Sprintf("%.1f %cB",
|
|
float64(b)/float64(div), "KMGTPE"[exp])
|
|
}
|
|
|
|
const (
|
|
eq = '='
|
|
space = ' '
|
|
)
|
|
|
|
// parses the request values (params, query and fields).
|
|
// returns the length of written bytes for parsing request values
|
|
// and the total.
|
|
func parseRequestValues(buf interface {
|
|
io.StringWriter
|
|
io.ByteWriter
|
|
Len() int
|
|
}, code int, pathParams memstore.Store, query []memstore.StringEntry, fields memstore.Store) (int, int) {
|
|
n := buf.Len()
|
|
if !context.StatusCodeNotSuccessful(code) {
|
|
// collect path parameters on a successful request-response only.
|
|
for _, entry := range pathParams {
|
|
buf.WriteString(entry.Key)
|
|
buf.WriteByte(eq)
|
|
buf.WriteString(fmt.Sprintf("%v", entry.ValueRaw))
|
|
buf.WriteByte(space)
|
|
}
|
|
}
|
|
|
|
for _, entry := range query {
|
|
buf.WriteString(entry.Key)
|
|
buf.WriteByte(eq)
|
|
buf.WriteString(entry.Value)
|
|
buf.WriteByte(space)
|
|
}
|
|
|
|
for _, entry := range fields {
|
|
buf.WriteString(entry.Key)
|
|
buf.WriteByte(eq)
|
|
buf.WriteString(fmt.Sprintf("%v", entry.ValueRaw))
|
|
buf.WriteByte(space)
|
|
}
|
|
|
|
total := buf.Len()
|
|
return total - n, total
|
|
}
|
|
|
|
type (
|
|
// Formatter is responsible to print a Log to the accesslog's writer.
|
|
Formatter interface {
|
|
// SetOutput should inject the accesslog's direct output,
|
|
// if this "dest" is used then the Formatter
|
|
// should manually control its concurrent use.
|
|
SetOutput(dest io.Writer)
|
|
// Format should print the Log.
|
|
// Returns nil error on handle successfully,
|
|
// otherwise the log will be printed using the default formatter
|
|
// and the error will be printed to the Iris Application's error log level.
|
|
// Should return true if this handled the logging, otherwise false to
|
|
// continue with the default print format.
|
|
Format(log *Log) (bool, error)
|
|
}
|
|
|
|
// Flusher can be implemented by a Writer or Formatter
|
|
// to call its Flush method on AccessLog.Close
|
|
// and on panic errors.
|
|
Flusher interface{ Flush() error }
|
|
// BufferTruncater can be implemented by writers
|
|
// that support buffering.
|
|
BufferTruncater interface{ Truncate(n int) }
|
|
// FileTruncater can be implemented by files
|
|
// that can support runtime size change.
|
|
FileTruncater interface{ Truncate(size int64) error }
|
|
)
|
|
|
|
var (
|
|
_ Formatter = (*JSON)(nil)
|
|
_ Formatter = (*Template)(nil)
|
|
_ Formatter = (*CSV)(nil)
|
|
)
|