Go Programlama Dili
  • KİTAP HAKKINDA
    • GİRİŞ
    • Katkıda Bulunanlar
  • BÖLÜM 1 (Ön Eğitim)
    • Golang Hakkında
    • Sıkça Sorulan Sorular
    • Go Derleyicisi Kurulumu
    • VSCode Go Eklentisi Yükleme
    • Go Projesi Oluşturma
    • Merhaba Dünya
    • VSCode Varsayılan Hata Ayıklayıcıyı Seçme
    • Farklı Platformlara Build (İnşa) Etme
    • Klasör Build Etme
    • Paketler
    • Yorum Satırı
    • Veri Tipleri
    • Aritmetik Operatörler
    • İlişkisel Operatörler
    • Mantıksal Operatörler
    • Atama Operatörleri
    • Değişkenler ve Atanması
    • Sabitler
    • Kod Gruplama İşlemi
    • Tür Dönüşümü
    • Bölüm 1 Sınav
  • BÖLÜM 2 (İşlem Yapma)
    • Fonksiyonlar
    • Fonksiyon Çeşitleri
    • Anonim Fonksiyonlar
    • Boş Tanımlayıcılar
    • Döngüler
    • If-Else
    • Switch
    • Defer
    • Pointers (İşaretçiler)
    • Bölüm 2 Sınav
  • BÖLÜM 3 (Gruplama)
    • Struct
    • Anonim Struct'lar
    • Struct Fonksiyonlar (Methodlar)
    • Diziler (Arrays)
    • Dilimler (Slices)
    • Range
    • Map
    • Interface
    • Dinamik Değişkenler
    • Generics
  • BÖLÜM 4 (Eş zamanlılık)
    • Goroutine
    • Kanallar (Channels)
    • Anonim Goroutine Fonksiyonlar
    • WaitGroup ile Asenkron İşlemleri Beklemek
    • Mutex ile Asenkron İşlem Sırası
    • Zamanlayıcılar (Tickers)
    • Select
  • BÖLÜM 5
    • init() Fonksiyonu (Ön Yükleme)
    • Import (Kütüphane Ekleme) Yöntemleri
    • Dışa Aktarma (Exporting)
    • Print Fonksiyonu Birkaç İnceleme
    • Format ve Kaçış Karakterleri
    • Çok Satırlı String Oluşturma
    • Sprintf
    • Golang'te Kullanıcıdan Giriş Alma
    • Testing (Test Etme)
    • Panic & Recover
  • BÖLÜM 6 (Paketler)
    • Strings
    • os/exec (Komut Satırına Erişim)
    • Komut Satırı Argümanları (Args)
    • Komut Satırı Bayrakları (Flags)
    • os/signal
    • Sort (Sıralama)
    • Strconv (String Çeviri)
    • Log (Kayıt)
    • Paket (Kütüphane) Yazmak
    • Regexp (Kurallı İfadeler)
  • BÖLÜM 7 (Dosya İşlemleri)
    • Çapraz Platform Dosya Yolları
    • İşletim Sistemini Görme
    • Dosya Varlığı Kontrolü
    • ioutil ile Dosya Okuma ve Yazma
    • Bir Dizindeki Dosya ve Klasörleri Sıralama
    • XML Parsing (Ayrıştırma)
    • JSON Parsing (Ayrıştırma)
      • Dinamik JSON Parsing Yöntemleri
    • ini Dosyası Okuma ve Düzenleme
    • Web Scrapper (goquery)
    • chromedp (Web Driver)
  • VERİTABANI
    • sqlite3
    • MySQL
    • MongoDB
    • GORM
  • Web Sunucu (Server), Ağ İşlemleri
    • net/http ile Web Server Oluşturma
    • HTML Şablonlar (Templates)
    • Statik Kütüphanesi ile Dosyaları Uygulamaya Gömme
    • Gin Web Kütüphanesi
      • Gin Dosya Yükleme
    • gRPC
    • Heroku'da Go Uygulaması Yayınlama
    • HTTP İstekleri (Requests)
    • RabbitMQ
      • RabbitMQ Kurulumu
      • Basitçe RabbitMQ Kullanımı
  • GUI (Grafiksel Kullanıcı Arayüzü)
    • WebView
    • notify (Bildirim)
  • Mikro-denetleyiciler
    • Gobot ile Arduino Yanıp-Sönen LED Yapımı
    • Tinygo ile Küçük Yerler için Golang
  • CLI
    • 🐍Cobra CLI
  • ⚙️Yapılandırma
    • 🐍Viper
  • Pratik Bilgiler
    • Go Geliştiricileri için Makefile
    • Derleme (Build) Detayını Görme
    • Visual Studio Code için Golang Özelleştirmeleri
  • 👾PLUS
    • Uber Go Stil Kılavuzu
