LoginSignup
79
80
記事投稿キャンペーン 「2024年!初アウトプットをしよう」

【2024】Go言語おすすめライブラリ15選

Last updated at Posted at 2024-01-03

Go言語ライブラリ15選

Golangのおすすめのライブラリを15個(+おまけ1個)紹介します。ライブラリの特徴、インストール方法、使い方を徹底解説していきます。

cmp

テストで等価性を調べるために使うライブラリ。reflect.DeepEqualははオブジェクトが完全一致していないと false になるが、cmp を使うとスライスの順番を無視できたり、一部のフィールドを比較対象から除外したり、様々なオプションを追加することができる。また、diffで差分を出すこともできる。

main.go
package main

import (
	"fmt"
	"reflect"

	"github.com/google/go-cmp/cmp"
	"github.com/google/go-cmp/cmp/cmpopts"
)

func main() {
	slice1 := []int{1, 2, 3}
	slice2 := []int{2, 3, 1}

	equal := reflect.DeepEqual(slice1, slice2)
	fmt.Println("reflect.DeepEqual:", equal) // false

	// cmpopts.SortSlices オプションを使用して、スライスの順序を無視して比較
	options := cmpopts.SortSlices(func(a, b int) bool { return a < b })

	diff := cmp.Diff(slice1, slice2, options)
	fmt.Println("cmp.Diff:", diff) // 差分なし

	equal2 := cmp.Equal(slice1, slice2, options)
	fmt.Println("cmp.Equal:", equal2) // true
}

GoConvey

GoConveyを使うと、ブラウザ上でテスト結果を見ることができるようになる。また、テストを監視してくれるので、テスト結果もホットリロードされる。

$ cd <project path>
$ go get github.com/smartystreets/goconvey
$ go install github.com/smartystreets/goconvey
$ $GOPATH/bin/goconvey

http://localhost:8080 にブラウザにアクセスする。

