Initial commit
commit
e02498ec0e
|
@ -0,0 +1,5 @@
|
|||
# Emacs
|
||||
*~
|
||||
|
||||
# Build output
|
||||
*.test
|
|
@ -0,0 +1,24 @@
|
|||
# BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2022, Sean Hickey (Wisellama)
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,11 @@
|
|||
all: linter test
|
||||
|
||||
test:
|
||||
go test
|
||||
|
||||
lint: linter
|
||||
linter:
|
||||
golangci-lint run
|
||||
|
||||
goimports_everything:
|
||||
find . -name "*.go" -exec goimports -w {} \;
|
|
@ -0,0 +1,13 @@
|
|||
# gopackagebase
|
||||
|
||||
```sh
|
||||
go get git.wisellama.rocks/Wisellama/gopackagebase@v0.0.1
|
||||
```
|
||||
|
||||
This modules contains basic setup and initialization functions that
|
||||
are useful to run at the beginning of a program. This integrates with
|
||||
[gosimpleconf][1] to also support parsing config files.
|
||||
|
||||
I wanted to create this package to centralize some of the basic setup
|
||||
boilerplate I had started to collect in my Go projects. This
|
||||
repository may be considered an anti-pattern, but it fits my use-case.
|
|
@ -0,0 +1,5 @@
|
|||
module git.wisellama.rocks/gopackagebase
|
||||
|
||||
go 1.17
|
||||
|
||||
require git.wisellama.rocks/Wisellama/gosimpleconf v0.1.0
|
|
@ -0,0 +1,2 @@
|
|||
git.wisellama.rocks/Wisellama/gosimpleconf v0.1.0 h1:Z2FAzARct8ShV4NSueC/y+PyuSQVcyo4WnW7GoZ9L10=
|
||||
git.wisellama.rocks/Wisellama/gosimpleconf v0.1.0/go.mod h1:Gg1vUTBRZD7qcXvdF8L50PsnL9coLt/XbWa5BwSDN/M=
|
|
@ -0,0 +1,53 @@
|
|||
package gopackagebase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"git.wisellama.rocks/Wisellama/gosimpleconf"
|
||||
)
|
||||
|
||||
type BaseConfig struct {
|
||||
Ctx context.Context
|
||||
Cancel func()
|
||||
ConfigMap gosimpleconf.ConfigMap
|
||||
}
|
||||
|
||||
// Initialize will configure all of the base stuff that we want
|
||||
// commonly set up at the beginning of a Go project.
|
||||
func Initialize(configFile string, defaultConfig gosimpleconf.ConfigMap) (*BaseConfig, error) {
|
||||
var err error
|
||||
|
||||
ctx, cancel := InitSignalHandler()
|
||||
|
||||
err = InitRNG()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configMap, err := gosimpleconf.ConfigureWithDefaults(configFile, defaultConfig)
|
||||
if err != nil {
|
||||
log.Fatalf("error during configure: %v\n", err)
|
||||
}
|
||||
|
||||
writeToFile := gosimpleconf.Bool(configMap["log.writeToFile"])
|
||||
logFilename := configMap["log.file"]
|
||||
|
||||
logWriter, err := SetupLogging(writeToFile, logFilename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fullCancel := func() {
|
||||
logWriter.Cleanup()
|
||||
cancel()
|
||||
}
|
||||
|
||||
baseConfig := BaseConfig{
|
||||
Ctx: ctx,
|
||||
Cancel: fullCancel,
|
||||
ConfigMap: configMap,
|
||||
}
|
||||
|
||||
return &baseConfig, nil
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package gopackagebase
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type logWriter struct {
|
||||
writeToFile bool
|
||||
logFile *os.File
|
||||
}
|
||||
|
||||
func (w *logWriter) Write(bytes []byte) (int, error) {
|
||||
t := time.Now().UTC().Format(time.RFC3339)
|
||||
return fmt.Fprintf(w.logFile, "%v %v", t, string(bytes))
|
||||
}
|
||||
|
||||
func (w *logWriter) Cleanup() {
|
||||
defer w.logFile.Close()
|
||||
}
|
||||
|
||||
func SetupLogging(writeToFile bool, logFilename string) (*logWriter, error) {
|
||||
var err error
|
||||
log.SetFlags(0)
|
||||
|
||||
logFile := os.Stdout
|
||||
if writeToFile {
|
||||
logFile, err = os.OpenFile(logFilename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
writer := &logWriter{
|
||||
writeToFile: writeToFile,
|
||||
logFile: logFile,
|
||||
}
|
||||
log.SetOutput(writer)
|
||||
|
||||
return writer, nil
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package gopackagebase
|
||||
|
||||
import (
|
||||
crypto_rand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
math_rand "math/rand"
|
||||
)
|
||||
|
||||
// InitRNG will grab some cryptographically secure bytes to use as the
|
||||
// seed for the non-crypto random number generator. Suggested on
|
||||
// StackOverflow to avoid time-based seeds:
|
||||
// https://stackoverflow.com/a/54491783
|
||||
func InitRNG() error {
|
||||
var b [8]byte
|
||||
_, err := crypto_rand.Read(b[:])
|
||||
if err != nil {
|
||||
err = fmt.Errorf("error seeding random number generator: %w", err)
|
||||
return err
|
||||
}
|
||||
math_rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package gopackagebase
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
func InitSignalHandler() (context.Context, func()) {
|
||||
// Setup some initial context for gracefully killing the program
|
||||
// with system interrupt signals like ctrl+c
|
||||
// https://pace.dev/blog/2020/02/17/repond-to-ctrl-c-interrupt-signals-gracefully-with-context-in-golang-by-mat-ryer.html
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
signalChan := make(chan os.Signal, 1)
|
||||
signal.Notify(signalChan, os.Interrupt)
|
||||
|
||||
// Start a goroutine to listen for signal interrupts
|
||||
go func() {
|
||||
select {
|
||||
case <-signalChan:
|
||||
// Graceful exit on ctrl+c
|
||||
log.Printf("Attempting to exit gracefully...\n")
|
||||
cancel()
|
||||
case <-ctx.Done():
|
||||
}
|
||||
<-signalChan // Hard exit on second ctrl+c
|
||||
log.Printf("Hard kill\n")
|
||||
os.Exit(2)
|
||||
}()
|
||||
|
||||
// Return a new cancel function that also sends the stop signal.
|
||||
killFunc := func() {
|
||||
signal.Stop(signalChan)
|
||||
cancel()
|
||||
}
|
||||
|
||||
return ctx, killFunc
|
||||
}
|
Loading…
Reference in New Issue