Add simple debug logging.

This commit is contained in:
Stanislav Nikitin 2020-12-23 16:28:57 +05:00
parent 935d5d8109
commit 95c8181d2a
Signed by: pztrn
GPG Key ID: 1E944A0F0568B550
10 changed files with 100 additions and 27 deletions

View File

@ -12,6 +12,7 @@ import (
"go.dev.pztrn.name/metricator/internal/common" "go.dev.pztrn.name/metricator/internal/common"
"go.dev.pztrn.name/metricator/internal/configuration" "go.dev.pztrn.name/metricator/internal/configuration"
"go.dev.pztrn.name/metricator/internal/httpserver" "go.dev.pztrn.name/metricator/internal/httpserver"
"go.dev.pztrn.name/metricator/internal/logger"
) )
func main() { func main() {
@ -25,8 +26,6 @@ func main() {
mainCtx, cancelFunc := context.WithCancel(context.Background()) mainCtx, cancelFunc := context.WithCancel(context.Background())
config := configuration.NewConfig() config := configuration.NewConfig()
httpSrv, httpStopped := httpserver.NewHTTPServer(mainCtx, config)
// Parse configuration. // Parse configuration.
flag.Parse() flag.Parse()
@ -40,8 +39,11 @@ func main() {
// Create applications. // Create applications.
apps := make([]*application.Application, 0, len(config.Applications)) apps := make([]*application.Application, 0, len(config.Applications))
logger := logger.NewLogger(config.Logger)
httpSrv, httpStopped := httpserver.NewHTTPServer(mainCtx, config, logger)
for appName, appConfig := range config.Applications { for appName, appConfig := range config.Applications {
app := application.NewApplication(mainCtx, appName, appConfig) app := application.NewApplication(mainCtx, appName, appConfig, logger)
app.Start() app.Start()
httpSrv.RegisterHandlerForApplication(appName, app.GetHandler()) httpSrv.RegisterHandlerForApplication(appName, app.GetHandler())

View File

@ -2,11 +2,11 @@ package application
import ( import (
"context" "context"
"log"
"net/http" "net/http"
"sync" "sync"
"go.dev.pztrn.name/metricator/internal/common" "go.dev.pztrn.name/metricator/internal/common"
"go.dev.pztrn.name/metricator/internal/logger"
"go.dev.pztrn.name/metricator/internal/storage" "go.dev.pztrn.name/metricator/internal/storage"
"go.dev.pztrn.name/metricator/internal/storage/memory" "go.dev.pztrn.name/metricator/internal/storage/memory"
) )
@ -17,6 +17,7 @@ type Application struct {
config *Config config *Config
ctx context.Context ctx context.Context
doneChan chan struct{} doneChan chan struct{}
logger *logger.Logger
name string name string
storage storage.Metrics storage storage.Metrics
@ -29,11 +30,12 @@ type Application struct {
} }
// NewApplication creates new application. // NewApplication creates new application.
func NewApplication(ctx context.Context, name string, config *Config) *Application { func NewApplication(ctx context.Context, name string, config *Config, logger *logger.Logger) *Application {
a := &Application{ a := &Application{
config: config, config: config,
ctx: ctx, ctx: ctx,
doneChan: make(chan struct{}), doneChan: make(chan struct{}),
logger: logger,
name: name, name: name,
} }
a.initialize() a.initialize()
@ -54,9 +56,9 @@ func (a *Application) GetHandler() common.HTTPHandlerFunc {
// Initializes internal things like storage, HTTP client, etc. // Initializes internal things like storage, HTTP client, etc.
func (a *Application) initialize() { func (a *Application) initialize() {
a.storage, a.storageDone = memory.NewStorage(a.ctx, a.name+" storage") a.storage, a.storageDone = memory.NewStorage(a.ctx, a.name+" storage", a.logger)
log.Printf("Application '%s' initialized with configuration: %+v\n", a.name, a.config) a.logger.Debugf("Application '%s' initialized with configuration: %+v\n", a.name, a.config)
} }
// Start starts asynchronous things like data fetching, storage cleanup, etc. // Start starts asynchronous things like data fetching, storage cleanup, etc.
@ -71,7 +73,7 @@ func (a *Application) Start() {
// We should wait until storage routines are also stopped. // We should wait until storage routines are also stopped.
<-a.storage.GetDoneChan() <-a.storage.GetDoneChan()
log.Println("Application", a.name, "stopped") a.logger.Infoln("Application", a.name, "stopped")
a.doneChan <- struct{}{} a.doneChan <- struct{}{}
}() }()

View File

@ -2,7 +2,6 @@ package application
import ( import (
"io/ioutil" "io/ioutil"
"log"
"net/http" "net/http"
"time" "time"
) )
@ -23,11 +22,11 @@ func (a *Application) fetch() {
a.fetchIsRunning = true a.fetchIsRunning = true
a.fetchIsRunningMutex.Unlock() a.fetchIsRunningMutex.Unlock()
log.Println("Fetching data for", a.name) a.logger.Infoln("Fetching data for", a.name)
req, err := http.NewRequestWithContext(a.ctx, "GET", a.config.Endpoint, nil) req, err := http.NewRequestWithContext(a.ctx, "GET", a.config.Endpoint, nil)
if err != nil { if err != nil {
log.Println("Failed to create request for", a.name, "metrics:", err.Error()) a.logger.Infoln("Failed to create request for", a.name, "metrics:", err.Error())
return return
} }
@ -38,7 +37,7 @@ func (a *Application) fetch() {
resp, err := a.httpClient.Do(req) resp, err := a.httpClient.Do(req)
if err != nil { if err != nil {
log.Println("Failed to execute request for", a.name, "metrics:", err.Error()) a.logger.Infoln("Failed to execute request for", a.name, "metrics:", err.Error())
return return
} }
@ -47,7 +46,7 @@ func (a *Application) fetch() {
body, err := ioutil.ReadAll(resp.Body) body, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Println("Failed to read response body for", a.name, "metrics:", err.Error()) a.logger.Infoln("Failed to read response body for", a.name, "metrics:", err.Error())
return return
} }
@ -70,7 +69,7 @@ func (a *Application) startFetcher() {
Timeout: time.Second * 5, Timeout: time.Second * 5,
} }
defer log.Println("Fetcher for", a.name, "completed") defer a.logger.Debugln("Fetcher for", a.name, "completed")
// First fetch should be executed ASAP. // First fetch should be executed ASAP.
a.fetch() a.fetch()

View File

@ -26,7 +26,7 @@ func (a *Application) parse(body string) map[string]models.Metric {
continue continue
} }
// log.Println("Analyzing line:", line) a.logger.Debugln("Analyzing line:", line)
name = a.getMetricName(line) name = a.getMetricName(line)
metric, found := data[name] metric, found := data[name]
@ -76,12 +76,12 @@ func (a *Application) parse(body string) map[string]models.Metric {
metric.Value = a.getMetricValue(line) metric.Value = a.getMetricValue(line)
// log.Printf("Got metric: %+v\n", metric) a.logger.Debugln("Got metric: %+v\n", metric)
data[name] = metric data[name] = metric
} }
// log.Printf("Data parsed: %+v\n", data) a.logger.Debugln("Data parsed: %+v\n", data)
return data return data
} }

View File

@ -10,6 +10,7 @@ import (
"strings" "strings"
"go.dev.pztrn.name/metricator/internal/application" "go.dev.pztrn.name/metricator/internal/application"
"go.dev.pztrn.name/metricator/internal/logger"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -24,6 +25,8 @@ type Config struct {
// Applications describes configuration for remote application's endpoints. // Applications describes configuration for remote application's endpoints.
// Key is an application's name. // Key is an application's name.
Applications map[string]*application.Config `yaml:"applications"` Applications map[string]*application.Config `yaml:"applications"`
// Logger is a logging configuration.
Logger *logger.Config `yaml:"logger"`
} }
// NewConfig returns new configuration. // NewConfig returns new configuration.

View File

@ -2,7 +2,6 @@ package httpserver
import ( import (
"context" "context"
"log"
"net" "net"
"net/http" "net/http"
"strings" "strings"
@ -10,6 +9,7 @@ import (
"go.dev.pztrn.name/metricator/internal/common" "go.dev.pztrn.name/metricator/internal/common"
"go.dev.pztrn.name/metricator/internal/configuration" "go.dev.pztrn.name/metricator/internal/configuration"
"go.dev.pztrn.name/metricator/internal/logger"
) )
// HTTPServer is a controlling structure for HTTP server. // HTTPServer is a controlling structure for HTTP server.
@ -17,15 +17,17 @@ type HTTPServer struct {
config *configuration.Config config *configuration.Config
ctx context.Context ctx context.Context
doneChan chan struct{} doneChan chan struct{}
logger *logger.Logger
handler *handler handler *handler
server *http.Server server *http.Server
} }
func NewHTTPServer(ctx context.Context, cfg *configuration.Config) (*HTTPServer, chan struct{}) { func NewHTTPServer(ctx context.Context, cfg *configuration.Config, logger *logger.Logger) (*HTTPServer, chan struct{}) {
h := &HTTPServer{ h := &HTTPServer{
config: cfg, config: cfg,
ctx: ctx, ctx: ctx,
doneChan: make(chan struct{}), doneChan: make(chan struct{}),
logger: logger,
} }
h.initialize() h.initialize()
@ -40,6 +42,8 @@ func (h *HTTPServer) getRequestContext(_ net.Listener) context.Context {
// Initializes handler and HTTP server structure. // Initializes handler and HTTP server structure.
func (h *HTTPServer) initialize() { func (h *HTTPServer) initialize() {
h.logger.Debugln("Initializing HTTP server...")
h.handler = &handler{ h.handler = &handler{
handlers: make(map[string]common.HTTPHandlerFunc), handlers: make(map[string]common.HTTPHandlerFunc),
} }
@ -58,6 +62,7 @@ func (h *HTTPServer) initialize() {
// RegisterHandlerForApplication registers HTTP handler for application. // RegisterHandlerForApplication registers HTTP handler for application.
func (h *HTTPServer) RegisterHandlerForApplication(name string, handler common.HTTPHandlerFunc) { func (h *HTTPServer) RegisterHandlerForApplication(name string, handler common.HTTPHandlerFunc) {
h.logger.Debugln("Registering handler for application", name)
h.handler.register(name, handler) h.handler.register(name, handler)
} }
@ -68,21 +73,21 @@ func (h *HTTPServer) Start() {
err := h.server.ListenAndServe() err := h.server.ListenAndServe()
if err != nil { if err != nil {
if !strings.Contains(err.Error(), "Server closed") { if !strings.Contains(err.Error(), "Server closed") {
log.Println("HTTP server failed to listen:", err.Error()) h.logger.Infoln("HTTP server failed to listen:", err.Error())
} }
} }
}() }()
go func() { go func() {
<-h.ctx.Done() <-h.ctx.Done()
log.Println("Shutting down HTTP server") h.logger.Infoln("Shutting down HTTP server")
err := h.server.Shutdown(h.ctx) err := h.server.Shutdown(h.ctx)
if err != nil && !strings.Contains(err.Error(), "context canceled") { if err != nil && !strings.Contains(err.Error(), "context canceled") {
log.Println("Failed to stop HTTP server:", err.Error()) h.logger.Infoln("Failed to stop HTTP server:", err.Error())
} }
log.Println("HTTP server stopped") h.logger.Infoln("HTTP server stopped")
h.doneChan <- struct{}{} h.doneChan <- struct{}{}
}() }()

View File

@ -0,0 +1,6 @@
package logger
// Config represents logging configuration.
type Config struct {
Debug bool `yaml:"debug"`
}

41
internal/logger/logger.go Normal file
View File

@ -0,0 +1,41 @@
package logger
import "log"
// Logger responsible for all logging actions.
type Logger struct {
config *Config
}
// NewLogger creates new logging wrapper and returns it to caller.
func NewLogger(config *Config) *Logger {
l := &Logger{config: config}
return l
}
// Debugf is a wrapper around log.Printf which will work only when debug mode
// is enabled.
func (l *Logger) Debugf(template string, params ...interface{}) {
if l.config.Debug {
log.Printf(template, params...)
}
}
// Debugln is a wrapper around log.Println which will work only when debug mode
// is enabled.
func (l *Logger) Debugln(params ...interface{}) {
if l.config.Debug {
log.Println(params...)
}
}
// Infof is a wrapper around log.Printf.
func (l *Logger) Infof(template string, params ...interface{}) {
log.Printf(template, params...)
}
// Infoln is a wrapper around log.Println.
func (l *Logger) Infoln(params ...interface{}) {
log.Println(params...)
}

View File

@ -3,9 +3,9 @@ package memory
import ( import (
"context" "context"
"errors" "errors"
"log"
"sync" "sync"
"go.dev.pztrn.name/metricator/internal/logger"
"go.dev.pztrn.name/metricator/internal/models" "go.dev.pztrn.name/metricator/internal/models"
) )
@ -15,6 +15,7 @@ var ErrMetricNotFound = errors.New("metric not found")
type Storage struct { type Storage struct {
ctx context.Context ctx context.Context
doneChan chan struct{} doneChan chan struct{}
logger *logger.Logger
name string name string
data map[string]models.Metric data map[string]models.Metric
@ -22,10 +23,11 @@ type Storage struct {
} }
// NewStorage creates new in-memory storage to use. // NewStorage creates new in-memory storage to use.
func NewStorage(ctx context.Context, name string) (*Storage, chan struct{}) { func NewStorage(ctx context.Context, name string, logger *logger.Logger) (*Storage, chan struct{}) {
s := &Storage{ s := &Storage{
ctx: ctx, ctx: ctx,
doneChan: make(chan struct{}), doneChan: make(chan struct{}),
logger: logger,
name: name, name: name,
} }
s.initialize() s.initialize()
@ -35,19 +37,27 @@ func NewStorage(ctx context.Context, name string) (*Storage, chan struct{}) {
// Get returns data from storage by key. // Get returns data from storage by key.
func (s *Storage) Get(key string) (models.Metric, error) { func (s *Storage) Get(key string) (models.Metric, error) {
s.logger.Debugln("Retrieving data for", key, "key from storage...")
s.dataMutex.RLock() s.dataMutex.RLock()
defer s.dataMutex.RUnlock() defer s.dataMutex.RUnlock()
data, found := s.data[key] data, found := s.data[key]
if !found { if !found {
s.logger.Infoln("Key", key, "not found in storage!")
return models.NewMetric("", "", "", nil), ErrMetricNotFound return models.NewMetric("", "", "", nil), ErrMetricNotFound
} }
s.logger.Debugf("Key %s found: %+v\n", key, data)
return data, nil return data, nil
} }
// GetAsSlice returns all data from storage as slice. // GetAsSlice returns all data from storage as slice.
func (s *Storage) GetAsSlice() []models.Metric { func (s *Storage) GetAsSlice() []models.Metric {
s.logger.Debugln("Returning all stored metrics as slice...")
metrics := make([]models.Metric, 0, len(s.data)) metrics := make([]models.Metric, 0, len(s.data))
for _, metric := range s.data { for _, metric := range s.data {
@ -80,7 +90,7 @@ func (s *Storage) Put(data map[string]models.Metric) {
} }
} }
log.Println("Put", len(data), "items in", s.name) s.logger.Debugln("Put", len(data), "items in", s.name)
} }
// Start starts asynchronous things if needed. // Start starts asynchronous things if needed.
@ -89,7 +99,7 @@ func (s *Storage) Start() {
go func() { go func() {
<-s.ctx.Done() <-s.ctx.Done()
log.Println("In-memory storage", s.name, "done") s.logger.Infoln("In-memory storage", s.name, "done")
s.doneChan <- struct{}{} s.doneChan <- struct{}{}
}() }()

View File

@ -10,3 +10,8 @@ applications:
# Timeout between requests. Not neccessarily be exact and requests might # Timeout between requests. Not neccessarily be exact and requests might
# be sent in 60 or more seconds (in this example). # be sent in 60 or more seconds (in this example).
time_between_requests: 60s time_between_requests: 60s
# Logger's configuration.
logger:
# Print debug output or not?
debug: false