<commit_before>package jwt

import (
	"crypto/rsa"
	"errors"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"

	"bytes"

	"time"

	"io/ioutil"
	"path"

	"github.com/dgrijalva/jwt-go"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// global declaration of the backend to enable caching secret key material across requests
// when keys are specified in the config file
var b = backend{
	cache: make(map[string]keycache),
}

// AuthBackend represents a backend interface that retrieves secret key material
// to validate tokens
type AuthBackend interface {
	GetHMACSecret() (b []byte)
	GetRSAPublicKey() (r *rsa.PublicKey)
	IsConfigValid() (v bool)
}

func (h Auth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	// if the request path is any of the configured paths, validate JWT
	for _, p := range h.Rules {
		if !httpserver.Path(r.URL.Path).Matches(p.Path) {
			continue
		}

		// strip potentially spoofed claims
		for header, _ := range r.Header {
			if strings.HasPrefix(header, "Token-Claim-") {
				r.Header.Del(header)
			}
		}

		// Check excepted paths for this rule and allow access without validating any token
		var isExceptedPath bool
		for _, e := range p.ExceptedPaths {
			if httpserver.Path(r.URL.Path).Matches(e) {
				isExceptedPath = true
			}
		}
		if isExceptedPath {
			continue
		}
		if r.URL.Path == "/" && p.AllowRoot {
			// special case for protecting children of the root path, only allow access to base directory with directive `allowbase`
			continue
		}

		// Path matches, look for unvalidated token
		uToken, err := ExtractToken(r)
		if err != nil {
			if p.Passthrough {
				continue
			}
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// Initialize a new caching layer if this is the first request to a protected path.
		// Cache only operates when key material is stored on disk.  When using environment variables
		// this has no effect.
		_, ok := b.cache[p.KeyFile]
		if !ok {
			b.cache[p.KeyFile] = keycache{
				KeyFile:     p.KeyFile,
				KeyFileType: p.KeyFileType,
			}
		}
		b.current = b.cache[p.KeyFile]

		// Validate token
		vToken, err := ValidateToken(uToken, b)
		if err != nil {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}
		vClaims, err := Flatten(vToken.Claims.(jwt.MapClaims), "", DotStyle)
		if err != nil {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// If token contains rules with allow or deny, evaluate
		if len(p.AccessRules) > 0 {
			var isAuthorized []bool
			for _, rule := range p.AccessRules {
				v := vClaims[rule.Claim]
				ruleMatches := contains(v, rule.Value) || v == rule.Value
				switch rule.Authorize {
				case ALLOW:
					isAuthorized = append(isAuthorized, ruleMatches)
				case DENY:
					isAuthorized = append(isAuthorized, !ruleMatches)
				default:
					return handleUnauthorized(w, r, p, h.Realm), fmt.Errorf("unknown rule type")
				}
			}
			// test all flags, if any are true then ok to pass
			ok := false
			for _, result := range isAuthorized {
				if result {
					ok = true
				}
			}
			if !ok {
				return handleForbidden(w, r, p, h.Realm), nil
			}
		}

		// set claims as separate headers for downstream to consume
		for claim, value := range vClaims {
			headerName := "Token-Claim-" + strings.ToUpper(claim)
			switch v := value.(type) {
			case string:
				r.Header.Set(headerName, v)
			case int64:
				r.Header.Set(headerName, strconv.FormatInt(v, 10))
			case bool:
				r.Header.Set(headerName, strconv.FormatBool(v))
			case int32:
				r.Header.Set(headerName, strconv.FormatInt(int64(v), 10))
			case float32:
				r.Header.Set(headerName, strconv.FormatFloat(float64(v), 'f', -1, 32))
			case float64:
				r.Header.Set(headerName, strconv.FormatFloat(v, 'f', -1, 64))
			case []interface{}:
				b := bytes.NewBufferString("")
				for i, item := range v {
					if i > 0 {
						b.WriteString(",")
					}
					b.WriteString(fmt.Sprintf("%v", item))
				}
				r.Header.Set(headerName, b.String())
			default:
				// ignore, because, JWT spec says in https://tools.ietf.org/html/rfc7519#section-4
				//     all claims that are not understood
				//     by implementations MUST be ignored.
			}
		}

		return h.Next.ServeHTTP(w, r)
	}
	// pass request if no paths protected with JWT
	return h.Next.ServeHTTP(w, r)
}

// ExtractToken will find a JWT token passed one of three ways: (1) as the Authorization
// header in the form `Bearer <JWT Token>`; (2) as a cookie named `jwt_token`; (3) as
// a URL query paramter of the form https://example.com?token=<JWT token>
func ExtractToken(r *http.Request) (string, error) {
	jwtHeader := strings.Split(r.Header.Get("Authorization"), " ")
	if jwtHeader[0] == "Bearer" && len(jwtHeader) == 2 {
		return jwtHeader[1], nil
	}

	jwtCookie, err := r.Cookie("jwt_token")
	if err == nil {
		return jwtCookie.Value, nil
	}

	jwtQuery := r.URL.Query().Get("token")
	if jwtQuery != "" {
		return jwtQuery, nil
	}

	return "", fmt.Errorf("no token found")
}

// ValidateToken will return a parsed token if it passes validation, or an
// error if any part of the token fails validation.  Possible errors include
// malformed tokens, unknown/unspecified signing algorithms, missing secret key,
// tokens that are not valid yet (i.e., 'nbf' field), tokens that are expired,
// and tokens that fail signature verification (forged)
func ValidateToken(uToken string, b AuthBackend) (*jwt.Token, error) {
	if len(uToken) == 0 {
		return nil, fmt.Errorf("Token length is zero")
	}

	if !b.IsConfigValid() {
		return nil, errors.New("No valid configuration for JWT validation found")
	}

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("HMAC: Unexpected signing method: %v", t.Header["alg"])
			}
			return hmac, nil
		})

		if err != nil {
			return nil, err
		}

		return token, nil

	case rsa != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
				return nil, fmt.Errorf("RSA: Unexpected signing method: %v", t.Header["alg"])
			}
			return rsa, nil
		})
		if err != nil {
			return nil, err
		}

		return token, nil
	default:
		return nil, errors.New("No valid configuration for JWT validation found")
	}

}

type backend struct {
	current keycache
	cache   map[string]keycache
}

type keycache struct {
	KeyFile     string
	KeyFileType EncryptionType
	Key         []byte
	ModifyTime  time.Time
}

func (b backend) GetHMACSecret() []byte {
	switch b.current.KeyFileType {
	case RSA:
		return nil
	case HMAC:
		if secret, err := readKeyFromFile(&b); err == nil {
			return secret
		}
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	default:
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	}
}

func (b backend) GetRSAPublicKey() *rsa.PublicKey {

	switch b.current.KeyFileType {
	case HMAC:
		return nil
	case RSA:
		if pem, err := readKeyFromFile(&b); err == nil {
			rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
			if err != nil {
				return nil
			}
			return rsaPub
		}
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	default:
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	}
}

func (b backend) IsConfigValid() bool {

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil && rsa == nil:
		return true
	case hmac == nil && rsa != nil:
		return true
	default:
		return false
	}
}

// readKeyFromFile attempts to read key material from the path specified in the config.
// If the path is not absolute it will attempt to find it as a relative path from the
// working directory.  To prevent issues with concurrent read/write to the filesystem
// on every request, it will cache the result of the file read and only re-read the file
// when the modification time is earlier than the cached value
func readKeyFromFile(b *backend) ([]byte, error) {

	var keyfilePath string
	if path.IsAbs(b.current.KeyFile) {
		keyfilePath = b.current.KeyFile
	} else {
		wd, err := os.Getwd()
		if err != nil {
			return nil, err
		}
		keyfilePath = path.Join(wd, b.current.KeyFile)
	}

	finfo, err := os.Stat(keyfilePath)
	if os.IsNotExist(err) {
		return nil, err
	}

	cachehit, ok := b.cache[b.current.KeyFile]
	if !ok {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}

		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	if finfo.ModTime().After(cachehit.ModifyTime) {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}
		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	return cachehit.Key, nil
}

// handleUnauthorized checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleUnauthorized(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}

	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"invalid_token\"", realm))
	return http.StatusUnauthorized
}

// handleForbidden checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleForbidden(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}
	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"insufficient_scope\"", realm))
	return http.StatusForbidden
}

// contains checks weather list is a slice ans containts the
// supplied string value.
func contains(list interface{}, value string) bool {
	switch l := list.(type) {
	case []interface{}:
		for _, v := range l {
			if v == value {
				return true
			}
		}
	}
	return false
}
<commit_msg>allow passthrough on an invalid token<commit_after>package jwt

import (
	"crypto/rsa"
	"errors"
	"fmt"
	"net/http"
	"os"
	"strconv"
	"strings"

	"bytes"

	"time"

	"io/ioutil"
	"path"

	"github.com/dgrijalva/jwt-go"
	"github.com/mholt/caddy/caddyhttp/httpserver"
)

// global declaration of the backend to enable caching secret key material across requests
// when keys are specified in the config file
var b = backend{
	cache: make(map[string]keycache),
}

// AuthBackend represents a backend interface that retrieves secret key material
// to validate tokens
type AuthBackend interface {
	GetHMACSecret() (b []byte)
	GetRSAPublicKey() (r *rsa.PublicKey)
	IsConfigValid() (v bool)
}

func (h Auth) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
	// if the request path is any of the configured paths, validate JWT
	for _, p := range h.Rules {
		if !httpserver.Path(r.URL.Path).Matches(p.Path) {
			continue
		}

		// strip potentially spoofed claims
		for header, _ := range r.Header {
			if strings.HasPrefix(header, "Token-Claim-") {
				r.Header.Del(header)
			}
		}

		// Check excepted paths for this rule and allow access without validating any token
		var isExceptedPath bool
		for _, e := range p.ExceptedPaths {
			if httpserver.Path(r.URL.Path).Matches(e) {
				isExceptedPath = true
			}
		}
		if isExceptedPath {
			continue
		}
		if r.URL.Path == "/" && p.AllowRoot {
			// special case for protecting children of the root path, only allow access to base directory with directive `allowbase`
			continue
		}

		// Path matches, look for unvalidated token
		uToken, err := ExtractToken(r)
		if err != nil {
			if p.Passthrough {
				continue
			}
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// Initialize a new caching layer if this is the first request to a protected path.
		// Cache only operates when key material is stored on disk.  When using environment variables
		// this has no effect.
		_, ok := b.cache[p.KeyFile]
		if !ok {
			b.cache[p.KeyFile] = keycache{
				KeyFile:     p.KeyFile,
				KeyFileType: p.KeyFileType,
			}
		}
		b.current = b.cache[p.KeyFile]

		// Validate token
		vToken, err := ValidateToken(uToken, b)
		if err != nil {
			if p.Passthrough {
				continue
			}
			return handleUnauthorized(w, r, p, h.Realm), nil
		}
		vClaims, err := Flatten(vToken.Claims.(jwt.MapClaims), "", DotStyle)
		if err != nil {
			return handleUnauthorized(w, r, p, h.Realm), nil
		}

		// If token contains rules with allow or deny, evaluate
		if len(p.AccessRules) > 0 {
			var isAuthorized []bool
			for _, rule := range p.AccessRules {
				v := vClaims[rule.Claim]
				ruleMatches := contains(v, rule.Value) || v == rule.Value
				switch rule.Authorize {
				case ALLOW:
					isAuthorized = append(isAuthorized, ruleMatches)
				case DENY:
					isAuthorized = append(isAuthorized, !ruleMatches)
				default:
					return handleUnauthorized(w, r, p, h.Realm), fmt.Errorf("unknown rule type")
				}
			}
			// test all flags, if any are true then ok to pass
			ok := false
			for _, result := range isAuthorized {
				if result {
					ok = true
				}
			}
			if !ok {
				return handleForbidden(w, r, p, h.Realm), nil
			}
		}

		// set claims as separate headers for downstream to consume
		for claim, value := range vClaims {
			headerName := "Token-Claim-" + strings.ToUpper(claim)
			switch v := value.(type) {
			case string:
				r.Header.Set(headerName, v)
			case int64:
				r.Header.Set(headerName, strconv.FormatInt(v, 10))
			case bool:
				r.Header.Set(headerName, strconv.FormatBool(v))
			case int32:
				r.Header.Set(headerName, strconv.FormatInt(int64(v), 10))
			case float32:
				r.Header.Set(headerName, strconv.FormatFloat(float64(v), 'f', -1, 32))
			case float64:
				r.Header.Set(headerName, strconv.FormatFloat(v, 'f', -1, 64))
			case []interface{}:
				b := bytes.NewBufferString("")
				for i, item := range v {
					if i > 0 {
						b.WriteString(",")
					}
					b.WriteString(fmt.Sprintf("%v", item))
				}
				r.Header.Set(headerName, b.String())
			default:
				// ignore, because, JWT spec says in https://tools.ietf.org/html/rfc7519#section-4
				//     all claims that are not understood
				//     by implementations MUST be ignored.
			}
		}

		return h.Next.ServeHTTP(w, r)
	}
	// pass request if no paths protected with JWT
	return h.Next.ServeHTTP(w, r)
}

