hzDocs

Using is Detectors

is.inDeveloping()?

介绍

hedzr.is 是一个以环境检测函数为主的基础支持库。

is provides numerous detectors for checking the states of environment (build, executive, ...).

is 中提供了大量 Golang 运行环境检测工具,有的独立工作,有的需要配合 go build tags 协同工作。 总的来说,它们可以帮助你更好地确定当前运行环境,让你的代码能够据此进行适配。

Features

  • is.State(which) bool: the universal detector entry - via RegisterStateGetter(state string, getter func() bool) to add your own ones. Since v0.5.11
  • is.Env() holds a global struct for CLI app basic states, such as: verbose/quiet/debug/trace....
    • DebugMode/DebugLevel, TraceMode/TraceLevel, ColorMode, ...
  • is.InDebugging() bool, is.InTesting() bool, and is.InTracing() bool, ....
  • is.DebugBuild() bool.
  • is.K8sBuild() bool, is.DockerBuild() bool, ....
  • is.ColoredTty() bool, ....
  • is.Color() to get an indexer for the functions in our term/color subpackage, ...
  • Terminal Colorizer, Detector, unescape tools.
  • stringtool: RandomStringPure, case-converters ...
  • basics: closable, closer, signals.
    • easier Press any key to exit... prompt: is.Signals().Catch()
  • exec: Run, RunWithOutput, Sudo, ...
  • go1.23.7+ required since v0.7.0
  • go 1.22.7+ required

To using environment detecting utilities better and smoother, some terminal (and stringtool, basics) tools are bundled together.

Since v0.6.0, is.InDebugging() checks if the running process' parent is dlv. The old DebugMode and DebugBuild are still work:

  • InDebugging: checks this process is being debugged by dlv.
  • DebugBuild: -tags=delve is set at building.
  • DebugMode: --debug is specified at command line.

分类

基础环境

  • InDebugging: checks this process is being debugged by dlv.

  • DebugBuild: -tags=delve is set at building.

  • DebugMode: --debug is specified at command line.

  • is.InTracing / InTestingT

  • is.InTesting / InTestingT

  • is.InDevelopingTime

  • is.InVscodeTerminal

  • is.InK8s

  • is.InIstio

  • is.InDocker / InDockerEnvSimple

  • Build (need -tags=xx)

    • is.K8sBuild: need -tags=k8s present
    • is.IstioBuild: need -tags=istio present
    • is.DockerBuild: need -tags=docker present
    • is.VerboseBuild: need -tags=verbose present
    • is.DebugBuild: need -tags=delve present
    • buildtags.IsBuildTagExists

States / Env

  • VerboseModeEnabled
  • GetVerboseLevel / SetVerboseMode / SetVerboseLevel
  • QuietModeEnabled
  • GetQuietLevel / SetQuietMode / SetQuietLevel
  • NoColorMode
  • GetNoColorLevel / SetNoColorMode / SetNoColorLevel
  • DebugMode
  • GetDebugLevel / SetDebugMode / SetDebugLevel
  • Tracing
  • TraceMode
  • GetTraceLevel / SetTraceMode / SetTraceLevel

Terminal / Tty

  • is.Terminal(file)
  • is.TerminalFd(fd)
  • is.Tty(wr)
  • is.ColoredTty(wr)
  • is.AnsiEscaped(s) (IsTtyEscaped(s))
  • StripEscapes(s)
  • ReadPassword
  • GetTtySize
  • is.GetTtySizeByName(filename) (cols,rows,err)
  • is.GetTtySizeByFile(file) (cols,rows,err)
  • is.GetTtySizeByFd(fd) (cols,rows,err)
  • StartupByDoubleClick() bool

Basics

  • closers
    • Peripheral, Closable, Closer
    • RegisterClosable
    • RegisterClosers
    • RegisterCloseFns
  • is.Signals().Catcher()
  • is.FileExists(filepath)
  • is.ToBool, StringToBool

