feat: Implement simple page metrics
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				ci/woodpecker/push/woodpecker Pipeline was successful
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	ci/woodpecker/push/woodpecker Pipeline was successful
				
			This commit is contained in:
		
							parent
							
								
									0341ed8219
								
							
						
					
					
						commit
						8f09aa959b
					
				@ -24,7 +24,7 @@ import (
 | 
				
			|||||||
	"github.com/urfave/cli/v2"
 | 
						"github.com/urfave/cli/v2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func handleSubdomain(pagesDomain, domain, cname, path, giteaUrl, defaultCsp string, giteaClient *repo.GiteaClient, w http.ResponseWriter) {
 | 
					func handleSubdomain(pagesDomain, domain, cname, path, giteaUrl, defaultCsp string, giteaClient *repo.GiteaClient, lokiConfig *pages.LokiMetricConfig, w http.ResponseWriter) {
 | 
				
			||||||
	username := ""
 | 
						username := ""
 | 
				
			||||||
	if cname != "" {
 | 
						if cname != "" {
 | 
				
			||||||
		// If we are accessed via a CNAME, then CNAME contains our <user>.<pages domain> value.
 | 
							// If we are accessed via a CNAME, then CNAME contains our <user>.<pages domain> value.
 | 
				
			||||||
@ -60,10 +60,10 @@ func handleSubdomain(pagesDomain, domain, cname, path, giteaUrl, defaultCsp stri
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pages.ServeFile(username, repo.Name, path, defaultCsp, giteaClient, w)
 | 
						pages.ServeFile(username, repo.Name, path, defaultCsp, domain, giteaClient, lokiConfig, w)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func Handler(pagesDomain, giteaUrl, defaultCsp string, giteaClient *repo.GiteaClient) http.HandlerFunc {
 | 
					func Handler(pagesDomain, giteaUrl, defaultCsp string, giteaClient *repo.GiteaClient, lokiConfig *pages.LokiMetricConfig) http.HandlerFunc {
 | 
				
			||||||
	return func(w http.ResponseWriter, req *http.Request) {
 | 
						return func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
		w.Header().Set("Server", "rio")
 | 
							w.Header().Set("Server", "rio")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -79,7 +79,7 @@ func Handler(pagesDomain, giteaUrl, defaultCsp string, giteaClient *repo.GiteaCl
 | 
				
			|||||||
		// Is a direct subdomain requested?
 | 
							// Is a direct subdomain requested?
 | 
				
			||||||
		if strings.HasSuffix(req.Host, pagesDomain) {
 | 
							if strings.HasSuffix(req.Host, pagesDomain) {
 | 
				
			||||||
			log.Debug("Domain can be directly handled")
 | 
								log.Debug("Domain can be directly handled")
 | 
				
			||||||
			handleSubdomain(pagesDomain, req.Host, "", req.URL.Path, giteaUrl, defaultCsp, giteaClient, w)
 | 
								handleSubdomain(pagesDomain, req.Host, "", req.URL.Path, giteaUrl, defaultCsp, giteaClient, lokiConfig, w)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -96,7 +96,7 @@ func Handler(pagesDomain, giteaUrl, defaultCsp string, giteaClient *repo.GiteaCl
 | 
				
			|||||||
		//       pages domain makes no sense.
 | 
							//       pages domain makes no sense.
 | 
				
			||||||
		if strings.HasSuffix(cname, "."+pagesDomain) {
 | 
							if strings.HasSuffix(cname, "."+pagesDomain) {
 | 
				
			||||||
			log.Debugf("%s is alias of %s and can be handled after a CNAME query", req.Host, cname)
 | 
								log.Debugf("%s is alias of %s and can be handled after a CNAME query", req.Host, cname)
 | 
				
			||||||
			handleSubdomain(pagesDomain, req.Host, cname, req.URL.Path, giteaUrl, defaultCsp, giteaClient, w)
 | 
								handleSubdomain(pagesDomain, req.Host, cname, req.URL.Path, giteaUrl, defaultCsp, giteaClient, lokiConfig, w)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -131,6 +131,7 @@ func runServer(ctx *cli.Context) error {
 | 
				
			|||||||
	acmeDnsProvider := ctx.String("acme-dns-provider")
 | 
						acmeDnsProvider := ctx.String("acme-dns-provider")
 | 
				
			||||||
	acmeDisable := ctx.Bool("acme-disable")
 | 
						acmeDisable := ctx.Bool("acme-disable")
 | 
				
			||||||
	defaultCsp := ctx.String("default-csp")
 | 
						defaultCsp := ctx.String("default-csp")
 | 
				
			||||||
 | 
						lokiUrl := ctx.String("loki-url")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Init Logging
 | 
						// Init Logging
 | 
				
			||||||
	if ctx.Bool("debug") {
 | 
						if ctx.Bool("debug") {
 | 
				
			||||||
@ -139,6 +140,19 @@ func runServer(ctx *cli.Context) error {
 | 
				
			|||||||
		log.SetLevel(log.InfoLevel)
 | 
							log.SetLevel(log.InfoLevel)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set up the Loki metrics
 | 
				
			||||||
 | 
						var lokiConfig pages.LokiMetricConfig
 | 
				
			||||||
 | 
						if lokiUrl == "" {
 | 
				
			||||||
 | 
							lokiConfig = pages.LokiMetricConfig{
 | 
				
			||||||
 | 
								Enabled: false,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							lokiConfig = pages.LokiMetricConfig{
 | 
				
			||||||
 | 
								Enabled: true,
 | 
				
			||||||
 | 
								Url:     lokiUrl,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Setup the Gitea stuff
 | 
						// Setup the Gitea stuff
 | 
				
			||||||
	httpClient := http.Client{Timeout: 10 * time.Second}
 | 
						httpClient := http.Client{Timeout: 10 * time.Second}
 | 
				
			||||||
	giteaApiClient, err := gitea.NewClient(
 | 
						giteaApiClient, err := gitea.NewClient(
 | 
				
			||||||
@ -240,7 +254,7 @@ func runServer(ctx *cli.Context) error {
 | 
				
			|||||||
		defer waitGroup.Done()
 | 
							defer waitGroup.Done()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		log.Debug("Listening on main HTTP server")
 | 
							log.Debug("Listening on main HTTP server")
 | 
				
			||||||
		if err := http.Serve(listener, Handler(domain, giteaUrl, defaultCsp, &giteaClient)); err != nil {
 | 
							if err := http.Serve(listener, Handler(domain, giteaUrl, defaultCsp, &giteaClient, &lokiConfig)); err != nil {
 | 
				
			||||||
			log.Fatal(fmt.Errorf("Listening failed: %v", err))
 | 
								log.Fatal(fmt.Errorf("Listening failed: %v", err))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		log.Debug("Listening on main HTTP server done!")
 | 
							log.Debug("Listening on main HTTP server done!")
 | 
				
			||||||
@ -350,6 +364,12 @@ func main() {
 | 
				
			|||||||
				Value:   "",
 | 
									Value:   "",
 | 
				
			||||||
				EnvVars: []string{"DEFAULT_CSP"},
 | 
									EnvVars: []string{"DEFAULT_CSP"},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
 | 
								&cli.StringFlag{
 | 
				
			||||||
 | 
									Name:    "loki-url",
 | 
				
			||||||
 | 
									Usage:   "The URL for Loki metric pings",
 | 
				
			||||||
 | 
									Value:   "",
 | 
				
			||||||
 | 
									EnvVars: []string{"LOKI_URL"},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										56
									
								
								internal/pages/metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								internal/pages/metrics.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,56 @@
 | 
				
			|||||||
 | 
					package pages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log "github.com/sirupsen/logrus"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type LokiMetricConfig struct {
 | 
				
			||||||
 | 
						Url     string
 | 
				
			||||||
 | 
						Enabled bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Checks if we should send a metric ping to Loki based on the served path.
 | 
				
			||||||
 | 
					func (c *LokiMetricConfig) shouldSendMetrics(path string) bool {
 | 
				
			||||||
 | 
						return strings.HasSuffix(path, ".html") && c.Enabled
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *LokiMetricConfig) sendMetricPing(domain, path string) {
 | 
				
			||||||
 | 
						data := map[string]interface{}{
 | 
				
			||||||
 | 
							"steams": []map[string]interface{}{
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									"stream": map[string]string{
 | 
				
			||||||
 | 
										// Labels
 | 
				
			||||||
 | 
										"service": "rio",
 | 
				
			||||||
 | 
										"domain":  domain,
 | 
				
			||||||
 | 
										"type":    "metric",
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									"values": [][]interface{}{
 | 
				
			||||||
 | 
										{
 | 
				
			||||||
 | 
											strconv.Itoa(int(time.Now().UnixNano())),
 | 
				
			||||||
 | 
											"path=" + path,
 | 
				
			||||||
 | 
										},
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						jsonData, err := json.Marshal(data)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Errorf("Failed to send metric ping to Loki: %v", err)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Send the ping to the Loki server
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							http.Post(
 | 
				
			||||||
 | 
								c.Url,
 | 
				
			||||||
 | 
								"application/json",
 | 
				
			||||||
 | 
								strings.NewReader(string(jsonData)),
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -45,7 +45,7 @@ func addHeaders(csp, contentType string, contentLength int, w http.ResponseWrite
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ServeFile(username, reponame, path, defaultCsp string, giteaClient *repo.GiteaClient, w http.ResponseWriter) {
 | 
					func ServeFile(username, reponame, path, defaultCsp, domain string, giteaClient *repo.GiteaClient, metricConfig *LokiMetricConfig, w http.ResponseWriter) {
 | 
				
			||||||
	// Strip away a starting / as it messes with Gitea
 | 
						// Strip away a starting / as it messes with Gitea
 | 
				
			||||||
	if path[:1] == "/" {
 | 
						if path[:1] == "/" {
 | 
				
			||||||
		path = path[1:]
 | 
							path = path[1:]
 | 
				
			||||||
@ -116,4 +116,9 @@ func ServeFile(username, reponame, path, defaultCsp string, giteaClient *repo.Gi
 | 
				
			|||||||
	addHeaders(csp, mimeType, len(content), w)
 | 
						addHeaders(csp, mimeType, len(content), w)
 | 
				
			||||||
	w.WriteHeader(200)
 | 
						w.WriteHeader(200)
 | 
				
			||||||
	w.Write(content)
 | 
						w.Write(content)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Tell Loki about if, if desired
 | 
				
			||||||
 | 
						if metricConfig.shouldSendMetrics(path) {
 | 
				
			||||||
 | 
							metricConfig.sendMetricPing(domain, path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user