viper

package
v1.22.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 17, 2026 License: MIT Imports: 19 Imported by: 0

README

Viper Package

Go Version Tests Coverage

Enhanced configuration management wrapper for spf13/viper with remote provider support, custom decode hooks, and file watching capabilities.


Table of Contents


Overview

This package provides a production-ready wrapper around spf13/viper for configuration management in Go applications. It extends viper's functionality with remote configuration support, custom type conversion hooks, and enhanced error handling.

Design Philosophy
  1. Multi-Source: Support file, environment, remote, and default configurations
  2. Type-Safe: Custom decode hooks for complex type conversions
  3. Thread-Safe: Atomic operations and proper synchronization for concurrent access
  4. Flexible: Support for JSON, YAML, TOML, and other formats
  5. Observable: File watching with reload callbacks for dynamic configuration

Key Features

  • Multiple Configuration Sources:

    • File-based: JSON, YAML, TOML, HCL, INI, ENV, properties
    • Environment Variables: Automatic binding with custom prefix
    • Remote Providers: ETCD support with automatic reloading
    • Default Config: Fallback configuration via io.Reader
  • Advanced Unmarshalling:

    • Standard unmarshalling with type conversion
    • Exact unmarshalling (strict mode)
    • Key-specific unmarshalling for nested structures
    • Custom decode hooks for complex types
  • Dynamic Configuration:

    • File system watching with change detection
    • Remote configuration polling and updates
    • Reload callbacks for application reconfiguration
    • Key unsetting for runtime modifications
  • Type System:

    • Native support for all Go primitives
    • Time and duration parsing
    • Slices and maps
    • Custom type conversion via hooks
  • Thread Safety:

    • Atomic operations for state management
    • Concurrent-safe hook registration
    • Thread-safe configuration access

Installation

go get github.com/nabbar/golib/viper

Requirements:

  • Go 1.18 or higher
  • Dependencies automatically managed by Go modules

Architecture

Package Structure
viper/
├── interface.go         # Main Viper interface and factory
├── model.go            # Internal implementation and types
├── config.go           # Configuration initialization
├── viper.go            # Getter methods for all types
├── unmarshall.go       # Unmarshalling operations
├── hook.go             # Custom decode hook management
├── cleaner.go          # Configuration key unsetting
├── watch.go            # File and remote watching
├── remote.go           # Remote provider integration
├── errors.go           # Error codes and messages
└── doc.go              # Package documentation
Component Overview
┌─────────────────────────────────────────────────────────┐
│                    Viper Interface                       │
│   New(), Config(), Get*(), Unmarshal(), Watch()         │
└──────────────┬──────────────┬──────────────┬────────────┘
               │              │              │
      ┌────────▼─────┐  ┌────▼─────┐  ┌────▼────────┐
      │ Config Mgmt  │  │  Hooks   │  │   Watch     │
      │              │  │          │  │             │
      │ File, Env    │  │ Custom   │  │ FS, Remote  │
      │ Remote, Def  │  │ Decode   │  │ Callbacks   │
      └──────────────┘  └──────────┘  └─────────────┘
Component Purpose Thread-Safe
Config Multi-source configuration loading
Getters Type-safe value retrieval
Unmarshalling Struct binding with hooks
Hooks Custom type conversion
Watch Dynamic configuration updates
Remote ETCD integration
Configuration Priority

Configuration values are resolved in the following order (highest to lowest):

1. Explicit Set() calls
2. Environment variables (with prefix)
3. Configuration file
4. Remote configuration (ETCD)
5. Default values

Quick Start

Basic File Configuration

Load configuration from a file:

package main

import (
    "context"
    
    "github.com/nabbar/golib/logger"
    "github.com/nabbar/golib/logger/level"
    "github.com/nabbar/golib/viper"
)

func main() {
    ctx := func() context.Context { return context.Background() }
    log := func() logger.Logger { return logger.New(ctx) }
    
    v := viper.New(ctx, log)
    
    // Set configuration file
    if err := v.SetConfigFile("/etc/myapp/config.yaml"); err != nil {
        panic(err)
    }
    
    // Load configuration
    if err := v.Config(level.ErrorLevel, level.InfoLevel); err != nil {
        panic(err)
    }
    
    // Read values
    appName := v.GetString("app.name")
    port := v.GetInt("app.port")
    debug := v.GetBool("app.debug")
}
Environment Variables

