Compare commits

..

3 Commits

Author SHA1 Message Date
e8ffec6b7e updated hws to use new hlog and ezconf 2026-02-25 22:17:25 +11:00
1745458a95 updated hlog to use new ezconf 2026-02-25 22:06:27 +11:00
f3d6a01105 added new way to integrate with ezconf 2026-02-25 22:01:25 +11:00
13 changed files with 112 additions and 100 deletions

View File

@@ -1,8 +1,24 @@
package ezconf
// Integration is an interface that packages can implement to provide
type Integration struct {
Name string
ConfigPointer any
ConfigFunc func() (any, error)
GroupName string
}
func NewIntegration(name, groupname string, cfgptr any, cfgfunc func() (any, error)) *Integration {
return &Integration{
name,
cfgptr,
cfgfunc,
groupname,
}
}
// IntegrationDepr is an interface that packages can implement to provide
// easy integration with ezconf
type Integration interface {
type IntegrationDepr interface {
// Name returns the name to use when registering the config
Name() string
@@ -16,8 +32,34 @@ type Integration interface {
GroupName() string
}
// AddIntegration registers a package using an Integration object returned by another package
func (cl *ConfigLoader) AddIntegration(integration *Integration) error {
// Add config struct for tag parsing
configPtr := integration.ConfigPointer
if err := cl.AddConfigStruct(configPtr, integration.GroupName); err != nil {
return err
}
// Add config function
if err := cl.AddConfigFunc(integration.Name, integration.ConfigFunc); err != nil {
return err
}
return nil
}
// AddIntegrations registers multiple integrations at once
func (cl *ConfigLoader) AddIntegrations(integrations ...*Integration) error {
for _, integration := range integrations {
if err := cl.AddIntegration(integration); err != nil {
return err
}
}
return nil
}
// RegisterIntegration registers a package that implements the Integration interface
func (cl *ConfigLoader) RegisterIntegration(integration Integration) error {
func (cl *ConfigLoader) RegisterIntegration(integration IntegrationDepr) error {
// Add config struct for tag parsing
configPtr := integration.ConfigPointer()
if err := cl.AddConfigStruct(configPtr, integration.GroupName()); err != nil {
@@ -33,7 +75,7 @@ func (cl *ConfigLoader) RegisterIntegration(integration Integration) error {
}
// RegisterIntegrations registers multiple integrations at once
func (cl *ConfigLoader) RegisterIntegrations(integrations ...Integration) error {
func (cl *ConfigLoader) RegisterIntegrations(integrations ...IntegrationDepr) error {
for _, integration := range integrations {
if err := cl.RegisterIntegration(integration); err != nil {
return err

View File

@@ -210,5 +210,5 @@ func TestRegisterIntegrations_PartialFailure(t *testing.T) {
func TestIntegration_Interface(t *testing.T) {
// Verify that mockIntegration implements Integration interface
var _ Integration = (*mockIntegration)(nil)
var _ IntegrationDepr = (*mockIntegration)(nil)
}

View File

@@ -9,11 +9,11 @@ import (
// It can be populated from environment variables using ConfigFromEnv
// or created programmatically.
type Config struct {
LogLevel Level // ENV LOG_LEVEL: Log level for the logger - trace, debug, info, warn, error, fatal, panic (default: info)
LogOutput string // ENV LOG_OUTPUT: Output destination for logs - console, file, or both (default: console)
LogDir string // ENV LOG_DIR: Directory path for log files (required when LOG_OUTPUT is "file" or "both")
LogFileName string // ENV LOG_FILE_NAME: Name of the log file (required when LOG_OUTPUT is "file" or "both")
LogAppend bool // ENV LOG_APPEND: Append to existing log file or overwrite (default: true)
LogLevel Level `ezconf:"LOG_LEVEL,description:Log level for the logger - trace debug info warn error fatal panic,default:info"`
LogOutput string `ezconf:"LOG_OUTPUT,description:Output destination for logs - console file or both,default:console"`
LogDir string `ezconf:"LOG_DIR,description:Directory path for log files,required:when LOG_OUTPUT is file or both"`
LogFileName string `ezconf:"LOG_FILE_NAME,description:Name of the log file,required:when LOG_OUTPUT is file or both"`
LogAppend bool `ezconf:"LOG_APPEND,description:Append to existing log file or overwrite,default:true"`
}
// ConfigFromEnv loads logger configuration from environment variables.

View File

@@ -1,35 +1,9 @@
package hlog
import "runtime"
import "git.haelnorr.com/h/golib/ezconf"
// EZConfIntegration provides integration with ezconf for automatic configuration
type EZConfIntegration struct{}
// PackagePath returns the path to the hlog package for source parsing
func (e EZConfIntegration) PackagePath() string {
_, filename, _, _ := runtime.Caller(0)
// Return directory of this file
return filename[:len(filename)-len("/ezconf.go")]
}
// ConfigFunc returns the ConfigFromEnv function for ezconf
func (e EZConfIntegration) ConfigFunc() func() (interface{}, error) {
return func() (interface{}, error) {
return ConfigFromEnv()
}
}
// Name returns the name to use when registering with ezconf
func (e EZConfIntegration) Name() string {
return "hlog"
}
// GroupName returns the display name for grouping environment variables
func (e EZConfIntegration) GroupName() string {
return "HLog"
}
// NewEZConfIntegration creates a new EZConf integration helper
func NewEZConfIntegration() EZConfIntegration {
return EZConfIntegration{}
// NewEZConfIntegration creates a new EZConf integration
func NewEZConfIntegration() *ezconf.Integration {
return ezconf.NewIntegration("hlog", "HLog",
&Config{}, func() (any, error) { return ConfigFromEnv() })
}

View File

@@ -7,6 +7,8 @@ require (
github.com/rs/zerolog v1.34.0
)
require git.haelnorr.com/h/golib/ezconf v0.2.1
require (
git.haelnorr.com/h/golib/env v0.9.1
github.com/mattn/go-colorable v0.1.13 // indirect

View File

@@ -1,5 +1,7 @@
git.haelnorr.com/h/golib/env v0.9.1 h1:2Vsj+mJKnO5f1Md1GO5v9ggLN5zWa0baCewcSHTjoNY=
git.haelnorr.com/h/golib/env v0.9.1/go.mod h1:glUQVdA1HMKX1avTDyTyuhcr36SSxZtlJxKDT5KTztg=
git.haelnorr.com/h/golib/ezconf v0.2.1 h1:axMyKtgO9Zk6E8CrYrLpMzifvpjz73yxCQq0lOtuhck=
git.haelnorr.com/h/golib/ezconf v0.2.1/go.mod h1:rETDcjpcEyyeBgCiZSU617wc0XycwZSC5+IAOtXmwP8=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=

View File

@@ -7,13 +7,13 @@ import (
)
type Config struct {
Host string // ENV HWS_HOST: Host to listen on (default: 127.0.0.1)
Port uint64 // ENV HWS_PORT: Port to listen on (default: 3000)
GZIP bool // ENV HWS_GZIP: Flag for GZIP compression on requests (default: false)
ReadHeaderTimeout time.Duration // ENV HWS_READ_HEADER_TIMEOUT: Timeout for reading request headers in seconds (default: 2)
WriteTimeout time.Duration // ENV HWS_WRITE_TIMEOUT: Timeout for writing requests in seconds (default: 10)
IdleTimeout time.Duration // ENV HWS_IDLE_TIMEOUT: Timeout for idle connections in seconds (default: 120)
ShutdownDelay time.Duration // ENV HWS_SHUTDOWN_DELAY: Delay in seconds before server shutsdown when Shutdown is called (default: 5)
Host string `ezconf:"HWS_HOST,description:Host to listen on,default:127.0.0.1"`
Port uint64 `ezconf:"HWS_PORT,description:Port to listen on,default:3000"`
GZIP bool `ezconf:"HWS_GZIP,description:Flag for GZIP compression on requests,default:false"`
ReadHeaderTimeout time.Duration `ezconf:"HWS_READ_HEADER_TIMEOUT,description:Timeout for reading request headers in seconds,default:2"`
WriteTimeout time.Duration `ezconf:"HWS_WRITE_TIMEOUT,description:Timeout for writing requests in seconds,default:10"`
IdleTimeout time.Duration `ezconf:"HWS_IDLE_TIMEOUT,description:Timeout for idle connections in seconds,default:120"`
ShutdownDelay time.Duration `ezconf:"HWS_SHUTDOWN_DELAY,description:Delay in seconds before server shuts down when Shutdown is called,default:5"`
}
// ConfigFromEnv returns a Config struct loaded from the environment variables

View File

@@ -1,35 +1,9 @@
package hws
import "runtime"
import "git.haelnorr.com/h/golib/ezconf"
// EZConfIntegration provides integration with ezconf for automatic configuration
type EZConfIntegration struct{}
// PackagePath returns the path to the hws package for source parsing
func (e EZConfIntegration) PackagePath() string {
_, filename, _, _ := runtime.Caller(0)
// Return directory of this file
return filename[:len(filename)-len("/ezconf.go")]
}
// ConfigFunc returns the ConfigFromEnv function for ezconf
func (e EZConfIntegration) ConfigFunc() func() (any, error) {
return func() (any, error) {
return ConfigFromEnv()
}
}
// Name returns the name to use when registering with ezconf
func (e EZConfIntegration) Name() string {
return "hws"
}
// GroupName returns the display name for grouping environment variables
func (e EZConfIntegration) GroupName() string {
return "HWS"
}
// NewEZConfIntegration creates a new EZConf integration helper
func NewEZConfIntegration() EZConfIntegration {
return EZConfIntegration{}
// NewEZConfIntegration creates a new EZConf integration
func NewEZConfIntegration() *ezconf.Integration {
return ezconf.NewIntegration("hws", "HWS",
&Config{}, func() (any, error) { return ConfigFromEnv() })
}

View File

@@ -4,22 +4,24 @@ go 1.25.5
require (
git.haelnorr.com/h/golib/env v0.9.1
git.haelnorr.com/h/golib/hlog v0.9.0
git.haelnorr.com/h/golib/hlog v0.11.0
git.haelnorr.com/h/golib/notify v0.1.0
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.11.1
k8s.io/apimachinery v0.35.0
)
require git.haelnorr.com/h/golib/ezconf v0.2.1
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/gobwas/glob v0.2.3
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rs/zerolog v1.34.0 // indirect
golang.org/x/sys v0.12.0 // indirect
golang.org/x/sys v0.41.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect

View File

@@ -1,7 +1,9 @@
git.haelnorr.com/h/golib/env v0.9.1 h1:2Vsj+mJKnO5f1Md1GO5v9ggLN5zWa0baCewcSHTjoNY=
git.haelnorr.com/h/golib/env v0.9.1/go.mod h1:glUQVdA1HMKX1avTDyTyuhcr36SSxZtlJxKDT5KTztg=
git.haelnorr.com/h/golib/hlog v0.9.0 h1:ib8n2MdmiRK2TF067p220kXmhDe9aAnlcsgpuv+QpvE=
git.haelnorr.com/h/golib/hlog v0.9.0/go.mod h1:oOlzb8UVHUYP1k7dN5PSJXVskAB2z8EYgRN85jAi0Zk=
git.haelnorr.com/h/golib/ezconf v0.2.1 h1:axMyKtgO9Zk6E8CrYrLpMzifvpjz73yxCQq0lOtuhck=
git.haelnorr.com/h/golib/ezconf v0.2.1/go.mod h1:rETDcjpcEyyeBgCiZSU617wc0XycwZSC5+IAOtXmwP8=
git.haelnorr.com/h/golib/hlog v0.11.0 h1:tCT8HWs51Nbin58sCTLcq5re6CZqo5/IHCzk3G+S3vQ=
git.haelnorr.com/h/golib/hlog v0.11.0/go.mod h1:HjhXS5G3A0BwOZq7nu2qpNBtvOFiCa1GbAuBRxAkYqs=
git.haelnorr.com/h/golib/notify v0.1.0 h1:xdf6zd21F6n+SuGTeJiuLNMf6zFXMvwpKD0gmNq8N10=
git.haelnorr.com/h/golib/notify v0.1.0/go.mod h1:ARqaRmCYb8LMURhDM75sG+qX+YpqXmUVeAtacwjHjBc=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@@ -12,11 +14,13 @@ github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ4
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -28,8 +32,9 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@@ -17,6 +17,10 @@ import (
func Test_GZIP_Compression(t *testing.T) {
var buf bytes.Buffer
dbg, _ := hlog.LogLevel("debug")
logcfg := &hlog.Config{
LogLevel: dbg,
}
t.Run("GZIP enabled compresses response", func(t *testing.T) {
server, err := hws.NewServer(&hws.Config{
Host: "127.0.0.1",
@@ -25,7 +29,7 @@ func Test_GZIP_Compression(t *testing.T) {
})
require.NoError(t, err)
logger, err := hlog.NewLogger(hlog.LogLevel("Debug"), &buf, nil, "")
logger, err := hlog.NewLogger(logcfg, &buf)
require.NoError(t, err)
err = server.AddLogger(logger)
@@ -80,7 +84,7 @@ func Test_GZIP_Compression(t *testing.T) {
})
require.NoError(t, err)
logger, err := hlog.NewLogger(hlog.LogLevel("Debug"), &buf, nil, "")
logger, err := hlog.NewLogger(logcfg, &buf)
require.NoError(t, err)
err = server.AddLogger(logger)
@@ -131,7 +135,7 @@ func Test_GZIP_Compression(t *testing.T) {
})
require.NoError(t, err)
logger, err := hlog.NewLogger(hlog.LogLevel("Debug"), &buf, nil, "")
logger, err := hlog.NewLogger(logcfg, &buf)
require.NoError(t, err)
err = server.AddLogger(logger)
@@ -179,20 +183,20 @@ func Test_GzipResponseWriter(t *testing.T) {
t.Run("Can write through gzip writer", func(t *testing.T) {
var buf bytes.Buffer
gzWriter := gzip.NewWriter(&buf)
testData := []byte("Test data to compress")
n, err := gzWriter.Write(testData)
require.NoError(t, err)
assert.Equal(t, len(testData), n)
err = gzWriter.Close()
require.NoError(t, err)
// Decompress and verify
gzReader, err := gzip.NewReader(&buf)
require.NoError(t, err)
defer gzReader.Close()
decompressed, err := io.ReadAll(gzReader)
require.NoError(t, err)
assert.Equal(t, testData, decompressed)
@@ -215,9 +219,9 @@ func Test_GzipResponseWriter(t *testing.T) {
req := httptest.NewRequest("GET", "/test", nil)
req.Header.Set("Accept-Encoding", "gzip")
rr := httptest.NewRecorder()
wrapped.ServeHTTP(rr, req)
// Note: This is a simplified test
})
}

View File

@@ -25,6 +25,10 @@ func Test_AddLogger(t *testing.T) {
}
func Test_LogError_AllLevels(t *testing.T) {
dbg, _ := hlog.LogLevel("debug")
logcfg := &hlog.Config{
LogLevel: dbg,
}
t.Run("DEBUG level", func(t *testing.T) {
var buf bytes.Buffer
// Create server with logger explicitly set to Debug level
@@ -34,7 +38,7 @@ func Test_LogError_AllLevels(t *testing.T) {
})
require.NoError(t, err)
logger, err := hlog.NewLogger(hlog.LogLevel("debug"), &buf, nil, "")
logger, err := hlog.NewLogger(logcfg, &buf)
require.NoError(t, err)
err = server.AddLogger(logger)

View File

@@ -31,8 +31,12 @@ func createTestServer(t *testing.T, w io.Writer) *hws.Server {
ShutdownDelay: 0, // No delay for tests
})
require.NoError(t, err)
dbg, _ := hlog.LogLevel("debug")
logcfg := &hlog.Config{
LogLevel: dbg,
}
logger, err := hlog.NewLogger(hlog.LogLevel("Debug"), w, nil, "")
logger, err := hlog.NewLogger(logcfg, w)
require.NoError(t, err)
err = server.AddLogger(logger)
@@ -228,5 +232,4 @@ func Test_NewServer(t *testing.T) {
}
})
}
}