utilities

  • dir subpackage
  • exec subpackage
  • term/color subpackage
    • escaping tools: GetCPT()/GetCPTC()/GetCPTNC()
    • Highlight, Dimf, Text, Dim, ToDim, ToHighlight, ToColor, ...

Usages

package main
 
import (
    "context"
    "fmt"
    "log/slog"
    "os"
    "sync"
    "time"
 
    "github.com/hedzr/is"
    "github.com/hedzr/is/basics"
    "github.com/hedzr/is/term/color"
)
 
func main() {
    defer basics.Close()
 
    is.RegisterStateGetter("custom", func() bool { return is.InVscodeTerminal() })
 
    println(is.InTesting())
    println(is.State("in-testing"))
    println(is.State("custom")) // detects a state with custom detector
    println(is.Env().GetDebugLevel())
    if is.InDebugMode() {
        slog.SetDefault(slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{AddSource: true, Level: slog.LevelDebug})))
    }
 
    // or:
    //    is.Color().GetColorTranslator().Translate("<b>bold</b>")
    fmt.Printf("%v", color.GetCPT().Translate(`<code>code</code> | <kbd>CTRL</kbd>
        <b>bold / strong / em</b>
        <i>italic / cite</i>
        <u>underline</u>
        <mark>inverse mark</mark>
        <del>strike / del </del>
        <font color="green">green text</font>
`, color.FgDefault))
 
    ctx, cancel := context.WithCancel(context.Background())
    catcher := is.Signals().Catch()
    catcher.
        WithPrompt("Press CTRL-C to quit...").
        WithOnLoop(dbStarter, cacheStarter, mqStarter).
        WithOnSignalCaught(func(sig os.Signal, wg *sync.WaitGroup) {
            println()
            slog.Info("signal caught", "sig", sig)
            cancel() // cancel user's loop, see Wait(...)
        }).
        Wait(func(stopChan chan<- os.Signal, wgDone *sync.WaitGroup) {
            slog.Debug("entering looper's loop...")
            go func() {
                // to terminate this app after a while automatically:
                time.Sleep(10 * time.Second)
                stopChan <- os.Interrupt
            }()
            <-ctx.Done()  // waiting until any os signal caught
            wgDone.Done() // and complete myself
        })
}
 
func dbStarter(stopChan chan<- os.Signal, wgDone *sync.WaitGroup) {
    // initializing database connections...
    // ...
    wgDone.Done()
}
 
func cacheStarter(stopChan chan<- os.Signal, wgDone *sync.WaitGroup) {
    // initializing redis cache connections...
    // ...
    wgDone.Done()
}
 
func mqStarter(stopChan chan<- os.Signal, wgDone *sync.WaitGroup) {
    // initializing message queue connections...
    // ...
    wgDone.Done()
}

Result is similar with:

image-20240113071930661

NOTE that is.Signals().Catch() will produce a prompt and enter a infinite loop to wait for user's keystroke pressed.

Build Tags

Some functions want special buildtags presented. These are including:

  • verbose: See VerboseBuild, ...
  • delve: See DebugBuild, ...
  • k8s: See K8sBuild
  • istio: See IstioBuild
  • docker: See DockerBuild
  • ...
  • buildtags.IsBuildTagExists(tag) bool

Colorizes

The test codes:

import "github.com/hedzr/is/term/color"
 
func TestGetCPT(t *testing.T) {
t.Logf("%v", color.GetCPT().Translate(`<code>code</code> | <kbd>CTRL</kbd>
    <b>bold / strong / em</b>
    <i>italic / cite</i>
    <u>underline</u>
    <mark>inverse mark</mark>
    <del>strike / del </del>
    <font color="green">green text</font>
    `, color.FgDefault))
}

Result:

image-20231107100150520

And more:

func TestStripLeftTabs(t *testing.T) {
t.Logf("%v", color.StripLeftTabs(`
 
        <code>code</code>
    NC Cool
     But it's tight.
      Hold On!
    Hurry Up.
    `))
}
 
