Skip to content
A Diagnostic Logging package for Go
Go
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
d
README.md
d.test.go
notes.txt

README.md

Package "d"

import "d"

It's OK to change the package name, of course; "diag" might be good.

NOTE: Unlike normal Github repository README files, this is near full documentation based on the Go package documentation format. Even more documentation can be found in notes.txt.

Overview

This is a "diagnostic logging" package. Basically, one places calls to the function Bug(variable) throughout your code - and nothing will happen.

But, preceded by the call Log(d.ON), and with the call End() right before your code's exit point (end of main() is usually a good place), the logged diagnostics will print (FIFO) with the argument to Bug() displayed, formatted not unlike "%+v", with a neat "two back" call trace (and in other ways) - like this from the test program (d.test.go):

$ go run d.test.go
d.test.go Version 1.1, d.Version 1.5
(exit)(d.test.go,main,41) exited

d.Log:
(proc.go,main,198)(d.test.go,main,33) nine: 9
(proc.go,main,198)(d.test.go,main,34) "newlines\r\n"
(d.test.go,main,35)(d.test.go,a,45) <true>
(d.test.go,main,35)(d.test.go,a,46) <false>
(d.test.go,a,47)(d.test.go,b,51) <nil>
(d.test.go,a,47)(d.test.go,b,52) <empty>
(d.test.go,c,57) 26
(d.test.go,main,35)(d.test.go,a,47)(d.test.go,b,53)(d.test.go,c,58) 26
(d.test.go,b,53)(d.test.go,c,60) []string{"a b"}
(d.test.go,b,53)(d.test.go,c,61) []string{"a b"} []string
(d.test.go,b,53)(d.test.go,c,62) []string{"a b"}
(d.test.go,c,64)(d.test.go,e,71) "I am not ignored"
(proc.go,main,198)(d.test.go,main,39) main.Args{a:interface {}(nil), \
    b:interface {}(nil), c:interface {}(nil)}

A Closer Look

Take this example diagnostic output:

d.Log:
(filea.go,123,funca)(fileb.go,42,initb) value of varb is <nil>

That means func funca in filea.go at line 123 called func initb in fileb.go and the diagnostic value of varb is <nil> was issued at line 42 (in fileb.go), by the diagnostic call:

d.Bug(varb,"value of varb is")

The point of this kind of diagnostic output is to capture and "see" a point or multiple points in the code in realtime (though printed at program end).

If the "point" in the code is unknown, one place a diagnostic somewhere near where one thinks it is; then maybe one a bit before that, and perhaps one a bit after... (Like "debugging" hardware: one places the (scope) probe in the middle of a circuit board - signal bad, move the probe half-way up - signal good, move probe half-way down... Repeat until source of error found.)

There are many Bug() function options, runtime options and configuration constants, as well as many support functions.

Package Files

d/d.go
d/d_test.go         (lame)
d.test.go           (useful)

Constants

Log function constants.

OFF                 logging off (default)
ON                  logging on 
PRINT               diagnostics display immediately, not at end

Diagnostic function options.

NOTRACE             diagnostic without trace (calls string)
RETURN              return formated diagnostic
GET                 return formated diagnostic without calls string
TYPE                convert argument to it's type
CATTYPE             append argument type
ALWAYS              always display (see filters)
VSHARP              format with "%#v"
VPLUS               format with "%+v"
NOQ                 do not quote strings
NOESC               do not escape message (see ESC next section)
NOTRUNC             do not truncate message (see TRUNC)
HERENOW             log diagnostic even if logging off
BASET               experimental

The const PRINT can also be an option. The consts VSHARP and VPLUS can also be Log() values.

Configuration options are user preferences manually changed.

LOGMSG = "\nd.Log:" first line of output
BASENAME = 1        remove base path for file names (0, 1, 2)
BASEFNAME = 1       remove base path for function names (0, 1, 2)
EOL = "\n"          the diagnostic end of line
TRUNC = 120         truncate output string length
NOMAINDOT = true    remove "main." from function names
ESC = true          convert characters < 32
ESCSTR = "\\%03o"   how to convert characters < 32 (or \t\n\r...)
FARRAY = true       display arrays with "%#v"
EMPTY = true        display empty strings as "<empty>"

Variables

No variables are exported.

Functions

func Bug

func Bug(arg ...interface{}) string

Bug is to put a diagnostic into the queue, stored in the format of the examples above: file, function and line number in a two "back" trace, the source line that called Bug() and the source line that called the function with the Bug() call.

The first argument is any variable and is formated with "%+v", with special cases for true, false and "" (empty string). Strings are enclosed in quotes unless the option NOQ is set.

A string return is really only used for the RETURN option to make that call return the diagnostic string. (Otherwise it returns an empty string.)

Example of default and VSHARP options:

(d.test.go,b,49)(d.test.go,c,64) main.rectangle
(d.test.go,b,49)(d.test.go,c,65) main.rectangle{length:10.5, breadth:25.1, color:"red"}

The VSHARP option - or log value - is useful to distinguish between:

[]int{1,2}
[]string{"1","2"}

which are identical with "%+v". As are these:

[]string{"one","two"}
[]string{"one two"}

The following options effect diagnostic output but are configuration options set by editing the source.

Example of the ESC with Bug("newline\r\n"):

(proc.go,main,198)(d.test.go,main,18) newline\r\n

Example of TRUNC = 4:

(proc.go,main,198)(d.test.go,main,18) newl

(With the NOTRUNC or RETURN option, TRUNC is ignored.)