Use environment variables with prefix:

v := viper.New(ctx, log)

// Set environment prefix
v.SetEnvVarsPrefix("MYAPP")

// Set home-based config
v.SetHomeBaseName("myapp")
v.SetConfigFile("") // Will look for ~/.myapp

// Load config (env vars override file values)
v.Config(level.ErrorLevel, level.InfoLevel)

// MYAPP_DATABASE_HOST overrides database.host
dbHost := v.GetString("database.host")
Unmarshal to Struct

Bind configuration to Go structs:

type AppConfig struct {
    Name    string
    Version string
    Server  struct {
        Host string
        Port int
    }
    Database struct {
        Driver   string
        Host     string
        Port     int
        User     string
        Password string
    }
}

v := viper.New(ctx, log)
v.SetConfigFile("config.yaml")
v.Config(level.ErrorLevel, level.InfoLevel)

var config AppConfig
if err := v.Unmarshal(&config); err != nil {
    panic(err)
}

// Or unmarshal specific key
var dbConfig DatabaseConfig
if err := v.UnmarshalKey("database", &dbConfig); err != nil {
    panic(err)
}
Default Configuration

Provide fallback configuration:

import "bytes"

v := viper.New(ctx, log)

// Set default config as fallback
v.SetDefaultConfig(func() io.Reader {
    defaultConfig := `{
        "app": {
            "name": "MyApp",
            "port": 8080,
            "debug": false
        }
    }`
    return bytes.NewReader([]byte(defaultConfig))
})

v.SetConfigFile("/etc/myapp/config.json")

// Will use default if file not found
err := v.Config(level.ErrorLevel, level.InfoLevel)
// err will indicate if default was used

Performance

Memory Efficiency

The viper wrapper maintains minimal memory overhead:

  • Configuration Storage: O(n) where n is number of keys
  • Hook Management: O(h) where h is number of hooks (max 255)
  • Watch Operations: O(1) goroutine for file/remote watching
Thread Safety

All operations are thread-safe through:

  • Atomic Operations: atomic.Uint32 for hook indexing
  • Context Storage: Thread-safe libctx.Config for hook storage
  • Viper Core: Underlying viper library is thread-safe
Throughput
Operation Performance Notes
Get Value ~100 ns/op Direct map lookup
Unmarshal ~10 µs/op Depends on struct size
File Watch ~1 ms/event fsnotify overhead
Remote Poll ~5s interval Configurable

Measured on AMD64, Go 1.21


Use Cases

This library is designed for scenarios requiring flexible configuration management:

Microservices

  • Centralized configuration via ETCD
  • Environment-specific overrides
  • Dynamic reconfiguration without restart
  • Secret management with secure remote providers

Cloud-Native Applications

  • 12-factor app compliance (config via environment)
  • Container-friendly configuration
  • Kubernetes ConfigMap integration
  • Multi-environment deployments

CLI Applications

  • User home directory configuration
  • Command-line flag integration
  • Configuration file discovery
  • Default values with overrides

Web Services

  • Hot-reload configuration changes
  • Feature flags and A/B testing
  • Multi-tenant configuration
  • Environment-based settings

Development Workflows

  • Local development overrides
  • Test environment configuration
  • CI/CD pipeline integration
  • Configuration validation

Configuration Sources

File-Based Configuration

Supported formats: JSON, YAML, TOML, HCL, INI, ENV, properties

v := viper.New(ctx, log)

// Explicit file path
v.SetConfigFile("/path/to/config.yaml")

// Or search in home directory
v.SetHomeBaseName("myapp")  // Looks for ~/.myapp
v.SetConfigFile("")

// Load configuration
v.Config(level.ErrorLevel, level.InfoLevel)
Environment Variables

Automatic environment variable binding:

v.SetEnvVarsPrefix("MYAPP")
v.SetHomeBaseName("myapp")
v.SetConfigFile("")

// MYAPP_SERVER_PORT overrides server.port
// MYAPP_DATABASE_HOST overrides database.host
Remote Configuration (ETCD)

Dynamic configuration from ETCD:

type Config struct {
    App AppSettings
    DB  DatabaseSettings
}

v := viper.New(ctx, log)

