From d3c3a96920588a1d0cf6ea0a3617abfca750e6b7 Mon Sep 17 00:00:00 2001 From: Lei Xue Date: Tue, 29 Nov 2016 11:21:40 +0800 Subject: [PATCH] delete cit to use new gotgt as main cmd, use logrus instead of glog --- .gitignore | 3 +- .travis.yml | 2 +- Godeps/Godeps.json | 4 - .../src/github.com/golang/glog/LICENSE | 191 --- .../src/github.com/golang/glog/README | 44 - .../src/github.com/golang/glog/glog.go | 1180 ----------------- .../src/github.com/golang/glog/glog_file.go | 124 -- .../src/github.com/golang/glog/glog_test.go | 415 ------ Makefile.am | 13 +- README.md | 2 +- cmd/cmd.go | 19 +- citd.go => cmd/daemon.go | 88 +- citadm.go => gotgt.go | 4 +- pkg/apiserver/apiserver.go | 22 +- pkg/port/iscsit/iscsid.go | 132 +- pkg/scsi/backingstore.go | 10 +- pkg/scsi/backingstore/common.go | 4 +- pkg/scsi/error.go | 2 +- pkg/scsi/sbc.go | 20 +- pkg/scsi/scsi.go | 10 +- pkg/scsi/scsilumap.go | 6 +- pkg/scsi/spc.go | 16 +- pkg/scsi/target.go | 6 +- 23 files changed, 193 insertions(+), 2124 deletions(-) delete mode 100644 Godeps/_workspace/src/github.com/golang/glog/LICENSE delete mode 100644 Godeps/_workspace/src/github.com/golang/glog/README delete mode 100644 Godeps/_workspace/src/github.com/golang/glog/glog.go delete mode 100644 Godeps/_workspace/src/github.com/golang/glog/glog_file.go delete mode 100644 Godeps/_workspace/src/github.com/golang/glog/glog_test.go rename citd.go => cmd/daemon.go (60%) rename citadm.go => gotgt.go (92%) diff --git a/.gitignore b/.gitignore index 230c3e0..b3c13ee 100644 --- a/.gitignore +++ b/.gitignore @@ -42,8 +42,7 @@ _testmain.go # intellij idea configure .idea -/citadm -/citd +/gotgt *.out examples/* *.bak diff --git a/.travis.yml b/.travis.yml index e39e673..a007614 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,7 @@ script: - dd if=/dev/zero of=/var/tmp/disk.img bs=1024 count=10240 - mkdir ${HOME}/.gotgt - echo '{"storages":[{"deviceID":1000,"path":"file:/var/tmp/disk.img","online":true}],"iscsiportals":[{"id":0,"portal":"127.0.0.1:3260"}],"iscsitargets":{"iqn.2016-09.com.gotgt.gostor:example_tgt_0":{"tpgts":{"1":[0]},"luns":{"0":1000}}}}' > ${HOME}/.gotgt/config.json - - ./citd -v 4 1>/dev/null 2>&1 & + - ./gotgt daemon --log debug 1>/dev/null 2>&1 & # libiscsi test - mkdir ${HOME}/libiscsi - git clone https://github.com/gostor/libiscsi ${HOME}/libiscsi diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index 1e74d74..0bc17c9 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -26,10 +26,6 @@ "Comment": "v0.2.0-8-g990a1a1", "Rev": "990a1a1a70b0da4c4cb70e117971a4f0babfbf1a" }, - { - "ImportPath": "github.com/golang/glog", - "Rev": "23def4e6c14b4da8ac2ed8007337bc5eb5007998" - }, { "ImportPath": "github.com/gorilla/context", "Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd" diff --git a/Godeps/_workspace/src/github.com/golang/glog/LICENSE b/Godeps/_workspace/src/github.com/golang/glog/LICENSE deleted file mode 100644 index 37ec93a..0000000 --- a/Godeps/_workspace/src/github.com/golang/glog/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/Godeps/_workspace/src/github.com/golang/glog/README b/Godeps/_workspace/src/github.com/golang/glog/README deleted file mode 100644 index 387b4eb..0000000 --- a/Godeps/_workspace/src/github.com/golang/glog/README +++ /dev/null @@ -1,44 +0,0 @@ -glog -==== - -Leveled execution logs for Go. - -This is an efficient pure Go implementation of leveled logs in the -manner of the open source C++ package - https://github.com/google/glog - -By binding methods to booleans it is possible to use the log package -without paying the expense of evaluating the arguments to the log. -Through the -vmodule flag, the package also provides fine-grained -control over logging at the file level. - -The comment from glog.go introduces the ideas: - - Package glog implements logging analogous to the Google-internal - C++ INFO/ERROR/V setup. It provides functions Info, Warning, - Error, Fatal, plus formatting variants such as Infof. It - also provides V-style logging controlled by the -v and - -vmodule=file=2 flags. - - Basic examples: - - glog.Info("Prepare to repel boarders") - - glog.Fatalf("Initialization failed: %s", err) - - See the documentation for the V function for an explanation - of these examples: - - if glog.V(2) { - glog.Info("Starting transaction...") - } - - glog.V(2).Infoln("Processed", nItems, "elements") - - -The repository contains an open source version of the log package -used inside Google. The master copy of the source lives inside -Google, not here. The code in this repo is for export only and is not itself -under development. Feature requests will be ignored. - -Send bug reports to golang-nuts@googlegroups.com. diff --git a/Godeps/_workspace/src/github.com/golang/glog/glog.go b/Godeps/_workspace/src/github.com/golang/glog/glog.go deleted file mode 100644 index 54bd7af..0000000 --- a/Godeps/_workspace/src/github.com/golang/glog/glog.go +++ /dev/null @@ -1,1180 +0,0 @@ -// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ -// -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package glog implements logging analogous to the Google-internal C++ INFO/ERROR/V setup. -// It provides functions Info, Warning, Error, Fatal, plus formatting variants such as -// Infof. It also provides V-style logging controlled by the -v and -vmodule=file=2 flags. -// -// Basic examples: -// -// glog.Info("Prepare to repel boarders") -// -// glog.Fatalf("Initialization failed: %s", err) -// -// See the documentation for the V function for an explanation of these examples: -// -// if glog.V(2) { -// glog.Info("Starting transaction...") -// } -// -// glog.V(2).Infoln("Processed", nItems, "elements") -// -// Log output is buffered and written periodically using Flush. Programs -// should call Flush before exiting to guarantee all log output is written. -// -// By default, all log statements write to files in a temporary directory. -// This package provides several flags that modify this behavior. -// As a result, flag.Parse must be called before any logging is done. -// -// -logtostderr=false -// Logs are written to standard error instead of to files. -// -alsologtostderr=false -// Logs are written to standard error as well as to files. -// -stderrthreshold=ERROR -// Log events at or above this severity are logged to standard -// error as well as to files. -// -log_dir="" -// Log files will be written to this directory instead of the -// default temporary directory. -// -// Other flags provide aids to debugging. -// -// -log_backtrace_at="" -// When set to a file and line number holding a logging statement, -// such as -// -log_backtrace_at=gopherflakes.go:234 -// a stack trace will be written to the Info log whenever execution -// hits that statement. (Unlike with -vmodule, the ".go" must be -// present.) -// -v=0 -// Enable V-leveled logging at the specified level. -// -vmodule="" -// The syntax of the argument is a comma-separated list of pattern=N, -// where pattern is a literal file name (minus the ".go" suffix) or -// "glob" pattern and N is a V level. For instance, -// -vmodule=gopher*=3 -// sets the V level to 3 in all Go files whose names begin "gopher". -// -package glog - -import ( - "bufio" - "bytes" - "errors" - "flag" - "fmt" - "io" - stdLog "log" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - "sync" - "sync/atomic" - "time" -) - -// severity identifies the sort of log: info, warning etc. It also implements -// the flag.Value interface. The -stderrthreshold flag is of type severity and -// should be modified only through the flag.Value interface. The values match -// the corresponding constants in C++. -type severity int32 // sync/atomic int32 - -// These constants identify the log levels in order of increasing severity. -// A message written to a high-severity log file is also written to each -// lower-severity log file. -const ( - infoLog severity = iota - warningLog - errorLog - fatalLog - numSeverity = 4 -) - -const severityChar = "IWEF" - -var severityName = []string{ - infoLog: "INFO", - warningLog: "WARNING", - errorLog: "ERROR", - fatalLog: "FATAL", -} - -// get returns the value of the severity. -func (s *severity) get() severity { - return severity(atomic.LoadInt32((*int32)(s))) -} - -// set sets the value of the severity. -func (s *severity) set(val severity) { - atomic.StoreInt32((*int32)(s), int32(val)) -} - -// String is part of the flag.Value interface. -func (s *severity) String() string { - return strconv.FormatInt(int64(*s), 10) -} - -// Get is part of the flag.Value interface. -func (s *severity) Get() interface{} { - return *s -} - -// Set is part of the flag.Value interface. -func (s *severity) Set(value string) error { - var threshold severity - // Is it a known name? - if v, ok := severityByName(value); ok { - threshold = v - } else { - v, err := strconv.Atoi(value) - if err != nil { - return err - } - threshold = severity(v) - } - logging.stderrThreshold.set(threshold) - return nil -} - -func severityByName(s string) (severity, bool) { - s = strings.ToUpper(s) - for i, name := range severityName { - if name == s { - return severity(i), true - } - } - return 0, false -} - -// OutputStats tracks the number of output lines and bytes written. -type OutputStats struct { - lines int64 - bytes int64 -} - -// Lines returns the number of lines written. -func (s *OutputStats) Lines() int64 { - return atomic.LoadInt64(&s.lines) -} - -// Bytes returns the number of bytes written. -func (s *OutputStats) Bytes() int64 { - return atomic.LoadInt64(&s.bytes) -} - -// Stats tracks the number of lines of output and number of bytes -// per severity level. Values must be read with atomic.LoadInt64. -var Stats struct { - Info, Warning, Error OutputStats -} - -var severityStats = [numSeverity]*OutputStats{ - infoLog: &Stats.Info, - warningLog: &Stats.Warning, - errorLog: &Stats.Error, -} - -// Level is exported because it appears in the arguments to V and is -// the type of the v flag, which can be set programmatically. -// It's a distinct type because we want to discriminate it from logType. -// Variables of type level are only changed under logging.mu. -// The -v flag is read only with atomic ops, so the state of the logging -// module is consistent. - -// Level is treated as a sync/atomic int32. - -// Level specifies a level of verbosity for V logs. *Level implements -// flag.Value; the -v flag is of type Level and should be modified -// only through the flag.Value interface. -type Level int32 - -// get returns the value of the Level. -func (l *Level) get() Level { - return Level(atomic.LoadInt32((*int32)(l))) -} - -// set sets the value of the Level. -func (l *Level) set(val Level) { - atomic.StoreInt32((*int32)(l), int32(val)) -} - -// String is part of the flag.Value interface. -func (l *Level) String() string { - return strconv.FormatInt(int64(*l), 10) -} - -// Get is part of the flag.Value interface. -func (l *Level) Get() interface{} { - return *l -} - -// Set is part of the flag.Value interface. -func (l *Level) Set(value string) error { - v, err := strconv.Atoi(value) - if err != nil { - return err - } - logging.mu.Lock() - defer logging.mu.Unlock() - logging.setVState(Level(v), logging.vmodule.filter, false) - return nil -} - -// moduleSpec represents the setting of the -vmodule flag. -type moduleSpec struct { - filter []modulePat -} - -// modulePat contains a filter for the -vmodule flag. -// It holds a verbosity level and a file pattern to match. -type modulePat struct { - pattern string - literal bool // The pattern is a literal string - level Level -} - -// match reports whether the file matches the pattern. It uses a string -// comparison if the pattern contains no metacharacters. -func (m *modulePat) match(file string) bool { - if m.literal { - return file == m.pattern - } - match, _ := filepath.Match(m.pattern, file) - return match -} - -func (m *moduleSpec) String() string { - // Lock because the type is not atomic. TODO: clean this up. - logging.mu.Lock() - defer logging.mu.Unlock() - var b bytes.Buffer - for i, f := range m.filter { - if i > 0 { - b.WriteRune(',') - } - fmt.Fprintf(&b, "%s=%d", f.pattern, f.level) - } - return b.String() -} - -// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the -// struct is not exported. -func (m *moduleSpec) Get() interface{} { - return nil -} - -var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N") - -// Syntax: -vmodule=recordio=2,file=1,gfs*=3 -func (m *moduleSpec) Set(value string) error { - var filter []modulePat - for _, pat := range strings.Split(value, ",") { - if len(pat) == 0 { - // Empty strings such as from a trailing comma can be ignored. - continue - } - patLev := strings.Split(pat, "=") - if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 { - return errVmoduleSyntax - } - pattern := patLev[0] - v, err := strconv.Atoi(patLev[1]) - if err != nil { - return errors.New("syntax error: expect comma-separated list of filename=N") - } - if v < 0 { - return errors.New("negative value for vmodule level") - } - if v == 0 { - continue // Ignore. It's harmless but no point in paying the overhead. - } - // TODO: check syntax of filter? - filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)}) - } - logging.mu.Lock() - defer logging.mu.Unlock() - logging.setVState(logging.verbosity, filter, true) - return nil -} - -// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters -// that require filepath.Match to be called to match the pattern. -func isLiteral(pattern string) bool { - return !strings.ContainsAny(pattern, `\*?[]`) -} - -// traceLocation represents the setting of the -log_backtrace_at flag. -type traceLocation struct { - file string - line int -} - -// isSet reports whether the trace location has been specified. -// logging.mu is held. -func (t *traceLocation) isSet() bool { - return t.line > 0 -} - -// match reports whether the specified file and line matches the trace location. -// The argument file name is the full path, not the basename specified in the flag. -// logging.mu is held. -func (t *traceLocation) match(file string, line int) bool { - if t.line != line { - return false - } - if i := strings.LastIndex(file, "/"); i >= 0 { - file = file[i+1:] - } - return t.file == file -} - -func (t *traceLocation) String() string { - // Lock because the type is not atomic. TODO: clean this up. - logging.mu.Lock() - defer logging.mu.Unlock() - return fmt.Sprintf("%s:%d", t.file, t.line) -} - -// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the -// struct is not exported -func (t *traceLocation) Get() interface{} { - return nil -} - -var errTraceSyntax = errors.New("syntax error: expect file.go:234") - -// Syntax: -log_backtrace_at=gopherflakes.go:234 -// Note that unlike vmodule the file extension is included here. -func (t *traceLocation) Set(value string) error { - if value == "" { - // Unset. - t.line = 0 - t.file = "" - } - fields := strings.Split(value, ":") - if len(fields) != 2 { - return errTraceSyntax - } - file, line := fields[0], fields[1] - if !strings.Contains(file, ".") { - return errTraceSyntax - } - v, err := strconv.Atoi(line) - if err != nil { - return errTraceSyntax - } - if v <= 0 { - return errors.New("negative or zero value for level") - } - logging.mu.Lock() - defer logging.mu.Unlock() - t.line = v - t.file = file - return nil -} - -// flushSyncWriter is the interface satisfied by logging destinations. -type flushSyncWriter interface { - Flush() error - Sync() error - io.Writer -} - -func init() { - flag.BoolVar(&logging.toStderr, "logtostderr", false, "log to standard error instead of files") - flag.BoolVar(&logging.alsoToStderr, "alsologtostderr", false, "log to standard error as well as files") - flag.Var(&logging.verbosity, "v", "log level for V logs") - flag.Var(&logging.stderrThreshold, "stderrthreshold", "logs at or above this threshold go to stderr") - flag.Var(&logging.vmodule, "vmodule", "comma-separated list of pattern=N settings for file-filtered logging") - flag.Var(&logging.traceLocation, "log_backtrace_at", "when logging hits line file:N, emit a stack trace") - - // Default stderrThreshold is ERROR. - logging.stderrThreshold = errorLog - - logging.setVState(0, nil, false) - go logging.flushDaemon() -} - -// Flush flushes all pending log I/O. -func Flush() { - logging.lockAndFlushAll() -} - -// loggingT collects all the global state of the logging setup. -type loggingT struct { - // Boolean flags. Not handled atomically because the flag.Value interface - // does not let us avoid the =true, and that shorthand is necessary for - // compatibility. TODO: does this matter enough to fix? Seems unlikely. - toStderr bool // The -logtostderr flag. - alsoToStderr bool // The -alsologtostderr flag. - - // Level flag. Handled atomically. - stderrThreshold severity // The -stderrthreshold flag. - - // freeList is a list of byte buffers, maintained under freeListMu. - freeList *buffer - // freeListMu maintains the free list. It is separate from the main mutex - // so buffers can be grabbed and printed to without holding the main lock, - // for better parallelization. - freeListMu sync.Mutex - - // mu protects the remaining elements of this structure and is - // used to synchronize logging. - mu sync.Mutex - // file holds writer for each of the log types. - file [numSeverity]flushSyncWriter - // pcs is used in V to avoid an allocation when computing the caller's PC. - pcs [1]uintptr - // vmap is a cache of the V Level for each V() call site, identified by PC. - // It is wiped whenever the vmodule flag changes state. - vmap map[uintptr]Level - // filterLength stores the length of the vmodule filter chain. If greater - // than zero, it means vmodule is enabled. It may be read safely - // using sync.LoadInt32, but is only modified under mu. - filterLength int32 - // traceLocation is the state of the -log_backtrace_at flag. - traceLocation traceLocation - // These flags are modified only under lock, although verbosity may be fetched - // safely using atomic.LoadInt32. - vmodule moduleSpec // The state of the -vmodule flag. - verbosity Level // V logging level, the value of the -v flag/ -} - -// buffer holds a byte Buffer for reuse. The zero value is ready for use. -type buffer struct { - bytes.Buffer - tmp [64]byte // temporary byte array for creating headers. - next *buffer -} - -var logging loggingT - -// setVState sets a consistent state for V logging. -// l.mu is held. -func (l *loggingT) setVState(verbosity Level, filter []modulePat, setFilter bool) { - // Turn verbosity off so V will not fire while we are in transition. - logging.verbosity.set(0) - // Ditto for filter length. - atomic.StoreInt32(&logging.filterLength, 0) - - // Set the new filters and wipe the pc->Level map if the filter has changed. - if setFilter { - logging.vmodule.filter = filter - logging.vmap = make(map[uintptr]Level) - } - - // Things are consistent now, so enable filtering and verbosity. - // They are enabled in order opposite to that in V. - atomic.StoreInt32(&logging.filterLength, int32(len(filter))) - logging.verbosity.set(verbosity) -} - -// getBuffer returns a new, ready-to-use buffer. -func (l *loggingT) getBuffer() *buffer { - l.freeListMu.Lock() - b := l.freeList - if b != nil { - l.freeList = b.next - } - l.freeListMu.Unlock() - if b == nil { - b = new(buffer) - } else { - b.next = nil - b.Reset() - } - return b -} - -// putBuffer returns a buffer to the free list. -func (l *loggingT) putBuffer(b *buffer) { - if b.Len() >= 256 { - // Let big buffers die a natural death. - return - } - l.freeListMu.Lock() - b.next = l.freeList - l.freeList = b - l.freeListMu.Unlock() -} - -var timeNow = time.Now // Stubbed out for testing. - -/* -header formats a log header as defined by the C++ implementation. -It returns a buffer containing the formatted header and the user's file and line number. -The depth specifies how many stack frames above lives the source line to be identified in the log message. - -Log lines have this form: - Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... -where the fields are defined as follows: - L A single character, representing the log level (eg 'I' for INFO) - mm The month (zero padded; ie May is '05') - dd The day (zero padded) - hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds - threadid The space-padded thread ID as returned by GetTID() - file The file name - line The line number - msg The user-supplied message -*/ -func (l *loggingT) header(s severity, depth int) (*buffer, string, int) { - _, file, line, ok := runtime.Caller(3 + depth) - if !ok { - file = "???" - line = 1 - } else { - slash := strings.LastIndex(file, "/") - if slash >= 0 { - file = file[slash+1:] - } - } - return l.formatHeader(s, file, line), file, line -} - -// formatHeader formats a log header using the provided file name and line number. -func (l *loggingT) formatHeader(s severity, file string, line int) *buffer { - now := timeNow() - if line < 0 { - line = 0 // not a real line number, but acceptable to someDigits - } - if s > fatalLog { - s = infoLog // for safety. - } - buf := l.getBuffer() - - // Avoid Fprintf, for speed. The format is so simple that we can do it quickly by hand. - // It's worth about 3X. Fprintf is hard. - _, month, day := now.Date() - hour, minute, second := now.Clock() - // Lmmdd hh:mm:ss.uuuuuu threadid file:line] - buf.tmp[0] = severityChar[s] - buf.twoDigits(1, int(month)) - buf.twoDigits(3, day) - buf.tmp[5] = ' ' - buf.twoDigits(6, hour) - buf.tmp[8] = ':' - buf.twoDigits(9, minute) - buf.tmp[11] = ':' - buf.twoDigits(12, second) - buf.tmp[14] = '.' - buf.nDigits(6, 15, now.Nanosecond()/1000, '0') - buf.tmp[21] = ' ' - buf.nDigits(7, 22, pid, ' ') // TODO: should be TID - buf.tmp[29] = ' ' - buf.Write(buf.tmp[:30]) - buf.WriteString(file) - buf.tmp[0] = ':' - n := buf.someDigits(1, line) - buf.tmp[n+1] = ']' - buf.tmp[n+2] = ' ' - buf.Write(buf.tmp[:n+3]) - return buf -} - -// Some custom tiny helper functions to print the log header efficiently. - -const digits = "0123456789" - -// twoDigits formats a zero-prefixed two-digit integer at buf.tmp[i]. -func (buf *buffer) twoDigits(i, d int) { - buf.tmp[i+1] = digits[d%10] - d /= 10 - buf.tmp[i] = digits[d%10] -} - -// nDigits formats an n-digit integer at buf.tmp[i], -// padding with pad on the left. -// It assumes d >= 0. -func (buf *buffer) nDigits(n, i, d int, pad byte) { - j := n - 1 - for ; j >= 0 && d > 0; j-- { - buf.tmp[i+j] = digits[d%10] - d /= 10 - } - for ; j >= 0; j-- { - buf.tmp[i+j] = pad - } -} - -// someDigits formats a zero-prefixed variable-width integer at buf.tmp[i]. -func (buf *buffer) someDigits(i, d int) int { - // Print into the top, then copy down. We know there's space for at least - // a 10-digit number. - j := len(buf.tmp) - for { - j-- - buf.tmp[j] = digits[d%10] - d /= 10 - if d == 0 { - break - } - } - return copy(buf.tmp[i:], buf.tmp[j:]) -} - -func (l *loggingT) println(s severity, args ...interface{}) { - buf, file, line := l.header(s, 0) - fmt.Fprintln(buf, args...) - l.output(s, buf, file, line, false) -} - -func (l *loggingT) print(s severity, args ...interface{}) { - l.printDepth(s, 1, args...) -} - -func (l *loggingT) printDepth(s severity, depth int, args ...interface{}) { - buf, file, line := l.header(s, depth) - fmt.Fprint(buf, args...) - if buf.Bytes()[buf.Len()-1] != '\n' { - buf.WriteByte('\n') - } - l.output(s, buf, file, line, false) -} - -func (l *loggingT) printf(s severity, format string, args ...interface{}) { - buf, file, line := l.header(s, 0) - fmt.Fprintf(buf, format, args...) - if buf.Bytes()[buf.Len()-1] != '\n' { - buf.WriteByte('\n') - } - l.output(s, buf, file, line, false) -} - -// printWithFileLine behaves like print but uses the provided file and line number. If -// alsoLogToStderr is true, the log message always appears on standard error; it -// will also appear in the log file unless --logtostderr is set. -func (l *loggingT) printWithFileLine(s severity, file string, line int, alsoToStderr bool, args ...interface{}) { - buf := l.formatHeader(s, file, line) - fmt.Fprint(buf, args...) - if buf.Bytes()[buf.Len()-1] != '\n' { - buf.WriteByte('\n') - } - l.output(s, buf, file, line, alsoToStderr) -} - -// output writes the data to the log files and releases the buffer. -func (l *loggingT) output(s severity, buf *buffer, file string, line int, alsoToStderr bool) { - l.mu.Lock() - if l.traceLocation.isSet() { - if l.traceLocation.match(file, line) { - buf.Write(stacks(false)) - } - } - data := buf.Bytes() - if !flag.Parsed() { - os.Stderr.Write([]byte("ERROR: logging before flag.Parse: ")) - os.Stderr.Write(data) - } else if l.toStderr { - os.Stderr.Write(data) - } else { - if alsoToStderr || l.alsoToStderr || s >= l.stderrThreshold.get() { - os.Stderr.Write(data) - } - if l.file[s] == nil { - if err := l.createFiles(s); err != nil { - os.Stderr.Write(data) // Make sure the message appears somewhere. - l.exit(err) - } - } - switch s { - case fatalLog: - l.file[fatalLog].Write(data) - fallthrough - case errorLog: - l.file[errorLog].Write(data) - fallthrough - case warningLog: - l.file[warningLog].Write(data) - fallthrough - case infoLog: - l.file[infoLog].Write(data) - } - } - if s == fatalLog { - // If we got here via Exit rather than Fatal, print no stacks. - if atomic.LoadUint32(&fatalNoStacks) > 0 { - l.mu.Unlock() - timeoutFlush(10 * time.Second) - os.Exit(1) - } - // Dump all goroutine stacks before exiting. - // First, make sure we see the trace for the current goroutine on standard error. - // If -logtostderr has been specified, the loop below will do that anyway - // as the first stack in the full dump. - if !l.toStderr { - os.Stderr.Write(stacks(false)) - } - // Write the stack trace for all goroutines to the files. - trace := stacks(true) - logExitFunc = func(error) {} // If we get a write error, we'll still exit below. - for log := fatalLog; log >= infoLog; log-- { - if f := l.file[log]; f != nil { // Can be nil if -logtostderr is set. - f.Write(trace) - } - } - l.mu.Unlock() - timeoutFlush(10 * time.Second) - os.Exit(255) // C++ uses -1, which is silly because it's anded with 255 anyway. - } - l.putBuffer(buf) - l.mu.Unlock() - if stats := severityStats[s]; stats != nil { - atomic.AddInt64(&stats.lines, 1) - atomic.AddInt64(&stats.bytes, int64(len(data))) - } -} - -// timeoutFlush calls Flush and returns when it completes or after timeout -// elapses, whichever happens first. This is needed because the hooks invoked -// by Flush may deadlock when glog.Fatal is called from a hook that holds -// a lock. -func timeoutFlush(timeout time.Duration) { - done := make(chan bool, 1) - go func() { - Flush() // calls logging.lockAndFlushAll() - done <- true - }() - select { - case <-done: - case <-time.After(timeout): - fmt.Fprintln(os.Stderr, "glog: Flush took longer than", timeout) - } -} - -// stacks is a wrapper for runtime.Stack that attempts to recover the data for all goroutines. -func stacks(all bool) []byte { - // We don't know how big the traces are, so grow a few times if they don't fit. Start large, though. - n := 10000 - if all { - n = 100000 - } - var trace []byte - for i := 0; i < 5; i++ { - trace = make([]byte, n) - nbytes := runtime.Stack(trace, all) - if nbytes < len(trace) { - return trace[:nbytes] - } - n *= 2 - } - return trace -} - -// logExitFunc provides a simple mechanism to override the default behavior -// of exiting on error. Used in testing and to guarantee we reach a required exit -// for fatal logs. Instead, exit could be a function rather than a method but that -// would make its use clumsier. -var logExitFunc func(error) - -// exit is called if there is trouble creating or writing log files. -// It flushes the logs and exits the program; there's no point in hanging around. -// l.mu is held. -func (l *loggingT) exit(err error) { - fmt.Fprintf(os.Stderr, "log: exiting because of error: %s\n", err) - // If logExitFunc is set, we do that instead of exiting. - if logExitFunc != nil { - logExitFunc(err) - return - } - l.flushAll() - os.Exit(2) -} - -// syncBuffer joins a bufio.Writer to its underlying file, providing access to the -// file's Sync method and providing a wrapper for the Write method that provides log -// file rotation. There are conflicting methods, so the file cannot be embedded. -// l.mu is held for all its methods. -type syncBuffer struct { - logger *loggingT - *bufio.Writer - file *os.File - sev severity - nbytes uint64 // The number of bytes written to this file -} - -func (sb *syncBuffer) Sync() error { - return sb.file.Sync() -} - -func (sb *syncBuffer) Write(p []byte) (n int, err error) { - if sb.nbytes+uint64(len(p)) >= MaxSize { - if err := sb.rotateFile(time.Now()); err != nil { - sb.logger.exit(err) - } - } - n, err = sb.Writer.Write(p) - sb.nbytes += uint64(n) - if err != nil { - sb.logger.exit(err) - } - return -} - -// rotateFile closes the syncBuffer's file and starts a new one. -func (sb *syncBuffer) rotateFile(now time.Time) error { - if sb.file != nil { - sb.Flush() - sb.file.Close() - } - var err error - sb.file, _, err = create(severityName[sb.sev], now) - sb.nbytes = 0 - if err != nil { - return err - } - - sb.Writer = bufio.NewWriterSize(sb.file, bufferSize) - - // Write header. - var buf bytes.Buffer - fmt.Fprintf(&buf, "Log file created at: %s\n", now.Format("2006/01/02 15:04:05")) - fmt.Fprintf(&buf, "Running on machine: %s\n", host) - fmt.Fprintf(&buf, "Binary: Built with %s %s for %s/%s\n", runtime.Compiler, runtime.Version(), runtime.GOOS, runtime.GOARCH) - fmt.Fprintf(&buf, "Log line format: [IWEF]mmdd hh:mm:ss.uuuuuu threadid file:line] msg\n") - n, err := sb.file.Write(buf.Bytes()) - sb.nbytes += uint64(n) - return err -} - -// bufferSize sizes the buffer associated with each log file. It's large -// so that log records can accumulate without the logging thread blocking -// on disk I/O. The flushDaemon will block instead. -const bufferSize = 256 * 1024 - -// createFiles creates all the log files for severity from sev down to infoLog. -// l.mu is held. -func (l *loggingT) createFiles(sev severity) error { - now := time.Now() - // Files are created in decreasing severity order, so as soon as we find one - // has already been created, we can stop. - for s := sev; s >= infoLog && l.file[s] == nil; s-- { - sb := &syncBuffer{ - logger: l, - sev: s, - } - if err := sb.rotateFile(now); err != nil { - return err - } - l.file[s] = sb - } - return nil -} - -const flushInterval = 30 * time.Second - -// flushDaemon periodically flushes the log file buffers. -func (l *loggingT) flushDaemon() { - for _ = range time.NewTicker(flushInterval).C { - l.lockAndFlushAll() - } -} - -// lockAndFlushAll is like flushAll but locks l.mu first. -func (l *loggingT) lockAndFlushAll() { - l.mu.Lock() - l.flushAll() - l.mu.Unlock() -} - -// flushAll flushes all the logs and attempts to "sync" their data to disk. -// l.mu is held. -func (l *loggingT) flushAll() { - // Flush from fatal down, in case there's trouble flushing. - for s := fatalLog; s >= infoLog; s-- { - file := l.file[s] - if file != nil { - file.Flush() // ignore error - file.Sync() // ignore error - } - } -} - -// CopyStandardLogTo arranges for messages written to the Go "log" package's -// default logs to also appear in the Google logs for the named and lower -// severities. Subsequent changes to the standard log's default output location -// or format may break this behavior. -// -// Valid names are "INFO", "WARNING", "ERROR", and "FATAL". If the name is not -// recognized, CopyStandardLogTo panics. -func CopyStandardLogTo(name string) { - sev, ok := severityByName(name) - if !ok { - panic(fmt.Sprintf("log.CopyStandardLogTo(%q): unrecognized severity name", name)) - } - // Set a log format that captures the user's file and line: - // d.go:23: message - stdLog.SetFlags(stdLog.Lshortfile) - stdLog.SetOutput(logBridge(sev)) -} - -// logBridge provides the Write method that enables CopyStandardLogTo to connect -// Go's standard logs to the logs provided by this package. -type logBridge severity - -// Write parses the standard logging line and passes its components to the -// logger for severity(lb). -func (lb logBridge) Write(b []byte) (n int, err error) { - var ( - file = "???" - line = 1 - text string - ) - // Split "d.go:23: message" into "d.go", "23", and "message". - if parts := bytes.SplitN(b, []byte{':'}, 3); len(parts) != 3 || len(parts[0]) < 1 || len(parts[2]) < 1 { - text = fmt.Sprintf("bad log format: %s", b) - } else { - file = string(parts[0]) - text = string(parts[2][1:]) // skip leading space - line, err = strconv.Atoi(string(parts[1])) - if err != nil { - text = fmt.Sprintf("bad line number: %s", b) - line = 1 - } - } - // printWithFileLine with alsoToStderr=true, so standard log messages - // always appear on standard error. - logging.printWithFileLine(severity(lb), file, line, true, text) - return len(b), nil -} - -// setV computes and remembers the V level for a given PC -// when vmodule is enabled. -// File pattern matching takes the basename of the file, stripped -// of its .go suffix, and uses filepath.Match, which is a little more -// general than the *? matching used in C++. -// l.mu is held. -func (l *loggingT) setV(pc uintptr) Level { - fn := runtime.FuncForPC(pc) - file, _ := fn.FileLine(pc) - // The file is something like /a/b/c/d.go. We want just the d. - if strings.HasSuffix(file, ".go") { - file = file[:len(file)-3] - } - if slash := strings.LastIndex(file, "/"); slash >= 0 { - file = file[slash+1:] - } - for _, filter := range l.vmodule.filter { - if filter.match(file) { - l.vmap[pc] = filter.level - return filter.level - } - } - l.vmap[pc] = 0 - return 0 -} - -// Verbose is a boolean type that implements Infof (like Printf) etc. -// See the documentation of V for more information. -type Verbose bool - -// V reports whether verbosity at the call site is at least the requested level. -// The returned value is a boolean of type Verbose, which implements Info, Infoln -// and Infof. These methods will write to the Info log if called. -// Thus, one may write either -// if glog.V(2) { glog.Info("log this") } -// or -// glog.V(2).Info("log this") -// The second form is shorter but the first is cheaper if logging is off because it does -// not evaluate its arguments. -// -// Whether an individual call to V generates a log record depends on the setting of -// the -v and --vmodule flags; both are off by default. If the level in the call to -// V is at least the value of -v, or of -vmodule for the source file containing the -// call, the V call will log. -func V(level Level) Verbose { - // This function tries hard to be cheap unless there's work to do. - // The fast path is two atomic loads and compares. - - // Here is a cheap but safe test to see if V logging is enabled globally. - if logging.verbosity.get() >= level { - return Verbose(true) - } - - // It's off globally but it vmodule may still be set. - // Here is another cheap but safe test to see if vmodule is enabled. - if atomic.LoadInt32(&logging.filterLength) > 0 { - // Now we need a proper lock to use the logging structure. The pcs field - // is shared so we must lock before accessing it. This is fairly expensive, - // but if V logging is enabled we're slow anyway. - logging.mu.Lock() - defer logging.mu.Unlock() - if runtime.Callers(2, logging.pcs[:]) == 0 { - return Verbose(false) - } - v, ok := logging.vmap[logging.pcs[0]] - if !ok { - v = logging.setV(logging.pcs[0]) - } - return Verbose(v >= level) - } - return Verbose(false) -} - -// Info is equivalent to the global Info function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) Info(args ...interface{}) { - if v { - logging.print(infoLog, args...) - } -} - -// Infoln is equivalent to the global Infoln function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) Infoln(args ...interface{}) { - if v { - logging.println(infoLog, args...) - } -} - -// Infof is equivalent to the global Infof function, guarded by the value of v. -// See the documentation of V for usage. -func (v Verbose) Infof(format string, args ...interface{}) { - if v { - logging.printf(infoLog, format, args...) - } -} - -// Info logs to the INFO log. -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Info(args ...interface{}) { - logging.print(infoLog, args...) -} - -// InfoDepth acts as Info but uses depth to determine which call frame to log. -// InfoDepth(0, "msg") is the same as Info("msg"). -func InfoDepth(depth int, args ...interface{}) { - logging.printDepth(infoLog, depth, args...) -} - -// Infoln logs to the INFO log. -// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Infoln(args ...interface{}) { - logging.println(infoLog, args...) -} - -// Infof logs to the INFO log. -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Infof(format string, args ...interface{}) { - logging.printf(infoLog, format, args...) -} - -// Warning logs to the WARNING and INFO logs. -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Warning(args ...interface{}) { - logging.print(warningLog, args...) -} - -// WarningDepth acts as Warning but uses depth to determine which call frame to log. -// WarningDepth(0, "msg") is the same as Warning("msg"). -func WarningDepth(depth int, args ...interface{}) { - logging.printDepth(warningLog, depth, args...) -} - -// Warningln logs to the WARNING and INFO logs. -// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Warningln(args ...interface{}) { - logging.println(warningLog, args...) -} - -// Warningf logs to the WARNING and INFO logs. -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Warningf(format string, args ...interface{}) { - logging.printf(warningLog, format, args...) -} - -// Error logs to the ERROR, WARNING, and INFO logs. -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Error(args ...interface{}) { - logging.print(errorLog, args...) -} - -// ErrorDepth acts as Error but uses depth to determine which call frame to log. -// ErrorDepth(0, "msg") is the same as Error("msg"). -func ErrorDepth(depth int, args ...interface{}) { - logging.printDepth(errorLog, depth, args...) -} - -// Errorln logs to the ERROR, WARNING, and INFO logs. -// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Errorln(args ...interface{}) { - logging.println(errorLog, args...) -} - -// Errorf logs to the ERROR, WARNING, and INFO logs. -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Errorf(format string, args ...interface{}) { - logging.printf(errorLog, format, args...) -} - -// Fatal logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(255). -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Fatal(args ...interface{}) { - logging.print(fatalLog, args...) -} - -// FatalDepth acts as Fatal but uses depth to determine which call frame to log. -// FatalDepth(0, "msg") is the same as Fatal("msg"). -func FatalDepth(depth int, args ...interface{}) { - logging.printDepth(fatalLog, depth, args...) -} - -// Fatalln logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(255). -// Arguments are handled in the manner of fmt.Println; a newline is appended if missing. -func Fatalln(args ...interface{}) { - logging.println(fatalLog, args...) -} - -// Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs, -// including a stack trace of all running goroutines, then calls os.Exit(255). -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Fatalf(format string, args ...interface{}) { - logging.printf(fatalLog, format, args...) -} - -// fatalNoStacks is non-zero if we are to exit without dumping goroutine stacks. -// It allows Exit and relatives to use the Fatal logs. -var fatalNoStacks uint32 - -// Exit logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -// Arguments are handled in the manner of fmt.Print; a newline is appended if missing. -func Exit(args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.print(fatalLog, args...) -} - -// ExitDepth acts as Exit but uses depth to determine which call frame to log. -// ExitDepth(0, "msg") is the same as Exit("msg"). -func ExitDepth(depth int, args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.printDepth(fatalLog, depth, args...) -} - -// Exitln logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -func Exitln(args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.println(fatalLog, args...) -} - -// Exitf logs to the FATAL, ERROR, WARNING, and INFO logs, then calls os.Exit(1). -// Arguments are handled in the manner of fmt.Printf; a newline is appended if missing. -func Exitf(format string, args ...interface{}) { - atomic.StoreUint32(&fatalNoStacks, 1) - logging.printf(fatalLog, format, args...) -} diff --git a/Godeps/_workspace/src/github.com/golang/glog/glog_file.go b/Godeps/_workspace/src/github.com/golang/glog/glog_file.go deleted file mode 100644 index 65075d2..0000000 --- a/Godeps/_workspace/src/github.com/golang/glog/glog_file.go +++ /dev/null @@ -1,124 +0,0 @@ -// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ -// -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// File I/O for logs. - -package glog - -import ( - "errors" - "flag" - "fmt" - "os" - "os/user" - "path/filepath" - "strings" - "sync" - "time" -) - -// MaxSize is the maximum size of a log file in bytes. -var MaxSize uint64 = 1024 * 1024 * 1800 - -// logDirs lists the candidate directories for new log files. -var logDirs []string - -// If non-empty, overrides the choice of directory in which to write logs. -// See createLogDirs for the full list of possible destinations. -var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory") - -func createLogDirs() { - if *logDir != "" { - logDirs = append(logDirs, *logDir) - } - logDirs = append(logDirs, os.TempDir()) -} - -var ( - pid = os.Getpid() - program = filepath.Base(os.Args[0]) - host = "unknownhost" - userName = "unknownuser" -) - -func init() { - h, err := os.Hostname() - if err == nil { - host = shortHostname(h) - } - - current, err := user.Current() - if err == nil { - userName = current.Username - } - - // Sanitize userName since it may contain filepath separators on Windows. - userName = strings.Replace(userName, `\`, "_", -1) -} - -// shortHostname returns its argument, truncating at the first period. -// For instance, given "www.google.com" it returns "www". -func shortHostname(hostname string) string { - if i := strings.Index(hostname, "."); i >= 0 { - return hostname[:i] - } - return hostname -} - -// logName returns a new log file name containing tag, with start time t, and -// the name for the symlink for tag. -func logName(tag string, t time.Time) (name, link string) { - name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d", - program, - host, - userName, - tag, - t.Year(), - t.Month(), - t.Day(), - t.Hour(), - t.Minute(), - t.Second(), - pid) - return name, program + "." + tag -} - -var onceLogDirs sync.Once - -// create creates a new log file and returns the file and its filename, which -// contains tag ("INFO", "FATAL", etc.) and t. If the file is created -// successfully, create also attempts to update the symlink for that tag, ignoring -// errors. -func create(tag string, t time.Time) (f *os.File, filename string, err error) { - onceLogDirs.Do(createLogDirs) - if len(logDirs) == 0 { - return nil, "", errors.New("log: no log dirs") - } - name, link := logName(tag, t) - var lastErr error - for _, dir := range logDirs { - fname := filepath.Join(dir, name) - f, err := os.Create(fname) - if err == nil { - symlink := filepath.Join(dir, link) - os.Remove(symlink) // ignore err - os.Symlink(name, symlink) // ignore err - return f, fname, nil - } - lastErr = err - } - return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr) -} diff --git a/Godeps/_workspace/src/github.com/golang/glog/glog_test.go b/Godeps/_workspace/src/github.com/golang/glog/glog_test.go deleted file mode 100644 index 0fb376e..0000000 --- a/Godeps/_workspace/src/github.com/golang/glog/glog_test.go +++ /dev/null @@ -1,415 +0,0 @@ -// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/ -// -// Copyright 2013 Google Inc. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package glog - -import ( - "bytes" - "fmt" - stdLog "log" - "path/filepath" - "runtime" - "strconv" - "strings" - "testing" - "time" -) - -// Test that shortHostname works as advertised. -func TestShortHostname(t *testing.T) { - for hostname, expect := range map[string]string{ - "": "", - "host": "host", - "host.google.com": "host", - } { - if got := shortHostname(hostname); expect != got { - t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got) - } - } -} - -// flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter. -type flushBuffer struct { - bytes.Buffer -} - -func (f *flushBuffer) Flush() error { - return nil -} - -func (f *flushBuffer) Sync() error { - return nil -} - -// swap sets the log writers and returns the old array. -func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) { - l.mu.Lock() - defer l.mu.Unlock() - old = l.file - for i, w := range writers { - logging.file[i] = w - } - return -} - -// newBuffers sets the log writers to all new byte buffers and returns the old array. -func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter { - return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)}) -} - -// contents returns the specified log value as a string. -func contents(s severity) string { - return logging.file[s].(*flushBuffer).String() -} - -// contains reports whether the string is contained in the log. -func contains(s severity, str string, t *testing.T) bool { - return strings.Contains(contents(s), str) -} - -// setFlags configures the logging flags how the test expects them. -func setFlags() { - logging.toStderr = false -} - -// Test that Info works as advertised. -func TestInfo(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - Info("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) - } - if !contains(infoLog, "test", t) { - t.Error("Info failed") - } -} - -func TestInfoDepth(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - - f := func() { InfoDepth(1, "depth-test1") } - - // The next three lines must stay together - _, _, wantLine, _ := runtime.Caller(0) - InfoDepth(0, "depth-test0") - f() - - msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n") - if len(msgs) != 2 { - t.Fatalf("Got %d lines, expected 2", len(msgs)) - } - - for i, m := range msgs { - if !strings.HasPrefix(m, "I") { - t.Errorf("InfoDepth[%d] has wrong character: %q", i, m) - } - w := fmt.Sprintf("depth-test%d", i) - if !strings.Contains(m, w) { - t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m) - } - - // pull out the line number (between : and ]) - msg := m[strings.LastIndex(m, ":")+1:] - x := strings.Index(msg, "]") - if x < 0 { - t.Errorf("InfoDepth[%d]: missing ']': %q", i, m) - continue - } - line, err := strconv.Atoi(msg[:x]) - if err != nil { - t.Errorf("InfoDepth[%d]: bad line number: %q", i, m) - continue - } - wantLine++ - if wantLine != line { - t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine) - } - } -} - -func init() { - CopyStandardLogTo("INFO") -} - -// Test that CopyStandardLogTo panics on bad input. -func TestCopyStandardLogToPanic(t *testing.T) { - defer func() { - if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") { - t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s) - } - }() - CopyStandardLogTo("LOG") -} - -// Test that using the standard log package logs to INFO. -func TestStandardLog(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - stdLog.Print("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) - } - if !contains(infoLog, "test", t) { - t.Error("Info failed") - } -} - -// Test that the header has the correct format. -func TestHeader(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - defer func(previous func() time.Time) { timeNow = previous }(timeNow) - timeNow = func() time.Time { - return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local) - } - pid = 1234 - Info("test") - var line int - format := "I0102 15:04:05.067890 1234 glog_test.go:%d] test\n" - n, err := fmt.Sscanf(contents(infoLog), format, &line) - if n != 1 || err != nil { - t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog)) - } - // Scanf treats multiple spaces as equivalent to a single space, - // so check for correct space-padding also. - want := fmt.Sprintf(format, line) - if contents(infoLog) != want { - t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want) - } -} - -// Test that an Error log goes to Warning and Info. -// Even in the Info log, the source character will be E, so the data should -// all be identical. -func TestError(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - Error("test") - if !contains(errorLog, "E", t) { - t.Errorf("Error has wrong character: %q", contents(errorLog)) - } - if !contains(errorLog, "test", t) { - t.Error("Error failed") - } - str := contents(errorLog) - if !contains(warningLog, str, t) { - t.Error("Warning failed") - } - if !contains(infoLog, str, t) { - t.Error("Info failed") - } -} - -// Test that a Warning log goes to Info. -// Even in the Info log, the source character will be W, so the data should -// all be identical. -func TestWarning(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - Warning("test") - if !contains(warningLog, "W", t) { - t.Errorf("Warning has wrong character: %q", contents(warningLog)) - } - if !contains(warningLog, "test", t) { - t.Error("Warning failed") - } - str := contents(warningLog) - if !contains(infoLog, str, t) { - t.Error("Info failed") - } -} - -// Test that a V log goes to Info. -func TestV(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - logging.verbosity.Set("2") - defer logging.verbosity.Set("0") - V(2).Info("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) - } - if !contains(infoLog, "test", t) { - t.Error("Info failed") - } -} - -// Test that a vmodule enables a log in this file. -func TestVmoduleOn(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - logging.vmodule.Set("glog_test=2") - defer logging.vmodule.Set("") - if !V(1) { - t.Error("V not enabled for 1") - } - if !V(2) { - t.Error("V not enabled for 2") - } - if V(3) { - t.Error("V enabled for 3") - } - V(2).Info("test") - if !contains(infoLog, "I", t) { - t.Errorf("Info has wrong character: %q", contents(infoLog)) - } - if !contains(infoLog, "test", t) { - t.Error("Info failed") - } -} - -// Test that a vmodule of another file does not enable a log in this file. -func TestVmoduleOff(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - logging.vmodule.Set("notthisfile=2") - defer logging.vmodule.Set("") - for i := 1; i <= 3; i++ { - if V(Level(i)) { - t.Errorf("V enabled for %d", i) - } - } - V(2).Info("test") - if contents(infoLog) != "" { - t.Error("V logged incorrectly") - } -} - -// vGlobs are patterns that match/don't match this file at V=2. -var vGlobs = map[string]bool{ - // Easy to test the numeric match here. - "glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail. - "glog_test=2": true, - "glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed. - // These all use 2 and check the patterns. All are true. - "*=2": true, - "?l*=2": true, - "????_*=2": true, - "??[mno]?_*t=2": true, - // These all use 2 and check the patterns. All are false. - "*x=2": false, - "m*=2": false, - "??_*=2": false, - "?[abc]?_*t=2": false, -} - -// Test that vmodule globbing works as advertised. -func testVmoduleGlob(pat string, match bool, t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - defer logging.vmodule.Set("") - logging.vmodule.Set(pat) - if V(2) != Verbose(match) { - t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match) - } -} - -// Test that a vmodule globbing works as advertised. -func TestVmoduleGlob(t *testing.T) { - for glob, match := range vGlobs { - testVmoduleGlob(glob, match, t) - } -} - -func TestRollover(t *testing.T) { - setFlags() - var err error - defer func(previous func(error)) { logExitFunc = previous }(logExitFunc) - logExitFunc = func(e error) { - err = e - } - defer func(previous uint64) { MaxSize = previous }(MaxSize) - MaxSize = 512 - - Info("x") // Be sure we have a file. - info, ok := logging.file[infoLog].(*syncBuffer) - if !ok { - t.Fatal("info wasn't created") - } - if err != nil { - t.Fatalf("info has initial error: %v", err) - } - fname0 := info.file.Name() - Info(strings.Repeat("x", int(MaxSize))) // force a rollover - if err != nil { - t.Fatalf("info has error after big write: %v", err) - } - - // Make sure the next log file gets a file name with a different - // time stamp. - // - // TODO: determine whether we need to support subsecond log - // rotation. C++ does not appear to handle this case (nor does it - // handle Daylight Savings Time properly). - time.Sleep(1 * time.Second) - - Info("x") // create a new file - if err != nil { - t.Fatalf("error after rotation: %v", err) - } - fname1 := info.file.Name() - if fname0 == fname1 { - t.Errorf("info.f.Name did not change: %v", fname0) - } - if info.nbytes >= MaxSize { - t.Errorf("file size was not reset: %d", info.nbytes) - } -} - -func TestLogBacktraceAt(t *testing.T) { - setFlags() - defer logging.swap(logging.newBuffers()) - // The peculiar style of this code simplifies line counting and maintenance of the - // tracing block below. - var infoLine string - setTraceLocation := func(file string, line int, ok bool, delta int) { - if !ok { - t.Fatal("could not get file:line") - } - _, file = filepath.Split(file) - infoLine = fmt.Sprintf("%s:%d", file, line+delta) - err := logging.traceLocation.Set(infoLine) - if err != nil { - t.Fatal("error setting log_backtrace_at: ", err) - } - } - { - // Start of tracing block. These lines know about each other's relative position. - _, file, line, ok := runtime.Caller(0) - setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls. - Info("we want a stack trace here") - } - numAppearances := strings.Count(contents(infoLog), infoLine) - if numAppearances < 2 { - // Need 2 appearances, one in the log header and one in the trace: - // log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here - // ... - // github.com/glog/glog_test.go:280 (0x41ba91) - // ... - // We could be more precise but that would require knowing the details - // of the traceback format, which may not be dependable. - t.Fatal("got no trace back; log is ", contents(infoLog)) - } -} - -func BenchmarkHeader(b *testing.B) { - for i := 0; i < b.N; i++ { - buf, _, _ := logging.header(infoLog, 0) - logging.putBuffer(buf) - } -} diff --git a/Makefile.am b/Makefile.am index e0e4a51..32619f1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,13 +1,10 @@ export GOPATH:=$(abs_top_srcdir)/Godeps/_workspace:$(GOPATH) -all-local: build-cli build-daemon +all-local: build clean-local: - -rm -f citadm citd + -rm -f gotgt install-exec-local: - $(INSTALL_PROGRAM) citadm $(bindir) - $(INSTALL_PROGRAM) citd $(bindir) + $(INSTALL_PROGRAM) gotgt $(bindir) -build-cli: - go build citadm.go -build-daemon: - go build citd.go +build: + go build gotgt.go diff --git a/README.md b/README.md index f4a0418..fff134d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## gotgt [![Build Status](https://travis-ci.org/gostor/gotgt.svg)](https://travis-ci.org/gostor/gotgt) -Simple Golang SCSI Target framework, this includes two binaries, one is `citadm` which is command line to config and control, the other is `citd` which is a target daemon. +Simple Golang SCSI Target framework, this includes only one binary, you can start a daemon via `gotgt daemon` and control it via `gotgt list/create/rm`. ## Build diff --git a/cmd/cmd.go b/cmd/cmd.go index 3539f4e..a6fe842 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,3 +1,19 @@ +/* +Copyright 2016 The GoStor Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package cmd import ( @@ -10,7 +26,7 @@ import ( func NewCommand(cli *client.Client) *cobra.Command { var cmd = &cobra.Command{ - Use: "citadm", + Use: "gotgt", Short: "Gotgt is a very fast and stable SCSI target framework", Long: ``, Run: func(cmd *cobra.Command, args []string) { @@ -18,6 +34,7 @@ func NewCommand(cli *client.Client) *cobra.Command { }, } cmd.AddCommand( + newDaemonCommand(cli), newCreateCommand(cli), newRemoveCommand(cli), newListCommand(cli), diff --git a/citd.go b/cmd/daemon.go similarity index 60% rename from citd.go rename to cmd/daemon.go index 730f9de..1ea9a2a 100644 --- a/citd.go +++ b/cmd/daemon.go @@ -14,11 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. */ -// SCSI target daemon -package main +package cmd import ( - "flag" "fmt" "os" "os/signal" @@ -26,53 +24,65 @@ import ( "strings" "syscall" - "github.com/golang/glog" + log "github.com/Sirupsen/logrus" + "github.com/gostor/gotgt/pkg/api/client" "github.com/gostor/gotgt/pkg/apiserver" "github.com/gostor/gotgt/pkg/config" _ "github.com/gostor/gotgt/pkg/port/iscsit" "github.com/gostor/gotgt/pkg/scsi" _ "github.com/gostor/gotgt/pkg/scsi/backingstore" + "github.com/spf13/cobra" ) -func main() { - - flHelp := flag.Bool("help", false, "Print help message for Hyperd daemon") - flHost := flag.String("host", "tcp://127.0.0.1:23457", "Host for SCSI target daemon") - flDriver := flag.String("driver", "iscsi", "SCSI low level driver") - flag.Usage = func() { *flHelp = true } - flag.Parse() - flag.Set("logtostderr", "true") - if *flHelp == true { - fmt.Println(`Usage: - xxxd [OPTIONS] - -Application Options: - --host="" Host for SCSI target daemon - --driver=iscsi SCSI low level driver - -Help Options: - -h, --help Show this help message -`) - return +func newDaemonCommand(cli *client.Client) *cobra.Command { + var host string + var driver string + var logLevel string + var cmd = &cobra.Command{ + Use: "daemon", + Short: "Setup a daemon", + Long: `Setup the Gotgt's daemon`, + RunE: func(cmd *cobra.Command, args []string) error { + return createDaemon(host, driver, logLevel) + }, } + flags := cmd.Flags() + flags.StringVar(&logLevel, "log", "info", "Log level of SCSI target daemon") + flags.StringVar(&host, "host", "tcp://127.0.0.1:23457", "Host for SCSI target daemon") + flags.StringVar(&driver, "driver", "iscsi", "SCSI low level driver") + return cmd +} +func createDaemon(host, driver, level string) error { + switch level { + case "info": + log.SetLevel(log.InfoLevel) + case "warn": + log.SetLevel(log.WarnLevel) + case "debug": + log.SetLevel(log.DebugLevel) + case "panic", "fatal", "error": + log.SetLevel(log.ErrorLevel) + default: + return fmt.Errorf("unknown log level: %v", level) + } config, err := config.Load(config.ConfigDir()) if err != nil { - glog.Error(err) - os.Exit(1) + log.Error(err) + return err } err = scsi.InitSCSILUMap(config) if err != nil { - glog.Error(err) - os.Exit(1) + log.Error(err) + return err } scsiTarget := scsi.NewSCSITargetService() - targetDriver, err := scsi.NewTargetDriver(*flDriver, scsiTarget) + targetDriver, err := scsi.NewTargetDriver(driver, scsiTarget) if err != nil { - glog.Error(err) - os.Exit(1) + log.Error(err) + return err } for tgtname := range config.ISCSITargets { @@ -88,22 +98,23 @@ Help Options: } hosts := []string{} - if *flHost != "" { - hosts = append(hosts, *flHost) + if host != "" { + hosts = append(hosts, host) } for _, protoAddr := range hosts { protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { - glog.Errorf("bad format %s, expected PROTO://ADDR", protoAddr) - return + err = fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr) + log.Error(err) + return err } serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]}) } s, err := apiserver.New(serverConfig) if err != nil { - glog.Errorf(err.Error()) - return + log.Error(err) + return err } s.InitRouters() // The serve API routine never exits unless an error occurs @@ -122,10 +133,11 @@ Help Options: // If we have an error here it is unique to API (as daemonErr would have // exited the daemon process above) if errAPI != nil { - glog.Warningf("Shutting down due to ServeAPI error: %v", errAPI) + log.Warnf("Shutting down due to ServeAPI error: %v", errAPI) } case <-stopAll: break } s.Close() + return nil } diff --git a/citadm.go b/gotgt.go similarity index 92% rename from citadm.go rename to gotgt.go index a1ff1d7..4c32f92 100644 --- a/citadm.go +++ b/gotgt.go @@ -25,9 +25,11 @@ import ( "github.com/docker/go-connections/sockets" "github.com/gostor/gotgt/cmd" "github.com/gostor/gotgt/pkg/api/client" + "github.com/gostor/gotgt/pkg/version" ) func main() { + host := "tcp://127.0.0.1:23457" httpClient, err := newHTTPClient(host) if err != nil { @@ -35,7 +37,7 @@ func main() { os.Exit(1) } - cli, err := client.NewClient(host, "0.1", httpClient, nil) + cli, err := client.NewClient(host, version.Version, httpClient, nil) if err != nil { fmt.Fprintf(os.Stderr, "%v", err) os.Exit(1) diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index 7f20ceb..e629bb5 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -25,9 +25,9 @@ import ( "strconv" "strings" + log "github.com/Sirupsen/logrus" systemdActivation "github.com/coreos/go-systemd/activation" "github.com/docker/go-connections/sockets" - "github.com/golang/glog" "github.com/gorilla/mux" "github.com/gostor/gotgt/pkg/apiserver/httputils" "github.com/gostor/gotgt/pkg/apiserver/router" @@ -79,7 +79,7 @@ func New(cfg *Config) (*Server, error) { if err != nil { return nil, err } - glog.V(3).Infof("Server created for HTTP on %s (%s)", addr.Proto, addr.Addr) + log.Infof("Server created for HTTP on %s (%s)", addr.Proto, addr.Addr) s.servers = append(s.servers, srv...) } return s, nil @@ -89,7 +89,7 @@ func New(cfg *Config) (*Server, error) { func (s *Server) Close() { for _, srv := range s.servers { if err := srv.Close(); err != nil { - glog.Error(err) + log.Error(err) } } } @@ -104,7 +104,7 @@ func (s *Server) serveAPI() error { srv.srv.Handler = s.routerSwapper go func(srv *HTTPServer) { var err error - glog.V(3).Infof("API listen on %s", srv.l.Addr()) + log.Infof("API listen on %s", srv.l.Addr()) if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") { err = nil } @@ -142,7 +142,7 @@ func (s *HTTPServer) Close() error { func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) { if s.cfg.TLSConfig == nil || s.cfg.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert { - glog.Warning("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") + log.Warning("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } if l, err = sockets.NewTCPSocket(addr, s.cfg.TLSConfig); err != nil { return nil, err @@ -154,7 +154,7 @@ func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) { func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // log the handler call - glog.V(3).Infof("Calling %s %s", r.Method, r.URL.Path) + log.Infof("Calling %s %s", r.Method, r.URL.Path) // Define the context that we'll pass around to share info // like the docker-request-id. @@ -172,7 +172,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc { } if err := handlerFunc(ctx, w, r, vars); err != nil { - glog.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err) + log.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err) httputils.WriteError(w, err) } } @@ -195,12 +195,12 @@ func (s *Server) addRouter(r router.Router) { func (s *Server) createMux() *mux.Router { m := mux.NewRouter() - glog.V(3).Infof("Registering routers") + log.Infof("Registering routers") for _, apiRouter := range s.routers { for _, r := range apiRouter.Routes() { f := s.makeHTTPHandler(r.Handler()) - glog.V(3).Infof("Registering %s, %s", r.Method(), r.Path()) + log.Infof("Registering %s, %s", r.Method(), r.Path()) m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f) m.Path(r.Path()).Methods(r.Method()).Handler(f) } @@ -214,7 +214,7 @@ func (s *Server) createMux() *mux.Router { // the API execution. func (s *Server) Wait(waitChan chan error) { if err := s.serveAPI(); err != nil { - glog.Errorf("ServeAPI error: %v", err) + log.Errorf("ServeAPI error: %v", err) waitChan <- err return } @@ -307,7 +307,7 @@ func listenFD(addr string, tlsConfig *tls.Config) ([]net.Listener, error) { continue } if err := ls.Close(); err != nil { - glog.Errorf("Failed to close systemd activated file at fd %d: %v", fdOffset+3, err) + log.Errorf("Failed to close systemd activated file at fd %d: %v", fdOffset+3, err) } } return []net.Listener{listeners[fdOffset]}, nil diff --git a/pkg/port/iscsit/iscsid.go b/pkg/port/iscsit/iscsid.go index 3c156df..d7b58da 100644 --- a/pkg/port/iscsit/iscsid.go +++ b/pkg/port/iscsit/iscsid.go @@ -23,7 +23,7 @@ import ( "os" "strconv" - "github.com/golang/glog" + log "github.com/Sirupsen/logrus" "github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/config" "github.com/gostor/gotgt/pkg/scsi" @@ -124,25 +124,25 @@ func (s *ISCSITargetDriver) HasPortal(tgtName string, tpgt uint16, portal string func (s *ISCSITargetDriver) Run() error { l, err := net.Listen("tcp", ":3260") if err != nil { - glog.Error(err) + log.Error(err) os.Exit(1) } defer l.Close() for { - glog.Info("Listening ...") + log.Info("Listening ...") conn, err := l.Accept() if err != nil { - glog.Error(err) + log.Error(err) continue } - glog.Info(conn.LocalAddr().String()) - glog.Info("Accepting ...") + log.Info(conn.LocalAddr().String()) + log.Info("Accepting ...") iscsiConn := &iscsiConnection{conn: conn} iscsiConn.init() iscsiConn.rxIOState = IOSTATE_RX_BHS - glog.Infof("connection is connected from %s...\n", conn.RemoteAddr().String()) + log.Infof("connection is connected from %s...\n", conn.RemoteAddr().String()) // start a new thread to do with this command go s.handler(DATAIN, iscsiConn) } @@ -152,15 +152,15 @@ func (s *ISCSITargetDriver) Run() error { func (s *ISCSITargetDriver) handler(events byte, conn *iscsiConnection) { if events&DATAIN != 0 { - glog.V(1).Infof("rx handler processing...") + log.Debug("rx handler processing...") go s.rxHandler(conn) } if conn.state != CONN_STATE_CLOSE && events&DATAOUT != 0 { - glog.V(1).Infof("tx handler processing...") + log.Debug("tx handler processing...") s.txHandler(conn) } if conn.state == CONN_STATE_CLOSE { - glog.Warningf("iscsi connection[%d] closed", conn.CID) + log.Warningf("iscsi connection[%d] closed", conn.CID) conn.close() } } @@ -181,22 +181,22 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) { for { switch conn.rxIOState { case IOSTATE_RX_BHS: - glog.Infof("rx handler: IOSTATE_RX_BHS") + log.Debug("rx handler: IOSTATE_RX_BHS") buf, length, err := conn.readData(BHS_SIZE) if err != nil { - glog.Error(err) + log.Error(err) return } if length == 0 { - glog.Warningf("set connection to close") + log.Warningf("set connection to close") conn.state = CONN_STATE_CLOSE return } conn.rxBuffer = buf cmd, err = parseHeader(buf) if err != nil { - glog.Error(err) - glog.Warningf("set connection to close") + log.Error(err) + log.Warningf("set connection to close") conn.state = CONN_STATE_CLOSE return } @@ -205,8 +205,8 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) { conn.rxIOState = IOSTATE_RX_INIT_AHS break } - glog.V(2).Infof("got command: \n%s", cmd.String()) - glog.V(2).Infof("got buffer: %v", buf) + log.Debugf("got command: \n%s", cmd.String()) + log.Debugf("got buffer: %v", buf) final = true case IOSTATE_RX_INIT_AHS: conn.rxIOState = IOSTATE_RX_DATA @@ -227,24 +227,24 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) { for length < dl { b, l, err := conn.readData(dl - length) if err != nil { - glog.Error(err) + log.Error(err) return } length += l buf = append(buf, b...) } if length != dl { - glog.V(2).Infof("get length is %d, but expected %d", length, dl) - glog.Warningf("set connection to close") + log.Debugf("get length is %d, but expected %d", length, dl) + log.Warning("set connection to close") conn.state = CONN_STATE_CLOSE return } cmd.RawData = buf[:length] conn.rxBuffer = append(conn.rxBuffer, buf...) final = true - glog.Infof("got command: \n%s", cmd.String()) + log.Debugf("got command: \n%s", cmd.String()) default: - glog.Errorf("error %d %d\n", conn.state, conn.rxIOState) + log.Errorf("error %d %d\n", conn.state, conn.rxIOState) return } @@ -260,29 +260,29 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) { conn.resp = &ISCSICommand{} switch conn.req.OpCode { case OpLoginReq: - glog.Infof("OpLoginReq") + log.Debug("OpLoginReq") if err := s.iscsiExecLogin(conn); err != nil { - glog.Error(err) - glog.Warningf("set connection to close") + log.Error(err) + log.Warningf("set connection to close") conn.state = CONN_STATE_CLOSE } case OpLogoutReq: - glog.Infof("OpLogoutReq") + log.Debug("OpLogoutReq") if err := iscsiExecLogout(conn); err != nil { - glog.Warningf("set connection to close") + log.Warningf("set connection to close") conn.state = CONN_STATE_CLOSE } case OpTextReq: - glog.Infof("OpTextReq") + log.Debug("OpTextReq") if err := s.iscsiExecText(conn); err != nil { - glog.Warningf("set connection to close") + log.Warningf("set connection to close") conn.state = CONN_STATE_CLOSE } default: iscsiExecReject(conn) } - glog.V(2).Infof("connection state is %v", conn.state) - glog.V(2).Infof("%#v", conn.resp.String()) + log.Debugf("connection state is %v", conn.state) + log.Debugf("%#v", conn.resp.String()) s.handler(DATAOUT, conn) } } @@ -374,7 +374,7 @@ func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error { // create a new session sess, err := s.NewISCSISession(conn, cmd.ISID) if err != nil { - glog.Error(err) + log.Error(err) return err } itnexus := &api.ITNexus{uuid.NewV1(), GeniSCSIITNexusID(sess)} @@ -413,8 +413,8 @@ func (s *ISCSITargetDriver) iscsiExecText(conn *iscsiConnection) error { if st, ok := keys["SendTargets"]; ok { if st == "All" { for name, tgt := range s.iSCSITargets { - glog.V(2).Infof("iscsi target: %v", name) - //glog.V(2).Infof("iscsi target portals: %v", tgt.Portals) + log.Debugf("iscsi target: %v", name) + //log.Debugf("iscsi target portals: %v", tgt.Portals) result = append(result, util.KeyValue{"TargetName", name}) for _, tpgt := range tgt.TPGTs { @@ -512,23 +512,23 @@ func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) { if conn.state == CONN_STATE_SCSI && conn.txTask == nil { err := s.scsiCommandHandler(conn) if err != nil { - glog.Error(err) + log.Error(err) return } } for { switch conn.txIOState { case IOSTATE_TX_BHS: - glog.V(2).Infof("ready to write response") - glog.V(2).Infof("%s", conn.resp.String()) - glog.V(2).Infof("length of RawData is %d", len(conn.resp.RawData)) - glog.V(2).Infof("length of resp is %d", len(conn.resp.Bytes())) + log.Debug("ready to write response") + log.Debugf("%s", conn.resp.String()) + log.Debugf("length of RawData is %d", len(conn.resp.RawData)) + log.Debugf("length of resp is %d", len(conn.resp.Bytes())) if l, err := conn.write(conn.resp.Bytes()); err != nil { - glog.Error(err) + log.Error(err) return } else { conn.txIOState = IOSTATE_TX_INIT_AHS - glog.V(2).Infof("success to write %d length", l) + log.Debugf("success to write %d length", l) } case IOSTATE_TX_INIT_AHS: if hdigest != 0 { @@ -547,7 +547,7 @@ func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) { conn.txIOState = IOSTATE_TX_INIT_DDIGEST } default: - glog.Errorf("error %d %d\n", conn.state, conn.txIOState) + log.Errorf("error %d %d\n", conn.state, conn.txIOState) return } @@ -556,41 +556,41 @@ func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) { } } - glog.V(3).Infof("connection state: %d", conn.state) + log.Debugf("connection state: %d", conn.state) switch conn.state { case CONN_STATE_CLOSE, CONN_STATE_EXIT: - glog.Warningf("set connection to close") + log.Warnf("set connection to close") conn.state = CONN_STATE_CLOSE case CONN_STATE_SECURITY_LOGIN: conn.state = CONN_STATE_LOGIN - glog.V(3).Infof("CONN_STATE_LOGIN") + log.Debugf("CONN_STATE_LOGIN") case CONN_STATE_SECURITY_FULL, CONN_STATE_LOGIN_FULL: if conn.sessionType == SESSION_NORMAL { conn.state = CONN_STATE_KERNEL - glog.Infof("CONN_STATE_KERNEL") + log.Infof("CONN_STATE_KERNEL") conn.state = CONN_STATE_SCSI - glog.V(3).Infof("CONN_STATE_SCSI") + log.Debugf("CONN_STATE_SCSI") } else { conn.state = CONN_STATE_FULL - glog.V(3).Infof("CONN_STATE_FULL") + log.Debugf("CONN_STATE_FULL") } conn.rxIOState = IOSTATE_RX_BHS s.handler(DATAIN, conn) case CONN_STATE_SCSI: conn.txTask = nil default: - glog.Warningf("unexpected connection state: %d", conn.state) + log.Warnf("unexpected connection state: %d", conn.state) conn.rxIOState = IOSTATE_RX_BHS s.handler(DATAIN, conn) } - glog.Infof("%d", conn.state) + log.Infof("%d", conn.state) } func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error) { req := conn.req switch req.OpCode { case OpSCSICmd: - glog.V(2).Infof("SCSI Command processing...") + log.Debugf("SCSI Command processing...") scmd := &api.SCSICommand{} task := &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: scmd} if req.Write { @@ -599,7 +599,7 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error if !req.Final { task.unsolCount = 1 } - glog.V(2).Infof("SCSI write, R2T count: %d, unsol Count: %d, offset: %d", task.r2tCount, task.unsolCount, task.offset) + log.Debugf("SCSI write, R2T count: %d, unsol Count: %d, offset: %d", task.r2tCount, task.unsolCount, task.offset) if task.scmd.OutSDBBuffer.Buffer == nil { task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer([]byte{}) @@ -667,7 +667,7 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error conn.txIOState = IOSTATE_TX_BHS iscsiExecTMFunction(conn) case OpSCSIOut: - glog.V(1).Infof("iSCSI Data-out processing...") + log.Debugf("iSCSI Data-out processing...") var task *iscsiTask for _, t := range conn.session.PendingTasks { if t.tag == conn.req.TaskTag { @@ -676,16 +676,16 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error } if task == nil { err = fmt.Errorf("Cannot find iSCSI task with tag[%v]", conn.req.TaskTag) - glog.Error(err) + log.Error(err) return } task.offset = task.offset + conn.req.DataLen task.r2tCount = task.r2tCount - conn.req.DataLen task.scmd.OutSDBBuffer.Buffer.Write(conn.req.RawData) - glog.V(2).Infof("Final: %v", conn.req.Final) - glog.V(2).Infof("r2tCount: %v", task.r2tCount) + log.Debugf("Final: %v", conn.req.Final) + log.Debugf("r2tCount: %v", task.r2tCount) if !conn.req.Final { - glog.V(1).Infof("Not ready to exec the task") + log.Debugf("Not ready to exec the task") conn.rxIOState = IOSTATE_RX_BHS s.handler(DATAIN, conn) return nil @@ -697,7 +697,7 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error break } task.offset = 0 - glog.V(1).Infof("Process the Data-out package") + log.Debugf("Process the Data-out package") conn.rxTask = task if err = s.iscsiExecTask(task); err != nil { return @@ -730,11 +730,11 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error iscsiExecLogout(conn) case OpTextReq, OpSNACKReq: err = fmt.Errorf("Cannot handle yet %s", opCodeMap[conn.req.OpCode]) - glog.Error(err) + log.Error(err) return default: err = fmt.Errorf("Unknown op %s", opCodeMap[conn.req.OpCode]) - glog.Error(err) + log.Error(err) return } conn.rxIOState = IOSTATE_RX_BHS @@ -750,16 +750,16 @@ func (s *ISCSITargetDriver) iscsiTaskQueueHandler(task *iscsiTask) error { return s.iscsiExecTask(task) } cmdsn := cmd.CmdSN - glog.V(2).Infof("CmdSN of command is %d, ExpCmdSN of session is %d", cmdsn, sess.ExpCmdSN) + log.Debugf("CmdSN of command is %d, ExpCmdSN of session is %d", cmdsn, sess.ExpCmdSN) if cmdsn == sess.ExpCmdSN { retry: cmdsn += 1 sess.ExpCmdSN = cmdsn - glog.V(2).Infof("session's ExpCmdSN is %d", cmdsn) + log.Debugf("session's ExpCmdSN is %d", cmdsn) - glog.V(2).Infof("process task(%d)", task.cmd.CmdSN) + log.Debugf("process task(%d)", task.cmd.CmdSN) if err := s.iscsiExecTask(task); err != nil { - glog.Error(err) + log.Error(err) } if len(sess.PendingTasks) == 0 { return nil @@ -775,10 +775,10 @@ func (s *ISCSITargetDriver) iscsiTaskQueueHandler(task *iscsiTask) error { } else { if cmd.CmdSN < sess.ExpCmdSN { err := fmt.Errorf("unexpected cmd serial number: (%d, %d)", cmd.CmdSN, sess.ExpCmdSN) - glog.Error(err) + log.Error(err) return err } - glog.V(1).Infof("add task(%d) into task queue", task.cmd.CmdSN) + log.Debugf("add task(%d) into task queue", task.cmd.CmdSN) // add this connection into queue and set this task as pending task task.state = taskPending sess.PendingTasks.Push(task) diff --git a/pkg/scsi/backingstore.go b/pkg/scsi/backingstore.go index ab388c8..1178f01 100644 --- a/pkg/scsi/backingstore.go +++ b/pkg/scsi/backingstore.go @@ -21,7 +21,7 @@ import ( "fmt" "io" - "github.com/golang/glog" + log "github.com/Sirupsen/logrus" "github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/util" ) @@ -133,12 +133,12 @@ write: // hack: wbuf = []byte("hello world!") err = bs.Write(wbuf, int64(offset)) if err != nil { - glog.Error(err) + log.Error(err) key = MEDIUM_ERROR asc = ASC_READ_ERROR goto sense } - glog.V(2).Infof("write data at %d for length %d", offset, len(wbuf)) + log.Debugf("write data at %d for length %d", offset, len(wbuf)) var pg *api.ModePage for _, p := range lu.ModePages { if p.PageCode == 0x08 && p.SubPageCode == 0 { @@ -181,11 +181,11 @@ verify: bs.DataAdvise(int64(offset), int64(length), util.POSIX_FADV_WILLNEED) } } - glog.Infof("io done %s", string(scb)) + log.Infof("io done %s", string(scb)) return nil sense: if err != nil { - glog.Error(err) + log.Error(err) return err } diff --git a/pkg/scsi/backingstore/common.go b/pkg/scsi/backingstore/common.go index 6ed3540..b8827d1 100644 --- a/pkg/scsi/backingstore/common.go +++ b/pkg/scsi/backingstore/common.go @@ -20,7 +20,7 @@ import ( "fmt" "os" - "github.com/golang/glog" + log "github.com/Sirupsen/logrus" "github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/scsi" "github.com/gostor/gotgt/pkg/util" @@ -97,7 +97,7 @@ func (bs *FileBackingStore) Read(offset, tl int64) ([]byte, error) { func (bs *FileBackingStore) Write(wbuf []byte, offset int64) error { length, err := bs.file.WriteAt(wbuf, offset) if err != nil { - glog.Error(err) + log.Error(err) return err } if length != len(wbuf) { diff --git a/pkg/scsi/error.go b/pkg/scsi/error.go index a179a5e..6eccf5c 100644 --- a/pkg/scsi/error.go +++ b/pkg/scsi/error.go @@ -1,5 +1,5 @@ /* -Copyright 2015 The GoStor Authors All rights reserved. +Copyright 2016 The GoStor Authors All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/pkg/scsi/sbc.go b/pkg/scsi/sbc.go index 6468b8d..421066d 100644 --- a/pkg/scsi/sbc.go +++ b/pkg/scsi/sbc.go @@ -22,7 +22,7 @@ import ( "encoding/binary" "unsafe" - "github.com/golang/glog" + log "github.com/Sirupsen/logrus" "github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/util" "github.com/gostor/gotgt/pkg/version" @@ -331,7 +331,7 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat { if dev.Attrs.Removable && !dev.Attrs.Online { key = NOT_READY asc = ASC_MEDIUM_NOT_PRESENT - glog.Warningf("sense") + log.Warnf("sense") goto sense } @@ -343,7 +343,7 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat { if scb[1]&0xe0 != 0 { key = ILLEGAL_REQUEST asc = ASC_INVALID_FIELD_IN_CDB - glog.Warningf("sense") + log.Warnf("sense") goto sense } */ @@ -384,7 +384,7 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat { api.PRE_FETCH_10, api.PRE_FETCH_16, api.COMPARE_AND_WRITE: key = DATA_PROTECT asc = ASC_WRITE_PROTECT - glog.Warningf("sense") + log.Warnf("sense") goto sense } } @@ -397,14 +397,14 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat { if lba+uint64(tl) < lba || lba+uint64(tl) > dev.Size>>dev.BlockShift { key = ILLEGAL_REQUEST asc = ASC_LBA_OUT_OF_RANGE - glog.Warningf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift) + log.Warnf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift) goto sense } } else { if lba >= dev.Size>>dev.BlockShift { key = ILLEGAL_REQUEST asc = ASC_LBA_OUT_OF_RANGE - glog.Warningf("sense") + log.Warnf("sense") goto sense } } @@ -437,7 +437,7 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat { err = bsPerformCommand(dev.Storage, cmd) if err != nil { - glog.Error(err) + log.Error(err) key = HARDWARE_ERROR asc = ASC_INTERNAL_TGT_FAILURE } else { @@ -563,14 +563,14 @@ func SBCVerify(host int, cmd *api.SCSICommand) api.SAMStat { if lba+uint64(tl) < lba || lba+uint64(tl) > dev.Size>>dev.BlockShift { key = ILLEGAL_REQUEST asc = ASC_LBA_OUT_OF_RANGE - glog.Warningf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift) + log.Warnf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift) goto sense } } else { if lba >= dev.Size>>dev.BlockShift { key = ILLEGAL_REQUEST asc = ASC_LBA_OUT_OF_RANGE - glog.Warningf("sense") + log.Warnf("sense") goto sense } } @@ -579,7 +579,7 @@ func SBCVerify(host int, cmd *api.SCSICommand) api.SAMStat { cmd.TL = tl << dev.BlockShift err = bsPerformCommand(dev.Storage, cmd) if err != nil { - glog.Error(err) + log.Error(err) key = HARDWARE_ERROR asc = ASC_INTERNAL_TGT_FAILURE } else { diff --git a/pkg/scsi/scsi.go b/pkg/scsi/scsi.go index 5222c01..e3c5199 100644 --- a/pkg/scsi/scsi.go +++ b/pkg/scsi/scsi.go @@ -23,7 +23,7 @@ import ( "sync" "unsafe" - "github.com/golang/glog" + log "github.com/Sirupsen/logrus" "github.com/gostor/gotgt/pkg/api" "github.com/satori/go.uuid" ) @@ -80,21 +80,21 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro lun := *(*uint64)(unsafe.Pointer(&scmd.Lun)) scmd.Device = target.Devices[lun] - glog.V(2).Infof("scsi opcode: 0x%x, LUN: %d", int(scmd.SCB.Bytes()[0]), binary.LittleEndian.Uint64(scmd.Lun[:])) + log.Debugf("scsi opcode: 0x%x, LUN: %d", int(scmd.SCB.Bytes()[0]), binary.LittleEndian.Uint64(scmd.Lun[:])) if scmd.Device == nil { scmd.Device = target.LUN0 if lun != 0 { BuildSenseData(scmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB) scmd.Result = api.SAMStatCheckCondition.Stat - glog.Warningf("%v", api.SAMStatCheckCondition.Err) + log.Warnf("%v", api.SAMStatCheckCondition.Err) return nil } } result := scmd.Device.PerformCommand(tid, scmd) if result != api.SAMStatGood { scmd.Result = result.Stat - glog.Warningf("%v", result.Err) + log.Warnf("%v", result.Err) } return nil } @@ -162,7 +162,7 @@ func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) { senseBuffer.Truncate(int(inBufLen)) } } else { - glog.V(2).Infof("cannot calc cbd alloc length. truncate failed") + log.Debugf("cannot calc cbd alloc length. truncate failed") } cmd.Result = key cmd.SenseBuffer = senseBuffer diff --git a/pkg/scsi/scsilumap.go b/pkg/scsi/scsilumap.go index ec887ce..5803507 100644 --- a/pkg/scsi/scsilumap.go +++ b/pkg/scsi/scsilumap.go @@ -17,7 +17,7 @@ limitations under the License. package scsi import ( - "errors" + "fmt" "strconv" "sync" @@ -75,7 +75,7 @@ func InitSCSILUMap(config *config.Config) error { for _, bs := range config.Storages { lu, err := NewSCSILu(bs.DeviceID, bs.Path, bs.Online) if err != nil { - return errors.New("Init SCSI LU map error.") + return fmt.Errorf("Init SCSI LU map error: %v", err) } globalSCSILUMap.AllDevices[bs.DeviceID] = lu } @@ -84,7 +84,7 @@ func InitSCSILUMap(config *config.Config) error { for lunstr, deviceID := range tgt.LUNs { lun, err := strconv.ParseUint(lunstr, 10, 64) if err != nil { - return errors.New("LU Number must be a number") + return fmt.Errorf("LU Number must be a number") } mappingLUN(deviceID, lun, tgtName) // Init SCSISimpleReservationOperator diff --git a/pkg/scsi/spc.go b/pkg/scsi/spc.go index db6ff9c..0308b9b 100644 --- a/pkg/scsi/spc.go +++ b/pkg/scsi/spc.go @@ -22,7 +22,7 @@ import ( "encoding/binary" "fmt" - "github.com/golang/glog" + log "github.com/Sirupsen/logrus" "github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/util" "github.com/satori/go.uuid" @@ -341,7 +341,7 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat { // Get Allocation Length allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10]) if allocationLength < 16 { - glog.Warningf("goto sense, allocationLength < 16") + log.Warn("goto sense, allocationLength < 16") goto sense } @@ -577,24 +577,24 @@ func SPCReportSupportedOperationCodes(host int, cmd *api.SCSICommand) api.SAMSta rsa := util.GetUnalignedUint16(scb[4:]) switch reporting_options { case 0x00: /* report all */ - glog.V(3).Infof("Service Action: report all") + log.Debugf("Service Action: report all") err := reportOpcodesAll(cmd, rctd) if err != nil { - glog.Error(err) + log.Error(err) goto sense } case 0x01: /* report one no service action*/ - glog.V(3).Infof("Service Action: report one no service action") + log.Debugf("Service Action: report one no service action") err := reportOpcodeOne(cmd, rctd, opcode, rsa, false) if err != nil { - glog.Error(err) + log.Error(err) goto sense } case 0x02: /* report one service action */ - glog.V(3).Infof("Service Action: report one service action") + log.Debugf("Service Action: report one service action") err := reportOpcodeOne(cmd, rctd, opcode, rsa, true) if err != nil { - glog.Error(err) + log.Error(err) goto sense } default: diff --git a/pkg/scsi/target.go b/pkg/scsi/target.go index b92893f..30bdcd7 100644 --- a/pkg/scsi/target.go +++ b/pkg/scsi/target.go @@ -20,7 +20,7 @@ import ( "fmt" "unsafe" - "github.com/golang/glog" + log "github.com/Sirupsen/logrus" "github.com/gostor/gotgt/pkg/api" "github.com/satori/go.uuid" ) @@ -97,12 +97,12 @@ func deviceReserve(cmd *api.SCSICommand) error { } } if lu == nil { - glog.Errorf("invalid target and lun %d %s", cmd.Target.TID, lun) + log.Errorf("invalid target and lun %d %s", cmd.Target.TID, lun) return nil } if !uuid.Equal(lu.ReserveID, uuid.Nil) && uuid.Equal(lu.ReserveID, cmd.ITNexusID) { - glog.Errorf("already reserved %d, %d", lu.ReserveID, cmd.ITNexusID) + log.Errorf("already reserved %d, %d", lu.ReserveID, cmd.ITNexusID) return fmt.Errorf("already reserved") } lu.ReserveID = cmd.ITNexusID