Beispiel #1
0
// Get a new DBManager
func New(opts Opts) *DBManager {
	// Configure sqlPool
	poolOpts := sqlpool.Opts{
		Max:         int64(opts.MaxDBs),
		IdleTimeout: int64(opts.IdleTimeout),
		PreInit:     createDirectory,
		PostInit:    initializeDatabase,
	}
	pool := sqlpool.NewPool(poolOpts)

	// Start a new DBManager
	manager := DBManager{
		StartTime: time.Now(),
		Logger:    logger.New("[DBManager]"),
		Pool:      pool,
	}

	// Handle closing connections when app is killed
	go func() {
		<-opts.ClosingChannel
		manager.Pool.ForceClose()
		opts.ClosingChannel <- true
	}()

	return &manager
}
Beispiel #2
0
func init() {
	log.Info("registering heartbeat: docker")
	Add("docker", func(conf map[string]string) (Plugin, error) {
		host, ok := conf["host"]
		if !ok {
			host = os.Getenv("DOCKER_HOST")
			if host == "" {
				log.Info("no host information found, fallback to default: %s")
				host = DEFAULT_DOCKER_HOST
			}
		}

		log.Info("connecting to docker (%s)", host)
		// TODO support for tls
		docker, err := dockerclient.NewDockerClient(host, nil)
		if err != nil {
			return nil, err
		}

		return &DockerMonitor{
			docker: docker,
			logger: logger.New("sentinel.plugins.heartbeats.docker"),
		}, nil
	})
}
Beispiel #3
0
func init() {
	log.Info("registering adapter: shell")
	Add("shell", func(conf map[string]string) (Plugin, error) {

		return &Shell{
			logger: logger.New("sentinel.plugins.adapters.shell"),
		}, nil
	})
}
Beispiel #4
0
func init() {
	log.Info("registering actuator: debugger")
	Add("debug", func(adapter_ adapters.Plugin, conf map[string]string) (Plugin, error) {

		return &Debug{
			Adapter: adapter_,
			logger:  logger.New("sentinel.plugins.actuators.debug"),
		}, nil
	})
}
Beispiel #5
0
func init() {
	log.Info("registering heartbeat: clock")
	Add("cron", func(conf map[string]string) (Plugin, error) {
		interval, ok := conf["interval"]
		if !ok {
			interval = DEFAULT_INTERVAL
		}
		return &Clock{
			Interval: interval,
			logger:   logger.New("sentinel.plugins.heartbeats.clock"),
		}, nil
	})
}
Beispiel #6
0
func init() {
	log.Info("registering actuator: ping")
	Add("ping", func(adapter_ adapters.Plugin, conf map[string]string) (Plugin, error) {
		endpoint, ok := conf["endpoint"]
		if !ok {
			return nil, fmt.Errorf("no endpoint provided")
		}
		return &Ping{
			Endpoint: endpoint,
			Adapter:  adapter_,
			logger:   logger.New("sentinel.plugins.actuators.ping"),
		}, nil
	})
}
Beispiel #7
0
func GetGeoLite2Reader() (*maxminddb.Reader, error) {
	var log = logger.New("[GeoIP]")

	data, err := geolite2db.Asset("GeoLite2-Country.mmdb")
	if err != nil {
		log.Error("Unable to open GeoLite2-Country asset file: [%v]", err)
		return nil, err
	}

	db, err := maxminddb.FromBytes(data)
	if err != nil {
		log.Error("Unable to open GeoLite2-Country database: [%v]", err)
		return nil, err
	}

	return db, nil
}
Beispiel #8
0
func init() {
	log.Info("registering actuator: lua")
	Add("lua", func(adapter_ adapters.Plugin, conf map[string]string) (Plugin, error) {
		script, ok := conf["script"]
		if !ok {
			script = DEFAULT_LUA_SCRIPT_PATH
			// TODO check it exists
		}

		vm := lua.NewState()
		// NOTE defer vm.Close() ?

		return &LuaScript{
			Script:  script,
			VM:      vm,
			Adapter: adapter_,
			logger:  logger.New("sentinel.plugins.actuators.ping"),
		}, nil
	})
}
Beispiel #9
0
func init() {
	log.Info("registering adapter: serf")
	Add("serf", func(conf map[string]string) (Plugin, error) {
		// FIXME bad formatted addr (e.g. 127.0.0.1) crashes sentinel
		rpcAddr := get(conf, "addr", "127.0.0.1:7373").(string)
		rpcAuthKey := get(conf, "auth-key", "").(string)
		coalesce := get(conf, "coalesce", true).(bool)

		RPC, err := command.RPCClient(rpcAddr, rpcAuthKey)
		if err != nil {
			return nil, err
		}

		return &Serf{
			RPC:      RPC,
			logger:   logger.New("sentinel.plugins.adapters.serf"),
			coalesce: coalesce,
		}, nil
	})
}
Beispiel #10
0
// Return ISOCode for an IP
func GeoIpLookup(geolite2 *maxminddb.Reader, ipStr string) (string, error) {
	var log = logger.New("[GeoIP]")

	// Try to split port from ipStr
	host, _, err := net.SplitHostPort(ipStr)
	// Found a port in ipStr, update
	if err == nil {
		ipStr = host
	}

	ip := net.ParseIP(ipStr)

	result := lookupResult{}
	err = geolite2.Lookup(ip, &result)
	if err != nil {
		log.Error("Unable to lookup for IP %s: [%v]", ipStr, err)
		return "", err
	}

	return strings.ToLower(result.Country.ISOCode), nil
}
Beispiel #11
0
func NewRouter(opts RouterOpts) (http.Handler, error) {
	// Create the app router
	r := mux.NewRouter()
	var log = logger.New("[Router]")

	geolite2 := opts.Geolite2Reader

	// Initiate DB driver
	driver, err := sqlite.NewShardedDriver(opts.DriverOpts)
	if err != nil {
		return nil, err
	}

	/////
	// Query a DB over time
	/////
	r.Path("/{dbName}/time").
		Methods("GET").
		HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// Get params from URL
			vars := mux.Vars(req)
			dbName := vars["dbName"]

			// Parse request query
			if err := req.ParseForm(); err != nil {
				renderError(w, err)
				return
			}

			// Get timeRange if provided
			startTime := req.Form.Get("start")
			endTime := req.Form.Get("end")
			intervalStr := req.Form.Get("interval")

			// Convert startTime and endTime to a TimeRange
			timeRange, err := newTimeRange(startTime, endTime)
			if err != nil {
				renderError(w, &webErrors.InvalidTimeFormat)
				return
			}

			// Cast interval to an integer
			// Defaults to 1 day
			interval := 24 * 60 * 60
			if len(intervalStr) > 0 {
				interval, err = strconv.Atoi(intervalStr)
				if err != nil {
					renderError(w, &webErrors.InvalidInterval)
					return
				}
			}

			unique := false
			if strings.Compare(req.Form.Get("unique"), "true") == 0 {
				unique = true
			}

			// Construct Params object
			params := database.Params{
				DBName:    dbName,
				Interval:  interval,
				TimeRange: timeRange,
				Unique:    unique,
				URL:       req.URL,
			}

			analytics, err := driver.Series(params)
			if err != nil {
				renderError(w, normalizeDriverError(err))
				return
			}

			// Return query result
			render(w, analytics, nil)
		})

	/////
	// Count for a DB
	/////
	r.Path("/{dbName}/count").
		Methods("GET").
		HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// Get params from URL
			vars := mux.Vars(req)
			dbName := vars["dbName"]

			// Parse request query
			if err := req.ParseForm(); err != nil {
				renderError(w, err)
				return
			}

			// Get timeRange if provided
			startTime := req.Form.Get("start")
			endTime := req.Form.Get("end")

			// Convert startTime and endTime to a TimeRange
			timeRange, err := newTimeRange(startTime, endTime)
			if err != nil {
				renderError(w, &webErrors.InvalidTimeFormat)
				return
			}

			unique := false
			if strings.Compare(req.Form.Get("unique"), "true") == 0 {
				unique = true
			}

			// Construct Params object
			params := database.Params{
				DBName:    dbName,
				TimeRange: timeRange,
				Unique:    unique,
				URL:       req.URL,
			}

			analytics, err := driver.Count(params)
			if err != nil {
				renderError(w, normalizeDriverError(err))
				return
			}

			// Return query result
			render(w, analytics, nil)
		})

	/////
	// Query a DB by property
	/////
	r.Path("/{dbName}/{property}").
		Methods("GET").
		HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// Map allowed requests w/ columns names in DB schema
			allowedProperties := map[string]string{
				"countries": "countryCode",
				"platforms": "platform",
				"domains":   "refererDomain",
				"events":    "event",
			}
			// Get params from URL
			vars := mux.Vars(req)
			dbName := vars["dbName"]
			property := vars["property"]

			// Check that property is allowed to be queried
			property, ok := allowedProperties[property]
			if !ok {
				renderError(w, &webErrors.InvalidProperty)
				return
			}

			// Parse request query
			if err := req.ParseForm(); err != nil {
				renderError(w, err)
				return
			}

			// Get timeRange if provided
			startTime := req.Form.Get("start")
			endTime := req.Form.Get("end")

			timeRange, err := newTimeRange(startTime, endTime)
			if err != nil {
				renderError(w, &webErrors.InvalidTimeFormat)
				return
			}

			unique := false
			if strings.Compare(req.Form.Get("unique"), "true") == 0 {
				unique = true
			}

			// Construct Params object
			params := database.Params{
				DBName:    dbName,
				Property:  property,
				TimeRange: timeRange,
				Unique:    unique,
				URL:       req.URL,
			}

			analytics, err := driver.GroupBy(params)
			if err != nil {
				renderError(w, normalizeDriverError(err))
				return
			}

			// Return query result
			render(w, analytics, nil)
		})

	/////
	// Full query a DB
	/////
	r.Path("/{dbName}").
		Methods("GET").
		HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// Get dbName from URL
			vars := mux.Vars(req)
			dbName := vars["dbName"]

			// Parse request query
			if err := req.ParseForm(); err != nil {
				renderError(w, err)
				return
			}

			// Get timeRange if provided
			startTime := req.Form.Get("start")
			endTime := req.Form.Get("end")

			timeRange, err := newTimeRange(startTime, endTime)
			if err != nil {
				renderError(w, &webErrors.InvalidTimeFormat)
				return
			}

			// Construct Params object
			params := database.Params{
				DBName:    dbName,
				TimeRange: timeRange,
				URL:       req.URL,
			}

			analytics, err := driver.Query(params)
			if err != nil {
				renderError(w, normalizeDriverError(err))
				return
			}

			render(w, analytics, nil)
		})

	/////
	// Push a list of analytics to different DBs
	/////
	r.Path("/bulk").
		Methods("POST").
		HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// Parse JSON POST data
			postList := PostAnalytics{}
			jsonDecoder := json.NewDecoder(req.Body)
			err := jsonDecoder.Decode(&postList)

			// Invalid JSON
			if err != nil {
				log.Error("Invalid JSON format")
				log.Error("%v", err)
				renderError(w, &webErrors.InvalidJSON)
				return
			}

			// Group analytics by website
			analytics := make(map[string][]database.Analytic)

			for _, postData := range postList.List {
				// Skip analytic if website parameter missing
				if postData.Website == "" {
					log.Error("Skipping analytic: website parameter missing on POST data")
					continue
				}

				// Parse data
				analytic := parseAnalytic(postData, geolite2, log)

				// Add to list
				analytics[postData.Website] = append(analytics[postData.Website], analytic)
			}

			// Insert
			err = driver.BulkInsert(analytics)
			if err != nil {
				renderError(w, normalizeDriverError(err))
				return
			}

			log.Info("Successfully inserted analytics: %#v", analytics)

			render(w, nil, nil)
		})

	/////
	// Push analytics to a specific DB
	/////
	r.Path("/{dbName}").
		Methods("POST").
		HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// Get dbName from URL
			vars := mux.Vars(req)
			dbName := vars["dbName"]

			// Parse JSON POST data
			postData := PostData{}
			jsonDecoder := json.NewDecoder(req.Body)
			err := jsonDecoder.Decode(&postData)

			// Invalid JSON
			if err != nil {
				log.Error("Invalid JSON format")
				log.Error("%v", err)
				renderError(w, &webErrors.InvalidJSON)
				return
			}

			// Create Analytic to inject in DB
			analytic := database.Analytic{
				Time:  time.Now(),
				Event: postData.Event,
				Path:  postData.Path,
				Ip:    postData.Ip,
			}

			// Set time from POST data if passed
			if len(postData.Time) > 0 {
				analytic.Time, _ = time.Parse(time.RFC3339, postData.Time)
			}
			analytic.Time = analytic.Time.UTC()

			// Set analytic referer domain
			refererHeader := getReferrer(postData.Headers)
			if referrerURL, err := url.ParseRequestURI(refererHeader); err == nil {
				analytic.RefererDomain = referrerURL.Host
			}

			// Extract analytic platform from userAgent
			userAgent := getUserAgent(postData.Headers)
			analytic.Platform = utils.Platform(userAgent)

			// Get countryCode from GeoIp
			analytic.CountryCode, err = geoip.GeoIpLookup(geolite2, postData.Ip)

			// Construct Params object
			params := database.Params{
				DBName: dbName,
			}

			err = driver.Insert(params, analytic)
			if err != nil {
				renderError(w, normalizeDriverError(err))
				return
			}

			log.Info("Successfully inserted analytic: %#v", analytic)

			render(w, nil, nil)
		})

	/////
	// Push a list of analytics to a specific DB
	/////
	r.Path("/{dbName}/bulk").
		Methods("POST").
		HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// Get dbName from URL
			vars := mux.Vars(req)
			dbName := vars["dbName"]

			// Parse JSON POST data
			postList := PostAnalytics{}
			jsonDecoder := json.NewDecoder(req.Body)
			err := jsonDecoder.Decode(&postList)

			// Invalid JSON
			if err != nil {
				log.Error("Invalid JSON format:")
				log.Error("%v", err)
				renderError(w, &webErrors.InvalidJSON)
				return
			}

			// Create map of analytics for Bulk insert
			analytics := make(map[string][]database.Analytic, 1)

			for _, postData := range postList.List {
				// Parse data
				analytic := parseAnalytic(postData, geolite2, log)

				// Add analytic to list
				analytics[dbName] = append(analytics[dbName], analytic)
			}

			// Insert
			err = driver.BulkInsert(analytics)
			if err != nil {
				renderError(w, normalizeDriverError(err))
				return
			}

			log.Info("Successfully inserted analytics: %#v", analytics)

			render(w, nil, nil)
		})

	/////
	// Delete a DB
	/////
	r.Path("/{dbName}").
		Methods("DELETE").
		HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

			// Get dbName from URL
			vars := mux.Vars(req)
			dbName := vars["dbName"]

			// Construct Params object
			params := database.Params{
				DBName: dbName,
			}

			err := driver.Delete(params)
			if err != nil {
				renderError(w, normalizeDriverError(err))
				return
			}

			render(w, nil, nil)
		})

	return r, nil
}
Beispiel #12
0
package heartbeats

import (
	"github.com/azer/logger"
	"github.com/hackliff/serf/command/agent"
)

var log = logger.New("sentinel.plugins.heartbeats")

// Plugin is the interface to implement report triggers. Those run in
// their own gorountines, registering sensor plugins and  waiting for signals
// to execute them.
type Plugin interface {
	Schedule(string, agent.EventHandler)
	Stop()
}

// NOTE map[string]interface{} for better type support after casting ?
type Creator func(map[string]string) (Plugin, error)

var Plugins = map[string]Creator{}

func Add(name string, creator Creator) {
	Plugins[name] = creator
}
Beispiel #13
0
Datei: db.go Projekt: rjp/crud
package crud

import (
	stdsql "database/sql"
	"github.com/azer/crud/sql"
	"github.com/azer/logger"
)

var log = logger.New("crud")

type ExecFn func(string, ...interface{}) (stdsql.Result, error)
type QueryFn func(string, ...interface{}) (*stdsql.Rows, error)

type DB struct {
	Client *stdsql.DB
	Driver string
	URL    string
}

func (db *DB) Ping() error {
	return db.Client.Ping()
}

func (db *DB) Exec(sql string, params ...interface{}) (stdsql.Result, error) {
	log.Info(sql)
	return db.Client.Exec(sql, params...)
}

func (db *DB) Query(sql string, params ...interface{}) (*stdsql.Rows, error) {
	log.Info(sql)
	return db.Client.Query(sql, params...)
Beispiel #14
0
package actuators

import (
	"github.com/azer/logger"
	"github.com/hackliff/serf/serf"

	"github.com/hackliff/sentinel/plugins/adapters"
)

var log = logger.New("sentinel.plugins.actuators")

// TODO make it full compatible with telegraf plugins
// NOTE inspired by telegraf project plugin style
type Plugin interface {
	// NOTE String() method ?
	Description() string
	SampleConfig() string
	// NOTE a serf agent as argument for information and event triggers ?
	Gather(serf.Member, serf.Event) error
}

// NOTE map[string]interface{} for better type support after casting ?
type Creator func(adapters.Plugin, map[string]string) (Plugin, error)

var Plugins = map[string]Creator{}

func Add(name string, creator Creator) {
	Plugins[name] = creator
}
Beispiel #15
0
func main() {
	// App meta-data
	app := cli.NewApp()
	app.Version = "2.0.3"
	app.Name = "µAnalytics"
	app.Author = "Johan Preynat"
	app.Email = "*****@*****.**"
	app.Usage = "Fast sharded analytics database"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "user, u",
			Value:  "",
			Usage:  "Username",
			EnvVar: "MA_USER",
		},
		cli.StringFlag{
			Name:   "password, w",
			Value:  "",
			Usage:  "Password",
			EnvVar: "MA_PASSWORD",
		},
		cli.StringFlag{
			Name:   "port, p",
			Value:  "7070",
			Usage:  "Port to listen on",
			EnvVar: "PORT",
		},
		cli.StringFlag{
			Name:   "root, r",
			Value:  "./dbs/",
			Usage:  "Database directory",
			EnvVar: "MA_ROOT",
		},
		cli.IntFlag{
			Name:   "connections, c",
			Value:  1000,
			Usage:  "Max number of alive DB connections",
			EnvVar: "MA_POOL_SIZE",
		},
		cli.IntFlag{
			Name:   "idle-timeout, i",
			Value:  60,
			Usage:  "Idle timeout for DB connections in seconds",
			EnvVar: "MA_POOL_TIMEOUT",
		},
		cli.StringFlag{
			Name:   "cache-directory, d",
			Value:  ".diskache",
			Usage:  "Cache directory",
			EnvVar: "MA_CACHE_DIR",
		},
	}

	var log = logger.New("[Main]")

	// Main app code
	app.Action = func(ctx *cli.Context) {
		cacheDir := path.Clean(ctx.String("cache-directory"))
		cacheDir = path.Join(cacheDir, strings.Split(app.Version, ".")[0])

		// Set driver options
		driverOpts := database.DriverOpts{
			Directory:      path.Clean(ctx.String("root")),
			CacheDirectory: cacheDir,
			MaxDBs:         ctx.Int("connections"),
			IdleTimeout:    ctx.Int("idle-timeout"),
			ClosingChannel: make(chan bool, 1),
		}

		// Create Analytics directory if inexistant
		dirExists, err := utils.PathExists(driverOpts.Directory)
		if err != nil {
			log.Error("Analytics directory path error [%v]", err)
			os.Exit(1)
		}
		if !dirExists {
			log.Info("Analytics directory doesn't exist: %s", driverOpts.Directory)
			log.Info("Creating Analytics directory...")
			os.MkdirAll(driverOpts.Directory, os.ModePerm)
		} else {
			log.Info("Working with existing Analytics directory: %s", driverOpts.Directory)
		}

		// Initiate Geolite2 DB Reader
		geolite2, err := geoip.GetGeoLite2Reader()
		if err != nil {
			log.Info("Error [%v] obtaining a geolite2Reader", err)
			log.Info("Running without Geolite2")
		}

		// Handle exit by softly closing DB connections
		c := make(chan os.Signal, 1)
		signal.Notify(c, os.Interrupt)
		signal.Notify(c, syscall.SIGTERM)
		go func() {
			<-c
			log.Info("Closing database connections...")
			driverOpts.ClosingChannel <- true
			<-driverOpts.ClosingChannel
			log.Info("Connections closed successfully")
			log.Info("Closing Geolite2 connection...")
			geolite2.Close()
			log.Info("Geolite2 is now closed")
			log.Info("Goodbye!")
			os.Exit(1)
		}()

		// Authentication
		auth := &web.BasicAuth{
			Name: ctx.String("user"),
			Pass: ctx.String("password"),
		}

		// Setup server
		opts := ServerOpts{
			Port:           normalizePort(ctx.String("port")),
			Version:        app.Version,
			DriverOpts:     driverOpts,
			Geolite2Reader: geolite2,
			Auth:           auth,
		}

		log.Info("Launching server with: %#v", opts)

		server, err := NewServer(opts)
		if err != nil {
			log.Error("ServerSetup error [%v]", err)
			os.Exit(1)
		}

		// Run server
		if err := gracehttp.Serve(server); err != nil {
			log.Error("ListenAndServe error [%v]", err)
			os.Exit(1)
		}
	}

	// Parse CLI args and run
	app.Run(os.Args)
}
Beispiel #16
0
// adapters package stores various plugins aimed at bot communication with an
// operator.
package adapters

import "github.com/azer/logger"

var log = logger.New("sentinel.plugins.adapters")

type Envelope struct {
	Title     string
	Recipient string
}

type Plugin interface {
	Send(Envelope, string) error
}

// NOTE map[string]interface{} for better type support after casting ?
type Creator func(map[string]string) (Plugin, error)

var Plugins = map[string]Creator{}

func Add(name string, creator Creator) {
	Plugins[name] = creator
}
Beispiel #17
0
package coll

import (
	"github.com/azer/logger"
	"github.com/syndtr/goleveldb/leveldb"
)

var log = logger.New("level-collection")

var (
	Client *leveldb.DB
)

func Open(path string) error {
	log.Info("Opening %s", path)

	conn, err := leveldb.OpenFile(path, nil)
	Client = conn

	if err != nil {
		return err
	}

	return nil
}

func Set(key, value []byte) error {
	log.Info("Set", logger.Attrs{
		"key": key,
	})
Beispiel #18
0
package main

import (
	"errors"
	"github.com/azer/logger"
	"time"
)

var log = logger.New("e-mail")

func main() {
	log.Info("Sending an e-mail", logger.Attrs{
		"from": "*****@*****.**",
		"to":   "*****@*****.**",
	})

	err := errors.New("Too busy")

	log.Error("Failed to send e-mail. Error: %s", err, logger.Attrs{
		"from": "*****@*****.**",
		"to":   "*****@*****.**",
	})

	timer := log.Timer()
	time.Sleep(time.Millisecond * 500)
	timer.End("Created a new %s image", "bike", logger.Attrs{
		"id":    123456,
		"model": "bmx",
		"frame": "purple",
		"year":  2014,
	})
Beispiel #19
0
package config

import (
	"github.com/azer/logger"
	"github.com/hackliff/serf/command/agent"
	"github.com/olebedev/config"
)

var log = logger.New("sentinel.config")

const DEFAULT_NAME string = "sentinel"

type PluginConfig struct {
	Plugin string
	Opts   map[string]string
}

type HeartbeatConfig struct {
	// NOTE create Heartbeat plugin here ?
	Plugin  string
	Opts    map[string]string
	Filters []agent.EventFilter
}

type Config struct {
	Name      string
	Actuator  *PluginConfig
	Heartbeat *HeartbeatConfig
	Adapter   *PluginConfig
}
Beispiel #20
0
package main

import (
	"errors"
	"github.com/azer/logger"
	"time"
)

var app = logger.New("app")
var images = logger.New("images")
var socket = logger.New("websocket")
var users = logger.New("users")
var db = logger.New("database")

func main() {
	app.Info("Starting at %d", 9088)
	db.Info("Connecting to mysql://azer@localhost:9900/foobar")

	images.Info("Requesting an image at foo/bar.jpg")
	timer := images.Timer()
	time.Sleep(time.Millisecond * 250)
	timer.End("Fetched foo/bar.jpg")

	db.Error("Fatal connection error.")

	users.Info("%s just logged  from %s", "John", "Istanbul")

	socket.Info("Connecting...")

	err := errors.New("Unable to connect.")
	socket.Error("%v", err)
Beispiel #21
0
package main

import (
	"github.com/azer/logger"
	"time"
)

var log = logger.New("app")

func main() {
	log.Info("Starting at %d", 9088)

	log.Info("Requesting an image at foo/bar.jpg")
	timer := log.Timer()
	time.Sleep(time.Millisecond * 250)
	timer.End("Fetched foo/bar.jpg")

	log.Error("Failed to start, shutting down...")
}