Skip to content

Commit

Permalink
Merge pull request #369 from nicolai86/feature/introduce-logging-inte…
Browse files Browse the repository at this point in the history
…rface

Introduce Logger
  • Loading branch information
moul authored Jun 21, 2016
2 parents e6795dc + 9308618 commit a8dd2cf
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 108 deletions.
151 changes: 46 additions & 105 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ import (
"text/tabwriter"
"text/template"
"time"

log "github.com/Sirupsen/logrus"
"github.com/moul/anonuuid"
"github.com/moul/http2curl"
)

// Default values
Expand Down Expand Up @@ -68,9 +64,11 @@ type ScalewayAPI struct {
// Cache is used to quickly resolve identifiers from names
Cache *ScalewayCache

client *http.Client
anonuuid anonuuid.AnonUUID
verbose bool
client *http.Client
verbose bool

//
Logger
}

// ScalewayAPIError represents a Scaleway API Error
Expand All @@ -93,30 +91,31 @@ type ScalewayAPIError struct {

// Error returns a string representing the error
func (e ScalewayAPIError) Error() string {
if e.Message != "" {
return e.Message
}
if e.APIMessage != "" {
return e.APIMessage
}
if e.StatusCode != 0 {
return fmt.Sprintf("Invalid return code, got %d", e.StatusCode)
}
panic(e)
}

// Debug create a debug log entry with HTTP error informations
func (e ScalewayAPIError) Debug() {
log.WithFields(log.Fields{
var b bytes.Buffer
for k, v := range map[string]interface{}{
"StatusCode": e.StatusCode,
"Type": e.Type,
"Message": e.Message,
}).Debug(e.APIMessage)
"APIMessage": e.APIMessage,
} {
fmt.Fprintf(&b, " %-30s %s", fmt.Sprintf("%s: ", k), v)
}
return b.String()
}

// error.Fields handling
for k, v := range e.Fields {
log.Debugf(" %-30s %s", fmt.Sprintf("%s: ", k), v)
// HideAPICredentials removes API credentials from a string
func (s *ScalewayAPI) HideAPICredentials(input string) string {
output := input
if s.Token != "" {
output = strings.Replace(output, s.Token, "00000000-0000-4000-8000-000000000000", -1)
}
if s.Organization != "" {
output = strings.Replace(output, s.Organization, "00000000-0000-5000-9000-000000000000", -1)
}
if s.password != "" {
output = strings.Replace(output, s.password, "XX-XX-XX-XX", -1)
}
return output
}

// ScalewayIPAddress represents a Scaleway IP address
Expand Down Expand Up @@ -832,7 +831,7 @@ type MarketImages struct {
}

// NewScalewayAPI creates a ready-to-use ScalewayAPI client
func NewScalewayAPI(organization, token, userAgent string) (*ScalewayAPI, error) {
func NewScalewayAPI(organization, token, userAgent string, options ...func(*ScalewayAPI)) (*ScalewayAPI, error) {
cache, err := NewScalewayCache()
if err != nil {
return nil, err
Expand All @@ -842,13 +841,16 @@ func NewScalewayAPI(organization, token, userAgent string) (*ScalewayAPI, error)
Organization: organization,
Token: token,
Cache: cache,
Logger: NewDefaultLogger(),
verbose: os.Getenv("SCW_VERBOSE_API") != "",
password: "",
userAgent: userAgent,

// internal
anonuuid: *anonuuid.New(),
client: &http.Client{},
client: &http.Client{},
}
for _, option := range options {
option(s)
}

if os.Getenv("SCW_TLSVERIFY") == "0" {
Expand Down Expand Up @@ -882,15 +884,8 @@ func (s *ScalewayAPI) GetResponse(apiURL, resource string) (*http.Response, erro
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", s.userAgent)

curl, err := http2curl.GetCurlCommand(req)
if err != nil {
return nil, err
}
if os.Getenv("SCW_SENSITIVE") != "1" {
log.Debug(s.HideAPICredentials(curl.String()))
} else {
log.Debug(curl.String())
}
s.LogHTTP(req)

return s.client.Do(req)
}

Expand All @@ -911,15 +906,7 @@ func (s *ScalewayAPI) PostResponse(apiURL, resource string, data interface{}) (*
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", s.userAgent)

curl, err := http2curl.GetCurlCommand(req)
if err != nil {
return nil, err
}
if os.Getenv("SCW_SENSITIVE") != "1" {
log.Debug(s.HideAPICredentials(curl.String()))
} else {
log.Debug(curl.String())
}
s.LogHTTP(req)

return s.client.Do(req)
}
Expand All @@ -941,15 +928,7 @@ func (s *ScalewayAPI) PatchResponse(apiURL, resource string, data interface{}) (
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", s.userAgent)

curl, err := http2curl.GetCurlCommand(req)
if err != nil {
return nil, err
}
if os.Getenv("SCW_SENSITIVE") != "1" {
log.Debug(s.HideAPICredentials(curl.String()))
} else {
log.Debug(curl.String())
}
s.LogHTTP(req)

return s.client.Do(req)
}
Expand All @@ -971,15 +950,7 @@ func (s *ScalewayAPI) PutResponse(apiURL, resource string, data interface{}) (*h
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", s.userAgent)

curl, err := http2curl.GetCurlCommand(req)
if err != nil {
return nil, err
}
if os.Getenv("SCW_SENSITIVE") != "1" {
log.Debug(s.HideAPICredentials(curl.String()))
} else {
log.Debug(curl.String())
}
s.LogHTTP(req)

return s.client.Do(req)
}
Expand All @@ -996,15 +967,7 @@ func (s *ScalewayAPI) DeleteResponse(apiURL, resource string) (*http.Response, e
req.Header.Set("Content-Type", "application/json")
req.Header.Set("User-Agent", s.userAgent)

curl, err := http2curl.GetCurlCommand(req)
if err != nil {
return nil, err
}
if os.Getenv("SCW_SENSITIVE") != "1" {
log.Debug(s.HideAPICredentials(curl.String()))
} else {
log.Debug(curl.String())
}
s.LogHTTP(req)

return s.client.Do(req)
}
Expand All @@ -1027,22 +990,21 @@ func (s *ScalewayAPI) handleHTTPError(goodStatusCode []int, resp *http.Response)
if !good {
var scwError ScalewayAPIError

err := json.Unmarshal(body, &scwError)
if err != nil {
if err := json.Unmarshal(body, &scwError); err != nil {
return nil, err
}
scwError.StatusCode = resp.StatusCode
scwError.Debug()
s.Debugf("%s", scwError.Error())
return nil, scwError
}
if s.verbose {
var js bytes.Buffer

err = json.Indent(&js, body, "", " ")
if err != nil {
log.Debug(string(body))
s.Debugf("%s", string(body))
} else {
log.Debug(js.String())
s.Debugf("%s", js.String())
}
}
return body, nil
Expand All @@ -1069,12 +1031,11 @@ func (s *ScalewayAPI) GetServers(all bool, limit int) (*[]ScalewayServer, error)
return nil, err
}

var servers ScalewayServers

body, err := s.handleHTTPError([]int{200}, resp)
if err != nil {
return nil, err
}
var servers ScalewayServers
if err = json.Unmarshal(body, &servers); err != nil {
return nil, err
}
Expand Down Expand Up @@ -1708,8 +1669,6 @@ func (s *ScalewayUserdata) String() string {

// GetUserdata gets a specific userdata for a server
func (s *ScalewayAPI) GetUserdata(serverID, key string, metadata bool) (*ScalewayUserdata, error) {
var data ScalewayUserdata
var err error
var url, endpoint string

endpoint = ComputeAPI
Expand All @@ -1720,6 +1679,7 @@ func (s *ScalewayAPI) GetUserdata(serverID, key string, metadata bool) (*Scalewa
url = fmt.Sprintf("servers/%s/user_data/%s", serverID, key)
}

var err error
resp, err := s.GetResponse(endpoint, url)
if resp != nil {
defer resp.Body.Close()
Expand All @@ -1731,6 +1691,7 @@ func (s *ScalewayAPI) GetUserdata(serverID, key string, metadata bool) (*Scalewa
if resp.StatusCode != 200 {
return nil, fmt.Errorf("no such user_data %q (%d)", key, resp.StatusCode)
}
var data ScalewayUserdata
data, err = ioutil.ReadAll(resp.Body)
return &data, err
}
Expand Down Expand Up @@ -1760,12 +1721,7 @@ func (s *ScalewayAPI) PatchUserdata(serverID, key string, value []byte, metadata
req.Header.Set("Content-Type", "text/plain")
req.Header.Set("User-Agent", s.userAgent)

curl, err := http2curl.GetCurlCommand(req)
if os.Getenv("SCW_SENSITIVE") != "1" {
log.Debug(s.HideAPICredentials(curl.String()))
} else {
log.Debug(curl.String())
}
s.LogHTTP(req)

resp, err := s.client.Do(req)
if resp != nil {
Expand Down Expand Up @@ -2420,7 +2376,7 @@ func (s *ScalewayAPI) GetQuotas() (*ScalewayGetQuotas, error) {
// GetBootscriptID returns exactly one bootscript matching
func (s *ScalewayAPI) GetBootscriptID(needle, arch string) (string, error) {
// Parses optional type prefix, i.e: "bootscript:name" -> "name"
if anonuuid.IsUUID(needle) == nil {
if len(strings.Split(needle, ":")) == 1 {
return needle, nil
}

Expand All @@ -2440,21 +2396,6 @@ func (s *ScalewayAPI) GetBootscriptID(needle, arch string) (string, error) {
return "", showResolverResults(needle, bootscripts)
}

// HideAPICredentials removes API credentials from a string
func (s *ScalewayAPI) HideAPICredentials(input string) string {
output := input
if s.Token != "" {
output = strings.Replace(output, s.Token, s.anonuuid.FakeUUID(s.Token), -1)
}
if s.Organization != "" {
output = strings.Replace(output, s.Organization, s.anonuuid.FakeUUID(s.Organization), -1)
}
if s.password != "" {
output = strings.Replace(output, s.password, "XX-XX-XX-XX", -1)
}
return output
}

func rootNetDial(network, addr string) (net.Conn, error) {
dialer := net.Dialer{
Timeout: 10 * time.Second,
Expand Down
2 changes: 1 addition & 1 deletion pkg/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ func TestNewScalewayAPI(t *testing.T) {
So(api.Organization, ShouldEqual, "my-organization")
So(api.Cache, ShouldNotBeNil)
So(api.client, ShouldNotBeNil)
So(api.anonuuid, ShouldNotBeNil)
So(api.Logger, ShouldNotBeNil)
})
}
49 changes: 49 additions & 0 deletions pkg/api/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (C) 2015 Scaleway. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE.md file.

package api

import (
"fmt"
"log"
"net/http"
"os"
)

// Logger handles logging concerns for the Scaleway API SDK
type Logger interface {
LogHTTP(*http.Request)
Fatalf(format string, v ...interface{})
Debugf(format string, v ...interface{})
Infof(format string, v ...interface{})
Warnf(format string, v ...interface{})
}

// NewDefaultLogger returns a logger which is configured for stdout
func NewDefaultLogger() Logger {
return &defaultLogger{
Logger: log.New(os.Stdout, "", log.LstdFlags),
}
}

type defaultLogger struct {
*log.Logger
}

func (l *defaultLogger) LogHTTP(r *http.Request) {
l.Printf("%s %s\n", r.Method, r.URL.RawPath)
}
func (l *defaultLogger) Fatalf(format string, v ...interface{}) {
l.Printf("[FATAL] %s\n", fmt.Sprintf(format, v))
os.Exit(1)
}
func (l *defaultLogger) Debugf(format string, v ...interface{}) {
l.Printf("[DEBUG] %s\n", fmt.Sprintf(format, v))
}
func (l *defaultLogger) Infof(format string, v ...interface{}) {
l.Printf("[INFO ] %s\n", fmt.Sprintf(format, v))
}
func (l *defaultLogger) Warnf(format string, v ...interface{}) {
l.Printf("[WARN ] %s\n", fmt.Sprintf(format, v))
}
Loading

0 comments on commit a8dd2cf

Please sign in to comment.