Skip to content

Commit

Permalink
Merge pull request #42 from lixiaojun629/develop
Browse files Browse the repository at this point in the history
add optional upload log
  • Loading branch information
lixiaojun629 authored Sep 27, 2019
2 parents 11da614 + c8ff0fd commit 89f8009
Show file tree
Hide file tree
Showing 36 changed files with 1,093 additions and 4,343 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export VERSION=0.1.24
export VERSION=0.1.25

.PHONY : install
install:
Expand Down
118 changes: 83 additions & 35 deletions base/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const DefaultBaseURL = "https://api.ucloud.cn/"
const DefaultProfile = "default"

//Version 版本号
const Version = "0.1.24"
const Version = "0.1.25"

//ConfigIns 配置实例, 程序加载时生成
var ConfigIns = &AggConfig{
Expand Down Expand Up @@ -79,14 +79,15 @@ type GlobalFlag struct {

//CLIConfig cli_config element
type CLIConfig struct {
ProjectID string `json:"project_id"`
Region string `json:"region"`
Zone string `json:"zone"`
BaseURL string `json:"base_url"`
Timeout int `json:"timeout_sec"`
Profile string `json:"profile"`
Active bool `json:"active"` //是否生效
MaxRetryTimes *int `json:"max_retry_times"`
ProjectID string `json:"project_id"`
Region string `json:"region"`
Zone string `json:"zone"`
BaseURL string `json:"base_url"`
Timeout int `json:"timeout_sec"`
Profile string `json:"profile"`
Active bool `json:"active"` //是否生效
MaxRetryTimes *int `json:"max_retry_times"`
AgreeUploadLog bool `json:"agree_upload_log"`
}

//CredentialConfig credential element
Expand All @@ -98,16 +99,17 @@ type CredentialConfig struct {

//AggConfig 聚合配置 config+credential
type AggConfig struct {
Profile string `json:"profile"`
Active bool `json:"active"`
ProjectID string `json:"project_id"`
Region string `json:"region"`
Zone string `json:"zone"`
BaseURL string `json:"base_url"`
Timeout int `json:"timeout_sec"`
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key"`
MaxRetryTimes *int `json:"max_retry_times"`
Profile string `json:"profile"`
Active bool `json:"active"`
ProjectID string `json:"project_id"`
Region string `json:"region"`
Zone string `json:"zone"`
BaseURL string `json:"base_url"`
Timeout int `json:"timeout_sec"`
PublicKey string `json:"public_key"`
PrivateKey string `json:"private_key"`
MaxRetryTimes *int `json:"max_retry_times"`
AgreeUploadLog bool `json:"agree_upload_log"`
}

//ConfigPublicKey 输入公钥
Expand Down Expand Up @@ -136,6 +138,22 @@ func (p *AggConfig) ConfigPrivateKey() error {
return nil
}

//ConfigUploadLog agree upload log or not
func (p *AggConfig) ConfigUploadLog() error {
var input string
fmt.Print("Do you agree to upload log in local file ~/.ucloud/cli.log to help ucloud-cli get better(yes/no):")
_, err := fmt.Scanf("%s\n", &input)
if err != nil {
HandleError(err)
return err
}

if str := strings.ToLower(input); str == "y" || str == "ye" || str == "yes" {
p.AgreeUploadLog = true
}
return nil
}

//GetClientConfig 用来生成sdkClient
func (p *AggConfig) GetClientConfig(isDebug bool) *sdk.Config {
clientConfig := &sdk.Config{
Expand Down Expand Up @@ -169,6 +187,7 @@ func (p *AggConfig) copyToCLIConfig(target *CLIConfig) {
target.Zone = p.Zone
target.Active = p.Active
target.MaxRetryTimes = p.MaxRetryTimes
target.AgreeUploadLog = p.AgreeUploadLog
}

func (p *AggConfig) copyToCredentialConfig(target *CredentialConfig) {
Expand Down Expand Up @@ -217,7 +236,7 @@ func NewAggConfigManager(cfgFile, credFile *os.File) (*AggConfigManager, error)
//Append config to list, override if already exist the same profile
func (p *AggConfigManager) Append(config *AggConfig) error {
if _, ok := p.configs[config.Profile]; ok {
return fmt.Errorf("profile %s exists already", config.Profile)
return fmt.Errorf("profile [%s] exists already", config.Profile)
}

if config.Active && config.Profile != p.activeProfile {
Expand Down Expand Up @@ -279,16 +298,17 @@ func (p *AggConfigManager) Load() error {
}

p.configs[profile] = &AggConfig{
PrivateKey: cred.PrivateKey,
PublicKey: cred.PublicKey,
Profile: config.Profile,
ProjectID: config.ProjectID,
Region: config.Region,
Zone: config.Zone,
BaseURL: config.BaseURL,
Timeout: config.Timeout,
Active: config.Active,
MaxRetryTimes: config.MaxRetryTimes,
PrivateKey: cred.PrivateKey,
PublicKey: cred.PublicKey,
Profile: config.Profile,
ProjectID: config.ProjectID,
Region: config.Region,
Zone: config.Zone,
BaseURL: config.BaseURL,
Timeout: config.Timeout,
Active: config.Active,
MaxRetryTimes: config.MaxRetryTimes,
AgreeUploadLog: config.AgreeUploadLog,
}
}

Expand Down Expand Up @@ -465,6 +485,37 @@ func LoadUserInfo() (*uaccount.UserInfo, error) {
return &user, nil
}

//GetUserInfo from local file and remote api
func GetUserInfo() (*uaccount.UserInfo, error) {
user, err := LoadUserInfo()
if err == nil {
return user, nil
}

req := BizClient.NewGetUserInfoRequest()
resp, err := BizClient.GetUserInfo(req)

if err != nil {
return nil, err
}

if len(resp.DataSet) == 1 {
user = &resp.DataSet[0]
bytes, err := json.Marshal(user)
if err != nil {
return nil, err
}
fileFullPath := GetConfigDir() + "/user.json"
err = ioutil.WriteFile(fileFullPath, bytes, 0600)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("GetUserInfo DataSet length: %d", len(resp.DataSet))
}
return user, nil
}

//OldConfig 0.1.7以及之前版本的配置struct
type OldConfig struct {
PublicKey string `json:"public_key"`
Expand Down Expand Up @@ -585,18 +636,15 @@ func InitConfig() {
HandleError(err)
}
} else {
var ok bool
ins, ok = AggConfigListIns.GetAggConfigByProfile(Global.Profile)
if !ok {
LogError("Profile %s does not exist", Global.Profile)
}
ins, _ = AggConfigListIns.GetAggConfigByProfile(Global.Profile)
}

if ins != nil {
ConfigIns = ins
} else {
ins = ConfigIns
}
logCmd()

bc, err := GetBizClient(ConfigIns)
if err != nil {
Expand Down
117 changes: 114 additions & 3 deletions base/log.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
package base

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
"runtime"
"strings"
"sync"
"time"

uuid "github.com/satori/go.uuid"
log "github.com/sirupsen/logrus"

"github.com/ucloud/ucloud-sdk-go/ucloud/request"
"github.com/ucloud/ucloud-sdk-go/ucloud/version"
)

const DefaultDasURL = "https://das-rpt.ucloud.cn/log"

//Logger 日志
var logger *log.Logger
var mu sync.Mutex
var out = Cxt.GetWriter()
var tracer = Tracer{DefaultDasURL}

func initConfigDir() {
if _, err := os.Stat(GetLogFileDir()); os.IsNotExist(err) {
Expand All @@ -23,6 +34,7 @@ func initConfigDir() {
}
}
}

func initLog() error {
initConfigDir()
file, err := os.OpenFile(GetLogFilePath(), os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
Expand All @@ -33,10 +45,21 @@ func initLog() error {
logger.SetNoLock()
logger.AddHook(NewLogRotateHook(file))
logger.SetOutput(file)
LogInfo(fmt.Sprintf("command: %s", strings.Join(os.Args, " ")))

return nil
}

func logCmd() {
args := make([]string, len(os.Args))
copy(args, os.Args)
for idx, arg := range args {
if strings.Contains(arg, "password") && idx <= len(args)-2 {
args[idx+1] = strings.Repeat("*", 8)
}
}
LogInfo(fmt.Sprintf("command: %s", strings.Join(args, " ")))
}

//GetLogger return point of logger
func GetLogger() *log.Logger {
return logger
Expand All @@ -54,21 +77,47 @@ func GetLogFilePath() string {

//LogInfo 记录日志
func LogInfo(logs ...string) {
_, ok := os.LookupEnv("COMP_LINE")
if ok {
return
}
mu.Lock()
defer mu.Unlock()
goID := curGoroutineID()
for _, line := range logs {
logger.WithField("GoroutineID", goID).Info(line)
logger.WithField("goroutine_id", goID).Info(line)
}
if ConfigIns.AgreeUploadLog {
UploadLogs(logs, "info", goID)
}
}

//LogError 记录日志
func LogError(logs ...string) {
_, ok := os.LookupEnv("COMP_LINE")
if ok {
return
}
mu.Lock()
defer mu.Unlock()
goID := curGoroutineID()
for _, line := range logs {
logger.WithField("GoroutineID", goID).Error(line)
logger.WithField("goroutine_id", goID).Error(line)
fmt.Fprintln(out, line)
}
if ConfigIns.AgreeUploadLog {
UploadLogs(logs, "error", goID)
}
}

//UploadLogs send logs to das server
func UploadLogs(logs []string, level string, goID int64) {
var lines []string
for _, log := range logs {
line := fmt.Sprintf("time=%s level=%s goroutine_id=%d msg=%s", time.Now().Format(time.RFC3339Nano), level, goID, log)
lines = append(lines, line)
}
tracer.Send(lines)
}

//LogRotateHook rotate log file
Expand Down Expand Up @@ -143,3 +192,65 @@ func ToQueryMap(req request.Common) map[string]string {
delete(reqMap, "Password")
return reqMap
}

//Tracer upload log to server if allowed
type Tracer struct {
DasUrl string
}

func (t Tracer) wrapLogs(log []string) ([]byte, error) {
dataSet := make([]map[string]interface{}, 0)
dataItem := map[string]interface{}{
"level": "info",
"topic": "api",
"log": log,
}
dataSet = append(dataSet, dataItem)
reqUUID := uuid.NewV4()
sessionID := uuid.NewV4()
user, err := GetUserInfo()
if err != nil {
return nil, err
}
payload := map[string]interface{}{
"aid": "iywtleaa",
"uuid": reqUUID,
"sid": sessionID,
"ds": dataSet,
"cs": map[string]interface{}{
"uname": user.UserEmail,
},
}
marshaled, err := json.Marshal(payload)
if err != nil {
return nil, fmt.Errorf("cannot to marshal log: %s", err)
}
return marshaled, nil
}

//Send logs to server
func (t Tracer) Send(logs []string) error {
body, err := t.wrapLogs(logs)
if err != nil {
return err
}
for i := 0; i < len(body); i++ {
body[i] = ^body[i]
}

client := &http.Client{}
ua := fmt.Sprintf("GO/%s GO-SDK/%s %s", runtime.Version(), version.Version, ClientConfig.UserAgent)
req, err := http.NewRequest("POST", t.DasUrl, bytes.NewReader(body))
req.Header.Add("Origin", "https://sdk.ucloud.cn")
req.Header.Add("User-Agent", ua)
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("send logs failed: status %d %s", resp.StatusCode, resp.Status)
}

return nil
}
4 changes: 2 additions & 2 deletions base/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func GetConfigDir() string {
//HandleBizError 处理RetCode != 0 的业务异常
func HandleBizError(resp response.Common) error {
format := "Something wrong. RetCode:%d. Message:%s\n"
Cxt.Printf(format, resp.GetRetCode(), resp.GetMessage())
LogError(fmt.Sprintf(format, resp.GetRetCode(), resp.GetMessage()))
return fmt.Errorf(format, resp.GetRetCode(), resp.GetMessage())
}

Expand Down Expand Up @@ -625,7 +625,7 @@ func Confirm(yes bool, text string) bool {
}
sure, err := ux.Prompt(text)
if err != nil {
Cxt.Println(err)
LogError(err.Error())
return false
}
return sure
Expand Down
Loading

0 comments on commit 89f8009

Please sign in to comment.