github.com/thiagoyeds/go-cloud@v0.26.0/server/requestlog/ncsa.go (about)

     1  // Copyright 2018 The Go Cloud Development Kit Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package requestlog
    16  
    17  import (
    18  	"io"
    19  	"strconv"
    20  	"sync"
    21  )
    22  
    23  // An NCSALogger writes log entries to an io.Writer in the
    24  // Combined Log Format.
    25  //
    26  // Details at http://httpd.apache.org/docs/current/logs.html#combined
    27  type NCSALogger struct {
    28  	onErr func(error)
    29  
    30  	mu  sync.Mutex
    31  	w   io.Writer
    32  	buf []byte
    33  }
    34  
    35  // NewNCSALogger returns a new logger that writes to w.
    36  // A nil onErr is treated the same as func(error) {}.
    37  func NewNCSALogger(w io.Writer, onErr func(error)) *NCSALogger {
    38  	return &NCSALogger{
    39  		w:     w,
    40  		onErr: onErr,
    41  	}
    42  }
    43  
    44  // Log writes an entry line to its writer.  Multiple concurrent calls
    45  // will produce sequential writes to its writer.
    46  func (l *NCSALogger) Log(ent *Entry) {
    47  	if err := l.log(ent); err != nil && l.onErr != nil {
    48  		l.onErr(err)
    49  	}
    50  }
    51  
    52  func (l *NCSALogger) log(ent *Entry) error {
    53  	defer l.mu.Unlock()
    54  	l.mu.Lock()
    55  	l.buf = formatEntry(l.buf[:0], ent)
    56  	_, err := l.w.Write(l.buf)
    57  	return err
    58  }
    59  
    60  func formatEntry(b []byte, ent *Entry) []byte {
    61  	const ncsaTime = "02/Jan/2006:15:04:05 -0700"
    62  	if ent.RemoteIP == "" {
    63  		b = append(b, '-')
    64  	} else {
    65  		b = append(b, ent.RemoteIP...)
    66  	}
    67  	b = append(b, " - - ["...)
    68  	b = ent.ReceivedTime.AppendFormat(b, ncsaTime)
    69  	b = append(b, "] \""...)
    70  	b = append(b, ent.RequestMethod...)
    71  	b = append(b, ' ')
    72  	b = append(b, ent.RequestURL...)
    73  	b = append(b, ' ')
    74  	b = append(b, ent.Proto...)
    75  	b = append(b, "\" "...)
    76  	b = strconv.AppendInt(b, int64(ent.Status), 10)
    77  	b = append(b, ' ')
    78  	b = strconv.AppendInt(b, int64(ent.ResponseBodySize), 10)
    79  	b = append(b, ' ')
    80  	b = strconv.AppendQuote(b, ent.Referer)
    81  	b = append(b, ' ')
    82  	b = strconv.AppendQuote(b, ent.UserAgent)
    83  	b = append(b, '\n')
    84  	return b
    85  }