Powered by GitBook
On this page
  • Viper nedir?
  • Nasıl kullanılır?
  • Yapılandırma dosyasını oluşturma
  • Ortam Değişkenleri ile kullanımı
  • Varsayılan değerleri atama
  • Unmarshal

Was this helpful?

  1. Yapılandırma

Viper

PreviousCobra CLINextGo Geliştiricileri için Makefile

Last updated 2 years ago

Was this helpful?

Viper nedir?

Viper, da dahil olmak üzere Go uygulamaları için eksiksiz bir yapılandırma çözümüdür. Bir uygulama içinde çalışmak üzere tasarlanmıştır. Her türlü yapılandırma gereksinimini ve biçimini karşılayabilir.

Desteklediği yöntemler şunlardır:

  • varsayılanları ayarlama,

  • JSON, TOML, YAML, HCL, envfile ve Java properties config dosyalarından okuma,

  • yapılandırma dosyalarını canlı izleme ve yeniden okuma (opsiyonel),

  • ortam değişkenlerinden okuma,

  • uzak yapılandırma sistemlerinden (etcd ya da Consul) okuma ve canlı izleme,

  • komut-satırı flag'lerinden okuma,

  • buffer'dan okuma,

  • açık değerler atama.

Viper, tüm uygulama yapılandırma ihtiyaçlarınız için bir kayıt defteri olarak düşünülebilir.

Nasıl kullanılır?

Bu kısımda, json tipinde bir yapılandırma dosyası üzerinden, yapılandırmamızı nasıl yönetebileceğimizi (okuma, yazma...) göreceğiz.

Öncelikle projemizi oluşturalım.

go mod init <proje>

daha sonra ihtiyacımız olduğu için main.go ve config.json isimli dosyalarımızı oluşturalım. Sonuç olarak projemizin içeriği aşağıdaki gibi olacak.

$ tree .
.
├── config.json
├── go.mod
├── go.sum
└── main.go

Viper paketini kuralım

go get github.com/spf13/viper

config.json dosyamızda aşağıdaki gibi bir yapı oluşturalım.

config.json
{
  "host": "127.0.0.1",
  "port": "80"
}

Artık config.json dosyamızı host ve port değerlerini okuyup yazmak için kullanabiliriz.

main.go dosyamızın içeriği de aşağıdaki gibi olsun.

main.go
package main

import (
	"github.com/spf13/viper"
	"log"
)

func main() {
	vp := viper.New() // yeni bir viper örneği oluşturalım

	vp.AddConfigPath(".")      // config dosyamızın bulunduğu dizin
	vp.SetConfigName("config") // config dosyamızın ismi
	vp.SetConfigType("json")   // config dosyamızın uzantısı

	if err := vp.ReadInConfig(); err != nil {
		log.Fatalf("yapılandırma dosyası okunurken hata oluştu: %v", err)
	}
}

Yukarıdaki yöntem ile oluşturduğumuz yapılandırma dosyasının içeriğini artık yönetebiliriz.

Hadi nasıl okuyacağımıza bakalım.

vp.Get("host") // 127.0.0.1

Get() fonksiyonu ile belirli bir anahtarın değerini okuyabiliriz. Burada dikkat edilmesi gereken nokta Get() fonksiyonu bize any tipinde bir değer döndürür. Döndürülen bu değeri asıl tipine çevirmeden kullanamayacağımız durumlar olabilir. Bu durumlarda type assertion yaparak değeri çıkartabiliriz ;fakat daha kolay bir yöntem olarak GetString() fonksiyonunu kullanabiliriz

vp.GetString("host") // 127.0.0.1

Tabi ki bu yöntem sadece string tipi için geçerli değil, hepsine tek tek değinmek uzun süreceği için direkt olarak dönüştürebileceğimiz tiplere kısa değinelim.

bool

string, []string, map[string]interface{}, map[string]string{}, map[string][]string{}

int, int32, int64, []int

uint, uint16, uint32, uint64

float64

time.Time, time.Duration

Yukarıda verilen tipler için ilgili fonksiyonu Get<tip-ismi>() yazarak bulabilirsiniz. Bu şekilde kullanamadığınız tipleri de type assertion yaparak dönüştürebilirsiniz.

Şimdi de yapılandırma dosyamızdaki bir anahtara nasıl atama yapacağımıza bakalım.

vp.Set("host", "example.com") // atama işlemi

// değişiklikleri yapılandırma dosyasına yazdıralım
if err := vp.WriteConfig(); err != nil {
    log.Fatalf("yapılandırma dosyası yazılırken hata oluştu: %v", err)
}

Yazdırma işleminde sonra config.json dosyamız aşağıdaki gibi değişecektir.

config.json
{
  "host": "example.com",
  "port": "80"
}