// Configure remote provider
v.SetRemoteProvider("etcd")
v.SetRemoteEndpoint("http://localhost:2379")
v.SetRemotePath("/config/myapp")
v.SetRemoteModel(&Config{})

// Optional: Secure connection
v.SetRemoteSecureKey("/path/to/encryption.key")

// Load and watch for changes
err := v.Config(level.ErrorLevel, level.InfoLevel)
// Configuration will auto-reload every 5 seconds
Default Configuration

Fallback when no config file exists:

v.SetDefaultConfig(func() io.Reader {
    return bytes.NewReader([]byte(`{
        "server": {"port": 8080},
        "database": {"host": "localhost"}
    }`))
})

Advanced Features

Custom Decode Hooks

Register custom type conversion hooks:

import (
    "reflect"
    "strings"
    
    "github.com/go-viper/mapstructure/v2"
)

type CustomType string

// Register hook for custom type
hook := func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
    if t == reflect.TypeOf(CustomType("")) {
        if str, ok := data.(string); ok {
            return CustomType(strings.ToUpper(str)), nil
        }
    }
    return data, nil
}

v.HookRegister(hook)

// Now unmarshal will use the hook
var config MyConfig
v.Unmarshal(&config)
File Watching

Watch for configuration file changes:

v := viper.New(ctx, log)
v.SetConfigFile("config.yaml")
v.Config(level.ErrorLevel, level.InfoLevel)

// Set reload callback
v.SetRemoteReloadFunc(func() {
    log().Info("Configuration reloaded")
    // Reload application components
    reloadApp()
})

// Start watching
v.WatchFS(level.InfoLevel)
// Callback will be triggered on file changes
Configuration Key Unsetting

Remove configuration keys at runtime:

// Unset single key
v.Unset("app.debug")

// Unset multiple keys
v.Unset("cache.enabled", "cache.ttl", "cache.size")

// Unset nested keys
v.Unset("database.connection.pool.max")

// Other keys remain intact
port := v.GetInt("app.port") // Still available
Exact Unmarshalling

Strict mode that fails on unknown fields:

type StrictConfig struct {
    Name string
    Port int
}

// UnmarshalExact returns error if config has extra fields
err := v.UnmarshalExact(&config)
if err != nil {
    // Config contains fields not in StrictConfig
}

Best Practices

1. Always Handle Errors

// ✅ Good
func loadConfig() error {
    v := viper.New(ctx, log)
    if err := v.SetConfigFile("config.yaml"); err != nil {
        return fmt.Errorf("set config file: %w", err)
    }
    if err := v.Config(level.ErrorLevel, level.InfoLevel); err != nil {
        return fmt.Errorf("load config: %w", err)
    }
    return nil
}

// ❌ Bad: Silent failures
func loadConfigBad() {
    v := viper.New(ctx, log)
    v.SetConfigFile("config.yaml")
    v.Config(level.ErrorLevel, level.InfoLevel) // Ignoring error
}

2. Use Struct Tags for Clarity

// ✅ Good: Clear mapping
type Config struct {
    AppName string `mapstructure:"app_name"`
    Port    int    `mapstructure:"server_port"`
}

// ❌ Bad: Implicit mapping
type Config struct {
    AppName string // Unclear what config key this maps to
    Port    int
}

3. Provide Defaults

// ✅ Good: Fallback configuration
v.SetDefaultConfig(func() io.Reader {
    return bytes.NewReader([]byte(`{"port": 8080}`))
})

// ❌ Bad: No defaults, app may crash
v.SetConfigFile("config.yaml")
v.Config(level.ErrorLevel, level.InfoLevel)
port := v.GetInt("port") // Returns 0 if not set

4. Validate Configuration

// ✅ Good: Validate after loading
type Config struct {
    Port int
    Host string
}

func loadAndValidate() (*Config, error) {
    var cfg Config
    if err := v.Unmarshal(&cfg); err != nil {
        return nil, err
    }
    
    if cfg.Port < 1 || cfg.Port > 65535 {
        return nil, fmt.Errorf("invalid port: %d", cfg.Port)
    }
    if cfg.Host == "" {
        return nil, fmt.Errorf("host is required")
    }
    
    return &cfg, nil
}

5. Use Environment-Specific Configs

