generated from Yandex-Practicum/go-musthave-metrics-tpl
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* отрефакторил проект * iter2 * исправления по результатам работы статического анализатора * ещё исправление
- Loading branch information
1 parent
e056a6c
commit 20345c1
Showing
14 changed files
with
783 additions
and
400 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,32 @@ | ||
package main | ||
|
||
func main() {} | ||
import ( | ||
"context" | ||
"github.com/StasMerzlyakov/go-metrics/internal/agent" | ||
"os" | ||
"os/signal" | ||
"syscall" | ||
) | ||
|
||
func main() { | ||
configuration := agent.Configuration{ | ||
ServerAddr: "http://localhost:8080", | ||
ContentType: "text/plain", | ||
PollIntervalSec: 2, | ||
ReportIntervalSec: 10, | ||
} | ||
|
||
// Взято отсюда: "Реализация Graceful Shutdown в Go"(https://habr.com/ru/articles/771626/) | ||
// скорее на будущее для сервера | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
exit := make(chan os.Signal, 1) | ||
signal.Notify(exit, os.Interrupt, syscall.SIGTERM) | ||
|
||
if agent, err := agent.CreateAgent(ctx, configuration); err != nil { | ||
panic(err) | ||
} else { | ||
<-exit | ||
cancel() | ||
agent.Wait() // ожидаение завершения go-рутин в агенте | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,14 @@ | ||
package main | ||
|
||
import ( | ||
"context" | ||
"github.com/StasMerzlyakov/go-metrics/internal/server" | ||
) | ||
|
||
func main() { | ||
if err := server.CreateServer(); err != nil { | ||
ctx := context.Background() // TODO | ||
configuration := server.Configuration{} // TODO | ||
if err := server.CreateServer(ctx, configuration); err != nil { | ||
panic(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package agent | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/StasMerzlyakov/go-metrics/internal/storage" | ||
"sync" | ||
"time" | ||
) | ||
|
||
type Configuration struct { | ||
ServerAddr string | ||
ContentType string | ||
PollIntervalSec int | ||
ReportIntervalSec int | ||
// TODO - http timeout | ||
} | ||
|
||
type Agent interface { | ||
Wait() | ||
} | ||
|
||
func CreateAgent(ctx context.Context, config Configuration) (Agent, error) { | ||
agent := &agent{ | ||
metricsSource: NewRuntimeMetricsSource(), | ||
resultSender: NewHTTPResultSender(config.ServerAddr, config.ContentType), | ||
gaugeStorage: storage.NewMemoryFloat64Storage(), | ||
poolCounter: 0, | ||
} | ||
go agent.PoolMetrics(ctx, config.PollIntervalSec) | ||
go agent.ReportMetrics(ctx, config.ReportIntervalSec) | ||
agent.wg.Add(2) | ||
return agent, nil | ||
} | ||
|
||
type agent struct { | ||
metricsSource MetricsSource | ||
resultSender ResultSender | ||
gaugeStorage storage.MetricsStorage[float64] | ||
poolCounter int64 | ||
wg sync.WaitGroup | ||
} | ||
|
||
func (a *agent) Wait() { | ||
a.wg.Wait() | ||
} | ||
|
||
func (a *agent) PoolMetrics(ctx context.Context, pollIntervalSec int) { | ||
counter := 0 | ||
for { | ||
select { | ||
case <-ctx.Done(): | ||
fmt.Printf("[%v] PoolMetrics DONE\n", time.Now()) | ||
a.wg.Done() | ||
return | ||
default: | ||
time.Sleep(1 * time.Second) // Будем просыпаться каждую секунду для проверки ctx | ||
counter++ | ||
if counter == pollIntervalSec { | ||
counter = 0 | ||
for k, v := range a.metricsSource.PollMetrics() { | ||
a.gaugeStorage.Set(k, v) | ||
} | ||
a.poolCounter = a.metricsSource.PollCount() | ||
fmt.Printf("[%v] PoolMetrics - %v\n", time.Now(), a.poolCounter) | ||
} | ||
} | ||
} | ||
} | ||
|
||
func (a *agent) ReportMetrics(ctx context.Context, reportIntervalSec int) { | ||
counter := 0 | ||
for { | ||
select { | ||
case <-ctx.Done(): | ||
fmt.Printf("[%v] ReportMetrics DONE\n", time.Now()) | ||
a.wg.Done() | ||
return | ||
default: | ||
time.Sleep(1 * time.Second) // Будем просыпаться каждую секунду для проверки ctx | ||
counter++ | ||
if counter == reportIntervalSec { | ||
counter = 0 | ||
for _, key := range a.gaugeStorage.Keys() { | ||
val, _ := a.gaugeStorage.Get(key) | ||
_ = a.resultSender.SendGauge(key, val) | ||
} | ||
_ = a.resultSender.SendCounter("PoolCount", a.poolCounter) | ||
fmt.Printf("[%v] ReportMetrics success - %v\n", time.Now(), a.poolCounter) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package agent | ||
|
||
import "errors" | ||
|
||
type MetricsSource interface { | ||
PollMetrics() map[string]float64 | ||
PollCount() int64 | ||
} | ||
|
||
var ErrServerInteraction = errors.New("server interaction error") | ||
|
||
type ResultSender interface { | ||
SendGauge(name string, value float64) error | ||
SendCounter(name string, value int64) error | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package agent | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"strings" | ||
"sync" | ||
) | ||
|
||
func NewHTTPResultSender(serverAdd string, contentType string) ResultSender { | ||
return &httpResultSender{ | ||
serverAdd: serverAdd, | ||
contentType: contentType, | ||
urlPattern: "/update/%v/%v/%v", | ||
} | ||
} | ||
|
||
type httpResultSender struct { | ||
serverAdd string | ||
contentType string | ||
urlPattern string | ||
client *http.Client | ||
sm sync.Mutex | ||
} | ||
|
||
func (h *httpResultSender) initIfNecessary() error { | ||
if h.client == nil { | ||
h.sm.Lock() | ||
defer h.sm.Unlock() | ||
if h.client == nil { | ||
h.client = &http.Client{} | ||
h.serverAdd = strings.TrimSuffix(h.serverAdd, "/") | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (h *httpResultSender) store(url string) error { | ||
if err := h.initIfNecessary(); err != nil { | ||
return err | ||
} | ||
fullURL := h.serverAdd + url | ||
res, err := h.client.Post(fullURL, h.contentType, nil) | ||
if err != nil { | ||
fmt.Printf("server interation error: %v\n", err.Error()) // log error | ||
return err | ||
} | ||
defer res.Body.Close() | ||
if res.StatusCode != http.StatusOK { | ||
resBody, err := io.ReadAll(res.Body) | ||
// log error | ||
if err == nil { | ||
fmt.Printf("server error: \n status: %v\n content: %v\n", res.StatusCode, string(resBody)) | ||
} else { | ||
fmt.Printf("server error: \n status: %v\n content read error: %v\n", res.StatusCode, err.Error()) | ||
} | ||
return ErrServerInteraction | ||
} | ||
return nil | ||
} | ||
|
||
func (h *httpResultSender) SendGauge(name string, value float64) error { | ||
url := fmt.Sprintf(h.urlPattern, "gauge", name, value) | ||
return h.store(url) | ||
} | ||
|
||
func (h *httpResultSender) SendCounter(name string, value int64) error { | ||
url := fmt.Sprintf(h.urlPattern, "counter", name, value) | ||
return h.store(url) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package agent | ||
|
||
import ( | ||
"math/rand" | ||
"runtime" | ||
"sync/atomic" | ||
) | ||
|
||
func NewRuntimeMetricsSource() MetricsSource { | ||
return &runtimeMetrics{} | ||
} | ||
|
||
type runtimeMetrics struct { | ||
counter int64 | ||
} | ||
|
||
func (rm *runtimeMetrics) PollCount() int64 { | ||
return rm.counter | ||
} | ||
|
||
func (rm *runtimeMetrics) PollMetrics() map[string]float64 { | ||
defer atomic.AddInt64(&rm.counter, 1) | ||
var memStats runtime.MemStats | ||
return map[string]float64{ | ||
"Alloc": float64(memStats.Alloc), | ||
"BuckHashSys": float64(memStats.BuckHashSys), | ||
"Frees": float64(memStats.Frees), | ||
"GCCPUFraction": memStats.GCCPUFraction, | ||
"GCSys": float64(memStats.GCSys), | ||
"HeapAlloc": float64(memStats.HeapAlloc), | ||
"HeapIdle": float64(memStats.HeapIdle), | ||
"HeapInuse": float64(memStats.HeapInuse), | ||
"HeapObjects": float64(memStats.HeapObjects), | ||
"HeapReleased": float64(memStats.HeapReleased), | ||
"HeapSys": float64(memStats.HeapSys), | ||
"LastGC": float64(memStats.LastGC), | ||
"Lookups": float64(memStats.Lookups), | ||
"MCacheInuse": float64(memStats.MCacheInuse), | ||
"MCacheSys": float64(memStats.MCacheSys), | ||
"MSpanInuse": float64(memStats.MSpanInuse), | ||
"MSpanSys": float64(memStats.MSpanSys), | ||
"Mallocs": float64(memStats.Mallocs), | ||
"NextGC": float64(memStats.NextGC), | ||
"NumForcedGC": float64(memStats.NumForcedGC), | ||
"NumGC": float64(memStats.NumGC), | ||
"OtherSys": float64(memStats.OtherSys), | ||
"PauseTotalNs": float64(memStats.PauseTotalNs), | ||
"StackInuse": float64(memStats.StackInuse), | ||
"StackSys": float64(memStats.StackSys), | ||
"Sys": float64(memStats.Sys), | ||
"TotalAlloc": float64(memStats.TotalAlloc), | ||
"RandomValue": rand.Float64(), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package agent | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"testing" | ||
) | ||
|
||
func TestRuntimeMetrics(t *testing.T) { | ||
rm := &runtimeMetrics{} | ||
expectedKeys := []string{ | ||
"Alloc", | ||
"BuckHashSys", | ||
"Frees", | ||
"GCCPUFraction", | ||
"GCSys", | ||
"HeapAlloc", | ||
"HeapIdle", | ||
"HeapInuse", | ||
"HeapObjects", | ||
"HeapReleased", | ||
"HeapSys", | ||
"LastGC", | ||
"Lookups", | ||
"MCacheInuse", | ||
"MCacheSys", | ||
"MSpanInuse", | ||
"MSpanSys", | ||
"Mallocs", | ||
"NextGC", | ||
"NumForcedGC", | ||
"NumGC", | ||
"OtherSys", | ||
"PauseTotalNs", | ||
"StackInuse", | ||
"StackSys", | ||
"Sys", | ||
"TotalAlloc", | ||
"RandomValue", | ||
} | ||
result := rm.PollMetrics() | ||
assert.Equal(t, len(expectedKeys), len(result)) | ||
for _, expectedKey := range expectedKeys { | ||
_, ok := result[expectedKey] | ||
require.Truef(t, ok, "pollMetrics doesn't contain key %v", expectedKey) | ||
} | ||
|
||
rm.PollMetrics() | ||
rm.PollMetrics() | ||
rm.PollMetrics() | ||
assert.Equal(t, int64(4), rm.PollCount()) | ||
} |
Oops, something went wrong.