├── vendor ├── github.com │ ├── go-chi │ │ └── chi │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── LICENSE │ │ │ ├── CONTRIBUTING.md │ │ │ ├── chain.go │ │ │ ├── chi.go │ │ │ ├── context.go │ │ │ ├── CHANGELOG.md │ │ │ ├── mux.go │ │ │ ├── README.md │ │ │ └── tree.go │ ├── kr │ │ └── logfmt │ │ │ ├── .gitignore │ │ │ ├── Readme │ │ │ ├── scanner.go │ │ │ ├── unquote.go │ │ │ └── decode.go │ ├── go-stack │ │ └── stack │ │ │ ├── go.mod │ │ │ ├── .travis.yml │ │ │ ├── LICENSE.md │ │ │ ├── README.md │ │ │ └── stack.go │ ├── go-logfmt │ │ └── logfmt │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── doc.go │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── fuzz.go │ │ │ ├── decode.go │ │ │ ├── jsonstring.go │ │ │ └── encode.go │ └── go-kit │ │ └── kit │ │ ├── log │ │ ├── nop_logger.go │ │ ├── logfmt_logger.go │ │ ├── json_logger.go │ │ ├── value.go │ │ ├── stdlib.go │ │ ├── sync.go │ │ ├── README.md │ │ ├── log.go │ │ └── doc.go │ │ └── LICENSE └── modules.txt ├── .gitignore ├── go.mod ├── Dockerfile ├── .travis.yml ├── README.md ├── service.go ├── logging.go ├── go.sum ├── LICENSE ├── cmd └── pathfinder │ └── main.go ├── server └── server.go ├── path └── path.go └── api └── api.yaml /vendor/github.com/go-chi/chi/.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.sw? 3 | .vscode 4 | -------------------------------------------------------------------------------- /vendor/github.com/kr/logfmt/.gitignore: -------------------------------------------------------------------------------- 1 | *.test 2 | *.swp 3 | *.prof 4 | -------------------------------------------------------------------------------- /vendor/github.com/go-stack/stack/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-stack/stack 2 | -------------------------------------------------------------------------------- /vendor/github.com/go-logfmt/logfmt/.gitignore: -------------------------------------------------------------------------------- 1 | _testdata/ 2 | _testdata2/ 3 | logfmt-fuzz.zip 4 | logfmt.test.exe 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | /pathfinder 3 | *.exe 4 | *.exe~ 5 | *.dll 6 | *.so 7 | *.dylib 8 | 9 | # Test binary, build with `go test -c` 10 | *.test 11 | 12 | # Output of the go coverage tool, specifically when used with LiteIDE 13 | *.out 14 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/nop_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | type nopLogger struct{} 4 | 5 | // NewNopLogger returns a logger that doesn't do anything. 6 | func NewNopLogger() Logger { return nopLogger{} } 7 | 8 | func (nopLogger) Log(...interface{}) error { return nil } 9 | -------------------------------------------------------------------------------- /vendor/github.com/go-stack/stack/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - 1.7.x 5 | - 1.8.x 6 | - 1.9.x 7 | - 1.10.x 8 | - 1.11.x 9 | - tip 10 | 11 | before_install: 12 | - go get github.com/mattn/goveralls 13 | 14 | script: 15 | - goveralls -service=travis-ci 16 | -------------------------------------------------------------------------------- /vendor/github.com/go-logfmt/logfmt/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | sudo: false 3 | go: 4 | - 1.3 5 | - 1.4 6 | - 1.5 7 | - 1.6 8 | - tip 9 | 10 | before_install: 11 | - go get github.com/mattn/goveralls 12 | - go get golang.org/x/tools/cmd/cover 13 | 14 | script: 15 | - goveralls -service=travis-ci 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/marcusolsson/pathfinder 2 | 3 | require ( 4 | github.com/go-chi/chi v3.3.3+incompatible 5 | github.com/go-kit/kit v0.7.0 6 | github.com/go-logfmt/logfmt v0.3.0 // indirect 7 | github.com/go-stack/stack v1.8.0 // indirect 8 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect 9 | ) 10 | -------------------------------------------------------------------------------- /vendor/modules.txt: -------------------------------------------------------------------------------- 1 | # github.com/go-chi/chi v3.3.3+incompatible 2 | github.com/go-chi/chi 3 | # github.com/go-kit/kit v0.7.0 4 | github.com/go-kit/kit/log 5 | # github.com/go-logfmt/logfmt v0.3.0 6 | github.com/go-logfmt/logfmt 7 | # github.com/go-stack/stack v1.8.0 8 | github.com/go-stack/stack 9 | # github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 10 | github.com/kr/logfmt 11 | -------------------------------------------------------------------------------- /vendor/github.com/go-logfmt/logfmt/doc.go: -------------------------------------------------------------------------------- 1 | // Package logfmt implements utilities to marshal and unmarshal data in the 2 | // logfmt format. The logfmt format records key/value pairs in a way that 3 | // balances readability for humans and simplicity of computer parsing. It is 4 | // most commonly used as a more human friendly alternative to JSON for 5 | // structured logging. 6 | package logfmt 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.10.3-alpine AS build-env 2 | ADD . /go/src/github.com/marcusolsson/pathfinder 3 | WORKDIR /go/src/github.com/marcusolsson/pathfinder 4 | RUN GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -a -installsuffix cgo -o goapp ./cmd/pathfinder/main.go 5 | 6 | FROM alpine:3.7 7 | WORKDIR /app 8 | COPY --from=build-env /go/src/github.com/marcusolsson/pathfinder/goapp /app/ 9 | ADD docs /docs 10 | EXPOSE 8080 11 | CMD ["./goapp"] 12 | 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | language: go 4 | go: 5 | - 1.10.x 6 | - 1.11.x 7 | 8 | services: 9 | - docker 10 | 11 | env: 12 | - GO111MODULE=on 13 | 14 | install: 15 | - go get -u github.com/golang/lint/golint 16 | 17 | matrix: 18 | include: 19 | - go: "1.10.x" 20 | script: go test -v ./... 21 | - go: "1.11.x" 22 | script: go test -v -mod=vendor ./... 23 | 24 | after_success: 25 | - docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD 26 | - docker build -t $DOCKER_IMAGE_NAME . 27 | - docker push $DOCKER_IMAGE_NAME 28 | -------------------------------------------------------------------------------- /vendor/github.com/go-chi/chi/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.7.x 5 | - 1.8.x 6 | - 1.9.x 7 | - 1.10.x 8 | - 1.11.x 9 | 10 | install: 11 | - go get -u golang.org/x/tools/cmd/goimports 12 | - go get -u github.com/golang/lint/golint 13 | 14 | script: 15 | - go get -d -t ./... 16 | - go vet ./... 17 | - golint ./... 18 | - go test ./... 19 | - > 20 | go_version=$(go version); 21 | if [ ${go_version:13:4} = "1.11" ]; then 22 | goimports -d -e ./ | grep '.*' && { echo; echo "Aborting due to non-empty goimports output."; exit 1; } || :; 23 | fi 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | pathfinder 2 | ========== 3 | 4 | [![Build Status](https://travis-ci.org/marcusolsson/pathfinder.svg?branch=master)](https://travis-ci.org/marcusolsson/pathfinder) 5 | [![License MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](LICENSE) 6 | 7 | The routing context from the original DDD Sample Application, written in Go. 8 | 9 | ## Running the application 10 | 11 | Start the application on port 8080 (or whatever the `PORT` variable is set to). 12 | 13 | ``` 14 | go run cmd/pathfinder/main.go 15 | ``` 16 | 17 | ### Docker 18 | 19 | You can also run the application using Docker: 20 | 21 | ``` 22 | docker run --name some-pathfinder -p 8080:8080 marcusolsson/pathfinder 23 | ``` 24 | 25 | ## Try it! 26 | 27 | ``` 28 | curl 'localhost:8080/paths?from=SESTO&to=FIHEL' 29 | ``` 30 | -------------------------------------------------------------------------------- /service.go: -------------------------------------------------------------------------------- 1 | package pathfinder 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/marcusolsson/pathfinder/path" 7 | ) 8 | 9 | // ErrInvalidArgument is used if the user provides invalid arguments. 10 | var ErrInvalidArgument = errors.New("invalid argument") 11 | 12 | // PathService provides the shortest path "algorithm". 13 | type PathService interface { 14 | ShortestPath(origin, destination string) ([]path.TransitPath, error) 15 | } 16 | 17 | type pathService struct{} 18 | 19 | // NewPathService creates a new path service. 20 | func NewPathService() PathService { 21 | return &pathService{} 22 | } 23 | 24 | func (*pathService) ShortestPath(origin, destination string) ([]path.TransitPath, error) { 25 | if origin == "" || destination == "" { 26 | return nil, ErrInvalidArgument 27 | } 28 | return path.FindShortestPath(origin, destination), nil 29 | } 30 | -------------------------------------------------------------------------------- /logging.go: -------------------------------------------------------------------------------- 1 | package pathfinder 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/go-kit/kit/log" 7 | 8 | "github.com/marcusolsson/pathfinder/path" 9 | ) 10 | 11 | type loggingMiddleware struct { 12 | logger log.Logger 13 | next PathService 14 | } 15 | 16 | // NewLoggingMiddleware creates a new logging middleware. 17 | func NewLoggingMiddleware(logger log.Logger, next PathService) PathService { 18 | return &loggingMiddleware{ 19 | logger: logger, 20 | next: next, 21 | } 22 | } 23 | 24 | func (s *loggingMiddleware) ShortestPath(origin, destination string) (paths []path.TransitPath, err error) { 25 | defer func(begin time.Time) { 26 | _ = s.logger.Log( 27 | "method", "shortest_path", 28 | "origin", origin, 29 | "destination", destination, 30 | "took", time.Since(begin), 31 | "err", err, 32 | ) 33 | }(time.Now()) 34 | return s.next.ShortestPath(origin, destination) 35 | } 36 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-chi/chi v3.3.3+incompatible h1:KHkmBEMNkwKuK4FdQL7N2wOeB9jnIx7jR5wsuSBEFI8= 2 | github.com/go-chi/chi v3.3.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= 3 | github.com/go-kit/kit v0.7.0 h1:ApufNmWF1H6/wUbAG81hZOHmqwd0zRf8mNfLjYj/064= 4 | github.com/go-kit/kit v0.7.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 5 | github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= 6 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 7 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 8 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 9 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= 10 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Marcus Olsson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Peter Bourgon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /vendor/github.com/go-stack/stack/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Chris Hines 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/go-logfmt/logfmt/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 go-logfmt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /vendor/github.com/go-chi/chi/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-present Peter Kieltyka (https://github.com/pkieltyka), Google Inc. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/github.com/go-chi/chi/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Prerequisites 4 | 5 | 1. [Install Go][go-install]. 6 | 2. Download the sources and switch the working directory: 7 | 8 | ```bash 9 | go get -u -d github.com/go-chi/chi 10 | cd $GOPATH/src/github.com/go-chi/chi 11 | ``` 12 | 13 | ## Submitting a Pull Request 14 | 15 | A typical workflow is: 16 | 17 | 1. [Fork the repository.][fork] [This tip maybe also helpful.][go-fork-tip] 18 | 2. [Create a topic branch.][branch] 19 | 3. Add tests for your change. 20 | 4. Run `go test`. If your tests pass, return to the step 3. 21 | 5. Implement the change and ensure the steps from the previous step pass. 22 | 6. Run `goimports -w .`, to ensure the new code conforms to Go formatting guideline. 23 | 7. [Add, commit and push your changes.][git-help] 24 | 8. [Submit a pull request.][pull-req] 25 | 26 | [go-install]: https://golang.org/doc/install 27 | [go-fork-tip]: http://blog.campoy.cat/2014/03/github-and-go-forking-pull-requests-and.html 28 | [fork]: https://help.github.com/articles/fork-a-repo 29 | [branch]: http://learn.github.com/p/branching.html 30 | [git-help]: https://guides.github.com 31 | [pull-req]: https://help.github.com/articles/using-pull-requests 32 | -------------------------------------------------------------------------------- /vendor/github.com/kr/logfmt/Readme: -------------------------------------------------------------------------------- 1 | Go package for parsing (and, eventually, generating) 2 | log lines in the logfmt style. 3 | 4 | See http://godoc.org/github.com/kr/logfmt for format, and other documentation and examples. 5 | 6 | Copyright (C) 2013 Keith Rarick, Blake Mizerany 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 13 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/logfmt_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "sync" 7 | 8 | "github.com/go-logfmt/logfmt" 9 | ) 10 | 11 | type logfmtEncoder struct { 12 | *logfmt.Encoder 13 | buf bytes.Buffer 14 | } 15 | 16 | func (l *logfmtEncoder) Reset() { 17 | l.Encoder.Reset() 18 | l.buf.Reset() 19 | } 20 | 21 | var logfmtEncoderPool = sync.Pool{ 22 | New: func() interface{} { 23 | var enc logfmtEncoder 24 | enc.Encoder = logfmt.NewEncoder(&enc.buf) 25 | return &enc 26 | }, 27 | } 28 | 29 | type logfmtLogger struct { 30 | w io.Writer 31 | } 32 | 33 | // NewLogfmtLogger returns a logger that encodes keyvals to the Writer in 34 | // logfmt format. Each log event produces no more than one call to w.Write. 35 | // The passed Writer must be safe for concurrent use by multiple goroutines if 36 | // the returned Logger will be used concurrently. 37 | func NewLogfmtLogger(w io.Writer) Logger { 38 | return &logfmtLogger{w} 39 | } 40 | 41 | func (l logfmtLogger) Log(keyvals ...interface{}) error { 42 | enc := logfmtEncoderPool.Get().(*logfmtEncoder) 43 | enc.Reset() 44 | defer logfmtEncoderPool.Put(enc) 45 | 46 | if err := enc.EncodeKeyvals(keyvals...); err != nil { 47 | return err 48 | } 49 | 50 | // Add newline to the end of the buffer 51 | if err := enc.EndRecord(); err != nil { 52 | return err 53 | } 54 | 55 | // The Logger interface requires implementations to be safe for concurrent 56 | // use by multiple goroutines. For this implementation that means making 57 | // only one call to l.w.Write() for each call to Log. 58 | if _, err := l.w.Write(enc.buf.Bytes()); err != nil { 59 | return err 60 | } 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/go-stack/stack/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/go-stack/stack?status.svg)](https://godoc.org/github.com/go-stack/stack) 2 | [![Go Report Card](https://goreportcard.com/badge/go-stack/stack)](https://goreportcard.com/report/go-stack/stack) 3 | [![TravisCI](https://travis-ci.org/go-stack/stack.svg?branch=master)](https://travis-ci.org/go-stack/stack) 4 | [![Coverage Status](https://coveralls.io/repos/github/go-stack/stack/badge.svg?branch=master)](https://coveralls.io/github/go-stack/stack?branch=master) 5 | 6 | # stack 7 | 8 | Package stack implements utilities to capture, manipulate, and format call 9 | stacks. It provides a simpler API than package runtime. 10 | 11 | The implementation takes care of the minutia and special cases of interpreting 12 | the program counter (pc) values returned by runtime.Callers. 13 | 14 | ## Versioning 15 | 16 | Package stack publishes releases via [semver](http://semver.org/) compatible Git 17 | tags prefixed with a single 'v'. The master branch always contains the latest 18 | release. The develop branch contains unreleased commits. 19 | 20 | ## Formatting 21 | 22 | Package stack's types implement fmt.Formatter, which provides a simple and 23 | flexible way to declaratively configure formatting when used with logging or 24 | error tracking packages. 25 | 26 | ```go 27 | func DoTheThing() { 28 | c := stack.Caller(0) 29 | log.Print(c) // "source.go:10" 30 | log.Printf("%+v", c) // "pkg/path/source.go:10" 31 | log.Printf("%n", c) // "DoTheThing" 32 | 33 | s := stack.Trace().TrimRuntime() 34 | log.Print(s) // "[source.go:15 caller.go:42 main.go:14]" 35 | } 36 | ``` 37 | 38 | See the docs for all of the supported formatting options. 39 | -------------------------------------------------------------------------------- /vendor/github.com/go-chi/chi/chain.go: -------------------------------------------------------------------------------- 1 | package chi 2 | 3 | import "net/http" 4 | 5 | // Chain returns a Middlewares type from a slice of middleware handlers. 6 | func Chain(middlewares ...func(http.Handler) http.Handler) Middlewares { 7 | return Middlewares(middlewares) 8 | } 9 | 10 | // Handler builds and returns a http.Handler from the chain of middlewares, 11 | // with `h http.Handler` as the final handler. 12 | func (mws Middlewares) Handler(h http.Handler) http.Handler { 13 | return &ChainHandler{mws, h, chain(mws, h)} 14 | } 15 | 16 | // HandlerFunc builds and returns a http.Handler from the chain of middlewares, 17 | // with `h http.Handler` as the final handler. 18 | func (mws Middlewares) HandlerFunc(h http.HandlerFunc) http.Handler { 19 | return &ChainHandler{mws, h, chain(mws, h)} 20 | } 21 | 22 | // ChainHandler is a http.Handler with support for handler composition and 23 | // execution. 24 | type ChainHandler struct { 25 | Middlewares Middlewares 26 | Endpoint http.Handler 27 | chain http.Handler 28 | } 29 | 30 | func (c *ChainHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 31 | c.chain.ServeHTTP(w, r) 32 | } 33 | 34 | // chain builds a http.Handler composed of an inline middleware stack and endpoint 35 | // handler in the order they are passed. 36 | func chain(middlewares []func(http.Handler) http.Handler, endpoint http.Handler) http.Handler { 37 | // Return ahead of time if there aren't any middlewares for the chain 38 | if len(middlewares) == 0 { 39 | return endpoint 40 | } 41 | 42 | // Wrap the end handler with the middleware chain 43 | h := middlewares[len(middlewares)-1](endpoint) 44 | for i := len(middlewares) - 2; i >= 0; i-- { 45 | h = middlewares[i](h) 46 | } 47 | 48 | return h 49 | } 50 | -------------------------------------------------------------------------------- /vendor/github.com/go-logfmt/logfmt/README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/go-logfmt/logfmt?status.svg)](https://godoc.org/github.com/go-logfmt/logfmt) 2 | [![Go Report Card](https://goreportcard.com/badge/go-logfmt/logfmt)](https://goreportcard.com/report/go-logfmt/logfmt) 3 | [![TravisCI](https://travis-ci.org/go-logfmt/logfmt.svg?branch=master)](https://travis-ci.org/go-logfmt/logfmt) 4 | [![Coverage Status](https://coveralls.io/repos/github/go-logfmt/logfmt/badge.svg?branch=master)](https://coveralls.io/github/go-logfmt/logfmt?branch=master) 5 | 6 | # logfmt 7 | 8 | Package logfmt implements utilities to marshal and unmarshal data in the [logfmt 9 | format](https://brandur.org/logfmt). It provides an API similar to 10 | [encoding/json](http://golang.org/pkg/encoding/json/) and 11 | [encoding/xml](http://golang.org/pkg/encoding/xml/). 12 | 13 | The logfmt format was first documented by Brandur Leach in [this 14 | article](https://brandur.org/logfmt). The format has not been formally 15 | standardized. The most authoritative public specification to date has been the 16 | documentation of a Go Language [package](http://godoc.org/github.com/kr/logfmt) 17 | written by Blake Mizerany and Keith Rarick. 18 | 19 | ## Goals 20 | 21 | This project attempts to conform as closely as possible to the prior art, while 22 | also removing ambiguity where necessary to provide well behaved encoder and 23 | decoder implementations. 24 | 25 | ## Non-goals 26 | 27 | This project does not attempt to formally standardize the logfmt format. In the 28 | event that logfmt is standardized this project would take conforming to the 29 | standard as a goal. 30 | 31 | ## Versioning 32 | 33 | Package logfmt publishes releases via [semver](http://semver.org/) compatible Git tags prefixed with a single 'v'. 34 | -------------------------------------------------------------------------------- /cmd/pathfinder/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "flag" 7 | "net/http" 8 | "os" 9 | "os/signal" 10 | "syscall" 11 | "time" 12 | 13 | "github.com/go-kit/kit/log" 14 | "github.com/marcusolsson/pathfinder" 15 | "github.com/marcusolsson/pathfinder/server" 16 | ) 17 | 18 | const defaultPort = "8080" 19 | 20 | func main() { 21 | var ( 22 | addr = envString("PORT", defaultPort) 23 | httpAddr = flag.String("http.addr", ":"+addr, "HTTP listen address") 24 | ) 25 | 26 | flag.Parse() 27 | 28 | logger := log.NewJSONLogger(os.Stderr) 29 | logger = log.With(logger, "ts", log.DefaultTimestampUTC) 30 | 31 | var ps pathfinder.PathService 32 | { 33 | ps = pathfinder.NewPathService() 34 | ps = pathfinder.NewLoggingMiddleware(log.With(logger, "component", "path"), ps) 35 | } 36 | 37 | httpLogger := log.With(logger, "component", "http") 38 | 39 | stop := make(chan os.Signal, 1) 40 | 41 | signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM) 42 | 43 | srv := server.New(ps, httpLogger) 44 | 45 | h := http.Server{ 46 | Addr: *httpAddr, 47 | Handler: srv, 48 | } 49 | 50 | go func() { 51 | log.With(httpLogger, "addr", *httpAddr).Log("msg", "listening") 52 | 53 | if err := h.ListenAndServe(); err != nil { 54 | httpLogger.Log("error", errors.New("unable to serve http")) 55 | os.Exit(1) 56 | } 57 | }() 58 | 59 | <-stop 60 | 61 | logger.Log("msg", "shutting down") 62 | 63 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 64 | defer cancel() 65 | 66 | if err := h.Shutdown(ctx); err != nil { 67 | logger.Log("msg", errors.New("unable to shut down server")) 68 | return 69 | } 70 | 71 | logger.Log("msg", "terminated") 72 | } 73 | 74 | func envString(env, fallback string) string { 75 | e := os.Getenv(env) 76 | if e == "" { 77 | return fallback 78 | } 79 | return e 80 | } 81 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | 7 | "github.com/go-chi/chi" 8 | "github.com/go-kit/kit/log" 9 | "github.com/marcusolsson/pathfinder" 10 | ) 11 | 12 | // Server holds the dependencies for a HTTP server. 13 | type Server struct { 14 | Paths pathfinder.PathService 15 | Logger log.Logger 16 | router chi.Router 17 | } 18 | 19 | // New returns a new HTTP server. 20 | func New(ps pathfinder.PathService, logger log.Logger) *Server { 21 | s := &Server{ 22 | Paths: ps, 23 | Logger: logger, 24 | } 25 | 26 | r := chi.NewRouter() 27 | 28 | r.Use(accessControl) 29 | 30 | r.Get("/paths", s.shortestPaths) 31 | r.Method("GET", "/docs", http.StripPrefix("/docs/", http.FileServer(http.Dir("api")))) 32 | 33 | s.router = r 34 | 35 | return s 36 | } 37 | 38 | func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 39 | s.router.ServeHTTP(w, r) 40 | } 41 | 42 | func (s *Server) shortestPaths(w http.ResponseWriter, r *http.Request) { 43 | var ( 44 | from = r.URL.Query().Get("from") 45 | to = r.URL.Query().Get("to") 46 | ) 47 | 48 | paths, err := s.Paths.ShortestPath(from, to) 49 | if err != nil { 50 | if err == pathfinder.ErrInvalidArgument { 51 | w.WriteHeader(http.StatusBadRequest) 52 | return 53 | } 54 | 55 | w.WriteHeader(http.StatusInternalServerError) 56 | return 57 | } 58 | 59 | response := struct { 60 | Paths interface{} `json:"paths"` 61 | }{ 62 | Paths: paths, 63 | } 64 | 65 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 66 | 67 | if err := json.NewEncoder(w).Encode(response); err != nil { 68 | w.WriteHeader(http.StatusInternalServerError) 69 | return 70 | } 71 | } 72 | 73 | func accessControl(h http.Handler) http.Handler { 74 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 75 | w.Header().Set("Access-Control-Allow-Origin", "*") 76 | w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS") 77 | w.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type") 78 | 79 | if r.Method == "OPTIONS" { 80 | return 81 | } 82 | 83 | h.ServeHTTP(w, r) 84 | }) 85 | } 86 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/json_logger.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "encoding" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "reflect" 9 | ) 10 | 11 | type jsonLogger struct { 12 | io.Writer 13 | } 14 | 15 | // NewJSONLogger returns a Logger that encodes keyvals to the Writer as a 16 | // single JSON object. Each log event produces no more than one call to 17 | // w.Write. The passed Writer must be safe for concurrent use by multiple 18 | // goroutines if the returned Logger will be used concurrently. 19 | func NewJSONLogger(w io.Writer) Logger { 20 | return &jsonLogger{w} 21 | } 22 | 23 | func (l *jsonLogger) Log(keyvals ...interface{}) error { 24 | n := (len(keyvals) + 1) / 2 // +1 to handle case when len is odd 25 | m := make(map[string]interface{}, n) 26 | for i := 0; i < len(keyvals); i += 2 { 27 | k := keyvals[i] 28 | var v interface{} = ErrMissingValue 29 | if i+1 < len(keyvals) { 30 | v = keyvals[i+1] 31 | } 32 | merge(m, k, v) 33 | } 34 | return json.NewEncoder(l.Writer).Encode(m) 35 | } 36 | 37 | func merge(dst map[string]interface{}, k, v interface{}) { 38 | var key string 39 | switch x := k.(type) { 40 | case string: 41 | key = x 42 | case fmt.Stringer: 43 | key = safeString(x) 44 | default: 45 | key = fmt.Sprint(x) 46 | } 47 | 48 | // We want json.Marshaler and encoding.TextMarshaller to take priority over 49 | // err.Error() and v.String(). But json.Marshall (called later) does that by 50 | // default so we force a no-op if it's one of those 2 case. 51 | switch x := v.(type) { 52 | case json.Marshaler: 53 | case encoding.TextMarshaler: 54 | case error: 55 | v = safeError(x) 56 | case fmt.Stringer: 57 | v = safeString(x) 58 | } 59 | 60 | dst[key] = v 61 | } 62 | 63 | func safeString(str fmt.Stringer) (s string) { 64 | defer func() { 65 | if panicVal := recover(); panicVal != nil { 66 | if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { 67 | s = "NULL" 68 | } else { 69 | panic(panicVal) 70 | } 71 | } 72 | }() 73 | s = str.String() 74 | return 75 | } 76 | 77 | func safeError(err error) (s interface{}) { 78 | defer func() { 79 | if panicVal := recover(); panicVal != nil { 80 | if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { 81 | s = nil 82 | } else { 83 | panic(panicVal) 84 | } 85 | } 86 | }() 87 | s = err.Error() 88 | return 89 | } 90 | -------------------------------------------------------------------------------- /vendor/github.com/kr/logfmt/scanner.go: -------------------------------------------------------------------------------- 1 | package logfmt 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | var ErrUnterminatedString = errors.New("logfmt: unterminated string") 9 | 10 | func gotoScanner(data []byte, h Handler) (err error) { 11 | saveError := func(e error) { 12 | if err == nil { 13 | err = e 14 | } 15 | } 16 | 17 | var c byte 18 | var i int 19 | var m int 20 | var key []byte 21 | var val []byte 22 | var ok bool 23 | var esc bool 24 | 25 | garbage: 26 | if i == len(data) { 27 | return 28 | } 29 | 30 | c = data[i] 31 | switch { 32 | case c > ' ' && c != '"' && c != '=': 33 | key, val = nil, nil 34 | m = i 35 | i++ 36 | goto key 37 | default: 38 | i++ 39 | goto garbage 40 | } 41 | 42 | key: 43 | if i >= len(data) { 44 | if m >= 0 { 45 | key = data[m:i] 46 | saveError(h.HandleLogfmt(key, nil)) 47 | } 48 | return 49 | } 50 | 51 | c = data[i] 52 | switch { 53 | case c > ' ' && c != '"' && c != '=': 54 | i++ 55 | goto key 56 | case c == '=': 57 | key = data[m:i] 58 | i++ 59 | goto equal 60 | default: 61 | key = data[m:i] 62 | i++ 63 | saveError(h.HandleLogfmt(key, nil)) 64 | goto garbage 65 | } 66 | 67 | equal: 68 | if i >= len(data) { 69 | if m >= 0 { 70 | i-- 71 | key = data[m:i] 72 | saveError(h.HandleLogfmt(key, nil)) 73 | } 74 | return 75 | } 76 | 77 | c = data[i] 78 | switch { 79 | case c > ' ' && c != '"' && c != '=': 80 | m = i 81 | i++ 82 | goto ivalue 83 | case c == '"': 84 | m = i 85 | i++ 86 | esc = false 87 | goto qvalue 88 | default: 89 | if key != nil { 90 | saveError(h.HandleLogfmt(key, val)) 91 | } 92 | i++ 93 | goto garbage 94 | } 95 | 96 | ivalue: 97 | if i >= len(data) { 98 | if m >= 0 { 99 | val = data[m:i] 100 | saveError(h.HandleLogfmt(key, val)) 101 | } 102 | return 103 | } 104 | 105 | c = data[i] 106 | switch { 107 | case c > ' ' && c != '"' && c != '=': 108 | i++ 109 | goto ivalue 110 | default: 111 | val = data[m:i] 112 | saveError(h.HandleLogfmt(key, val)) 113 | i++ 114 | goto garbage 115 | } 116 | 117 | qvalue: 118 | if i >= len(data) { 119 | if m >= 0 { 120 | saveError(ErrUnterminatedString) 121 | } 122 | return 123 | } 124 | 125 | c = data[i] 126 | switch c { 127 | case '\\': 128 | i += 2 129 | esc = true 130 | goto qvalue 131 | case '"': 132 | i++ 133 | val = data[m:i] 134 | if esc { 135 | val, ok = unquoteBytes(val) 136 | if !ok { 137 | saveError(fmt.Errorf("logfmt: error unquoting bytes %q", string(val))) 138 | goto garbage 139 | } 140 | } else { 141 | val = val[1 : len(val)-1] 142 | } 143 | saveError(h.HandleLogfmt(key, val)) 144 | goto garbage 145 | default: 146 | i++ 147 | goto qvalue 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /vendor/github.com/go-logfmt/logfmt/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package logfmt 4 | 5 | import ( 6 | "bufio" 7 | "bytes" 8 | "fmt" 9 | "io" 10 | "reflect" 11 | 12 | kr "github.com/kr/logfmt" 13 | ) 14 | 15 | // Fuzz checks reserialized data matches 16 | func Fuzz(data []byte) int { 17 | parsed, err := parse(data) 18 | if err != nil { 19 | return 0 20 | } 21 | var w1 bytes.Buffer 22 | if err = write(parsed, &w1); err != nil { 23 | panic(err) 24 | } 25 | parsed, err = parse(w1.Bytes()) 26 | if err != nil { 27 | panic(err) 28 | } 29 | var w2 bytes.Buffer 30 | if err = write(parsed, &w2); err != nil { 31 | panic(err) 32 | } 33 | if !bytes.Equal(w1.Bytes(), w2.Bytes()) { 34 | panic(fmt.Sprintf("reserialized data does not match:\n%q\n%q\n", w1.Bytes(), w2.Bytes())) 35 | } 36 | return 1 37 | } 38 | 39 | // FuzzVsKR checks go-logfmt/logfmt against kr/logfmt 40 | func FuzzVsKR(data []byte) int { 41 | parsed, err := parse(data) 42 | parsedKR, errKR := parseKR(data) 43 | 44 | // github.com/go-logfmt/logfmt is a stricter parser. It returns errors for 45 | // more inputs than github.com/kr/logfmt. Ignore any inputs that have a 46 | // stict error. 47 | if err != nil { 48 | return 0 49 | } 50 | 51 | // Fail if the more forgiving parser finds an error not found by the 52 | // stricter parser. 53 | if errKR != nil { 54 | panic(fmt.Sprintf("unmatched error: %v", errKR)) 55 | } 56 | 57 | if !reflect.DeepEqual(parsed, parsedKR) { 58 | panic(fmt.Sprintf("parsers disagree:\n%+v\n%+v\n", parsed, parsedKR)) 59 | } 60 | return 1 61 | } 62 | 63 | type kv struct { 64 | k, v []byte 65 | } 66 | 67 | func parse(data []byte) ([][]kv, error) { 68 | var got [][]kv 69 | dec := NewDecoder(bytes.NewReader(data)) 70 | for dec.ScanRecord() { 71 | var kvs []kv 72 | for dec.ScanKeyval() { 73 | kvs = append(kvs, kv{dec.Key(), dec.Value()}) 74 | } 75 | got = append(got, kvs) 76 | } 77 | return got, dec.Err() 78 | } 79 | 80 | func parseKR(data []byte) ([][]kv, error) { 81 | var ( 82 | s = bufio.NewScanner(bytes.NewReader(data)) 83 | err error 84 | h saveHandler 85 | got [][]kv 86 | ) 87 | for err == nil && s.Scan() { 88 | h.kvs = nil 89 | err = kr.Unmarshal(s.Bytes(), &h) 90 | got = append(got, h.kvs) 91 | } 92 | if err == nil { 93 | err = s.Err() 94 | } 95 | return got, err 96 | } 97 | 98 | type saveHandler struct { 99 | kvs []kv 100 | } 101 | 102 | func (h *saveHandler) HandleLogfmt(key, val []byte) error { 103 | if len(key) == 0 { 104 | key = nil 105 | } 106 | if len(val) == 0 { 107 | val = nil 108 | } 109 | h.kvs = append(h.kvs, kv{key, val}) 110 | return nil 111 | } 112 | 113 | func write(recs [][]kv, w io.Writer) error { 114 | enc := NewEncoder(w) 115 | for _, rec := range recs { 116 | for _, f := range rec { 117 | if err := enc.EncodeKeyval(f.k, f.v); err != nil { 118 | return err 119 | } 120 | } 121 | if err := enc.EndRecord(); err != nil { 122 | return err 123 | } 124 | } 125 | return nil 126 | } 127 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/value.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/go-stack/stack" 7 | ) 8 | 9 | // A Valuer generates a log value. When passed to With or WithPrefix in a 10 | // value element (odd indexes), it represents a dynamic value which is re- 11 | // evaluated with each log event. 12 | type Valuer func() interface{} 13 | 14 | // bindValues replaces all value elements (odd indexes) containing a Valuer 15 | // with their generated value. 16 | func bindValues(keyvals []interface{}) { 17 | for i := 1; i < len(keyvals); i += 2 { 18 | if v, ok := keyvals[i].(Valuer); ok { 19 | keyvals[i] = v() 20 | } 21 | } 22 | } 23 | 24 | // containsValuer returns true if any of the value elements (odd indexes) 25 | // contain a Valuer. 26 | func containsValuer(keyvals []interface{}) bool { 27 | for i := 1; i < len(keyvals); i += 2 { 28 | if _, ok := keyvals[i].(Valuer); ok { 29 | return true 30 | } 31 | } 32 | return false 33 | } 34 | 35 | // Timestamp returns a timestamp Valuer. It invokes the t function to get the 36 | // time; unless you are doing something tricky, pass time.Now. 37 | // 38 | // Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which 39 | // are TimestampFormats that use the RFC3339Nano format. 40 | func Timestamp(t func() time.Time) Valuer { 41 | return func() interface{} { return t() } 42 | } 43 | 44 | // TimestampFormat returns a timestamp Valuer with a custom time format. It 45 | // invokes the t function to get the time to format; unless you are doing 46 | // something tricky, pass time.Now. The layout string is passed to 47 | // Time.Format. 48 | // 49 | // Most users will want to use DefaultTimestamp or DefaultTimestampUTC, which 50 | // are TimestampFormats that use the RFC3339Nano format. 51 | func TimestampFormat(t func() time.Time, layout string) Valuer { 52 | return func() interface{} { 53 | return timeFormat{ 54 | time: t(), 55 | layout: layout, 56 | } 57 | } 58 | } 59 | 60 | // A timeFormat represents an instant in time and a layout used when 61 | // marshaling to a text format. 62 | type timeFormat struct { 63 | time time.Time 64 | layout string 65 | } 66 | 67 | func (tf timeFormat) String() string { 68 | return tf.time.Format(tf.layout) 69 | } 70 | 71 | // MarshalText implements encoding.TextMarshaller. 72 | func (tf timeFormat) MarshalText() (text []byte, err error) { 73 | // The following code adapted from the standard library time.Time.Format 74 | // method. Using the same undocumented magic constant to extend the size 75 | // of the buffer as seen there. 76 | b := make([]byte, 0, len(tf.layout)+10) 77 | b = tf.time.AppendFormat(b, tf.layout) 78 | return b, nil 79 | } 80 | 81 | // Caller returns a Valuer that returns a file and line from a specified depth 82 | // in the callstack. Users will probably want to use DefaultCaller. 83 | func Caller(depth int) Valuer { 84 | return func() interface{} { return stack.Caller(depth) } 85 | } 86 | 87 | var ( 88 | // DefaultTimestamp is a Valuer that returns the current wallclock time, 89 | // respecting time zones, when bound. 90 | DefaultTimestamp = TimestampFormat(time.Now, time.RFC3339Nano) 91 | 92 | // DefaultTimestampUTC is a Valuer that returns the current time in UTC 93 | // when bound. 94 | DefaultTimestampUTC = TimestampFormat( 95 | func() time.Time { return time.Now().UTC() }, 96 | time.RFC3339Nano, 97 | ) 98 | 99 | // DefaultCaller is a Valuer that returns the file and line where the Log 100 | // method was invoked. It can only be used with log.With. 101 | DefaultCaller = Caller(3) 102 | ) 103 | -------------------------------------------------------------------------------- /vendor/github.com/kr/logfmt/unquote.go: -------------------------------------------------------------------------------- 1 | package logfmt 2 | 3 | import ( 4 | "strconv" 5 | "unicode" 6 | "unicode/utf16" 7 | "unicode/utf8" 8 | ) 9 | 10 | // Taken from Go's encoding/json 11 | 12 | // Copyright 2010 The Go Authors. All rights reserved. 13 | // Use of this source code is governed by a BSD-style 14 | // license that can be found in the LICENSE file. 15 | 16 | // getu4 decodes \uXXXX from the beginning of s, returning the hex value, 17 | // or it returns -1. 18 | func getu4(s []byte) rune { 19 | if len(s) < 6 || s[0] != '\\' || s[1] != 'u' { 20 | return -1 21 | } 22 | r, err := strconv.ParseUint(string(s[2:6]), 16, 64) 23 | if err != nil { 24 | return -1 25 | } 26 | return rune(r) 27 | } 28 | 29 | // unquote converts a quoted JSON string literal s into an actual string t. 30 | // The rules are different than for Go, so cannot use strconv.Unquote. 31 | func unquote(s []byte) (t string, ok bool) { 32 | s, ok = unquoteBytes(s) 33 | t = string(s) 34 | return 35 | } 36 | 37 | func unquoteBytes(s []byte) (t []byte, ok bool) { 38 | if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' { 39 | return 40 | } 41 | s = s[1 : len(s)-1] 42 | 43 | // Check for unusual characters. If there are none, 44 | // then no unquoting is needed, so return a slice of the 45 | // original bytes. 46 | r := 0 47 | for r < len(s) { 48 | c := s[r] 49 | if c == '\\' || c == '"' || c < ' ' { 50 | break 51 | } 52 | if c < utf8.RuneSelf { 53 | r++ 54 | continue 55 | } 56 | rr, size := utf8.DecodeRune(s[r:]) 57 | if rr == utf8.RuneError && size == 1 { 58 | break 59 | } 60 | r += size 61 | } 62 | if r == len(s) { 63 | return s, true 64 | } 65 | 66 | b := make([]byte, len(s)+2*utf8.UTFMax) 67 | w := copy(b, s[0:r]) 68 | for r < len(s) { 69 | // Out of room? Can only happen if s is full of 70 | // malformed UTF-8 and we're replacing each 71 | // byte with RuneError. 72 | if w >= len(b)-2*utf8.UTFMax { 73 | nb := make([]byte, (len(b)+utf8.UTFMax)*2) 74 | copy(nb, b[0:w]) 75 | b = nb 76 | } 77 | switch c := s[r]; { 78 | case c == '\\': 79 | r++ 80 | if r >= len(s) { 81 | return 82 | } 83 | switch s[r] { 84 | default: 85 | return 86 | case '"', '\\', '/', '\'': 87 | b[w] = s[r] 88 | r++ 89 | w++ 90 | case 'b': 91 | b[w] = '\b' 92 | r++ 93 | w++ 94 | case 'f': 95 | b[w] = '\f' 96 | r++ 97 | w++ 98 | case 'n': 99 | b[w] = '\n' 100 | r++ 101 | w++ 102 | case 'r': 103 | b[w] = '\r' 104 | r++ 105 | w++ 106 | case 't': 107 | b[w] = '\t' 108 | r++ 109 | w++ 110 | case 'u': 111 | r-- 112 | rr := getu4(s[r:]) 113 | if rr < 0 { 114 | return 115 | } 116 | r += 6 117 | if utf16.IsSurrogate(rr) { 118 | rr1 := getu4(s[r:]) 119 | if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar { 120 | // A valid pair; consume. 121 | r += 6 122 | w += utf8.EncodeRune(b[w:], dec) 123 | break 124 | } 125 | // Invalid surrogate; fall back to replacement rune. 126 | rr = unicode.ReplacementChar 127 | } 128 | w += utf8.EncodeRune(b[w:], rr) 129 | } 130 | 131 | // Quote, control characters are invalid. 132 | case c == '"', c < ' ': 133 | return 134 | 135 | // ASCII 136 | case c < utf8.RuneSelf: 137 | b[w] = c 138 | r++ 139 | w++ 140 | 141 | // Coerce to well-formed UTF-8. 142 | default: 143 | rr, size := utf8.DecodeRune(s[r:]) 144 | r += size 145 | w += utf8.EncodeRune(b[w:], rr) 146 | } 147 | } 148 | return b[0:w], true 149 | } 150 | -------------------------------------------------------------------------------- /vendor/github.com/go-kit/kit/log/stdlib.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | // StdlibWriter implements io.Writer by invoking the stdlib log.Print. It's 11 | // designed to be passed to a Go kit logger as the writer, for cases where 12 | // it's necessary to redirect all Go kit log output to the stdlib logger. 13 | // 14 | // If you have any choice in the matter, you shouldn't use this. Prefer to 15 | // redirect the stdlib log to the Go kit logger via NewStdlibAdapter. 16 | type StdlibWriter struct{} 17 | 18 | // Write implements io.Writer. 19 | func (w StdlibWriter) Write(p []byte) (int, error) { 20 | log.Print(strings.TrimSpace(string(p))) 21 | return len(p), nil 22 | } 23 | 24 | // StdlibAdapter wraps a Logger and allows it to be passed to the stdlib 25 | // logger's SetOutput. It will extract date/timestamps, filenames, and 26 | // messages, and place them under relevant keys. 27 | type StdlibAdapter struct { 28 | Logger 29 | timestampKey string 30 | fileKey string 31 | messageKey string 32 | } 33 | 34 | // StdlibAdapterOption sets a parameter for the StdlibAdapter. 35 | type StdlibAdapterOption func(*StdlibAdapter) 36 | 37 | // TimestampKey sets the key for the timestamp field. By default, it's "ts". 38 | func TimestampKey(key string) StdlibAdapterOption { 39 | return func(a *StdlibAdapter) { a.timestampKey = key } 40 | } 41 | 42 | // FileKey sets the key for the file and line field. By default, it's "caller". 43 | func FileKey(key string) StdlibAdapterOption { 44 | return func(a *StdlibAdapter) { a.fileKey = key } 45 | } 46 | 47 | // MessageKey sets the key for the actual log message. By default, it's "msg". 48 | func MessageKey(key string) StdlibAdapterOption { 49 | return func(a *StdlibAdapter) { a.messageKey = key } 50 | } 51 | 52 | // NewStdlibAdapter returns a new StdlibAdapter wrapper around the passed 53 | // logger. It's designed to be passed to log.SetOutput. 54 | func NewStdlibAdapter(logger Logger, options ...StdlibAdapterOption) io.Writer { 55 | a := StdlibAdapter{ 56 | Logger: logger, 57 | timestampKey: "ts", 58 | fileKey: "caller", 59 | messageKey: "msg", 60 | } 61 | for _, option := range options { 62 | option(&a) 63 | } 64 | return a 65 | } 66 | 67 | func (a StdlibAdapter) Write(p []byte) (int, error) { 68 | result := subexps(p) 69 | keyvals := []interface{}{} 70 | var timestamp string 71 | if date, ok := result["date"]; ok && date != "" { 72 | timestamp = date 73 | } 74 | if time, ok := result["time"]; ok && time != "" { 75 | if timestamp != "" { 76 | timestamp += " " 77 | } 78 | timestamp += time 79 | } 80 | if timestamp != "" { 81 | keyvals = append(keyvals, a.timestampKey, timestamp) 82 | } 83 | if file, ok := result["file"]; ok && file != "" { 84 | keyvals = append(keyvals, a.fileKey, file) 85 | } 86 | if msg, ok := result["msg"]; ok { 87 | keyvals = append(keyvals, a.messageKey, msg) 88 | } 89 | if err := a.Logger.Log(keyvals...); err != nil { 90 | return 0, err 91 | } 92 | return len(p), nil 93 | } 94 | 95 | const ( 96 | logRegexpDate = `(?P[0-9]{4}/[0-9]{2}/[0-9]{2})?[ ]?` 97 | logRegexpTime = `(?P