Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Logger #369

Merged
merged 3 commits into from
Jun 21, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported method ScalewayAPI.HideAPICredentials should have comment or be unexported

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.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package comment should be of the form "Package api ..."

// 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 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported type Logger should have comment or be unexported

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 {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exported function NewDefaultLogger should have comment or be unexported

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