docker内で起動している場合はgoconvey -host 0.0.0.0を叩かないと、ローカルのブラウザで起動できない。(gconvey in docker? · Issue #449 · smartystreets/goconvey)

以下のようにmain_test.goを作成する。

main_test.go
package main_test

import (
	"testing"
	. "github.com/smartystreets/goconvey/convey"
)

func TestIntegerStuff(t *testing.T) {
	Convey("Given some integer with a starting value", t, func() {
		x := 1

		Convey("When the integer is incremented", func() {
			x++

			Convey("The value should be greater by one", func() {
				So(x, ShouldEqual, 2)
			})
		})
	})
}

再度、http://localhost:8080 にブラウザにアクセスすると、テストが更新されている。

スクリーンショット 2024-01-03 3.08.09.png

protobuf

Protocol Buffers をgolangで使えるようにするライブラリ。

まず、 Protocol buffers のコンパイラをインストールする。以下はMacの場合。
Mac以外のやり方 : Protocol Buffer Compiler Installation | gRPC

コンパイラのインストール
brew install protobuf
protoc --version

golangコード自動生成用のプラグインをインストールする

goプラグインのインストール
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
protobuf/sample.protobuf
syntax = "proto3";

package tutorial;

option go_package = "./protobuf/gen";

message Person {
  string name = 1;
  int32 age = 2;
}
protoc -I=protobuf --go_out=./ sample.proto

すると./protobuf/gen/ に以下のような sample.pb.goが自動生成される。

sample.pb.go
./protobuf/gen/sample.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.32.0
// 	protoc        v4.25.1
// source: sample.proto

package gen

import (
	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
	reflect "reflect"
	sync "sync"
)

const (
	// Verify that this generated code is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
	// Verify that runtime/protoimpl is sufficiently up-to-date.
	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

type Person struct {
	state         protoimpl.MessageState
	sizeCache     protoimpl.SizeCache
	unknownFields protoimpl.UnknownFields

	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Age  int32  `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
}

func (x *Person) Reset() {
	*x = Person{}
	if protoimpl.UnsafeEnabled {
		mi := &file_sample_proto_msgTypes[0]
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		ms.StoreMessageInfo(mi)
	}
}

func (x *Person) String() string {
	return protoimpl.X.MessageStringOf(x)
}

func (*Person) ProtoMessage() {}

func (x *Person) ProtoReflect() protoreflect.Message {
	mi := &file_sample_proto_msgTypes[0]
	if protoimpl.UnsafeEnabled && x != nil {
		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
		if ms.LoadMessageInfo() == nil {
			ms.StoreMessageInfo(mi)
		}
		return ms
	}
	return mi.MessageOf(x)
}

// Deprecated: Use Person.ProtoReflect.Descriptor instead.
func (*Person) Descriptor() ([]byte, []int) {
	return file_sample_proto_rawDescGZIP(), []int{0}
}

func (x *Person) GetName() string {
	if x != nil {
		return x.Name
	}
	return ""
}

func (x *Person) GetAge() int32 {
	if x != nil {
		return x.Age
	}
	return 0
}

var File_sample_proto protoreflect.FileDescriptor

var file_sample_proto_rawDesc = []byte{
	0x0a, 0x0c, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08,
	0x74, 0x75, 0x74, 0x6f, 0x72, 0x69, 0x61, 0x6c, 0x22, 0x2e, 0x0a, 0x06, 0x50, 0x65, 0x72, 0x73,
	0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20,
	0x01, 0x28, 0x05, 0x52, 0x03, 0x61, 0x67, 0x65, 0x42, 0x10, 0x5a, 0x0e, 0x2e, 0x2f, 0x70, 0x72,
	0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x67, 0x65, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
	0x6f, 0x33,
}

var (
	file_sample_proto_rawDescOnce sync.Once
	file_sample_proto_rawDescData = file_sample_proto_rawDesc
)

func file_sample_proto_rawDescGZIP() []byte {
	file_sample_proto_rawDescOnce.Do(func() {
		file_sample_proto_rawDescData = protoimpl.X.CompressGZIP(file_sample_proto_rawDescData)
	})
	return file_sample_proto_rawDescData
}

var file_sample_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_sample_proto_goTypes = []interface{}{
	(*Person)(nil), // 0: tutorial.Person
}
var file_sample_proto_depIdxs = []int32{
	0, // [0:0] is the sub-list for method output_type
	0, // [0:0] is the sub-list for method input_type
	0, // [0:0] is the sub-list for extension type_name
	0, // [0:0] is the sub-list for extension extendee
	0, // [0:0] is the sub-list for field type_name
}

func init() { file_sample_proto_init() }
func file_sample_proto_init() {
	if File_sample_proto != nil {
		return
	}
	if !protoimpl.UnsafeEnabled {
		file_sample_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
			switch v := v.(*Person); i {
			case 0:
				return &v.state
			case 1:
				return &v.sizeCache
			case 2:
				return &v.unknownFields
			default:
				return nil
			}
		}
	}
	type x struct{}
	out := protoimpl.TypeBuilder{
		File: protoimpl.DescBuilder{
			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
			RawDescriptor: file_sample_proto_rawDesc,
			NumEnums:      0,
			NumMessages:   1,
			NumExtensions: 0,
			NumServices:   0,
		},
		GoTypes:           file_sample_proto_goTypes,
		DependencyIndexes: file_sample_proto_depIdxs,
		MessageInfos:      file_sample_proto_msgTypes,
	}.Build()
	File_sample_proto = out.File
	file_sample_proto_rawDesc = nil
	file_sample_proto_goTypes = nil
	file_sample_proto_depIdxs = nil
}

ちなみに、protobufは主にgRPCを実装する際に使われる。
参考 : gRPC公式ドキュメント

goose

goose は、GolangのMigrationツール。

インストール
go install github.com/pressly/goose/v3/cmd/goose@latest

今回はMySQLに接続してみる。

DB接続確認
goose mysql "$DB_USER:$DB_PASS@tcp($DB_HOST)/$DB_TABLE" status
terminal
$ goose create init sql
2024/01/03 02:33:01 Created new file: 20240103023301_init.sql
.
├── 20240103023301_init.sql
├── go.mod
├── go.sum
└── main.go

上記でできたSQLファイルを下記のように編集します。

20240103061927_init.sql
-- +goose Up
CREATE TABLE user (
    id int NOT NULL,
    name text,
    age int,
    PRIMARY KEY(id)
);

-- +goose Down
DROP TABLE user;

-- +goose Upの下にupするときの動作、-- +goose Downの下にDownした際の動作を実装する。

terminal
$ goose mysql "$DB_USER:$DB_PASS@tcp($DB_HOST)/$DB_TABLE" up
2024/01/03 06:26:10 OK   20240103061927_init.sql (16.55ms)
2024/01/03 06:26:10 goose: successfully migrated database to version: 20240103061927
$ goose mysql "$DB_USER:$DB_PASS@tcp($DB_HOST)/$DB_TABLE" status
2024/01/03 06:26:19     Applied At                  Migration
2024/01/03 06:26:19     =======================================
2024/01/03 06:26:19     Wed Jan  3 15:26:10 2024 -- 20240103061927_init.sql

userテーブルが作成されている。

mysql> show tables;
+------------------+
| Tables_in_dust   |
+------------------+
| goose_db_version |
| user             |
+------------------+
2 rows in set (0.00 sec)
mysql> desc user;
+-------+------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------+------+-----+---------+-------+
| id    | int  | NO   | PRI | NULL    |       |
| name  | text | YES  |     | NULL    |       |
| age   | int  | YES  |     | NULL    |       |
+-------+------+------+-----+---------+-------+
3 rows in set (0.00 sec)
terminal
$ goose mysql "$DB_USER:$DB_PASS@tcp($DB_HOST)/$DB_TABLE" down
2024/01/03 06:27:52 OK   20240103061927_init.sql (15.89ms)

downするとuserテーブルがdropされる。

mysql> show tables;
+------------------+
| Tables_in_dust   |
+------------------+
| goose_db_version |
+------------------+
1 row in set (0.00 sec)

fsnotify

fsnotifyは、ファイル監視ライブラリ。

以下のコマンドでインストールする

main.go
go get github.com/go-fsnotify/fsnotify
package main

import (
    "log"

    "github.com/fsnotify/fsnotify"
)

func main() {
    // Create new watcher.
    watcher, err := fsnotify.NewWatcher()
    if err != nil {
        log.Fatal(err)
    }
    defer watcher.Close()

    // Start listening for events.
    go func() {
        for {
            select {
            case event, ok := <-watcher.Events:
                if !ok {
                    return
                }
                log.Println("event:", event)
                if event.Has(fsnotify.Write) {
                    log.Println("modified file:", event.Name)
                }
            case err, ok := <-watcher.Errors:
                if !ok {
                    return
                }
                log.Println("error:", err)
            }
        }
    }()

    // Add a path.
    err = watcher.Add("/tmp")
    if err != nil {
        log.Fatal(err)
    }

    // Block main goroutine forever.
    <-make(chan struct{})
}

実行するとファイルの変更を監視してくれる。

terminal
$ go run main.go
2024/01/02 14:06:57 event: CREATE        "/tmp/go.1bf98f8185591c288e01bd9fb67907933818eb97559717d6f839f041449e0f35.3807461520.mod"
2024/01/02 14:06:57 event: WRITE         "/tmp/go.1bf98f8185591c288e01bd9fb67907933818eb97559717d6f839f041449e0f35.3807461520.mod"
     ・・・・

air

airはホットリロードを可能にするパッケージ。
以下はGinを使った例である。

go install github.com/cosmtrek/air@latest

.air.tomlに設定

.air.toml
root = "."
testdata_dir = "test"
tmp_dir = "tmp"

[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ."
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "test"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = true
follow_symlink = true
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
kill_delay = 500                                  #ms
log = "air.log"
send_interrupt = false
stop_on_error = true

[color]
# Customize each part's color. If no color found, use the raw app log.
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"

[log]
# Show log time
time = true

[misc]
# Delete tmp directory on exit
clean_on_exit = true

[screen]
clear_on_rebuild = false
main.go
package main

import "github.com/gin-gonic/gin"

func main() {
	r := gin.Default()
	r.GET("/hello", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "Hello World!",
		})
	})
	r.Run()
}

以下コマンドでホットリロード可能なサーバが起動する。

terminal
$ air

pretty

Golangにおける pretty-print
pretty.Formatter()を使うと綺麗に整列してくれる。

playground

package main

import (
	"fmt"

	"github.com/kr/pretty"
)

func main() {
	type myType struct {
		a, b int
	}
	var x = []myType{{1, 2}, {3, 4}, {5, 6}}
	fmt.Printf("%# v\n", x)
	fmt.Printf("%# v", pretty.Formatter(x))
}
実行結果
[]main.myType{main.myType{a: 1, b: 2}, main.myType{a: 3, b: 4}, main.myType{a: 5, b: 6}}
[]main.myType{
    {a:1, b:2},
    {a:3, b:4},
    {a:5, b:6},
}

cli

cli は、Golangでコマンドラインツールを構築するための、シンプルで高速なパッケージ。

main.go
package main

import (
	"fmt"
	"os"

	"github.com/urfave/cli/v2"
)

func main() {
	app := &cli.App{
		Name:  "HelloCLI",
		Usage: "A simple Hello, World! CLI application",
		Commands: []*cli.Command{
			{
				Name:    "greet",
				Aliases: []string{"g"},
				Usage:   "Greet someone",
				Flags: []cli.Flag{
					&cli.StringFlag{
						Name:    "name",
						Aliases: []string{"n"},
						Value:   "World",
						Usage:   "Specify the name for the greeting",
					},
				},
				Action: func(c *cli.Context) error {
					name := c.String("name")
					fmt.Printf("Hello, %s!\n", name)
					return nil
				},
			},
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		fmt.Println(err)
	}
}

このコマンドを実行してみる。

terminal
$ go run main.go greet --name Alice
Hello, Alice!

cobra

簡単にCLIを実装できるライブラリ
ただし、cobra cliがgo workspaceに対応していない。(すでにPRはあるが、まだ取り込まれていない)

インストール
go install github.com/spf13/cobra-cli@latest
go get -u github.com/spf13/cobra@latest

cobra-cli initするとcmdディレクトリとmain.goが生成される。

terminal
$ cobra-cli init
$ tree .
.
├── cmd
│   └── root.go
├── go.mod
├── go.sum
├── main.go

cobra-cli add xxxxxxコマンド を作成してくれる。

$ cobra-cli add hello
$ tree cmd
cmd
├── hello.go
└── root.go

自動生成されたcmd/hello.gohelloコマンド の処理を追加する。

cmd/hello.go
/*
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
*/
package cmd

import (
	"fmt"

	"github.com/spf13/cobra"
)

var helloCmd = &cobra.Command{
	Use:   "hello",
	Short: "This is hello command.",
	Long: `This is hello command.`,
	Run: func(cmd *cobra.Command, args []string) {
		// ここにコマンドの処理をかく
		fmt.Println("Hello world!")
	},
}

func init() {
	rootCmd.AddCommand(helloCmd)
}

helloコマンドを実行する。

terminal
$ go run main.go hello
Hello world!

viper

viper は toml, yaml, jsonなどの設定ファイルから環境変数を読み込むライブラリである。

実際に以下のtomlファイルから設定値を取得する。

インストール
go get github.com/spf13/viper
go get github.com/pelletier/go-toml # tomlの場合
go get gopkg.in/yaml.v2 # yamlの場合
config.toml
message = "Hello TOML!"
main.go
package main

import (
	"fmt"

	"github.com/spf13/viper"
)

func main() {
	viper.SetConfigName("config") // 設定ファイル名を指定
	viper.SetConfigType("toml")   // 設定ファイルの形式を指定
	viper.AddConfigPath(".")      // ファイルのpathを指定

	// 設定ファイルの読み込み
	err := viper.ReadInConfig()
	if err != nil {
		panic(fmt.Errorf("fatal error config file: %w", err))
	}
	// キーが "message" の値を取得
	message := viper.GetString("message")
	fmt.Println("message :", message)
}

実行すると

terminal
$ go run main.go
message : Hello TOML!

wire

Golangの依存性注入(DI)のライブラリ。

wireは更新少ない、genericsに対応していないという理由から、sambor/doの方がいいかも知れない。

インストール
go install github.com/google/wire/cmd/wire@latest

まず、以下のようにmain.gowire.goを作成する。

main.go
package main

import "fmt"

func main() {
	greeter := InitializeGreeter()
	message := greeter.Greet()
	fmt.Println(message)
}
wire.go
//go:build wireinject
// +build wireinject

package main

import (
	"github.com/google/wire"
)

type Message string

func NewMessage() Message {
	return "Hello, Wire!"
}

func NewGreeter(m Message) Greeter {
	return Greeter{Message: m}
}

type Greeter struct {
	Message Message
}

func (g Greeter) Greet() Message {
	return g.Message
}

func InitializeGreeter() Greeter {
	wire.Build(NewMessage, NewGreeter)
	return Greeter{}
}

wire.goのあるディレクトリでwireと叩くとwire_gen.goが自動生成される。

wire_gen.go
wire_gen.go
// Code generated by Wire. DO NOT EDIT.

//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinject

package main

// Injectors from wire.go:

func InitializeGreeter() Greeter {
	message := NewMessage()
	greeter := NewGreeter(message)
	return greeter
}

// wire.go:

type Message string

func NewMessage() Message {
	return "Hello, Wire!"
}

func NewGreeter(m Message) Greeter {
	return Greeter{Message: m}
}

type Greeter struct {
	Message Message
}

func (g Greeter) Greet() Message {
	return g.Message
}

fasthttp

Go 用の高速 HTTP 実装であり、net/http よりも最大10倍高速らしい。

インストール
go get github.com/valyala/fasthttp

簡単なサーバを起動してみる。

main.go
package main

import (
	"fmt"

	"github.com/valyala/fasthttp"
)

func requestHandler(ctx *fasthttp.RequestCtx) {
	fmt.Printf("Hello World!\n")
}

func main() {
	server := fasthttp.Server{
		Handler: requestHandler,
	}

	// ポート8080でサーバを起動
	err := server.ListenAndServe(":8080")
	if err != nil {
		fmt.Println("Error starting server:", err)
	}
}
terminal
$ go run main.go &
$ curl http://localhost:8080
Hello World!

prometheus

Prometheus は、モニタリング用のメトリクスを計測するためのライブラリ。メトリクスの登録、HTTP経由での公開(promhttpパッケージ)、プッシュゲートウェイへのプッシュ(pushパッケージ)などができる。

インストール
go get github.com/prometheus/client_golang/prometheus
go get github.com/prometheus/client_golang/prometheus/promauto
go get github.com/prometheus/client_golang/prometheus/promhttp
main.go
package main

import (
	"net/http"

	"github.com/prometheus/client_golang/prometheus/promhttp"
)

func main() {
	http.Handle("/metrics", promhttp.Handler())
	http.ListenAndServe(":8080", nil)
}

サーバを起動。

go run main.go

http://localhost:8080/metrics にアクセスすると以下のような画面が表示される。

image.png

logrus

logrus は、有名なロギングライブラリ。

main.go
package main

import (
	log "github.com/sirupsen/logrus"
)

func main() {
	// ログフィールドの設定
	log.WithFields(log.Fields{
		"id":   "001",
		"size": 10,
	}).Info("start logrus")

	// ログのレベルを変更して出力
	log.SetLevel(log.DebugLevel)
	log.Debug("hello world")
	log.Error("error")
	log.SetFormatter(&log.JSONFormatter{})
	log.Info("json format")
}

これを実行すると、以下のようになる。

terminal
$ go run main.go
INFO[0000] start logrus                                  id=001 size=10
DEBU[0000] hello world                                  
ERRO[0000] error                                        
{"level":"info","msg":"json format","time":"2024-01-02T17:32:08Z"}

color

colorを使うと、ターミナルに色付きで出力できる。 他にもアンダーラインや太字、背景色の変更もできる。

インストール
go get github.com/fatih/color

実際に文字に色をつけてみた。

main.go
package main

import (
	"github.com/fatih/color"
)

func main() {
	color.Black("Black")
	color.Red("Red")
	color.Blue("Blue")
	color.Green("Green")
	color.Yellow("Yellow")
	color.White("White")
	color.Magenta("Magenta")
	color.Cyan("Cyan")
	color.HiBlack("HiBlack")
	color.HiRed("HiRed")
	color.HiBlue("HiBlue")
	color.HiWhite("HiWhite")
	color.HiYellow("HiYellow")
	color.HiCyan("HiCyan")
	color.HiGreen("HiGreen")
	color.HiMagenta("HiMagenta")
}

go run main.go をすると

image.png

Go kit (おまけ)

Go言語でマイクロサービスを構築するためのツール。

16個目になったので、おまけです。

Go kitについての実装例は、長くなりそう、私自身がまだ使ったことがない、といった理由から、割愛させてください。以下2つの記事が参考になります。1つ目は公式チュートリアルで、2つ目は実際にTODOアプリを作成する記事。

最後に

以下のサイトにジャンル別でライブラリやフレームワークが大量にまとまっている。

avelino/awesome-go: A curated list of awesome Go frameworks, libraries and software

79
80
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
79
80