Skip to content

Commit

Permalink
Enable the log level to be specified with -log-level
Browse files Browse the repository at this point in the history
  • Loading branch information
Homme Zwaagstra committed Feb 24, 2015
1 parent 0f487c9 commit 63495af
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 12 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
bin
pkg
server
src/github.com
.go-bindata
55 changes: 52 additions & 3 deletions cmd/cesium-terrain-server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
package main

import (
"errors"
"flag"
"fmt"
"github.com/geo-data/cesium-terrain-server/log"
"github.com/geo-data/cesium-terrain-server/server"
"github.com/geo-data/cesium-terrain-server/stores"
"github.com/geo-data/cesium-terrain-server/stores/files"
Expand All @@ -12,7 +14,7 @@ import (
"github.com/geo-data/cesium-terrain-server/stores/tiles"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"log"
l "log"
"net/http"
"os"
"path/filepath"
Expand Down Expand Up @@ -60,12 +62,56 @@ func CreateTileStores(tilesetRoot, memcache string) []*tiles.Store {
return stores
}

type LogOpt struct {
Priority log.Priority
}

func NewLogOpt() *LogOpt {
return &LogOpt{
Priority: log.LOG_NOTICE,
}
}

func (this *LogOpt) String() string {
switch this.Priority {
case log.LOG_CRIT:
return "crit"
case log.LOG_ERR:
return "err"
case log.LOG_NOTICE:
return "notice"
default:
return "debug"
}
}

func (this *LogOpt) Set(level string) error {
switch level {
case "crit":
this.Priority = log.LOG_CRIT
case "err":
this.Priority = log.LOG_ERR
case "notice":
this.Priority = log.LOG_NOTICE
case "debug":
this.Priority = log.LOG_DEBUG
default:
return errors.New("choose one of crit, err, notice, debug")
}
return nil
}

func main() {
port := flag.Uint("port", 8000, "the port on which the server listens")
tilesetRoot := flag.String("dir", ".", "the root directory under which tileset directories reside")
memcache := flag.String("memcache", "", "memcache connection string for caching tiles e.g. localhost:11211")
logging := NewLogOpt()
flag.Var(logging, "log-level", "level at which logging occurs. One of crit, err, notice, debug")
flag.Parse()

// Set the logging
log.SetLog(l.New(os.Stderr, "", l.LstdFlags), logging.Priority)

// Generate a list of valid tile stores.
tileStores := CreateTileStores(*tilesetRoot, *memcache)

Expand All @@ -81,6 +127,9 @@ func main() {

http.Handle("/", handlers.CombinedLoggingHandler(os.Stdout, server.AddCorsHeader(r)))

log.Println("Terrain server listening on port", *port)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", *port), nil))
log.Notice(fmt.Sprintf("server listening on port %d", *port))
if err := http.ListenAndServe(fmt.Sprintf(":%d", *port), nil); err != nil {
log.Crit(fmt.Sprintf("server failed: %s", err))
os.Exit(1)
}
}
84 changes: 84 additions & 0 deletions log/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package log

import (
l "log"
"os"
)

type Priority int

const (
LOG_DEBUG Priority = iota
LOG_NOTICE
LOG_ERR
LOG_CRIT
)

// Logger interface is satisfied by syslog.Writer
type Logger interface {
Debug(m string) (err error)
Notice(m string) (err error)
Err(m string) (err error)
Crit(m string) (err error)
}

var std Logger = New(l.New(os.Stderr, "", l.LstdFlags), LOG_NOTICE)

func Debug(m string) error {
return std.Debug(m)
}

func Notice(m string) error {
return std.Notice(m)
}

func Err(m string) error {
return std.Err(m)
}

func Crit(m string) error {
return std.Crit(m)
}

type logProxy struct {
priority Priority
log *l.Logger
}

func New(logger *l.Logger, priority Priority) Logger {
return &logProxy{
log: logger,
priority: priority,
}
}

func (this *logProxy) write(m string, p Priority) (err error) {
if this.priority <= p {
err = this.log.Output(2, m)
}
return
}

func (this *logProxy) Debug(m string) (err error) {
return this.write("DEBUG: "+m, LOG_DEBUG)
}

func (this *logProxy) Notice(m string) (err error) {
return this.write("NOTICE: "+m, LOG_NOTICE)
}

func (this *logProxy) Err(m string) (err error) {
return this.write("ERROR: "+m, LOG_ERR)
}

func (this *logProxy) Crit(m string) (err error) {
return this.write("CRITICAL: "+m, LOG_CRIT)
}

func SetLogger(logger Logger) {
std = logger
}

func SetLog(log *l.Logger, priority Priority) {
SetLogger(New(log, priority))
}
184 changes: 184 additions & 0 deletions server/server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package server

import (
"errors"
"fmt"
"github.com/geo-data/cesium-terrain-server/assets"
"github.com/geo-data/cesium-terrain-server/log"
db "github.com/geo-data/cesium-terrain-server/stores"
"github.com/geo-data/cesium-terrain-server/stores/items"
"github.com/geo-data/cesium-terrain-server/stores/items/terrain"
"github.com/geo-data/cesium-terrain-server/stores/tiles"
"github.com/gorilla/mux"
"net/http"
"os"
"path/filepath"
)

// An HTTP handler which returns a terrain tile resource
func TerrainHandler(stores []*tiles.Store) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var (
t terrain.Terrain
err error
)

defer func() {
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
log.Err(err.Error())
}
}()

// get the tile coordinate from the URL
vars := mux.Vars(r)
err = t.ParseCoord(vars["x"], vars["y"], vars["z"])
if err != nil {
return
}

// Try and get a tile from the stores
var idx int
for i, store := range stores {
idx = i
err = store.LoadTile(vars["tileset"], &t)
if err == nil {
break
} else if err == db.ErrNoItem {
continue
} else {
return
}
}

if err == db.ErrNoItem {
if t.IsRoot() {
// serve up a blank tile as it is a missing root tile
data, err := assets.Asset("data/smallterrain-blank.terrain")
if err != nil {
return
} else {
err = t.UnmarshalBinary(data)
if err != nil {
return
}
}
} else {
err = nil
http.Error(w, errors.New("The terrain tile does not exist").Error(), http.StatusNotFound)
return
}
}

body, err := t.MarshalBinary()
if err != nil {
return
}

// send the tile to the client
headers := w.Header()
headers.Set("Content-Type", "application/octet-stream")
headers.Set("Content-Encoding", "gzip")
headers.Set("Content-Disposition", "attachment;filename="+vars["y"]+".terrain")
w.Write(body)

// Save the tile in any preceding stores that didn't have it.
if idx > 0 {
for j := 0; j < idx; j++ {
if err := stores[j].SaveTile(vars["tileset"], &t); err != nil {
log.Err(fmt.Sprintf("failed to store tileset: %s", err))
}
}
}
}
}