The BASENAME configuration value controls the removal of the "path" part of file names. If 0 no changes are made (which may mean long, not so easy to parse lines); if 1 the entire path is removed; if 2 the GOROOT and GOPATH strings are removed.

Example of BASENAME = 2:

d.Log:
(src/runtime/proc.go,runtime.main,198)(src/d.test.go,main,17) nine: 9
(src/runtime/proc.go,runtime.main,198)(src/d.test.go,main,21) newlines\r\n
(src/d.test.go,main,22)(src/d.test.go,a,29) <true>
(src/d.test.go,main,22)(src/d.test.go,a,30) <false>
...

However, for calls in packages, even a value of 2 can make for long call strings; like this hypothetical output:

(src/r/reg/reg.go,r/reg.Parserule,54)(src/r/reg/args.go,r/reg.Parseargs,101) <empty>

One can imagine output from packages in a location like this would be:

src/github.com/myd/reg

A second constant has been created for displaying the basename of functions, BASEFNAME; and if both are 1 the output is a bit cleaner:

(reg.go,reg.Parserule,54)(args.go,reg.Parseargs,101) <empty>

The name Bug() rather than Log() to log the diagnostics is because this code was ported from the "PHP Debug API". This package was going to be "debug", but that might conflict with existing packages and with the concept of "a debugger". (Also, this package is not like a typical "logger".) So the code description Diagnostic Logging was chosen. (And at first I felt the sound of "d bug" had a nice ring to it.)

An additional two arguments have been added, two ints, for the number of calls and the start of the calls respectively (changing just the depth is a more likely need):

d.Bug(0x1a,0,4)

(d.test.go,main,32)(d.test.go,a,43)(d.test.go,b,49)(d.test.go,c,60) 26

func Bugf

func Bugf(arg ...interface{})

Bugf is similar to Printf() for a diagnostic, with the limitation that format width and precision qualifiers are not supported. In addition, if the first argument is an int it will be used as the option value for Bug().

func Bugs

func Bugs(arg ...interface{})

Bugs calls Bug() with each of it's arguments; the first argument is for any option(s) to be passed to Bug(); all others get formatted by Bug() first - the result returned and not queued - which are concatenated for the final call to Bug().

func Caller

func Caller(n int) (string, string, string)

Caller returns file, function and line number strings n back from the caller.

func Def

func Def()

Def is for defer (useful at the start of main()) and is to capture any panics. It outputs the panic message with the call string (internally generated) of where the panic occurred. It then runs End().

func End

func End()

End outputs LOGMSG followed by the diagnostic queue, with each string appended with EOL. (The reasoning behind EOL as a configuration value is to help in transitioning to other environments, such as HTML output.)

Does nothing if logging not enabled or an empty queue or End() previously called.

func Err

func Err()

Err causes a panic to test Def().

func Exit

func Exit(a ...interface{})

Exit terminates the program with a message or a value to return:

(exit)(d.test.go,main,38) exited

The argument can be empty, a number for the exit value or a string for a message. (It also calls End() which will only display the log queue once.)

func Log

func Log(log ...interface{}) int

Log turns logging on or off; the use of ...interface{} is so that passing no argument returns the log level. The log level default is OFF (0).

A log value of ON (1) enables logging, with the default to queue each diagnostic to be displayed to stderr upon program termination.

A log value of PRINT (2) enables logging with each diagnostic displayed immediately (to stdout).

A log value of VSHARP formats variables with "%#v" instead of "%+v".

A call of Log(OFF) (0) after logging was enabled will turn it off, preventing any queued diagnostics from being displayed. (And can be turned on again, retaining what was in the queue, with any intervening diagnostics not stored.)

func Opt

func Opt(args ...interface{}) string

Opt sets or gets a runtime option, which are:

"func" : ""
"file" : ""
"nofunc" : ""
"nofile" : ""

Which filter diagnostics by function or file name: only from "func"; ignore from "nofunc"; only from "file"; ignore from "file". Values should be space separated for multiple names. With one argument it returns the value of the key. With two arguments is returns the previous value of the key.

A Bug() option of ALWAYS will bypass the filter options.

The options:

"count" : 2
"start" : 3

set the default count and start of the calls string defaults.

See d.test.go for examples.

func Types

func Types(types ...interface{})

Types stores defined types to be displayed with "$#v". Call with more than one type or multiple times with different types.

Change Log

Version 1.5

  • Fixed code comments as per godoc.
  • Fixed the "func" and "file" runtime options.
  • Added additional case for BASEFNAME.
  • Added EMPTY as a configuration option.
  • Added funcs Bugf(), Bugs() and Calls().
  • Added "count" and "start" as runtime options.
  • Added Bug() options VPLUS, NOQ, NOESC, NOTRUNC, GET, HERENOW.

Version 1.4

  • Fixed VSHARP as a log value.
  • Added Exit() and BASEFNAME.
  • Added options to control start and depth of call trace.

Version 1.3

  • Added Types() and ALWAYS and VSHARP.

Version 1.2

  • Fixed "nofunc" and "nofile" to operate as defined.
  • Added ability to use multiple names for those (and the "file") options.
  • Added Def() and Err(); changed caller() to Caller().
  • Removed CONVERTNL and added ESC and ESCSTR to escape all characters < 32.

Version 1.1

  • Added Opt() function and support for it.
  • Added const CATTYPE, TRUNC; expanded upon BASENAME.
  • Changed Log() constants a bit.
  • Changed end diagnostics to output to stderr.

--

Copyright © 2018 Andova Begarin.
Distributed under the Simplified BSD License.
You can’t perform that action at this time.