// ✅ Good: Environment-aware
env := os.Getenv("APP_ENV")
if env == "" {
    env = "development"
}

configFile := fmt.Sprintf("config.%s.yaml", env)
v.SetConfigFile(configFile)

Testing

The package includes comprehensive test coverage:

Total Specs:      104
Coverage:         73.3%
Race Detection:   ✅ Zero data races
Execution Time:   ~0.05s (without race), ~1.1s (with race)

Test Categories:

  • Creation and initialization (10 specs)
  • Getter methods for all types (17 specs)
  • Configuration loading (20 specs)
  • Unmarshalling operations (18 specs)
  • Custom decode hooks (10 specs)
  • Key unsetting (15 specs)
  • Error handling (11 specs)
  • Concurrent access (3 specs)

Quality Assurance:

  • ✅ Zero data races (verified with -race)
  • ✅ Thread-safe concurrent operations
  • ✅ All error codes tested
  • ✅ Edge cases covered

See TESTING.md for detailed testing documentation.


Contributing

Contributions are welcome! Please follow these guidelines:

Code Contributions

  • Do not use AI to generate package implementation code
  • AI may assist with tests, documentation, and bug fixing
  • All contributions must pass go test -race
  • Maintain or improve test coverage (≥70%)
  • Follow existing code style and patterns

Documentation

  • Update README.md for new features
  • Add examples for common use cases
  • Keep TESTING.md synchronized with test changes
  • Document all exported functions with GoDoc

Testing

  • Write tests for all new features
  • Test edge cases and error conditions
  • Verify thread safety with race detector
  • Add comments explaining complex scenarios

Pull Requests

  • Provide clear description of changes
  • Reference related issues
  • Include test results
  • Update documentation

See CONTRIBUTING.md for detailed guidelines.


Future Enhancements

Potential improvements for future versions:

Configuration Sources

  • Consul support
  • AWS Parameter Store integration
  • Azure Key Vault support
  • HashiCorp Vault integration

Features

  • Configuration validation framework
  • Schema-based validation (JSON Schema)
  • Configuration migration tools
  • Encrypted configuration values
  • Configuration versioning
  • Audit logging for config changes

Performance

  • Configuration caching layer
  • Lazy loading for large configs
  • Partial configuration updates
  • Optimized remote polling

Developer Experience

  • Configuration generator from structs
  • Interactive configuration wizard
  • Configuration diff tool
  • Hot-reload without callbacks

Suggestions and contributions are welcome via GitHub issues.


AI Transparency Notice

In accordance with Article 50.4 of the EU AI Act, AI assistance has been used for testing, documentation, and bug fixing under human supervision.


License

MIT License - See LICENSE file for details.


Resources

Documentation

Overview

Package viper provides a wrapper around spf13/viper for configuration management with enhanced features including remote configuration support, custom decode hooks, and file watching capabilities.

Overview

This package extends the functionality of github.com/spf13/viper by providing:

  • Remote configuration support (ETCD)
  • Custom decode hooks for type conversion
  • File system watching with reload callbacks
  • Configuration key unsetting
  • Thread-safe operations

Basic Usage

Create a new Viper instance and load configuration from a file:

import (
	"context"
	"github.com/nabbar/golib/logger"
	"github.com/nabbar/golib/viper"
)

ctx := func() context.Context { return context.Background() }
log := func() logger.Logger { return logger.New(ctx) }

v := viper.New(ctx, log)
v.SetConfigFile("/path/to/config.yaml")
err := v.Config(logger.ErrorLevel, logger.InfoLevel)
if err != nil {
	// Handle error
}

// Read values
name := v.GetString("app.name")
port := v.GetInt("app.port")

Configuration Sources

The package supports multiple configuration sources:

1. File-based configuration (JSON, YAML, TOML, etc.) 2. Environment variables with prefix 3. Remote configuration providers (ETCD) 4. Default configuration via io.Reader

Remote Configuration

Configure remote provider for dynamic configuration updates:

v.SetRemoteProvider("etcd")
v.SetRemoteEndpoint("http://localhost:2379")
v.SetRemotePath("/config/myapp")
v.SetRemoteModel(&MyConfig{})

err := v.Config(logger.ErrorLevel, logger.InfoLevel)
// Configuration will be automatically reloaded from remote

Custom Decode Hooks