Atama işlemini eğer var olmayan bir anahtar ile yapsaydık, yeni bir alan oluştururduk. Örnek:

vp.Set("user", "kaan")

Yapılandırma dosyamızdaki değişikliğe bakalım.

config.json
{
  "host": "example.com",
  "port": "80",
  "user": "kaan"
}

Anahtar değerinin boş olarak atanması veya silinmesi gibi işlemler uygulamanın kararsız çalışmasına neden olabileceği için bu tür fonksiyonların kullanımını engellemişler. Zaten anahtarın silinmesi için bir fonksiyon bulunmamaktadır. Bu yüzden viper paketinin kullanımı sadece Set ve Get işlemlerini destekler.

WriteConfig() fonksiyonu dosyanın yazılmasında karşılaşılan hataları döndürür.

Yapılandırma dosyasını oluşturma

Yapılandırma dosyasının uygulamamız tarafından oluşturulmasını istediğimiz durumlar olabilir. Bu durumlarda, yukarıdaki örneklerimizde gördüğümüzün aksine yapılandırma dosyamızı okumadan önce oluşturmamız gerekir.

main.go
package main

import (
	"github.com/spf13/viper"
	"log"
)

func main() {
	vp := viper.New() // yeni bir viper örneği oluşturalım

	vp.AddConfigPath(".")      // config dosyamızın bulunduğu dizin
	vp.SetConfigName("config") // config dosyamızın ismi
	vp.SetConfigType("json")   // config dosyamızın uzantısı

	vp.Set("host", "example.com")
	vp.Set("port", "80")

	// değişiklikleri oluşturmak istediğimiz yapılandırma dosyasına yazdıralım
	if err := vp.WriteConfig(); err != nil {
		log.Fatalf("yapılandırma dosyası yazılırken hata oluştu: %v", err)
	}
}

WriteConfig() fonksiyonunda dikkat edilmesi gereken ayrıntı, eğer oluşturulan yapılandırma dosyası hali hazırda bulunuyorsa bu dosyanın üzerine yazar, fakat üzerine yazma işleminde belirtilmemiş olan anahtarlar eski yapılandırma dosyasındaki hali ile gelir. Yani yapılandırma dosyası tamamen sıfırdan oluşturulmaz.

Eğer yapılandırma dosyamızın oluşturulduktan sonra bir daha değiştirilmemesini ve sadece hali hazırda bulunmadığı durumlarda oluşturulmasını istersek SafeWriteConfig() fonksiyonunu kullanabiliriz. Böylelikle yapılandırma dosyamız zaten oluşturulmuşsa bize hata döndürecektir.

Örnek olarak:

main.go
package main

import (
	"github.com/spf13/viper"
	"log"
)

func main() {
	vp := viper.New() // yeni bir viper örneği oluşturalım

	vp.AddConfigPath(".")      // config dosyamızın bulunduğu dizin
	vp.SetConfigName("config") // config dosyamızın ismi
	vp.SetConfigType("json")   // config dosyamızın uzantısı

	vp.Set("host", "example.com")
	vp.Set("port", "80")

	// değişiklikleri oluşturmak istediğimiz yapılandırma dosyasına yazdıralım
	if err := vp.SafeWriteConfig(); err != nil {
		log.Fatalf("yapılandırma dosyası yazılırken hata oluştu: %v", err)
	}
}

config.json dosyamız bulunmuyorsa, atanılan değerler ile birlikte yeni bir config.json dosyası oluşturulacaktır. Eğer config.json dosyamız bulunuyorsa, yukarıdaki işlemler yapıldığında SafeWriteConfig() fonksiyonu aşağıdaki gibi bir hata döndürecektir.

$ go run .
2022/10/06 17:50:56 yapılandırma dosyası yazılırken hata oluştu: Config File "/Volumes/SAMSUNG/main/Dev/go/viper-example/config.json" Already Exists

Eğer yapılandırma dosyamız iç içe bir yapıdan oluşuyorsa, okuma-yazma işlemlerinde alt-alana ulaşmak için . (nokta) kullanabiliriz.

Örnek config.json dosyamız;

config.json
{
  "user": {
    "full_name": "Kaan Kuscu",
    "age": 25
  }
}

Okuma işlemi için,

vp.Get("user.full_name") // Kaan Kuscu

Yazma işlemi de aynı şekilde,

vp.Set("user.full_name") // Kaan Kuscu

Ortam Değişkenleri ile kullanımı

Uygulamayı çalıştırdığımız sistemimizdeki ortam değişkenlerine erişmek istediğimizde, başvurabileceğimiz iki farklı yöntem var.

Bu yöntemlerden ilki kullanmak istediğimiz ortam değişkenini viper'a bildirmek. Örneğin TEST isminde bir ortam değişkenimiz olsun ve değeri de abc olsun. TEST'in değerini okuyabilmek için aşağıdaki yöntemleri kullanabiliriz.