// An HTTP handler which returns a tileset's `layer.json` file
func LayerHandler(tilesetRoot string, stores []db.Storer) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
var (
err error
response items.Item
)

defer func() {
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
log.Err(err.Error())
}
}()

vars := mux.Vars(r)
key := filepath.Join(vars["tileset"], "layer.json")

// Try and get a `layer.json` from the stores
var idx int
for i, store := range stores {
idx = i
err = store.Load(key, &response)
if err == nil {
break
} else if err == db.ErrNoItem {
continue
} else {
return
}
}

if err == db.ErrNoItem {
// check whether the tile directory exists
_, err = os.Stat(filepath.Join(tilesetRoot, vars["tileset"]))
if err != nil {
if os.IsNotExist(err) {
err = nil
http.Error(w,
fmt.Errorf("The tileset `%s` does not exist", vars["tileset"]).Error(),
http.StatusNotFound)
return
}
// There's some other problem (e.g. permissions)
return
}

// the directory exists: send the default `layer.json`

err = response.UnmarshalBinary([]byte(`{
"tilejson": "2.1.0",
"format": "heightmap-1.0",
"version": "1.0.0",
"scheme": "tms",
"tiles": ["{z}/{x}/{y}.terrain"]
}`))
if err != nil {
return
}
}

body, err := response.MarshalBinary()
if err != nil {
return
}

headers := w.Header()
headers.Set("Content-Type", "application/json")
w.Write(body)

// Save the json file in any preceding stores that didn't have it.
if idx > 0 {
for j := 0; j < idx; j++ {
if err := stores[j].Save(key, &response); err != nil {
log.Err(fmt.Sprintf("failed to store layer.json: %s", err))
}
}
}
}
}

// Return HTTP middleware which allows CORS requests from any domain
func AddCorsHeader(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
headers := w.Header()
headers.Set("Access-Control-Allow-Origin", "*")
next.ServeHTTP(w, r)
})
}
Loading

0 comments on commit 63495af

Please sign in to comment.