Register custom decode hooks for type conversion during unmarshalling:

import "github.com/go-viper/mapstructure/v2"

hook := func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
	// Custom conversion logic
	return data, nil
}

v.HookRegister(hook)

File Watching

Watch for configuration file changes and trigger reload:

v.SetRemoteReloadFunc(func() {
	// Reload application configuration
})
v.WatchFS(logger.InfoLevel)

Unmarshalling

Unmarshal configuration into structs:

type AppConfig struct {
	Name    string
	Port    int
	Debug   bool
}

var config AppConfig
err := v.Unmarshal(&config)

// Or unmarshal a specific key
var dbConfig DatabaseConfig
err := v.UnmarshalKey("database", &dbConfig)

Configuration Management

Unset configuration keys dynamically:

// Unset single key
err := v.Unset("app.debug")

// Unset multiple keys
err := v.Unset("app.debug", "app.verbose", "cache.enabled")

// Unset nested keys
err := v.Unset("database.connection.pool")

Error Handling

The package defines specific error codes for different failure scenarios:

  • ErrorParamEmpty: Required parameter is empty
  • ErrorParamMissing: Required parameter is missing
  • ErrorHomePathNotFound: Cannot retrieve user home path
  • ErrorBasePathNotFound: Cannot retrieve base config path
  • ErrorRemoteProvider: Cannot define remote provider
  • ErrorRemoteProviderSecure: Cannot define secure remote provider
  • ErrorRemoteProviderRead: Cannot read config from remote provider
  • ErrorRemoteProviderMarshall: Cannot marshall config from remote provider
  • ErrorConfigRead: Cannot read config from file
  • ErrorConfigReadDefault: Cannot read default config
  • ErrorConfigIsDefault: Using default config (warning)

Thread Safety

All operations are thread-safe and can be called concurrently from multiple goroutines. The underlying viper instance and custom hooks are properly synchronized.

Dependencies

This package depends on:

  • github.com/spf13/viper: Core configuration management
  • github.com/go-viper/mapstructure/v2: Struct decoding with hooks
  • github.com/nabbar/golib/context: Context management
  • github.com/nabbar/golib/logger: Logging functionality
  • github.com/nabbar/golib/errors: Error handling
  • github.com/fsnotify/fsnotify: File system notifications

See Also

For more information on the underlying viper library: https://github.com/spf13/viper

For mapstructure decode hooks: https://github.com/go-viper/mapstructure

Index

Constants

View Source
const (
	ErrorParamEmpty liberr.CodeError = iota + liberr.MinPkgViper
	ErrorParamMissing
	ErrorHomePathNotFound
	ErrorBasePathNotFound
	ErrorRemoteProvider
	ErrorRemoteProviderSecure
	ErrorRemoteProviderRead
	ErrorRemoteProviderMarshall
	ErrorConfigRead
	ErrorConfigReadDefault
	ErrorConfigIsDefault
)
View Source
const (
	// RemoteETCD is the identifier for ETCD remote configuration provider
	RemoteETCD = "etcd"
)

Remote provider constants

Variables

This section is empty.

Functions

This section is empty.

Types

type FuncConfigGet added in v1.10.0

type FuncConfigGet func(key string, model interface{}) error

FuncConfigGet is a function type for retrieving configuration values. It takes a key string and a model interface to unmarshal the value into.

type FuncSPFViper added in v1.10.0

type FuncSPFViper func() *spfvpr.Viper

FuncSPFViper is a function type that returns the underlying spf13/viper instance. This allows direct access to the viper library when needed.

type FuncViper added in v1.10.0

type FuncViper func() Viper

FuncViper is a function type that returns a Viper instance. This is commonly used for dependency injection and lazy initialization.

type Viper