if err := vp.BindEnv("TEST"); err != nil {
   log.Fatalln(err)
}

fmt.Println(vp.Get("TEST"))

Sisteminizde TEST isimli bir ortam değişkeni bulunmuyorsa, aşağıdaki gibi deneyebilirsiniz.

$ TEST="abc" go run .
abc

BindEnv() fonksiyonunun hata döndürmeme şartı sadece en az bir parametre girmemizdir.

Varsayılan değerleri atama

Yapılandırma dosyaların ve ortam değişkenlerinden okuma yaptığımızda, bazı anahtarlar tanımlı olmadığı için, sıfır-değerli halde gelebilir. Eğer bu istemediğimiz bir durumsa bu anahtarların varsayılan değerlerini ayarlayabiliriz. Örnek;

config.json
{
  "host": "localhost",
  "port": "80",
  "user": "root"
}

Gelelim kullanımına;

if err := vp.ReadInConfig(); err != nil {
	log.Fatalln(err)
}

vp.SetDefault("password", "1234") // anahtar, değer

fmt.Println(vp.GetString("password")) // 1234

config.json dosyamızda password isimli bir alan olmadığı için varsayılan olarak ayarladığımız 1234 değeri yazdırılacaktır.

Eğer varsayılan değerler çok sayıda olacaksa, şöyle bir yöntem kullanabiliriz.

defaults := map[string]any{
	"host":     "localhost",
	"user":     "root",
	"password": "1234",
}
	
for k, v := range defaults {
	vp.Set(k, v)
}

Unmarshal

Buraya kadar olan bölümlerde Viper'ı basitçe nasıl kullanacağımızı gördük. Ölçeklenebilir bir projede sadece bu yöntemleri kullarak ilerlememiz zor olduğu için işimizi kolaylaştıracak başka çözümler gerekiyor. Proje yapımızı düzenli tutmak için, bu yöntemlerimizi daha pratik hale getirmemiz gerekiyor.

Bunun için gruplandırma yaparak, yapılandırma ayarlarımızı struct halinde kullanabiliriz.

Örneğin aşağıdaki gibi bir yapılandırma dosyamız olsun.

config.json
{
  "api": {
    "host": "example.com",
    "port": "80",
    "ssl": true
  },
  "database": {
    "host": "localhost",
    "user": "root",
    "password": "1234",
    "name": "project"
  }
}

Yukarıdaki json yapısı aktarabileceğimiz struct'ları belirleyelim.

type Config struct {
	API      APIConfig      `mapstructure:"api"`
	Database DataBaseConfig `mapstructure:"database"`
}

type APIConfig struct {
	Host string `mapstructure:"host"`
	Port string `mapstructure:"port"`
	SSL  bool   `mapstructure:"ssl"`
}

type DataBaseConfig struct {
	Host     string `mapstructure:"host"`
	User     string `mapstructure:"user"`
	Password string `mapstructure:"password"`
	Name     string `mapstructure:"name"`
}

Yukarıdaki struct'larda dikkat edilmesi gereken ayrıntı, struct tag'lerdeki mapstructure anahtarıdır.

Yapılandırma dosyamızın tipi json olduğu için mapstructure yerine json'da kullanabilirdik. Fakat yapılandırma dosyamızın tipi, örnek olarak json'dan yaml'a geçseydi, struct tag'lerde yaml olarak düzenleme yapmamız gerekirdi. Bu yüzden tüm desteklenen yapılandırma dosyası tiplerinde unmarshal yapabilmesi için mapstructure olarak uyguladık.

Tabi ki bu yöntem her durumda işimizi görmeyebilir. Bazı durumlarda farklı dosya tiplerinde farklı anahtar isimlendirmeleri olabilir. Bu gibi durumlarda da dosya tipine göre struct tag'lerini düzenlemekte fayda vardır.

Unmarshal etmek için aşağıdaki yöntemi izleyebiliriz.

main.go
func main() {
	vp := viper.New()

	vp.AddConfigPath(".")
	vp.SetConfigName("config")
	vp.SetConfigType("json")

	// öncelikle yapılandırma dosyasını okumamız gerekiyor
	if err := vp.ReadInConfig(); err != nil {
		log.Fatalln(err)
	}
	
	var configs Config

	if err := vp.Unmarshal(&configs); err != nil {
		log.Fatalln(err)
	}

	fmt.Printf("%+v\n", configs)
}

Çıktımız aşağıdaki gibi olacaktır.

$ go run .
{API:{Host:example.com Port:80 SSL:true} Database:{Host:localhost User:root Password:1234 Name:project}}
⚙️
🐍
Page cover image
12-Factor uygulamaları
Viper logo