- 投稿日:2021-01-05T15:55:02+09:00
2021年Go言語フレームワークスター数比較
Go言語のフレームワーク(マイクロもフルスタックも含む)についてGithubの星比較です。(2021/01/05集計)
2021年版が見つからなかったので。ginが強いですねー
参考
Go言語Webフレームワークランキング
https://qiita.com/loftkun/items/1a9951d1864bebdc51e1Golang フレームワーク比較
https://qiita.com/yumin/items/5de33b068ead564ebcbf
- 投稿日:2021-01-05T00:43:50+09:00
Go言語インストール後の実行環境テスト
最終確認
インストール、Version確認後
VSCordでフォルダを作成し、期待する結果が出力されるまでテストする
Go言語の環境構築はこちら実行環境テスト
# テスト用ディレクトリを作成 $ mkdir -p ~/go/src/test # ディレクトリへ移動 $ cd ~/go/src/test # テスト用のファイルを作成 $ touch test.go # テスト用のファイルを編集 $ vi test.go//test.go package main import "fmt" func main() { fmt.Printf("test, go\n") }# 作成したディレクトリへ移動 $ cd ~/go/src/hello # ビルドしてバイナリファイルを生成 $ go build:wqで保存し、ビルドしてバイナリファイルを生成
実行する
$ ./testtest, go※エラーが出てしまう場合
$ go envGo言語関連の環境変数を確認し、PATHあたりを確認してみると良いのかな?
※出力結果を適応
テストで出力した内容を変更した場合は上書きが必要なので、再度ビルドする
$ go buildテストまでクリアしたので、これからGoの勉強を開始します!!
- 投稿日:2021-01-05T00:41:07+09:00
Golangで自作Logger中でzapライブラリを使う際、CallerにLoggerより上の階層を出す方法
この記事を参考に、zapというログライブラリを使ってみました。
その時にログ出力されるCallerの情報についてハマったことをメモとして残します。2021/1/6追記
conf.Build(zap.AddCallerSkip(x))
のように指定することで変更できることを知りました。おさらい
- よく見かけるmainに記載するコーディング例で実行した場合を記載します。
- 出力例からわかる通り
main/main.go:30
という内容が出力されます。- これはzapのログを出力したファイルのディレクトリ、ファイル、行が出力されており、トレースしやすい内容となっています。
package main import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" "time" ) func main() { conf := zap.Config{ Level: zap.NewAtomicLevelAt(zap.DebugLevel), Development: true, Encoding: "console", OutputPaths: []string{"stdout"}, ErrorOutputPaths: []string{"stdout"}, EncoderConfig: zapcore.EncoderConfig{ LevelKey: "level", TimeKey: "time", MessageKey: "msg", CallerKey: "caller", EncodeTime: zapcore.ISO8601TimeEncoder, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeCaller: zapcore.ShortCallerEncoder, }, } l, err := conf.Build() if err != nil { panic(err) } l.Info("Zap Start.", zap.Time("time", time.Now())) } // Output: // 2021-01-04T23:46:14.963+0900 info main/main.go:30 Zap Start. {"time": "2021-01-04T23:46:14.963+0900"} // フォルダ構成はこんな感じ。 // main // ├── go.mod // ├── go.sum // └── main.go困ったこと
- Webアプリで作るために、一度生成したzapインスタンスを使い回したいと思い、loggerパッケージを自作しました。
- これで問題なし!と思いきや、logが
logger/logger.go:41
になっちゃった。- 確かにzapのメソッドを呼び出しているのはlogger/logger.goだけど、それよりもひとつ上のCallerを知りたいのよ。
- ソースを読む限り、zapのConfigではひとつ上のCallerを指定することはできないみたい。
runtime
パッケージで別で出力するしかないのか。。。2021/1/6追記
conf.Build(zap.AddCallerSkip(x))
のように指定することで変更できることを知りました。// フォルダ構成はこんな感じ(Goのスタンダードから外れるけど今回は説明のため簡略化)。 // main // ├── go.mod // ├── go.sum // ├── main.go // └── logger // └── logger.gologger.gopackage logger import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( l *zap.Logger ) func SetUp() error { conf := zap.Config{ Level: zap.NewAtomicLevelAt(zap.DebugLevel), Development: true, Encoding: "console", OutputPaths: []string{"stdout"}, ErrorOutputPaths: []string{"stdout"}, EncoderConfig: zapcore.EncoderConfig{ LevelKey: "level", TimeKey: "time", MessageKey: "msg", CallerKey: "caller", EncodeTime: zapcore.ISO8601TimeEncoder, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeCaller: zapcore.ShortCallerEncoder, }, } var err error l, err = conf.Build() if err != nil { return err } return nil } func Info(msg string, fields ...zap.Field){ l.Info(msg,fields...) }main.gopackage main import ( "go.uber.org/zap" "main/logger" "time" ) func main() { err := logger.SetUp() if err != nil { panic(err) } logger.Info("Zap Start.", zap.Time("time", time.Now())) } // Output: // 2021-01-05T00:12:44.683+0900 info logger/logger.go:41 Zap Start. {"time": "2021-01-05T00:12:44.683+0900"} // ↑ここをlogger/logger.goじゃなく、main/main.goにしたい。解決策
- ラップするのではなく、zapの関数をそのままloggerのPublicな変数にぶちこむ!
logger.gopackage logger import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( l *zap.Logger Info func(msg string, fields ...zap.Field) //ここが変更点1 ) func SetUp() error { conf := zap.Config{ Level: zap.NewAtomicLevelAt(zap.DebugLevel), Development: true, Encoding: "console", OutputPaths: []string{"stdout"}, ErrorOutputPaths: []string{"stdout"}, EncoderConfig: zapcore.EncoderConfig{ LevelKey: "level", TimeKey: "time", MessageKey: "msg", CallerKey: "caller", EncodeTime: zapcore.ISO8601TimeEncoder, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeCaller: zapcore.ShortCallerEncoder, }, } var err error l, err = conf.Build() if err != nil { return err } Info = l.Info //ここが変更点2 return nil }main.goはさっきと一緒なので省略。
無事、main/main.goが取れました。
↓出力結果2021-01-05T00:19:47.157+0900 info main/main.go:14 Zap Start. {"time": "2021-01-05T00:19:47.157+0900"}最終的なLoggerの形
以下の方針のもと、最終的に以下の形にしました。
- 開発生産性を考慮し、基本的に
zap.SugaredLogger
でLoggingする方針にしました(zap.field書くのはめんどくさい)。- なんども出力して速度がどうしても気になる時にだけ、
zap.Logger
を使う。zap.Logger
は以下の理由からInfoとWarnのみ。
- エラー出力が出る時なんて速度より別問題。
- Debugは本番で出力しない=速度気にしなくていい。
これで進めようと思うlogger.gopackage logger import ( "go.uber.org/zap" "go.uber.org/zap/zapcore" ) var ( l *zap.Logger s *zap.SugaredLogger Debug func(args ...interface{}) Info func(args ...interface{}) Warn func(args ...interface{}) Error func(args ...interface{}) Debugf func(tmpl string, args ...interface{}) Infof func(tmpl string, args ...interface{}) Warnf func(tmpl string, args ...interface{}) Errorf func(tmpl string, args ...interface{}) InfoQuick func(msg string, fields ...zap.Field) WarnQuick func(msg string, fields ...zap.Field) ) func SetUp() error { //TODO: 環境変数つかって設定できるようにする conf := zap.Config{ Level: zap.NewAtomicLevelAt(zap.DebugLevel), Development: true, Encoding: "console", OutputPaths: []string{"stdout"}, ErrorOutputPaths: []string{"stdout"}, EncoderConfig: zapcore.EncoderConfig{ LevelKey: "level", TimeKey: "time", MessageKey: "msg", CallerKey: "caller", EncodeTime: zapcore.ISO8601TimeEncoder, EncodeLevel: zapcore.LowercaseLevelEncoder, EncodeCaller: zapcore.ShortCallerEncoder, }, } var err error l, err = conf.Build() if err != nil { return err } s = l.Sugar() //LogのCallerを正しく表示させるために、ファンクションをDIする形にする //例えば、別ファンクション func Debug(...){ s.Debug(...) } のように定義した場合、 //出力されるログのcallerが全てlogger/logger.goになってしまう。 Debug = s.Debug Info = s.Info Warn = s.Warn Error = s.Error Debugf = s.Debugf Infof = s.Infof Warnf = s.Warnf Errorf = s.Errorf InfoQuick = l.Info WarnQuick = l.Warn return nil }
- 思考錯誤しながらも最終的にはいい形になったのではないかと思っています。
- これを元に開発するのはこれからなので変わるかもしれませんが。
- loggerの実装自体、デファクトスタンダードがありそう、もっといい方法あれば教えてほしいです!
- 投稿日:2021-01-05T00:09:49+09:00
LeetCodeに毎日挑戦してみた 168. Excel Sheet Column Title(Python、Go)
Leetcodeとは
leetcode.com
ソフトウェア開発職のコーディング面接の練習といえばこれらしいです。
合計1500問以上のコーデイング問題が投稿されていて、実際の面接でも同じ問題が出されることは多いらしいとのことです。golang入門+アルゴリズム脳の強化のためにgoとPythonで解いていこうと思います。
38問目(問題168)
168. Excel Sheet Column Title
問題内容
Given a positive integer, return its corresponding column title as appear in an Excel sheet.
For example:
1 -> A 2 -> B 3 -> C ... 26 -> Z 27 -> AA 28 -> AB ...Example 1:
Input: 1 Output: "A"Example 2:
Input: 28 Output: "AB"Example 3:
Input: 701 Output: "ZY"考え方
数字を文字に変換する方針で解いていきます。ASCIIを知っているとわかりやすいです
26文字で一周なので26で割りながら0になったらループを終了します
最終的にresが逆順になっているので反転して返します。
解答コード
class Solution: def convertToTitle(self, n: int) -> str: res = "" while n > 0: n -= 1 # 26 -> "Z" res += chr(n % 26 + ord('A')) n //= 26 return res[::-1]
- Goでも書いてみます!
func convertToTitle(n int) string { result := "" for { if n <= 26 { result = string(n+64) + result break } else { result = string((n-1)%26+1+64) + result n = (n - 1) / 26 } } return result }