type Viper interface {
	// SetRemoteProvider sets the remote configuration provider (e.g., "etcd").
	SetRemoteProvider(provider string)

	// SetRemoteEndpoint sets the endpoint URL for the remote configuration provider.
	SetRemoteEndpoint(endpoint string)

	// SetRemotePath sets the path to the configuration in the remote provider.
	SetRemotePath(path string)

	// SetRemoteSecureKey sets the encryption key for secure remote connections.
	SetRemoteSecureKey(key string)

	// SetRemoteModel sets the model struct for unmarshalling remote configuration.
	SetRemoteModel(model interface{})

	// SetRemoteReloadFunc sets a callback function to be called when configuration is reloaded.
	SetRemoteReloadFunc(fct func())

	// SetHomeBaseName sets the base name for configuration file in home directory.
	// The actual file will be named ".<basename>" (e.g., ".myapp").
	SetHomeBaseName(base string)

	// SetEnvVarsPrefix sets the prefix for environment variables.
	// Environment variables will be read as PREFIX_KEY_NAME.
	SetEnvVarsPrefix(prefix string)

	// SetDefaultConfig sets a function that returns a default configuration reader.
	// This is used as fallback when no configuration file is found.
	SetDefaultConfig(fct func() io.Reader)

	// SetConfigFile sets the path to the configuration file.
	// If empty, it will search for config in home directory using SetHomeBaseName.
	SetConfigFile(fileConfig string) error

	// Config initializes the configuration from file or remote provider.
	// logLevelRemoteKO is used for remote errors, logLevelRemoteOK for success messages.
	Config(logLevelRemoteKO, logLevelRemoteOK loglvl.Level) error

	// Viper returns the underlying spf13/viper instance for advanced operations.
	Viper() *spfvpr.Viper

	// WatchFS starts watching the configuration file for changes.
	// When changes are detected, the reload function set by SetRemoteReloadFunc is called.
	WatchFS(logLevelFSInfo loglvl.Level)

	// Unset removes one or more configuration keys.
	// Supports nested keys using dot notation (e.g., "database.host").
	Unset(key ...string) error

	// HookRegister registers a custom decode hook for type conversion during unmarshalling.
	HookRegister(hook libmap.DecodeHookFunc)

	// HookReset removes all registered decode hooks.
	HookReset()

	// UnmarshalKey unmarshals a specific configuration key into the provided struct.
	UnmarshalKey(key string, rawVal interface{}) error

	// Unmarshal unmarshals the entire configuration into the provided struct.
	Unmarshal(rawVal interface{}) error

	// UnmarshalExact is like Unmarshal but returns an error if the config contains
	// fields that are not present in the target struct.
	UnmarshalExact(rawVal interface{}) error

	// GetBool returns the value associated with the key as a boolean.
	GetBool(key string) bool

	// GetString returns the value associated with the key as a string.
	GetString(key string) string

	// GetInt returns the value associated with the key as an integer.
	GetInt(key string) int

	// GetInt32 returns the value associated with the key as an int32.
	GetInt32(key string) int32

	// GetInt64 returns the value associated with the key as an int64.
	GetInt64(key string) int64

	// GetUint returns the value associated with the key as an unsigned integer.
	GetUint(key string) uint

	// GetUint16 returns the value associated with the key as a uint16.
	GetUint16(key string) uint16

	// GetUint32 returns the value associated with the key as a uint32.
	GetUint32(key string) uint32

	// GetUint64 returns the value associated with the key as a uint64.
	GetUint64(key string) uint64

	// GetFloat64 returns the value associated with the key as a float64.
	GetFloat64(key string) float64

	// GetTime returns the value associated with the key as time.Time.
	GetTime(key string) time.Time

	// GetDuration returns the value associated with the key as a time.Duration.
	GetDuration(key string) time.Duration

	// GetIntSlice returns the value associated with the key as a slice of int.
	GetIntSlice(key string) []int

	// GetStringSlice returns the value associated with the key as a slice of strings.
	GetStringSlice(key string) []string

	// GetStringMap returns the value associated with the key as a map of interfaces.
	GetStringMap(key string) map[string]any

	// GetStringMapString returns the value associated with the key as a map of strings.
	GetStringMapString(key string) map[string]string

	// GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings.
	GetStringMapStringSlice(key string) map[string][]string
}

Viper is the main interface for configuration management. It provides methods for setting up configuration sources, reading values, and managing decode hooks for custom type conversions.

All methods are safe for concurrent use.

func New

func New(ctx context.Context, log liblog.FuncLog) Viper

New creates a new Viper instance with the provided context and logger.

If log is nil, a default logger will be created using the provided context. The returned Viper instance is ready to use and thread-safe.

Example:

ctx := func() context.Context { return context.Background() }
log := func() logger.Logger { return logger.New(ctx) }
v := viper.New(ctx, log)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL