I've found a package for printing colours in golang. However, it has no simple way of printing no colour. And as my code was becoming messier due being filled with print statements I wanted to rewrite it. However, I have no clue how to create fstrings in a function.
How it looks in my code:
color.HEX("#B0DFE5").Print("[" + time.Now().Format("15:04:05") +"] ")
color.HEX("#FFFFFF").Printf("Changed %s to %s\n", name, new_name)
What I've created for normal prints:
func cprintInfo(message string) {
color.HEX("#B0DFE5").Print("[!] ")
color.HEX("#FFFFFF").Printf(message + "\n")
}
What I'm looking to create:
cfprintInfo("Hello %s", world)
// Hello world
Printf() expects a format string and (optional) arguments:
func (c RGBColor) Printf(format string, a ...interface{})
So mimic that:
func cfprintInfo(format string, args ...interface{}) {
color.HEX("#B0DFE5").Print("[!] ")
color.HEX("#FFFFFF").Printf(format, args...)
}
Related
As a background, I have a logging facility that wants to output filename and line number, as well as manage some other unique-per-caller information. In c++ this is relatively straightforward using static variables, but I'm having trouble coming up with an equivalent in Go.
I've come across runtime.Caller(), which will get me the PC of the caller (and thus uniquely identify it), but clocking in at ~500ns it's not a cost I want to pay on every invocation of every log statement!
As a really basic example of the behaviour I have
package main
import (
"fmt"
"runtime"
)
type Info struct {
file string
line int
// otherData
}
var infoMap = map[uintptr]Info{}
func Log(str string, args ...interface{}) {
pc, file, line, _ := runtime.Caller(1)
info, found := infoMap[pc]
if !found {
info = Info{file, line}
infoMap[pc] = info
// otherData would get generated here too
}
fmt.Println(info.file, info.line)
}
// client code
func foo() {
// do some work
Log("Here's a message with <> some <> args", "foo", "bar")
// do more work
Log("The cow jumped over the moon")
}
func main() {
foo()
foo()
}
This outputs
/tmp/foo/main.go 33
/tmp/foo/main.go 35
/tmp/foo/main.go 33
/tmp/foo/main.go 35
Now, runtime.Caller(1) is being evaluated on every call here. Ideally, it is evaluated once per statement.
Something like
func Log(str string, args ...interface{}) {
uniqueId = doSomethingFasterThanCaller()
info, found := infoMap[uniqueId]
if !found {
_, file, line, _ := runtime.Caller(1)
info = Info{file, line}
infoMap[pc] = info
// otherData would get generated here too
}
fmt.Println(info.file, info.line)
}
Or even something done from the caller that allows it to be uniquely identified without hardcoding ids.
func Log(uniqueId int, str string, args ...interface{}) {
info, found := infoMap[uniqueId]
if !found {
_, file, line, _ := runtime.Caller(1)
info = Info{file, line}
infoMap[uniqueId] = info
// otherData would get generated here too
}
fmt.Println(info.file, info.line)
}
func Foo() {
Log( uniqueIdGetter(), "message" )
Log( uniqueIdGetter(), "another message" )
// If I could get the offset from the beginning of the function
// to this line, something like this could work.
//
//Log((int)(reflect.ValueOf(Foo).Pointer()) + CODE_OFFSET, "message")
}
Is this something that can be done natively, or am I stuck with the cost of runtime.Caller (which does a bunch of extra work above-and-beyond just getting the pc, which is really all I need)?
Use a package-level variable to store a unique facility value for each function:
var fooFacility = &Facility{UniqeID: id, UsefulData: "the data"}
func foo() {
// do some work
fooFacility.Log( "Here's a message with <> some <> args", arg1, arg2 )
// do more work
fooFacility.Log( "The cow jumped over the moon" )
}
where Facility is something like:
type Facility struct {
UniqeID string
UsefulData string
once sync.Once
file string
line string
}
func (f *Facility) Log(fmt string, args ...interface{}) {
f.Once.Do(func() {
var ok bool
_, f.file, f.line, ok = runtime.Caller(2)
if !ok {
f.file = "???"
f.line = 0
}
})
// use file and line here
}
Use sync.Once to grab the file and line once.
I have a simple function I want to test:
func (t *Thing) print(min_verbosity int, message string) {
if t.verbosity >= minv {
fmt.Print(message)
}
}
But how can I test what the function actually sends to standard output? Test::Output does what I want in Perl. I know I could write all my own boilerplate to do the same in Go (as described here):
orig = os.Stdout
r,w,_ = os.Pipe()
thing.print("Some message")
var buf bytes.Buffer
io.Copy(&buf, r)
w.Close()
os.Stdout = orig
if(buf.String() != "Some message") {
t.Error("Failure!")
}
But that's a lot of extra work for every single test. I'm hoping there's a more standard way, or perhaps an abstraction library to handle this.
One thing to also remember, there's nothing stopping you from writing functions to avoid the boilerplate.
For example I have a command line app that uses log and I wrote this function:
func captureOutput(f func()) string {
var buf bytes.Buffer
log.SetOutput(&buf)
f()
log.SetOutput(os.Stderr)
return buf.String()
}
Then used it like this:
output := captureOutput(func() {
client.RemoveCertificate("www.example.com")
})
assert.Equal(t, "removed certificate www.example.com\n", output)
Using this assert library: http://godoc.org/github.com/stretchr/testify/assert.
You can do one of three things. The first is to use Examples.
The package also runs and verifies example code. Example functions may include a concluding line comment that begins with "Output:" and is compared with the standard output of the function when the tests are run. (The comparison ignores leading and trailing space.) These are examples of an example:
func ExampleHello() {
fmt.Println("hello")
// Output: hello
}
The second (and more appropriate, IMO) is to use fake functions for your IO. In your code you do:
var myPrint = fmt.Print
func (t *Thing) print(min_verbosity int, message string) {
if t.verbosity >= minv {
myPrint(message) // N.B.
}
}
And in your tests:
func init() {
myPrint = fakePrint // fakePrint records everything it's supposed to print.
}
func Test...
The third is to use fmt.Fprintf with an io.Writer that is os.Stdout in production code, but bytes.Buffer in tests.
You could consider adding a return statement to your function to return the string that is actually printed out.
func (t *Thing) print(min_verbosity int, message string) string {
if t.verbosity >= minv {
fmt.Print(message)
return message
}
return ""
}
Now, your test could just check the returned string against an expected string (rather than the print out). Maybe a bit more in-line with Test Driven Development (TDD).
And, in your production code, nothing would need to change, since you don't have to assign the return value of a function if you don't need it.
I want to create a function that will work exactly like fmt.Printf but also left pad the string with current timestamp. Ideally I would like to override printf and println to do this job, but the first solution is also ok.
This is what I've done:
func output(message string, a ...interface{}) {
fmt.Printf(getCurrentTime() + " " + message, a)
}
func getCurrentTime() string {
t := time.Now()
return t.Format("[2006-01-02 15:04:05]")
}
But it outputs strange results when I pass variables.
How should I do this?
If you want to pass variadic elements from a function to another, you have to expand them. From your example, a is an []interface, so you are passing only two arguments to the actual fmt.Printf: the message and an array.
You have to correct the call:
fmt.Printf(getCurrentTime() + " " + message, a...)
A little example to show what happens:
func exec(args ...interface{}) {
fmt.Println(args)
}
func insert(args ...interface{}) {
exec(args) // Prints [[5 42]]
exec(args...) // Prints [5 42]
}
func main() {
insert(5, "42")
}
I'm implementing a wrapper around the standard log package to make a logger with various log levels.
I have the following interface:
type Logger interface {
Trace(fmt string, args ...interface{})
Debug(fmt string, args ...interface{})
Info(fmt string, args ...interface{})
Warn(fmt string, args ...interface{})
Error(fmt string, args ...interface{})
Fatal(fmt string, args ...interface{})
Panic(fmt string, args ...interface{})
}
In the implementation I have something like this (not the exact code)
func Info(format string, args ...interface{}){
msg := fmt.Sprintf(format, args...)
log.Println(msg)
}
Now, assume I call my library like this:
logger.Info("Hello %s", "World")
I get the printout: "Hello %!(EXTRA string=WORLD)", instead of the expected "Hello World". There a similar output if I do
msg := fmt.Sprintf(format, args)
This returns "Hello World%!EXTRA []interface{}=[]".
I can't reproduce this behavior. Are you sure it's not a simple error that you forgot to show here?
https://play.golang.org/p/-jtmll17Xj
package main
import "fmt"
func Info(format string, args ...interface{}){
msg := fmt.Sprintf(format, args...)
fmt.Print(msg)
}
func main() {
Info("Hello %s", "World")
}
Prints
Hello World
According to the fmt docs, %!(EXTRA string=WORLD) is added to the string when you pass extra parameters, unexpected by the format. Maybe you are using the format string "Hello World" instead of "Hello %s", or passing the argument twice?
The error was between the chair and keyboard. I mixed up the following interfaces:
func Print(v ...interface{})
func Printf(format string, v ...interface{})
Some of my code was calling the library without a format string.See here for a more detailed example: http://play.golang.org/p/Xx79qujaFp
Happens aswell when your message has no verbs and the varargs are empty but coming from elsewhere:
func CustomPrintf(message string, a ...interface{}) {
fmt.Printf(message, a) // for no verbs in message you'll get this "EXTRA" suffix
}
That's a special case of a general error. You've got to spread a first. So use fmt.Printf(message, a...) to make it to varags again.
This can happen if you pass nil params to your format, for instance:
myStr := fmt.Sprintf("check/%s", "hello", nil)
will set the value of myStr to: "check/hello%!"
In this example the extra params was the third param with nil value.
so make sure to remove any extra params.
Also, happens due to a simple (typo?) if you forget/miss putting in the "%".
e.g.
fmt.Printf("v\n", myvar)
instead of
fmt.Printf("%v\n", myvar)
obviously not a "syntax" error, so nothing for the compiler to catch.
I realize this nets out to the same as some of the other answers, but I think this is clearer and (maybe?) the most common cause.
When I compile the following program
func myPrint(v ...interface{}) {
fmt.Println("Hello", v...)
}
func main() {
myPrint("new", "world")
}
I get a compilation error
too many arguments in call to fmt.Println
I thought v... is going to expand into 2nd, 3rd arguments and the fmt.Println would see three item variadic argument list. I thought it would be equivalent to
fmt.Println("Hello", "new", "world")
Why is it giving an error.
Try this. It prepends Hello to the variadic arguments, then prints them all at once with println.
package main
import "fmt"
func myPrint(v ...interface{}) {
a := append([]interface{}{"Hello"}, v...) // prepend "Hello" to variadics
fmt.Println(a...) // println the whole lot
}
func main() {
myPrint("new", "world")
}
You're mis-using the variadic shorthand in your call to fmt.Println(). What you're actually sending is 2 arguments: a single string, then the slice of type interface{} expanded. The function call will not concatenate that into a single slice.
This design will compile and run with the results you're expecting:
func myPrint(v ...interface{}) {
fmt.Print("Hello ")
fmt.Println(v...)
}
func main() {
myPrint("new", "world")
}