// ExtractToken will find a JWT token passed one of three ways: (1) as the Authorization
// header in the form `Bearer <JWT Token>`; (2) as a cookie named `jwt_token`; (3) as
// a URL query paramter of the form https://example.com?token=<JWT token>
func ExtractToken(r *http.Request) (string, error) {
	jwtHeader := strings.Split(r.Header.Get("Authorization"), " ")
	if jwtHeader[0] == "Bearer" && len(jwtHeader) == 2 {
		return jwtHeader[1], nil
	}

	jwtCookie, err := r.Cookie("jwt_token")
	if err == nil {
		return jwtCookie.Value, nil
	}

	jwtQuery := r.URL.Query().Get("token")
	if jwtQuery != "" {
		return jwtQuery, nil
	}

	return "", fmt.Errorf("no token found")
}

// ValidateToken will return a parsed token if it passes validation, or an
// error if any part of the token fails validation.  Possible errors include
// malformed tokens, unknown/unspecified signing algorithms, missing secret key,
// tokens that are not valid yet (i.e., 'nbf' field), tokens that are expired,
// and tokens that fail signature verification (forged)
func ValidateToken(uToken string, b AuthBackend) (*jwt.Token, error) {
	if len(uToken) == 0 {
		return nil, fmt.Errorf("Token length is zero")
	}

	if !b.IsConfigValid() {
		return nil, errors.New("No valid configuration for JWT validation found")
	}

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("HMAC: Unexpected signing method: %v", t.Header["alg"])
			}
			return hmac, nil
		})

		if err != nil {
			return nil, err
		}

		return token, nil

	case rsa != nil:
		token, err := jwt.Parse(uToken, func(t *jwt.Token) (interface{}, error) {
			if _, ok := t.Method.(*jwt.SigningMethodRSA); !ok {
				return nil, fmt.Errorf("RSA: Unexpected signing method: %v", t.Header["alg"])
			}
			return rsa, nil
		})
		if err != nil {
			return nil, err
		}

		return token, nil
	default:
		return nil, errors.New("No valid configuration for JWT validation found")
	}

}

type backend struct {
	current keycache
	cache   map[string]keycache
}

type keycache struct {
	KeyFile     string
	KeyFileType EncryptionType
	Key         []byte
	ModifyTime  time.Time
}

func (b backend) GetHMACSecret() []byte {
	switch b.current.KeyFileType {
	case RSA:
		return nil
	case HMAC:
		if secret, err := readKeyFromFile(&b); err == nil {
			return secret
		}
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	default:
		secret := os.Getenv("JWT_SECRET")
		if secret == "" {
			return nil
		}
		return []byte(secret)
	}
}

func (b backend) GetRSAPublicKey() *rsa.PublicKey {

	switch b.current.KeyFileType {
	case HMAC:
		return nil
	case RSA:
		if pem, err := readKeyFromFile(&b); err == nil {
			rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
			if err != nil {
				return nil
			}
			return rsaPub
		}
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	default:
		pem := os.Getenv("JWT_PUBLIC_KEY")
		if pem == "" {
			return nil
		}
		rsaPub, err := jwt.ParseRSAPublicKeyFromPEM([]byte(pem))
		if err != nil {
			return nil
		}
		return rsaPub
	}
}

func (b backend) IsConfigValid() bool {

	hmac := b.GetHMACSecret()
	rsa := b.GetRSAPublicKey()

	switch {
	case hmac != nil && rsa == nil:
		return true
	case hmac == nil && rsa != nil:
		return true
	default:
		return false
	}
}

// readKeyFromFile attempts to read key material from the path specified in the config.
// If the path is not absolute it will attempt to find it as a relative path from the
// working directory.  To prevent issues with concurrent read/write to the filesystem
// on every request, it will cache the result of the file read and only re-read the file
// when the modification time is earlier than the cached value
func readKeyFromFile(b *backend) ([]byte, error) {

	var keyfilePath string
	if path.IsAbs(b.current.KeyFile) {
		keyfilePath = b.current.KeyFile
	} else {
		wd, err := os.Getwd()
		if err != nil {
			return nil, err
		}
		keyfilePath = path.Join(wd, b.current.KeyFile)
	}

	finfo, err := os.Stat(keyfilePath)
	if os.IsNotExist(err) {
		return nil, err
	}

	cachehit, ok := b.cache[b.current.KeyFile]
	if !ok {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}

		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	if finfo.ModTime().After(cachehit.ModifyTime) {
		key, err := ioutil.ReadFile(keyfilePath)
		if err != nil {
			return nil, err
		}
		b.cache[b.current.KeyFile] = keycache{
			KeyFile:     b.current.KeyFile,
			KeyFileType: b.current.KeyFileType,
			Key:         key,
			ModifyTime:  finfo.ModTime(),
		}
		b.current = b.cache[b.current.KeyFile]
		return key, nil
	}

	return cachehit.Key, nil
}

// handleUnauthorized checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleUnauthorized(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}

	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"invalid_token\"", realm))
	return http.StatusUnauthorized
}

// handleForbidden checks, which action should be performed if access was denied.
// It returns the status code and writes the Location header in case of a redirect.
// Possible caddy variables in the location value will be substituted.
func handleForbidden(w http.ResponseWriter, r *http.Request, rule Rule, realm string) int {
	if rule.Redirect != "" {
		replacer := httpserver.NewReplacer(r, nil, "")
		http.Redirect(w, r, replacer.Replace(rule.Redirect), http.StatusSeeOther)
		return http.StatusSeeOther
	}
	w.Header().Add("WWW-Authenticate", fmt.Sprintf("Bearer realm=\"%s\",error=\"insufficient_scope\"", realm))
	return http.StatusForbidden
}

// contains checks weather list is a slice ans containts the
// supplied string value.
func contains(list interface{}, value string) bool {
	switch l := list.(type) {
	case []interface{}:
		for _, v := range l {
			if v == value {
				return true
			}
		}
	}
	return false
}
<|endoftext|>


<commit_before>package jsonutil

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"
	"unicode"
	"unicode/utf8"
)

type item struct {
	typ itemType
	pos int
	val string
}

func (i item) String() string {
	switch {
	case i.typ == itemEOF:
		return "EOF"
	case i.typ == itemError:
		return i.val
	}
	return fmt.Sprintf("%q", i.val)
}

type itemType int

const (
	itemError itemType = iota
	itemString
	itemText
	itemBlockComment
	itemLineComment
	itemWhitespace
	itemEOF
)

const eof = -1

type stateFn func(*lexer) stateFn

type lexer struct {
	input  *bufio.Reader
	buffer bytes.Buffer
	state  stateFn
	pos    int
	start  int
	items  chan item
}

func (l *lexer) nextItem() item {
	item := <-l.items
	return item
}

func lex(input io.Reader) *lexer {
	l := &lexer{
		input: bufio.NewReader(input),
		items: make(chan item),
	}
	go l.run()
	return l
}

func (l *lexer) run() {
	for l.state = lexText; l.state != nil; {
		l.state = l.state(l)
	}
}

func (l *lexer) next() rune {
	r, w, err := l.input.ReadRune()
	if err == io.EOF {
		return eof
	}
	l.pos += w
	l.buffer.WriteRune(r)
	return r
}

func (l *lexer) peek() rune {
	lead, err := l.input.Peek(1)
	if err == io.EOF {
		return eof
	} else if err != nil {
		panic(err)
	}

	p, err := l.input.Peek(runeLen(lead[0]))
	if err == io.EOF {
		return eof
	} else if err != nil {
		panic(err)
	}
	r, _ := utf8.DecodeRune(p)
	return r
}

func runeLen(lead byte) int {
	if lead >= 0xF0 {
		return 4
	} else if lead >= 0xE0 {
		return 3
	} else if lead >= 0xC0 {
		return 2
	} else {
		return 1
	}
}

func (l *lexer) emit(t itemType) {
	l.items <- item{t, l.start, l.buffer.String()}
	l.start = l.pos
	l.buffer.Truncate(0)
}

func (l *lexer) accept(valid string) bool {
	if strings.IndexRune(valid, l.peek()) >= 0 {
		l.next()
		return true
	}
	return false
}

func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
	return nil
}

func (l *lexer) hasPrefix(prefix string) bool {
	p, err := l.input.Peek(len(prefix))
	if err == io.EOF {
		return false
	} else if err != nil {
		panic(err)
	}
	return string(p) == prefix
}

// Accept next count runes. Normally called after hasPrefix().
func (l *lexer) nextRuneCount(count int) {
	for i := 0; i < count; i++ {
		l.next()
	}
}

const (
	doubleQuote  = `"`
	lineComment  = "//"
	leftComment  = "/*"
	rightComment = "*/"
)

func lexText(l *lexer) stateFn {
	for {
		if l.hasPrefix(doubleQuote) {
			if l.pos > l.start {
				l.emit(itemText)
			}
			return lexString
		} else if l.hasPrefix(lineComment) {
			if l.pos > l.start {
				l.emit(itemText)
			}
			return lexLineComment
		} else if l.hasPrefix(leftComment) {
			if l.pos > l.start {
				l.emit(itemText)
			}
			return lexBlockComment
		}

		r := l.peek()
		if unicode.IsSpace(r) {
			if l.pos > l.start {
				l.emit(itemText)
			}
			return lexWhitespace
		} else if r == eof {
			l.next()
			break
		} else {
			l.next()
		}
	}
	if l.pos > l.start {
		l.emit(itemText)
	}
	l.emit(itemEOF)
	return nil
}

func lexString(l *lexer) stateFn {
	l.next()
	for {
		switch r := l.next(); {
		case r == '"':
			l.emit(itemString)
			return lexText
		case r == '\\':
			if l.accept(`"\/bfnrt`) {
				break
			} else if r := l.next(); r == 'u' {
				for i := 0; i < 4; i++ {
					if !l.accept("0123456789ABCDEFabcdef") {
						return l.errorf("expected 4 hexadecimal digits")
					}
				}
			} else {
				return l.errorf("unsupported escape character")
			}
		case unicode.IsControl(r):
			return l.errorf("cannot contain control characters in strings")
		case r == eof:
			return l.errorf("unclosed string")
		}
	}
}

func lexWhitespace(l *lexer) stateFn {
	for unicode.IsSpace(l.peek()) {
		l.next()
	}
	l.emit(itemWhitespace)
	return lexText
}

func lexLineComment(l *lexer) stateFn {
	for {
		r := l.next()
		if r == '\n' || r == eof {
			if l.pos > l.start {
				l.emit(itemLineComment)
			}
			if r == eof {
				l.emit(itemEOF)
				return nil
			}
			return lexText
		}
	}
}

func lexBlockComment(l *lexer) stateFn {
	for {
		if l.hasPrefix(rightComment) {
			l.nextRuneCount(utf8.RuneCountInString(rightComment))
			if l.pos > l.start {
				l.emit(itemBlockComment)
			}
			return lexText
		}
		if l.next() == eof {
			break
		}
	}
	if l.pos > l.start {
		l.emit(itemText)
	}
	l.emit(itemEOF)
	return nil
}
<commit_msg>Use lexer.errorf() instead of panic().<commit_after>package jsonutil

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"
	"unicode"
	"unicode/utf8"
)

type item struct {
	typ itemType
	pos int
	val string
}

func (i item) String() string {
	switch {
	case i.typ == itemEOF:
		return "EOF"
	case i.typ == itemError:
		return i.val
	}
	return fmt.Sprintf("%q", i.val)
}

type itemType int

const (
	itemError itemType = iota
	itemString
	itemText
	itemBlockComment
	itemLineComment
	itemWhitespace
	itemEOF
)

const eof = -1

type stateFn func(*lexer) stateFn

type lexer struct {
	input  *bufio.Reader
	buffer bytes.Buffer
	state  stateFn
	pos    int
	start  int
	items  chan item
}

func (l *lexer) nextItem() item {
	item := <-l.items
	return item
}

func lex(input io.Reader) *lexer {
	l := &lexer{
		input: bufio.NewReader(input),
		items: make(chan item),
	}
	go l.run()
	return l
}

func (l *lexer) run() {
	for l.state = lexText; l.state != nil; {
		l.state = l.state(l)
	}
}

func (l *lexer) next() rune {
	r, w, err := l.input.ReadRune()
	if err == io.EOF {
		return eof
	}
	l.pos += w
	l.buffer.WriteRune(r)
	return r
}

func (l *lexer) peek() rune {
	lead, err := l.input.Peek(1)
	if err == io.EOF {
		return eof
	} else if err != nil {
		l.errorf("%s", err.Error())
		return 0
	}

	p, err := l.input.Peek(runeLen(lead[0]))
	if err == io.EOF {
		return eof
	} else if err != nil {
		l.errorf("%s", err.Error())
		return 0
	}
	r, _ := utf8.DecodeRune(p)
	return r
}

func runeLen(lead byte) int {
	if lead >= 0xF0 {
		return 4
	} else if lead >= 0xE0 {
		return 3
	} else if lead >= 0xC0 {
		return 2
	} else {
		return 1
	}
}

func (l *lexer) emit(t itemType) {
	l.items <- item{t, l.start, l.buffer.String()}
	l.start = l.pos
	l.buffer.Truncate(0)
}

func (l *lexer) accept(valid string) bool {
	if strings.IndexRune(valid, l.peek()) >= 0 {
		l.next()
		return true
	}
	return false
}

func (l *lexer) errorf(format string, args ...interface{}) stateFn {
	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)}
	return nil
}

func (l *lexer) hasPrefix(prefix string) bool {
	p, err := l.input.Peek(len(prefix))
	if err == io.EOF {
		return false
	} else if err != nil {
		l.errorf("%s", err.Error())
		return false
	}
	return string(p) == prefix
}

// Accept next count runes. Normally called after hasPrefix().
func (l *lexer) nextRuneCount(count int) {
	for i := 0; i < count; i++ {
		l.next()
	}
}

const (
	doubleQuote  = `"`
	lineComment  = "//"
	leftComment  = "/*"
	rightComment = "*/"
)

func lexText(l *lexer) stateFn {
	for {
		if l.hasPrefix(doubleQuote) {
			if l.pos > l.start {
				l.emit(itemText)
			}
			return lexString
		} else if l.hasPrefix(lineComment) {
			if l.pos > l.start {
				l.emit(itemText)
			}
			return lexLineComment
		} else if l.hasPrefix(leftComment) {
			if l.pos > l.start {
				l.emit(itemText)
			}
			return lexBlockComment
		}

		r := l.peek()
		if unicode.IsSpace(r) {
			if l.pos > l.start {
				l.emit(itemText)
			}
			return lexWhitespace
		} else if r == eof {
			l.next()
			break
		} else {
			l.next()
		}
	}
	if l.pos > l.start {
		l.emit(itemText)
	}
	l.emit(itemEOF)
	return nil
}

func lexString(l *lexer) stateFn {
	l.next()
	for {
		switch r := l.next(); {
		case r == '"':
			l.emit(itemString)
			return lexText
		case r == '\\':
			if l.accept(`"\/bfnrt`) {
				break
			} else if r := l.next(); r == 'u' {
				for i := 0; i < 4; i++ {
					if !l.accept("0123456789ABCDEFabcdef") {
						return l.errorf("expected 4 hexadecimal digits")
					}
				}
			} else {
				return l.errorf("unsupported escape character")
			}
		case unicode.IsControl(r):
			return l.errorf("cannot contain control characters in strings")
		case r == eof:
			return l.errorf("unclosed string")
		}
	}
}

func lexWhitespace(l *lexer) stateFn {
	for unicode.IsSpace(l.peek()) {
		l.next()
	}
	l.emit(itemWhitespace)
	return lexText
}

func lexLineComment(l *lexer) stateFn {
	for {
		r := l.next()
		if r == '\n' || r == eof {
			if l.pos > l.start {
				l.emit(itemLineComment)
			}
			if r == eof {
				l.emit(itemEOF)
				return nil
			}
			return lexText
		}
	}
}

func lexBlockComment(l *lexer) stateFn {
	for {
		if l.hasPrefix(rightComment) {
			l.nextRuneCount(utf8.RuneCountInString(rightComment))
			if l.pos > l.start {
				l.emit(itemBlockComment)
			}
			return lexText
		}
		if l.next() == eof {
			break
		}
	}
	if l.pos > l.start {
		l.emit(itemText)
	}
	l.emit(itemEOF)
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"net"
	"strings"
	"time"
)

type logEntry struct {
	clientIP  *net.IP
	cdnIP     *net.IP
	timestamp time.Time
	host      Dimension
	backend   Dimension
	frontend  Dimension
	useragent Dimension
}

// parseLog takes in an haproxy log line and returns a logEntry.
func parseLog(logLine string) *logEntry {
	var entry logEntry
	if logLine == "" {
		return nil
	}
	// This string parsing stuff was lifted from TPS
	var a, b int
	if a = strings.Index(logLine, "]:") + 3; a == -1 {
		return nil
	}
	if b = strings.Index(logLine[a:], ":"); b == -1 {
		return nil
	}
	clientIPString := logLine[a : a+b]
	clientIP := net.ParseIP(clientIPString)
	if clientIP == nil {
		return nil
	}
	entry.clientIP = &clientIP

	logLine = logLine[a+b:]
	// The subsequent square-bracketed string contains our timestamp
	if a = strings.Index(logLine, "[") + 1; a == -1 {
		return &entry
	}
	if b = strings.Index(logLine[a:], "]"); b == -1 {
		return &entry
	}
	timestampStr := logLine[a : a+b]
	entry.timestamp, _ = time.Parse("02/Jan/2006:15:04:05.999", timestampStr)

	logLine = logLine[a+b:]
	// The subsequent string is our frontend
	if a = strings.Index(logLine, " ") + 1; a == -1 {
		return &entry
	}
	if b = strings.Index(logLine[a:], " "); b == -1 {
		return &entry
	}
	entry.frontend = Dimension{Type: DimensionFrontend, Value: logLine[a : a+b]}

	logLine = logLine[a+b:]
	// The subsequent string is our backend
	if a = strings.Index(logLine, " ") + 1; a == -1 {
		return &entry
	}
	if b = strings.Index(logLine[a:], "/"); b == -1 {
		return &entry
	}
	entry.backend = Dimension{Type: DimensionBackend, Value: logLine[a : a+b]}

	logLine = logLine[a+b:]
	// The first curly-braced block contains our request headers
	if a = strings.Index(logLine, "{"); a == -1 {
		return &entry
	}
	if b = strings.Index(logLine[a:], "}"); b == -1 {
		return &entry
	}
	bracketedHeaders := logLine[a : a+b]
	headers := strings.Split(bracketedHeaders, "|")
	if len(headers) < 7 {
		return &entry
	}
	entry.useragent = Dimension{Type: DimensionUseragent, Value: headers[1]}
	entry.host = Dimension{Type: DimensionHost, Value: headers[2]}
	ipString := headers[7]
	cdnIP := net.ParseIP(ipString)
	if cdnIP == nil {
		return &entry
	}
	entry.cdnIP = &cdnIP
	return &entry
}
<commit_msg>Adjust log processing.<commit_after>package main

import (
	"net"
	"strings"
	"time"
)

type logEntry struct {
	clientIP  *net.IP
	cdnIP     *net.IP
	timestamp time.Time
	host      Dimension
	backend   Dimension
	frontend  Dimension
	useragent Dimension
}

// parseLog takes in an haproxy log line and returns a logEntry.
func parseLog(logLine string) *logEntry {
	var entry logEntry
	if logLine == "" {
		return nil
	}
	// This string parsing stuff was lifted from TPS
	var a, b int
	a = 0
	if b = strings.Index(logLine[a:], ":"); b == -1 {
		return nil
	}
	clientIPString := logLine[a : a+b]
	clientIP := net.ParseIP(clientIPString)
	if clientIP == nil {
		return nil
	}
	entry.clientIP = &clientIP

	logLine = logLine[a+b:]
	// The subsequent square-bracketed string contains our timestamp
	if a = strings.Index(logLine, "[") + 1; a == -1 {
		return &entry
	}
	if b = strings.Index(logLine[a:], "]"); b == -1 {
		return &entry
	}
	timestampStr := logLine[a : a+b]
	entry.timestamp, _ = time.Parse("02/Jan/2006:15:04:05.999", timestampStr)

	logLine = logLine[a+b:]
	// The subsequent string is our frontend
	if a = strings.Index(logLine, " ") + 1; a == -1 {
		return &entry
	}
	if b = strings.Index(logLine[a:], " "); b == -1 {
		return &entry
	}
	entry.frontend = Dimension{Type: DimensionFrontend, Value: logLine[a : a+b]}

	logLine = logLine[a+b:]
	// The subsequent string is our backend
	if a = strings.Index(logLine, " ") + 1; a == -1 {
		return &entry
	}
	if b = strings.Index(logLine[a:], "/"); b == -1 {
		return &entry
	}
	entry.backend = Dimension{Type: DimensionBackend, Value: logLine[a : a+b]}

	logLine = logLine[a+b:]
	// The first curly-braced block contains our request headers
	if a = strings.Index(logLine, "{"); a == -1 {
		return &entry
	}
	if b = strings.Index(logLine[a:], "}"); b == -1 {
		return &entry
	}
	bracketedHeaders := logLine[a : a+b]
	headers := strings.Split(bracketedHeaders, "|")
	if len(headers) < 7 {
		return &entry
	}
	entry.useragent = Dimension{Type: DimensionUseragent, Value: headers[1]}
	entry.host = Dimension{Type: DimensionHost, Value: headers[2]}
	ipString := headers[7]
	cdnIP := net.ParseIP(ipString)
	if cdnIP == nil {
		return &entry
	}
	entry.cdnIP = &cdnIP
	return &entry
}
<|endoftext|>


<commit_before>/* This implements an alternative logger to the one found in the standard
 * library with support for more logging levels, formatters and handlers.
 * The main goal is to provide easy and flexible way to handle new handlers and formats
 * Author: Robert Zaremba
 *
 * https://github.com/scale-it/go-log
 */
package log

import (
	"errors"
	"fmt"
	"io"
	"os"
	"sync"
)

// Flags from std log package
// These flags define which text to prefix to each log entry generated by the Logger.
const (
	// Bits or'ed together to control what's printed. There is no control over the
	// order they appear (the order listed here) or the format they present (as
	// described in the comments).  A colon appears after these items:
	//	2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
	Ldate         = 1 << iota     // the date: 2009/01/23
	Ltime                         // the time: 01:23:23
	Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
	Llongfile                     // full file name and line number: /a/b/c/d.go:23
	Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
	LstdFlags     = Ldate | Ltime // initial values for the standard logger
)

// Map of mutexes which will protect Writeres for simultaneous write
var writerMutexMap = make(map[io.Writer]*sync.Mutex)

// Represents how critical the logged
// message is.
type Level uint8

var Levels = struct {
	Trace    Level
	Debug    Level
	Info     Level
	Warning  Level
	Error    Level
	Critical Level
}{0, 10, 20, 30, 40, 50}

// Verbose names of the levels
var levelStrings = map[Level]string{
	Levels.Trace:    "TRACE",
	Levels.Debug:    "DEBUG",
	Levels.Info:     "INFO ",
	Levels.Warning:  "WARN ",
	Levels.Error:    "ERROR",
	Levels.Critical: "CRITIC",
}

// Verbose and colored names of the levels
var levelCStrings = map[Level]string{
	Levels.Trace:    levelStrings[Levels.Trace],
	Levels.Debug:    levelStrings[Levels.Debug],
	Levels.Info:     AnsiEscape(MAGENTA, levelStrings[Levels.Info], OFF),
	Levels.Warning:  AnsiEscape(YELLOW, levelStrings[Levels.Warning], OFF),
	Levels.Error:    AnsiEscape(RED, levelStrings[Levels.Error], OFF),
	Levels.Critical: AnsiEscape(RED, BOLD, levelStrings[Levels.Critical], OFF),
}

// Returns an log Level which name match given string.
// If there is no such Level, then Levels.Debug is returned
func String2Level(level string) (Level, error) {
	if level == "" {
		return Levels.Debug, errors.New("level is empty")
	}
	for li, ls := range levelStrings {
		if ls == level {
			return li, nil
		}
	}
	return Levels.Debug, errors.New("Wrong log level " + level)
}

type handler struct {
	writer io.Writer
	level  Level
	fmt    Formatter
}

type Logger struct {
	// Mutex to protect simultaneous appends to handlers
	mtx      sync.Mutex
	handlers []handler
}

// Instantiate a new Logger
func New() *Logger {
	return &Logger{sync.Mutex{}, make([]handler, 0)}
}

// Convenience function to create logger with StdFormatter
func NewStd(w io.Writer, level Level, flag int, colored bool) *Logger {
	l := Logger{sync.Mutex{}, make([]handler, 0)}
	l.AddHandler(w, level, StdFormatter{"", flag, colored})
	return &l
}

/* LOGGER
 * ------
 */

// Adds a handler, specifying the maximum log Level you want to be written to this output.
// For instance, if you pass Warning for level, all logs of type
// Warning, Error, and Critical would be logged to this handler.
// This method is thread safe. You can use it in multiple goroutines.
// You can also use the same writer in multiple Loggers.
func (this *Logger) AddHandler(writer io.Writer, level Level, fm Formatter) {
	this.mtx.Lock()
	if _, ok := writerMutexMap[writer]; !ok {
		writerMutexMap[writer] = &sync.Mutex{}
	}
	this.handlers = append(this.handlers, handler{writer, level, fm})
	this.mtx.Unlock()
}

// Logs a message for the given level. Most callers will likely
// prefer to use one of the provided convenience functions (Debug, Info...).
func (this *Logger) Log(level Level, msg string) {
	var out []byte
	for _, h := range this.handlers {
		if h.level <= level {
			out = h.fmt.Format(level, msg)
			mtx, _ := writerMutexMap[h.writer]
			mtx.Lock()
			h.writer.Write(out)
			mtx.Unlock()
		}
	}
}

// Logs a formatted message message for the given level.
// Wrapper around Log method
func (this *Logger) Logf(level Level, format string, v ...interface{}) {
	this.Log(level, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Trace(v ...interface{}) {
	// TODO: split the string
	this.Log(Levels.Trace, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Tracef(format string, v ...interface{}) {
	// TODO: split the string
	this.Log(Levels.Trace, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Debug(v ...interface{}) {
	this.Log(Levels.Debug, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Debugf(format string, v ...interface{}) {
	this.Log(Levels.Debug, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Info(v ...interface{}) {
	this.Log(Levels.Info, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Infof(format string, v ...interface{}) {
	this.Log(Levels.Info, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Warning(v ...interface{}) {
	this.Log(Levels.Warning, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Warningf(format string, v ...interface{}) {
	this.Log(Levels.Warning, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Error(v ...interface{}) {
	this.Log(Levels.Error, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Errorf(format string, v ...interface{}) {
	this.Log(Levels.Error, fmt.Sprintf(format+"\n", v...))
}

// Convenience function, will not terminate the program
func (this *Logger) Critical(v ...interface{}) {
	this.Log(Levels.Critical, fmt.Sprintln(v...))
}

// Convenience function, will not terminate the program
func (this *Logger) Criticalf(format string, v ...interface{}) {
	this.Log(Levels.Critical, fmt.Sprintf(format+"\n", v...))
}

// Convenience function, will terminate the program
func (this *Logger) Fatal(v ...interface{}) {
	this.Log(Levels.Critical, fmt.Sprintln(v...))
	os.Exit(1)
}

// Convenience function, will terminate the program
func (this *Logger) Fatalf(format string, v ...interface{}) {
	this.Log(Levels.Critical, fmt.Sprintf(format+"\n", v...))
	os.Exit(1)
}

// Convinience function to support io.Writer interface
func (this *Logger) Write(p []byte) (n int, err error) {
	n = len(p)
	this.Log(0, string(p))
	return
}
<commit_msg>Added Warn method as a short for Warning<commit_after>/* This implements an alternative logger to the one found in the standard
 * library with support for more logging levels, formatters and handlers.
 * The main goal is to provide easy and flexible way to handle new handlers and formats
 * Author: Robert Zaremba
 *
 * https://github.com/scale-it/go-log
 */
package log

import (
	"errors"
	"fmt"
	"io"
	"os"
	"sync"
)

// Flags from std log package
// These flags define which text to prefix to each log entry generated by the Logger.
const (
	// Bits or'ed together to control what's printed. There is no control over the
	// order they appear (the order listed here) or the format they present (as
	// described in the comments).  A colon appears after these items:
	//	2009/01/23 01:23:23.123123 /a/b/c/d.go:23: message
	Ldate         = 1 << iota     // the date: 2009/01/23
	Ltime                         // the time: 01:23:23
	Lmicroseconds                 // microsecond resolution: 01:23:23.123123.  assumes Ltime.
	Llongfile                     // full file name and line number: /a/b/c/d.go:23
	Lshortfile                    // final file name element and line number: d.go:23. overrides Llongfile
	LstdFlags     = Ldate | Ltime // initial values for the standard logger
)

// Map of mutexes which will protect Writeres for simultaneous write
var writerMutexMap = make(map[io.Writer]*sync.Mutex)

// Represents how critical the logged
// message is.
type Level uint8

var Levels = struct {
	Trace    Level
	Debug    Level
	Info     Level
	Warning  Level
	Error    Level
	Critical Level
}{0, 10, 20, 30, 40, 50}

// Verbose names of the levels
var levelStrings = map[Level]string{
	Levels.Trace:    "TRACE",
	Levels.Debug:    "DEBUG",
	Levels.Info:     "INFO ",
	Levels.Warning:  "WARN ",
	Levels.Error:    "ERROR",
	Levels.Critical: "CRITIC",
}

// Verbose and colored names of the levels
var levelCStrings = map[Level]string{
	Levels.Trace:    levelStrings[Levels.Trace],
	Levels.Debug:    levelStrings[Levels.Debug],
	Levels.Info:     AnsiEscape(MAGENTA, levelStrings[Levels.Info], OFF),
	Levels.Warning:  AnsiEscape(YELLOW, levelStrings[Levels.Warning], OFF),
	Levels.Error:    AnsiEscape(RED, levelStrings[Levels.Error], OFF),
	Levels.Critical: AnsiEscape(RED, BOLD, levelStrings[Levels.Critical], OFF),
}

// Returns an log Level which name match given string.
// If there is no such Level, then Levels.Debug is returned
func String2Level(level string) (Level, error) {
	if level == "" {
		return Levels.Debug, errors.New("level is empty")
	}
	for li, ls := range levelStrings {
		if ls == level {
			return li, nil
		}
	}
	return Levels.Debug, errors.New("Wrong log level " + level)
}

type handler struct {
	writer io.Writer
	level  Level
	fmt    Formatter
}

type Logger struct {
	// Mutex to protect simultaneous appends to handlers
	mtx      sync.Mutex
	handlers []handler
}

// Instantiate a new Logger
func New() *Logger {
	return &Logger{sync.Mutex{}, make([]handler, 0)}
}

// Convenience function to create logger with StdFormatter
func NewStd(w io.Writer, level Level, flag int, colored bool) *Logger {
	l := Logger{sync.Mutex{}, make([]handler, 0)}
	l.AddHandler(w, level, StdFormatter{"", flag, colored})
	return &l
}

/* LOGGER
 * ------
 */

// Adds a handler, specifying the maximum log Level you want to be written to this output.
// For instance, if you pass Warning for level, all logs of type
// Warning, Error, and Critical would be logged to this handler.
// This method is thread safe. You can use it in multiple goroutines.
// You can also use the same writer in multiple Loggers.
func (this *Logger) AddHandler(writer io.Writer, level Level, fm Formatter) {
	this.mtx.Lock()
	if _, ok := writerMutexMap[writer]; !ok {
		writerMutexMap[writer] = &sync.Mutex{}
	}
	this.handlers = append(this.handlers, handler{writer, level, fm})
	this.mtx.Unlock()
}

// Logs a message for the given level. Most callers will likely
// prefer to use one of the provided convenience functions (Debug, Info...).
func (this *Logger) Log(level Level, msg string) {
	var out []byte
	for _, h := range this.handlers {
		if h.level <= level {
			out = h.fmt.Format(level, msg)
			mtx, _ := writerMutexMap[h.writer]
			mtx.Lock()
			h.writer.Write(out)
			mtx.Unlock()
		}
	}
}

// Logs a formatted message message for the given level.
// Wrapper around Log method
func (this *Logger) Logf(level Level, format string, v ...interface{}) {
	this.Log(level, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Trace(v ...interface{}) {
	// TODO: split the string
	this.Log(Levels.Trace, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Tracef(format string, v ...interface{}) {
	// TODO: split the string
	this.Log(Levels.Trace, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Debug(v ...interface{}) {
	this.Log(Levels.Debug, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Debugf(format string, v ...interface{}) {
	this.Log(Levels.Debug, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Info(v ...interface{}) {
	this.Log(Levels.Info, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Infof(format string, v ...interface{}) {
	this.Log(Levels.Info, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Warning(v ...interface{}) {
	this.Log(Levels.Warning, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Warningf(format string, v ...interface{}) {
	this.Log(Levels.Warning, fmt.Sprintf(format+"\n", v...))
}

// Convenience function, short version of Warning
func (this *Logger) Warn(v ...interface{}) {
	this.Log(Levels.Warning, fmt.Sprintln(v...))
}

// Convenience function, short version of Warningf
func (this *Logger) Warnf(format string, v ...interface{}) {
	this.Log(Levels.Warning, fmt.Sprintf(format+"\n", v...))
}

// Convenience function
func (this *Logger) Error(v ...interface{}) {
	this.Log(Levels.Error, fmt.Sprintln(v...))
}

// Convenience function
func (this *Logger) Errorf(format string, v ...interface{}) {
	this.Log(Levels.Error, fmt.Sprintf(format+"\n", v...))
}

// Convenience function, will not terminate the program
func (this *Logger) Critical(v ...interface{}) {
	this.Log(Levels.Critical, fmt.Sprintln(v...))
}

// Convenience function, will not terminate the program
func (this *Logger) Criticalf(format string, v ...interface{}) {
	this.Log(Levels.Critical, fmt.Sprintf(format+"\n", v...))
}

// Convenience function, will terminate the program
func (this *Logger) Fatal(v ...interface{}) {
	this.Log(Levels.Critical, fmt.Sprintln(v...))
	os.Exit(1)
}

// Convenience function, will terminate the program
func (this *Logger) Fatalf(format string, v ...interface{}) {
	this.Log(Levels.Critical, fmt.Sprintf(format+"\n", v...))
	os.Exit(1)
}

// Convinience function to support io.Writer interface
func (this *Logger) Write(p []byte) (n int, err error) {
	n = len(p)
	this.Log(0, string(p))
	return
}
<|endoftext|>


<commit_before>// Package log provides a log interface
package log

// Logger is a generic logging interface
type Logger interface {
	Log(v ...interface{})
	Logf(format string, v ...interface{})
}

var (
	// The global default logger
	DefaultLogger Logger = &noOpLogger{}
)

// noOpLogger is used as a placeholder for the default logger
type noOpLogger struct{}

func (n *noOpLogger) Log(v ...interface{}) {}

func (n *noOpLogger) Logf(format string, v ...interface{}) {}

// Log logs using the default logger
func Log(v ...interface{}) {
	DefaultLogger.Log(v...)
}

// Logf logs formatted using the default logger
func Logf(format string, v ...interface{}) {
	DefaultLogger.Logf(format, v...)
}
<commit_msg>log: Godocs for Logger methods<commit_after>// Package log provides a log interface
package log

// Logger is a generic logging interface
type Logger interface {

	// Log inserts a log entry.  Arguments may be handled in the manner
	// of fmt.Print, but the underlying logger may also decide to handle
	// them differently.
	Log(v ...interface{})

	// Logf insets a log entry.  Arguments are handled in the manner of
	// fmt.Printf.
	Logf(format string, v ...interface{})
}

var (
	// The global default logger
	DefaultLogger Logger = &noOpLogger{}
)

// noOpLogger is used as a placeholder for the default logger
type noOpLogger struct{}

func (n *noOpLogger) Log(v ...interface{}) {}

func (n *noOpLogger) Logf(format string, v ...interface{}) {}

// Log logs using the default logger
func Log(v ...interface{}) {
	DefaultLogger.Log(v...)
}

// Logf logs formatted using the default logger
func Logf(format string, v ...interface{}) {
	DefaultLogger.Logf(format, v...)
}
<|endoftext|>


<commit_before>package cfutil

import (
	"context"
	"encoding/json"
	"fmt"
	"os"

	"github.com/Sirupsen/logrus"
)

type Logger interface {
	Debug(c context.Context, format string, args ...interface{})
	Info(c context.Context, format string, args ...interface{})
	Warning(c context.Context, format string, args ...interface{})
	Error(c context.Context, format string, args ...interface{})
	Critical(c context.Context, format string, args ...interface{})
	Raw(c context.Context, rawMessage string)
}

func NewLogger() Logger {
	newLogger := HSDPLogger{}
	appName, _ := GetApplicationName()
	newLogger.Init(appName, "", "", "")
	return newLogger
}

var log = NewLogger()

type HSDPLogger struct {
	logger   *logrus.Logger
	template logMessage
}

type Value struct {
	Message string `json:"message"`
}

type logMessage struct {
	App         string        `json:"app"`
	Value       Value         `json:"val"`
	Version     string        `json:"ver,omitempty"`
	Event       string        `json:"evt,omitempty"`
	Severity    string        `json:"sev,omitempty"`
	Transaction string        `json:"trns,omitempty"`
	User        string        `json:"usr,omitempty"`
	Server      string        `json:"srv,omitempty"`
	Service     string        `json:"service,omitempty"`
	Instance    string        `json:"inst,omitempty"`
	Category    string        `json:"cat,omitempty"`
	Component   string        `json:"cmp,omitempty"`
	Time        string        `json:"time,omitempty"`
	Fields      logrus.Fields `json:"fields,omitempty"`
}

func (f *HSDPLogger) Init(app, version, instance, component string) {
	f.logger = logrus.New()
	f.logger.Formatter = f
	f.logger.Out = os.Stdout

	f.template.App = app
	f.template.Version = version
	f.template.Instance = instance
	if f.template.Instance == "" {
		f.template.Instance = "not-specified"
	}
	f.template.Component = component
	f.template.Category = "Tracelog"
	f.template.Event = "1"
	f.template.Server = "not-set"
	f.template.Service = "not-set"
	f.template.User = "not-specified"
}

const KeyCorrelationID = "correlationid"

func correlationIDFromContext(c context.Context) string {
	return c.Value(KeyCorrelationID).(string)
}

func (f HSDPLogger) Raw(c context.Context, rawString string) {
	fmt.Print(rawString)
}

func (f HSDPLogger) Debug(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Debugf(format, args...)
}

func (f HSDPLogger) Info(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Infof(format, args...)
}

func (f HSDPLogger) Warning(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Warningf(format, args...)
}

func (f HSDPLogger) Error(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Errorf(format, args...)
}

func (f HSDPLogger) Critical(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Fatalf(format, args...)
}

func (f *HSDPLogger) Format(entry *logrus.Entry) ([]byte, error) {
	data := f.template
	data.Time = entry.Time.Format("2006-01-02T15:04:05.000Z07:00")
	data.Value.Message = entry.Message
	data.Severity = entry.Level.String()

	data.Fields = make(logrus.Fields, len(entry.Data))
	for k, v := range entry.Data {
		switch k {
		case "transaction", KeyCorrelationID:
			data.Transaction = v.(string)
			continue
		case "user":
			data.User = v.(string)
			continue
		}
		switch v := v.(type) {
		case error:
			data.Fields[k] = v.Error()
		default:
			data.Fields[k] = v
		}
	}
	serialized, err := json.Marshal(data)
	if err != nil {
		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
	}
	return append(serialized, '\n'), nil
}
<commit_msg>Check cast<commit_after>package cfutil

import (
	"context"
	"encoding/json"
	"fmt"
	"os"

	"github.com/Sirupsen/logrus"
)

type Logger interface {
	Debug(c context.Context, format string, args ...interface{})
	Info(c context.Context, format string, args ...interface{})
	Warning(c context.Context, format string, args ...interface{})
	Error(c context.Context, format string, args ...interface{})
	Critical(c context.Context, format string, args ...interface{})
	Raw(c context.Context, rawMessage string)
}

func NewLogger() Logger {
	newLogger := HSDPLogger{}
	appName, _ := GetApplicationName()
	newLogger.Init(appName, "", "", "")
	return newLogger
}

var log = NewLogger()

type HSDPLogger struct {
	logger   *logrus.Logger
	template logMessage
}

type Value struct {
	Message string `json:"message"`
}

type logMessage struct {
	App         string        `json:"app"`
	Value       Value         `json:"val"`
	Version     string        `json:"ver,omitempty"`
	Event       string        `json:"evt,omitempty"`
	Severity    string        `json:"sev,omitempty"`
	Transaction string        `json:"trns,omitempty"`
	User        string        `json:"usr,omitempty"`
	Server      string        `json:"srv,omitempty"`
	Service     string        `json:"service,omitempty"`
	Instance    string        `json:"inst,omitempty"`
	Category    string        `json:"cat,omitempty"`
	Component   string        `json:"cmp,omitempty"`
	Time        string        `json:"time,omitempty"`
	Fields      logrus.Fields `json:"fields,omitempty"`
}

func (f *HSDPLogger) Init(app, version, instance, component string) {
	f.logger = logrus.New()
	f.logger.Formatter = f
	f.logger.Out = os.Stdout

	f.template.App = app
	f.template.Version = version
	f.template.Instance = instance
	if f.template.Instance == "" {
		f.template.Instance = "not-specified"
	}
	f.template.Component = component
	f.template.Category = "Tracelog"
	f.template.Event = "1"
	f.template.Server = "not-set"
	f.template.Service = "not-set"
	f.template.User = "not-specified"
}

const KeyCorrelationID = "correlationid"

func correlationIDFromContext(c context.Context) string {
	if id, ok := c.Value(KeyCorrelationID).(string); ok {
		return id
	}
	return ""
}

func (f HSDPLogger) Raw(c context.Context, rawString string) {
	fmt.Print(rawString)
}

func (f HSDPLogger) Debug(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Debugf(format, args...)
}

func (f HSDPLogger) Info(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Infof(format, args...)
}

func (f HSDPLogger) Warning(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Warningf(format, args...)
}

func (f HSDPLogger) Error(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Errorf(format, args...)
}

func (f HSDPLogger) Critical(c context.Context, format string, args ...interface{}) {
	f.logger.WithField(KeyCorrelationID, correlationIDFromContext(c)).Fatalf(format, args...)
}

func (f *HSDPLogger) Format(entry *logrus.Entry) ([]byte, error) {
	data := f.template
	data.Time = entry.Time.Format("2006-01-02T15:04:05.000Z07:00")
	data.Value.Message = entry.Message
	data.Severity = entry.Level.String()

	data.Fields = make(logrus.Fields, len(entry.Data))
	for k, v := range entry.Data {
		switch k {
		case "transaction", KeyCorrelationID:
			data.Transaction = v.(string)
			continue
		case "user":
			data.User = v.(string)
			continue
		}
		switch v := v.(type) {
		case error:
			data.Fields[k] = v.Error()
		default:
			data.Fields[k] = v
		}
	}
	serialized, err := json.Marshal(data)
	if err != nil {
		return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
	}
	return append(serialized, '\n'), nil
}
<|endoftext|>


<commit_before>package log

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"
)

type StructuredLog struct {
	Path       string
	Duration   time.Duration
	RemoteAddr string
	Referer    string
	Status     int
}

type Logger struct {
	info           *log.Logger
	warning        *log.Logger
	error          *log.Logger
	slack          *log.Logger
	request        *log.Logger
	requestEncoder *json.Encoder
	callDepth      int
}

func New(prefix string, depth int) *Logger {

	requestLogger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
	l := &Logger{
		info:           log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		warning:        log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		error:          log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile),
		slack:          log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		request:        requestLogger,
		requestEncoder: json.NewEncoder(os.Stdout),
		callDepth:      depth,
	}

	l.SetPrefix(prefix)
	return l
}

var defaultLogger = New("", 3)

type BLogger interface {
	Errorf(format string, v ...interface{})
	Errorln(v ...interface{})
	Warningf(format string, v ...interface{})
	Warningln(v ...interface{})
	Infof(format string, v ...interface{})
	Infoln(v ...interface{})
	Requestln(v ...interface{})
	Panicln(v ...interface{})
	Panicf(format string, v ...interface{})
	Slackf(format string, v ...interface{})
	SlackLn(v ...interface{})
}

func (this *Logger) SetPrefix(prefix string) {

	if prefix != "" {
		prefix = prefix + " "
	}

	this.info.SetPrefix(prefix + "I: ")
	this.warning.SetPrefix(prefix + "W: ")
	this.error.SetPrefix(prefix + "E: ")
	this.slack.SetPrefix(prefix + "SLACK: ")
	this.request.SetPrefix(prefix + "R: ")
}

func (this *Logger) Errorf(format string, v ...interface{}) {
	this.error.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Errorln(v ...interface{}) {
	this.error.Output(this.callDepth, ln(v...))
}

func (this *Logger) Warningf(format string, v ...interface{}) {
	this.warning.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Warningln(v ...interface{}) {
	this.warning.Output(this.callDepth, ln(v...))
}

func (this *Logger) Infof(format string, v ...interface{}) {
	this.info.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Infoln(v ...interface{}) {
	this.info.Output(this.callDepth, ln(v...))
}

func (this *Logger) Requestln(v ...interface{}) {
	this.request.Println(v...)
}

func (this *Logger) RequestEncoder() *json.Encoder {
	return this.requestEncoder
}

func (this *Logger) Panicln(v ...interface{}) {
	string := ln(v...)
	this.error.Output(this.callDepth, string)
	panic(string)
}

func (this *Logger) Panicf(format string, v ...interface{}) {
	string := f(format, v...)
	this.error.Output(this.callDepth, string)
	panic(string)
}

func (this *Logger) Slackf(format string, v ...interface{}) {
	this.slack.Output(this.callDepth, f(format, v...))
}

func (this *Logger) SlackLn(v ...interface{}) {
	this.slack.Output(this.callDepth, ln(v...))
}

//Globals
func Errorf(format string, v ...interface{}) {
	defaultLogger.Errorf(format, v...)
}

func Errorln(v ...interface{}) {
	defaultLogger.Errorln(v...)
}

func Warningf(format string, v ...interface{}) {
	defaultLogger.Warningf(format, v...)
}

func Warningln(v ...interface{}) {
	defaultLogger.Warningln(v...)
}

func Infof(format string, v ...interface{}) {
	defaultLogger.Infof(format, v...)
}

func Infoln(v ...interface{}) {
	defaultLogger.Infoln(v...)
}

func Requestln(v ...interface{}) {
	defaultLogger.Requestln(v...)
}

func RequestObject(obj StructuredLog) {
	defaultLogger.requestEncoder.Encode(obj)
}

func Panicln(v ...interface{}) {
	defaultLogger.Panicln(v...)
}

func Panicf(format string, v ...interface{}) {
	defaultLogger.Panicf(format, v...)
}

func Slackf(format string, v ...interface{}) {
	defaultLogger.Slackf(format, v...)
}

func SlackLn(v ...interface{}) {
	defaultLogger.SlackLn(v...)
}

func SetPrefix(p string) {
	defaultLogger.SetPrefix(p)
}

func f(format string, v ...interface{}) string {
	return ln(fmt.Sprintf(format, v...))
}

func ln(a ...interface{}) string {
	return fmt.Sprintln(a...)
}
<commit_msg>expose logger<commit_after>package log

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
	"time"
)

type StructuredLog struct {
	Path       string
	Duration   time.Duration
	RemoteAddr string
	Referer    string
	Status     int
}

type Logger struct {
	info           *log.Logger
	warning        *log.Logger
	error          *log.Logger
	slack          *log.Logger
	request        *log.Logger
	requestEncoder *json.Encoder
	callDepth      int
}

func (this *Logger) ErrLogger() *log.Logger {
	return this.error
}

func New(prefix string, depth int) *Logger {

	requestLogger := log.New(os.Stdout, "", log.Ldate|log.Ltime)
	l := &Logger{
		info:           log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		warning:        log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		error:          log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile),
		slack:          log.New(os.Stdout, "", log.Ldate|log.Ltime|log.Lshortfile),
		request:        requestLogger,
		requestEncoder: json.NewEncoder(os.Stdout),
		callDepth:      depth,
	}

	l.SetPrefix(prefix)
	return l
}

var defaultLogger = New("", 3)

type BLogger interface {
	Errorf(format string, v ...interface{})
	Errorln(v ...interface{})
	Warningf(format string, v ...interface{})
	Warningln(v ...interface{})
	Infof(format string, v ...interface{})
	Infoln(v ...interface{})
	Requestln(v ...interface{})
	Panicln(v ...interface{})
	Panicf(format string, v ...interface{})
	Slackf(format string, v ...interface{})
	SlackLn(v ...interface{})
}

func (this *Logger) SetPrefix(prefix string) {

	if prefix != "" {
		prefix = prefix + " "
	}

	this.info.SetPrefix(prefix + "I: ")
	this.warning.SetPrefix(prefix + "W: ")
	this.error.SetPrefix(prefix + "E: ")
	this.slack.SetPrefix(prefix + "SLACK: ")
	this.request.SetPrefix(prefix + "R: ")
}

func (this *Logger) Errorf(format string, v ...interface{}) {
	this.error.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Errorln(v ...interface{}) {
	this.error.Output(this.callDepth, ln(v...))
}

func (this *Logger) Warningf(format string, v ...interface{}) {
	this.warning.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Warningln(v ...interface{}) {
	this.warning.Output(this.callDepth, ln(v...))
}

func (this *Logger) Infof(format string, v ...interface{}) {
	this.info.Output(this.callDepth, f(format, v...))
}

func (this *Logger) Infoln(v ...interface{}) {
	this.info.Output(this.callDepth, ln(v...))
}

func (this *Logger) Requestln(v ...interface{}) {
	this.request.Println(v...)
}

func (this *Logger) RequestEncoder() *json.Encoder {
	return this.requestEncoder
}

func (this *Logger) Panicln(v ...interface{}) {
	string := ln(v...)
	this.error.Output(this.callDepth, string)
	panic(string)
}

func (this *Logger) Panicf(format string, v ...interface{}) {
	string := f(format, v...)
	this.error.Output(this.callDepth, string)
	panic(string)
}

func (this *Logger) Slackf(format string, v ...interface{}) {
	this.slack.Output(this.callDepth, f(format, v...))
}

func (this *Logger) SlackLn(v ...interface{}) {
	this.slack.Output(this.callDepth, ln(v...))
}

//Globals
func DefaultLogger() *Logger {
	return defaultLogger
}

func Errorf(format string, v ...interface{}) {
	defaultLogger.Errorf(format, v...)
}

func Errorln(v ...interface{}) {
	defaultLogger.Errorln(v...)
}

func Warningf(format string, v ...interface{}) {
	defaultLogger.Warningf(format, v...)
}

func Warningln(v ...interface{}) {
	defaultLogger.Warningln(v...)
}

func Infof(format string, v ...interface{}) {
	defaultLogger.Infof(format, v...)
}

func Infoln(v ...interface{}) {
	defaultLogger.Infoln(v...)
}

func Requestln(v ...interface{}) {
	defaultLogger.Requestln(v...)
}

func RequestObject(obj StructuredLog) {
	defaultLogger.requestEncoder.Encode(obj)
}

func Panicln(v ...interface{}) {
	defaultLogger.Panicln(v...)
}

func Panicf(format string, v ...interface{}) {
	defaultLogger.Panicf(format, v...)
}

func Slackf(format string, v ...interface{}) {
	defaultLogger.Slackf(format, v...)
}

func SlackLn(v ...interface{}) {
	defaultLogger.SlackLn(v...)
}

func SetPrefix(p string) {
	defaultLogger.SetPrefix(p)
}

func f(format string, v ...interface{}) string {
	return ln(fmt.Sprintf(format, v...))
}

func ln(a ...interface{}) string {
	return fmt.Sprintln(a...)
}
<|endoftext|>


<commit_before>package lnd

import (
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/btcsuite/btcd/connmgr"
	"github.com/btcsuite/btclog"
	"github.com/jrick/logrotate/rotator"
	"github.com/lightninglabs/neutrino"
	sphinx "github.com/lightningnetwork/lightning-onion"
	"github.com/lightningnetwork/lnd/autopilot"
	"github.com/lightningnetwork/lnd/build"
	"github.com/lightningnetwork/lnd/chainntnfs"
	"github.com/lightningnetwork/lnd/chanbackup"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/lightningnetwork/lnd/channelnotifier"
	"github.com/lightningnetwork/lnd/contractcourt"
	"github.com/lightningnetwork/lnd/discovery"
	"github.com/lightningnetwork/lnd/htlcswitch"
	"github.com/lightningnetwork/lnd/invoices"
	"github.com/lightningnetwork/lnd/lnrpc/autopilotrpc"
	"github.com/lightningnetwork/lnd/lnrpc/chainrpc"
	"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
	"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
	"github.com/lightningnetwork/lnd/lnrpc/signrpc"
	"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
	"github.com/lightningnetwork/lnd/lnwallet"
	"github.com/lightningnetwork/lnd/monitoring"
	"github.com/lightningnetwork/lnd/netann"
	"github.com/lightningnetwork/lnd/routing"
	"github.com/lightningnetwork/lnd/signal"
	"github.com/lightningnetwork/lnd/sweep"
	"github.com/lightningnetwork/lnd/watchtower"
)

// Loggers per subsystem.  A single backend logger is created and all subsystem
// loggers created from it will write to the backend.  When adding new
// subsystems, add the subsystem logger variable here and to the
// subsystemLoggers map.
//
// Loggers can not be used before the log rotator has been initialized with a
// log file.  This must be performed early during application startup by
// calling initLogRotator.
var (
	logWriter = &build.LogWriter{}

	// backendLog is the logging backend used to create all subsystem
	// loggers.  The backend must not be used before the log rotator has
	// been initialized, or data races and/or nil pointer dereferences will
	// occur.
	backendLog = btclog.NewBackend(logWriter)

	// logRotator is one of the logging outputs.  It should be closed on
	// application shutdown.
	logRotator *rotator.Rotator

	ltndLog = build.NewSubLogger("LTND", backendLog.Logger)
	lnwlLog = build.NewSubLogger("LNWL", backendLog.Logger)
	peerLog = build.NewSubLogger("PEER", backendLog.Logger)
	discLog = build.NewSubLogger("DISC", backendLog.Logger)
	rpcsLog = build.NewSubLogger("RPCS", backendLog.Logger)
	srvrLog = build.NewSubLogger("SRVR", backendLog.Logger)
	ntfnLog = build.NewSubLogger("NTFN", backendLog.Logger)
	chdbLog = build.NewSubLogger("CHDB", backendLog.Logger)
	fndgLog = build.NewSubLogger("FNDG", backendLog.Logger)
	hswcLog = build.NewSubLogger("HSWC", backendLog.Logger)
	utxnLog = build.NewSubLogger("UTXN", backendLog.Logger)
	brarLog = build.NewSubLogger("BRAR", backendLog.Logger)
	cmgrLog = build.NewSubLogger("CMGR", backendLog.Logger)
	crtrLog = build.NewSubLogger("CRTR", backendLog.Logger)
	btcnLog = build.NewSubLogger("BTCN", backendLog.Logger)
	atplLog = build.NewSubLogger("ATPL", backendLog.Logger)
	cnctLog = build.NewSubLogger("CNCT", backendLog.Logger)
	sphxLog = build.NewSubLogger("SPHX", backendLog.Logger)
	swprLog = build.NewSubLogger("SWPR", backendLog.Logger)
	sgnrLog = build.NewSubLogger("SGNR", backendLog.Logger)
	wlktLog = build.NewSubLogger("WLKT", backendLog.Logger)
	arpcLog = build.NewSubLogger("ARPC", backendLog.Logger)
	invcLog = build.NewSubLogger("INVC", backendLog.Logger)
	nannLog = build.NewSubLogger("NANN", backendLog.Logger)
	wtwrLog = build.NewSubLogger("WTWR", backendLog.Logger)
	ntfrLog = build.NewSubLogger("NTFR", backendLog.Logger)
	irpcLog = build.NewSubLogger("IRPC", backendLog.Logger)
	chnfLog = build.NewSubLogger("CHNF", backendLog.Logger)
	chbuLog = build.NewSubLogger("CHBU", backendLog.Logger)
	promLog = build.NewSubLogger("PROM", backendLog.Logger)
)

// Initialize package-global logger variables.
func init() {
	lnwallet.UseLogger(lnwlLog)
	discovery.UseLogger(discLog)
	chainntnfs.UseLogger(ntfnLog)
	channeldb.UseLogger(chdbLog)
	htlcswitch.UseLogger(hswcLog)
	connmgr.UseLogger(cmgrLog)
	routing.UseLogger(crtrLog)
	neutrino.UseLogger(btcnLog)
	autopilot.UseLogger(atplLog)
	contractcourt.UseLogger(cnctLog)
	sphinx.UseLogger(sphxLog)
	signal.UseLogger(ltndLog)
	sweep.UseLogger(swprLog)
	signrpc.UseLogger(sgnrLog)
	walletrpc.UseLogger(wlktLog)
	autopilotrpc.UseLogger(arpcLog)
	invoices.UseLogger(invcLog)
	netann.UseLogger(nannLog)
	watchtower.UseLogger(wtwrLog)
	chainrpc.UseLogger(ntfrLog)
	invoicesrpc.UseLogger(irpcLog)
	channelnotifier.UseLogger(chnfLog)
	chanbackup.UseLogger(chbuLog)
	monitoring.UseLogger(promLog)

	addSubLogger(routerrpc.Subsystem, routerrpc.UseLogger)
}

// addSubLogger is a helper method to conveniently register the logger of a sub
// system.
func addSubLogger(subsystem string, useLogger func(btclog.Logger)) {
	logger := build.NewSubLogger(subsystem, backendLog.Logger)
	useLogger(logger)
	subsystemLoggers[subsystem] = logger
}

// subsystemLoggers maps each subsystem identifier to its associated logger.
var subsystemLoggers = map[string]btclog.Logger{
	"LTND": ltndLog,
	"LNWL": lnwlLog,
	"PEER": peerLog,
	"DISC": discLog,
	"RPCS": rpcsLog,
	"SRVR": srvrLog,
	"NTFN": ntfnLog,
	"CHDB": chdbLog,
	"FNDG": fndgLog,
	"HSWC": hswcLog,
	"UTXN": utxnLog,
	"BRAR": brarLog,
	"CMGR": cmgrLog,
	"CRTR": crtrLog,
	"BTCN": btcnLog,
	"ATPL": atplLog,
	"CNCT": cnctLog,
	"SPHX": sphxLog,
	"SWPR": swprLog,
	"SGNR": sgnrLog,
	"WLKT": wlktLog,
	"ARPC": arpcLog,
	"INVC": invcLog,
	"NANN": nannLog,
	"WTWR": wtwrLog,
	"NTFR": ntfnLog,
	"IRPC": irpcLog,
	"CHNF": chnfLog,
	"CHBU": chbuLog,
	"PROM": promLog,
}

// initLogRotator initializes the logging rotator to write logs to logFile and
// create roll files in the same directory.  It must be called before the
// package-global log rotator variables are used.
func initLogRotator(logFile string, MaxLogFileSize int, MaxLogFiles int) {
	logDir, _ := filepath.Split(logFile)
	err := os.MkdirAll(logDir, 0700)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create log directory: %v\n", err)
		os.Exit(1)
	}
	r, err := rotator.New(logFile, int64(MaxLogFileSize*1024), false, MaxLogFiles)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create file rotator: %v\n", err)
		os.Exit(1)
	}

	pr, pw := io.Pipe()
	go r.Run(pr)

	logWriter.RotatorPipe = pw
	logRotator = r
}

// setLogLevel sets the logging level for provided subsystem.  Invalid
// subsystems are ignored.  Uninitialized subsystems are dynamically created as
// needed.
func setLogLevel(subsystemID string, logLevel string) {
	// Ignore invalid subsystems.
	logger, ok := subsystemLoggers[subsystemID]
	if !ok {
		return
	}

	// Defaults to info if the log level is invalid.
	level, _ := btclog.LevelFromString(logLevel)
	logger.SetLevel(level)
}

// setLogLevels sets the log level for all subsystem loggers to the passed
// level. It also dynamically creates the subsystem loggers as needed, so it
// can be used to initialize the logging system.
func setLogLevels(logLevel string) {
	// Configure all sub-systems with the new logging level.  Dynamically
	// create loggers as needed.
	for subsystemID := range subsystemLoggers {
		setLogLevel(subsystemID, logLevel)
	}
}

// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string

// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
	return c()
}

// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
	return logClosure(c)
}
<commit_msg>log: add watchtower client logs<commit_after>package lnd

import (
	"fmt"
	"io"
	"os"
	"path/filepath"

	"github.com/btcsuite/btcd/connmgr"
	"github.com/btcsuite/btclog"
	"github.com/jrick/logrotate/rotator"
	"github.com/lightninglabs/neutrino"
	sphinx "github.com/lightningnetwork/lightning-onion"
	"github.com/lightningnetwork/lnd/autopilot"
	"github.com/lightningnetwork/lnd/build"
	"github.com/lightningnetwork/lnd/chainntnfs"
	"github.com/lightningnetwork/lnd/chanbackup"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/lightningnetwork/lnd/channelnotifier"
	"github.com/lightningnetwork/lnd/contractcourt"
	"github.com/lightningnetwork/lnd/discovery"
	"github.com/lightningnetwork/lnd/htlcswitch"
	"github.com/lightningnetwork/lnd/invoices"
	"github.com/lightningnetwork/lnd/lnrpc/autopilotrpc"
	"github.com/lightningnetwork/lnd/lnrpc/chainrpc"
	"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
	"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
	"github.com/lightningnetwork/lnd/lnrpc/signrpc"
	"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
	"github.com/lightningnetwork/lnd/lnwallet"
	"github.com/lightningnetwork/lnd/monitoring"
	"github.com/lightningnetwork/lnd/netann"
	"github.com/lightningnetwork/lnd/routing"
	"github.com/lightningnetwork/lnd/signal"
	"github.com/lightningnetwork/lnd/sweep"
	"github.com/lightningnetwork/lnd/watchtower"
	"github.com/lightningnetwork/lnd/watchtower/wtclient"
)

// Loggers per subsystem.  A single backend logger is created and all subsystem
// loggers created from it will write to the backend.  When adding new
// subsystems, add the subsystem logger variable here and to the
// subsystemLoggers map.
//
// Loggers can not be used before the log rotator has been initialized with a
// log file.  This must be performed early during application startup by
// calling initLogRotator.
var (
	logWriter = &build.LogWriter{}

	// backendLog is the logging backend used to create all subsystem
	// loggers.  The backend must not be used before the log rotator has
	// been initialized, or data races and/or nil pointer dereferences will
	// occur.
	backendLog = btclog.NewBackend(logWriter)

	// logRotator is one of the logging outputs.  It should be closed on
	// application shutdown.
	logRotator *rotator.Rotator

	ltndLog = build.NewSubLogger("LTND", backendLog.Logger)
	lnwlLog = build.NewSubLogger("LNWL", backendLog.Logger)
	peerLog = build.NewSubLogger("PEER", backendLog.Logger)
	discLog = build.NewSubLogger("DISC", backendLog.Logger)
	rpcsLog = build.NewSubLogger("RPCS", backendLog.Logger)
	srvrLog = build.NewSubLogger("SRVR", backendLog.Logger)
	ntfnLog = build.NewSubLogger("NTFN", backendLog.Logger)
	chdbLog = build.NewSubLogger("CHDB", backendLog.Logger)
	fndgLog = build.NewSubLogger("FNDG", backendLog.Logger)
	hswcLog = build.NewSubLogger("HSWC", backendLog.Logger)
	utxnLog = build.NewSubLogger("UTXN", backendLog.Logger)
	brarLog = build.NewSubLogger("BRAR", backendLog.Logger)
	cmgrLog = build.NewSubLogger("CMGR", backendLog.Logger)
	crtrLog = build.NewSubLogger("CRTR", backendLog.Logger)
	btcnLog = build.NewSubLogger("BTCN", backendLog.Logger)
	atplLog = build.NewSubLogger("ATPL", backendLog.Logger)
	cnctLog = build.NewSubLogger("CNCT", backendLog.Logger)
	sphxLog = build.NewSubLogger("SPHX", backendLog.Logger)
	swprLog = build.NewSubLogger("SWPR", backendLog.Logger)
	sgnrLog = build.NewSubLogger("SGNR", backendLog.Logger)
	wlktLog = build.NewSubLogger("WLKT", backendLog.Logger)
	arpcLog = build.NewSubLogger("ARPC", backendLog.Logger)
	invcLog = build.NewSubLogger("INVC", backendLog.Logger)
	nannLog = build.NewSubLogger("NANN", backendLog.Logger)
	wtwrLog = build.NewSubLogger("WTWR", backendLog.Logger)
	ntfrLog = build.NewSubLogger("NTFR", backendLog.Logger)
	irpcLog = build.NewSubLogger("IRPC", backendLog.Logger)
	chnfLog = build.NewSubLogger("CHNF", backendLog.Logger)
	chbuLog = build.NewSubLogger("CHBU", backendLog.Logger)
	promLog = build.NewSubLogger("PROM", backendLog.Logger)
	wtclLog = build.NewSubLogger("WTCL", backendLog.Logger)
)

// Initialize package-global logger variables.
func init() {
	lnwallet.UseLogger(lnwlLog)
	discovery.UseLogger(discLog)
	chainntnfs.UseLogger(ntfnLog)
	channeldb.UseLogger(chdbLog)
	htlcswitch.UseLogger(hswcLog)
	connmgr.UseLogger(cmgrLog)
	routing.UseLogger(crtrLog)
	neutrino.UseLogger(btcnLog)
	autopilot.UseLogger(atplLog)
	contractcourt.UseLogger(cnctLog)
	sphinx.UseLogger(sphxLog)
	signal.UseLogger(ltndLog)
	sweep.UseLogger(swprLog)
	signrpc.UseLogger(sgnrLog)
	walletrpc.UseLogger(wlktLog)
	autopilotrpc.UseLogger(arpcLog)
	invoices.UseLogger(invcLog)
	netann.UseLogger(nannLog)
	watchtower.UseLogger(wtwrLog)
	chainrpc.UseLogger(ntfrLog)
	invoicesrpc.UseLogger(irpcLog)
	channelnotifier.UseLogger(chnfLog)
	chanbackup.UseLogger(chbuLog)
	monitoring.UseLogger(promLog)
	wtclient.UseLogger(wtclLog)

	addSubLogger(routerrpc.Subsystem, routerrpc.UseLogger)
}

// addSubLogger is a helper method to conveniently register the logger of a sub
// system.
func addSubLogger(subsystem string, useLogger func(btclog.Logger)) {
	logger := build.NewSubLogger(subsystem, backendLog.Logger)
	useLogger(logger)
	subsystemLoggers[subsystem] = logger
}

// subsystemLoggers maps each subsystem identifier to its associated logger.
var subsystemLoggers = map[string]btclog.Logger{
	"LTND": ltndLog,
	"LNWL": lnwlLog,
	"PEER": peerLog,
	"DISC": discLog,
	"RPCS": rpcsLog,
	"SRVR": srvrLog,
	"NTFN": ntfnLog,
	"CHDB": chdbLog,
	"FNDG": fndgLog,
	"HSWC": hswcLog,
	"UTXN": utxnLog,
	"BRAR": brarLog,
	"CMGR": cmgrLog,
	"CRTR": crtrLog,
	"BTCN": btcnLog,
	"ATPL": atplLog,
	"CNCT": cnctLog,
	"SPHX": sphxLog,
	"SWPR": swprLog,
	"SGNR": sgnrLog,
	"WLKT": wlktLog,
	"ARPC": arpcLog,
	"INVC": invcLog,
	"NANN": nannLog,
	"WTWR": wtwrLog,
	"NTFR": ntfnLog,
	"IRPC": irpcLog,
	"CHNF": chnfLog,
	"CHBU": chbuLog,
	"PROM": promLog,
	"WTCL": wtclLog,
}

// initLogRotator initializes the logging rotator to write logs to logFile and
// create roll files in the same directory.  It must be called before the
// package-global log rotator variables are used.
func initLogRotator(logFile string, MaxLogFileSize int, MaxLogFiles int) {
	logDir, _ := filepath.Split(logFile)
	err := os.MkdirAll(logDir, 0700)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create log directory: %v\n", err)
		os.Exit(1)
	}
	r, err := rotator.New(logFile, int64(MaxLogFileSize*1024), false, MaxLogFiles)
	if err != nil {
		fmt.Fprintf(os.Stderr, "failed to create file rotator: %v\n", err)
		os.Exit(1)
	}

	pr, pw := io.Pipe()
	go r.Run(pr)

	logWriter.RotatorPipe = pw
	logRotator = r
}

// setLogLevel sets the logging level for provided subsystem.  Invalid
// subsystems are ignored.  Uninitialized subsystems are dynamically created as
// needed.
func setLogLevel(subsystemID string, logLevel string) {
	// Ignore invalid subsystems.
	logger, ok := subsystemLoggers[subsystemID]
	if !ok {
		return
	}

	// Defaults to info if the log level is invalid.
	level, _ := btclog.LevelFromString(logLevel)
	logger.SetLevel(level)
}

// setLogLevels sets the log level for all subsystem loggers to the passed
// level. It also dynamically creates the subsystem loggers as needed, so it
// can be used to initialize the logging system.
func setLogLevels(logLevel string) {
	// Configure all sub-systems with the new logging level.  Dynamically
	// create loggers as needed.
	for subsystemID := range subsystemLoggers {
		setLogLevel(subsystemID, logLevel)
	}
}

// logClosure is used to provide a closure over expensive logging operations so
// don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string

// String invokes the underlying function and returns the result.
func (c logClosure) String() string {
	return c()
}

// newLogClosure returns a new closure over a function that returns a string
// which itself provides a Stringer interface so that it can be used with the
// logging system.
func newLogClosure(c func() string) logClosure {
	return logClosure(c)
}
<|endoftext|>


<commit_before>package lru

import (
	"sync"

	"github.com/hashicorp/golang-lru/simplelru"
)

// Cache is a thread-safe fixed size LRU cache.
type Cache struct {
	lru  simplelru.LRUCache
	lock sync.RWMutex
}

// New creates an LRU of the given size.
func New(size int) (*Cache, error) {
	return NewWithEvict(size, nil)
}

// NewWithEvict constructs a fixed size cache with the given eviction
// callback.
func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) {
	lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted))
	if err != nil {
		return nil, err
	}
	c := &Cache{
		lru: lru,
	}
	return c, nil
}

// Purge is used to completely clear the cache.
func (c *Cache) Purge() {
	c.lock.Lock()
	c.lru.Purge()
	c.lock.Unlock()
}

// Add adds a value to the cache.  Returns true if an eviction occurred.
func (c *Cache) Add(key, value interface{}) (evicted bool) {
	c.lock.Lock()
	evicted = c.lru.Add(key, value)
	c.lock.Unlock()
	return evicted
}

// Get looks up a key's value from the cache.
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
	c.lock.Lock()
	value, ok = c.lru.Get(key)
	c.lock.Unlock()
	return value, ok
}

// Contains checks if a key is in the cache, without updating the
// recent-ness or deleting it for being stale.
func (c *Cache) Contains(key interface{}) bool {
	c.lock.RLock()
	containKey := c.lru.Contains(key)
	c.lock.RUnlock()
	return containKey
}

// Peek returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
	c.lock.RLock()
	value, ok = c.lru.Peek(key)
	c.lock.RUnlock()
	return value, ok
}

// ContainsOrAdd checks if a key is in the cache  without updating the
// recent-ness or deleting it for being stale,  and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.lru.Contains(key) {
		return true, false
	}
	evicted = c.lru.Add(key, value)
	return false, evicted
}

// Remove removes the provided key from the cache.
func (c *Cache) Remove(key interface{}) (present bool) {
	c.lock.Lock()
	present = c.lru.Remove(key)
	c.lock.Unlock()
	return
}

// RemoveOldest removes the oldest item from the cache.
func (c *Cache) RemoveOldest() (key interface{}, value interface{}, ok bool) {
	c.lock.Lock()
	key, value, ok = c.lru.RemoveOldest()
	c.lock.Unlock()
	return
}

// Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *Cache) Keys() []interface{} {
	c.lock.RLock()
	keys := c.lru.Keys()
	c.lock.RUnlock()
	return keys
}

// Len returns the number of items in the cache.
func (c *Cache) Len() int {
	c.lock.RLock()
	length := c.lru.Len()
	c.lock.RUnlock()
	return length
}
<commit_msg>lru: add the missing GetOldest()<commit_after>package lru

import (
	"sync"

	"github.com/hashicorp/golang-lru/simplelru"
)

// Cache is a thread-safe fixed size LRU cache.
type Cache struct {
	lru  simplelru.LRUCache
	lock sync.RWMutex
}

// New creates an LRU of the given size.
func New(size int) (*Cache, error) {
	return NewWithEvict(size, nil)
}

// NewWithEvict constructs a fixed size cache with the given eviction
// callback.
func NewWithEvict(size int, onEvicted func(key interface{}, value interface{})) (*Cache, error) {
	lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted))
	if err != nil {
		return nil, err
	}
	c := &Cache{
		lru: lru,
	}
	return c, nil
}

// Purge is used to completely clear the cache.
func (c *Cache) Purge() {
	c.lock.Lock()
	c.lru.Purge()
	c.lock.Unlock()
}

// Add adds a value to the cache.  Returns true if an eviction occurred.
func (c *Cache) Add(key, value interface{}) (evicted bool) {
	c.lock.Lock()
	evicted = c.lru.Add(key, value)
	c.lock.Unlock()
	return evicted
}

// Get looks up a key's value from the cache.
func (c *Cache) Get(key interface{}) (value interface{}, ok bool) {
	c.lock.Lock()
	value, ok = c.lru.Get(key)
	c.lock.Unlock()
	return value, ok
}

// Contains checks if a key is in the cache, without updating the
// recent-ness or deleting it for being stale.
func (c *Cache) Contains(key interface{}) bool {
	c.lock.RLock()
	containKey := c.lru.Contains(key)
	c.lock.RUnlock()
	return containKey
}

// Peek returns the key value (or undefined if not found) without updating
// the "recently used"-ness of the key.
func (c *Cache) Peek(key interface{}) (value interface{}, ok bool) {
	c.lock.RLock()
	value, ok = c.lru.Peek(key)
	c.lock.RUnlock()
	return value, ok
}

// ContainsOrAdd checks if a key is in the cache  without updating the
// recent-ness or deleting it for being stale,  and if not, adds the value.
// Returns whether found and whether an eviction occurred.
func (c *Cache) ContainsOrAdd(key, value interface{}) (ok, evicted bool) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.lru.Contains(key) {
		return true, false
	}
	evicted = c.lru.Add(key, value)
	return false, evicted
}

// Remove removes the provided key from the cache.
func (c *Cache) Remove(key interface{}) (present bool) {
	c.lock.Lock()
	present = c.lru.Remove(key)
	c.lock.Unlock()
	return
}

// RemoveOldest removes the oldest item from the cache.
func (c *Cache) RemoveOldest() (key interface{}, value interface{}, ok bool) {
	c.lock.Lock()
	key, value, ok = c.lru.RemoveOldest()
	c.lock.Unlock()
	return
}

// GetOldest returns the oldest entry
func (c *Cache) GetOldest() (key interface{}, value interface{}, ok bool) {
	c.lock.Lock()
	key, value, ok = c.lru.GetOldest()
	c.lock.Unlock()
	return
}

// Keys returns a slice of the keys in the cache, from oldest to newest.
func (c *Cache) Keys() []interface{} {
	c.lock.RLock()
	keys := c.lru.Keys()
	c.lock.RUnlock()
	return keys
}

// Len returns the number of items in the cache.
func (c *Cache) Len() int {
	c.lock.RLock()
	length := c.lru.Len()
	c.lock.RUnlock()
	return length
}
<|endoftext|>