func TestStripHTMLTags(t *testing.T) {
t.Logf("%v", color.StripHTMLTags(`
 
        <code>code</code>
    NC Cool
     But it's tight.
      Hold On!
    Hurry Up.
    `))
}

cmdr 相集成

Closers

is 提供一个子包 closers 来抽象你的基础设施。

type Peripheral interface {
  Close()
}

一个基础设施意味着一个需要在应用程序结束前实施其关闭操作的对象,可以是外部资源如数据库连接等,也可以是一个文件句柄。

在你的 app 中,可以使用 RegisterPeripheral 注册一个基础设施,并提供 defer 调用以便关闭所有这些对象。

func main() {
  defer is.Closers().Close()
 
  // ...RegisterPeripheral yours
}
 
The `Closers()` collects all closable objects and allow shutting down them at once.
 
```go
package main
 
import (
    "os"
 
    "github.com/hedzr/is/basics"
)
 
type redisHub struct{}
 
func (s *redisHub) Close() {
    // close the connections to redis servers
    println("redis connections closed")
}
 
func main() {
    defer basics.Close()
 
    tmpFile, _ := os.CreateTemp(os.TempDir(), "1*.log")
    basics.RegisterClosers(tmpFile)
 
    basics.RegisterCloseFn(func() {
        // do some shutdown operations here
        println("close single functor")
    })
 
    basics.RegisterPeripheral(&redisHub{})
}

with cmdr

cmdr 在它的工作流程中会自动执行相似的调用以便关闭已经注册的基础设施,因此 is + cmdr 可以更无缝地协同工作。

具体实施可以参考:

Signals

Signals() could catch OS signals and entering a infinite loop.

is.Signals() 能够拦截 POSIX 信号并回调你的响应函数。

For example, a tcp server could be:

package main
 
import (
    "context"
    "os"
    "sync"
 
    "github.com/hedzr/go-socketlib/net"
    "github.com/hedzr/is"
    logz "github.com/hedzr/logg/slog"
)
 
func main() {
    ctx, cancel := context.WithCancel(context.Background())
    defer cancel()
 
    logger := logz.New("new-dns")
    server := net.NewServer(":7099",
        net.WithServerOnListening(func(ss net.Server, l stdnet.Listener) {
            go runClient(ctx, ss, l)
        }),
        net.WithServerLogger(logger.WithSkip(1)),
    )
    defer server.Close()
 
    // make a catcher so that it can catch ths signals,
    catcher := is.Signals().Catch()
    catcher.
        // WithVerboseFn(func(msg string, args ...any) {
        //     logz.WithSkip(2).Verbose(fmt.Sprintf("[verbose] %s", fmt.Sprintf(msg, args...)))
        // }).
        WithOnSignalCaught(func(sig os.Signal, wg *sync.WaitGroup) {
            println()
            logz.Debug("signal caught", "sig", sig)
            if err := server.Shutdown(); err != nil {
                logz.Error("server shutdown error", "err", err)
            }
            cancel()
        }).
        Wait(func(stopChan chan<- os.Signal, wgShutdown *sync.WaitGroup) {
            logz.Debug("entering looper's loop...")
 
            server.WithOnShutdown(func(err error, ss net.Server) { wgShutdown.Done() })
            err := server.ListenAndServe(ctx, nil)
            if err != nil {
                logz.Fatal("server serve failed", "err", err)
            }
        })
}
 
func runClient(ctx context.Context, ss net.Server, l stdnet.Listener) {
    c := net.NewClient()
 
    if err := c.Dial("tcp", ":7099"); err != nil {
        logz.Fatal("connecting to server failed", "err", err, "server-endpoint", ":7099")
    }
    logz.Info("[client] connected", "server.addr", c.RemoteAddr())
    c.RunDemo(ctx)
}

:end:

How is this guide?

Edit on GitHub

Last updated on

On this page