Skip to content

Commit

Permalink
serve metrics under different port per default (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
philmtd authored Jan 8, 2023
1 parent 1079139 commit f286140
Show file tree
Hide file tree
Showing 13 changed files with 337 additions and 151 deletions.
4 changes: 2 additions & 2 deletions cmd/fullhouse/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
var log = logger.New("full-house")
var rootCmd *cobra.Command

var GitCommit = "dev"
var GitTag = "dev"
var GitCommit = ""
var GitTag = ""
var commandVersion string

func init() {
Expand Down
2 changes: 2 additions & 0 deletions config/fullhouse-default.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
fullHouse:
server:
port: 8080
metrics:
port: 8090
mode: production
votingSchemes:
- name: Fibonacci
Expand Down
363 changes: 247 additions & 116 deletions frontend/package-lock.json

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,32 @@
"private": true,
"dependencies": {
"@angular/animations": "^15.0.4",
"@angular/cdk": "^15.0.3",
"@angular/cdk": "^15.0.4",
"@angular/common": "^15.0.4",
"@angular/compiler": "^15.0.4",
"@angular/core": "^15.0.4",
"@angular/forms": "^15.0.4",
"@angular/material": "^15.0.3",
"@angular/material": "^15.0.4",
"@angular/platform-browser": "^15.0.4",
"@angular/platform-browser-dynamic": "^15.0.4",
"@angular/router": "^15.0.4",
"@messageformat/core": "^3.0.1",
"@ngx-translate/core": "^14.0.0",
"@ngx-translate/http-loader": "^7.0.0",
"@ngxs/logger-plugin": "3.7.6",
"@ngxs/storage-plugin": "3.7.6",
"@ngxs/store": "3.7.6",
"css-fx-layout": "2.1.0",
"ngx-translate-messageformat-compiler": "^6.2.0",
"rxjs": "~7.8.0",
"tslib": "^2.4.1",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^15.0.4",
"@angular/cli": "^15.0.4",
"@angular-devkit/build-angular": "^15.0.5",
"@angular/cli": "^15.0.5",
"@angular/compiler-cli": "^15.0.4",
"@ngxs/devtools-plugin": "3.7.6",
"@types/jasmine": "~4.3.1",
"@types/node": "^18.11.17",
"gzipper": "7.2.0",
Expand All @@ -44,7 +47,6 @@
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "^2.0.0",
"@ngxs/devtools-plugin": "3.7.6",
"typescript": "~4.8.4"
}
}
10 changes: 8 additions & 2 deletions frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ import {InvitePlayersDialogComponent} from "./components/invite-players-dialog/i
import {configureSvgIcons, configureTranslations, provideAnimationDriverBasedOnUserPreferences} from "./configuration/configuration";
import {ParticipantComponent} from "./game/participant/participant.component";
import {AnimationDriver} from "@angular/animations/browser";
import {MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltipDefaultOptions} from "@angular/material/tooltip";
import {MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltipDefaultOptions, MatTooltipModule} from "@angular/material/tooltip";
import {ThemingState} from "./store/theming/theming.state";
import {ThemeSwitcherComponent} from "./components/theme-switcher/theme-switcher.component";
import {MatMenuModule} from "@angular/material/menu";
import {FractionFilterPipe} from "./game/game/fraction-filter.pipe";
import {TranslateLoader, TranslateModule, TranslateService} from "@ngx-translate/core";
import {TranslateCompiler, TranslateLoader, TranslateModule, TranslateService} from "@ngx-translate/core";
import {TranslateHttpLoader} from "@ngx-translate/http-loader";
import {TranslateMessageFormatCompiler} from "ngx-translate-messageformat-compiler";

export function HttpLoaderFactory(http: HttpClient) {
return new TranslateHttpLoader(http, '/assets/i18n/', '.json');
Expand Down Expand Up @@ -74,11 +75,16 @@ export function HttpLoaderFactory(http: HttpClient) {
NgxsLoggerPluginModule.forRoot(),
MatIconModule,
MatMenuModule,
MatTooltipModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient]
},
compiler: {
provide: TranslateCompiler,
useClass: TranslateMessageFormatCompiler
}
})
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<div class="nav-container fx-layout-row">
<nav class="fx-flex fx-layout-row fx-align--start-x fx-align--x-center fx-gap--1em">
<a class="fx-layout-row fx-align--start-x fx-align--x-center fx-gap--1em" routerLink="/">
<img [src]="logoSrc" alt="Full House Logo" />
<img [matTooltip]="'components.navigation.appInfo' | translate:{version: version}" [src]="logoSrc" alt="Full House Logo" />
<h1>{{ navTitle || "Full House" }}</h1>
</a>
<div class="fx-flex"></div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {Component, Input} from "@angular/core";
import {Select, Store} from "@ngxs/store";
import {ThemingState} from "../../store/theming/theming.state";
import {Observable} from "rxjs";
import {Api} from "../../game/api/api.service";

@Component({
selector: 'navigation',
Expand All @@ -14,10 +15,12 @@ export class NavigationComponent {
@Select(ThemingState.isDarkMode) isDarkMode$: Observable<boolean>;

logoSrc: string = '/assets/pplogo-light.svg';

constructor(private store: Store) {
version: string = '';
constructor(private store: Store,
private api: Api) {
this.isDarkMode$.subscribe(isDarkMode => {
this.logoSrc = `/assets/pplogo-${isDarkMode ? 'dark' : 'light'}.svg`;
});
this.api.appInfo().subscribe(info => this.version = info.version);
}
}
6 changes: 5 additions & 1 deletion frontend/src/app/game/api/api.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {HttpClient} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {Game, Participant, VoteOption, VotingScheme} from "../model";
import {AppInfo, Game, Participant, VoteOption, VotingScheme} from "../model";

@Injectable()
export class Api {
Expand All @@ -13,6 +13,10 @@ export class Api {
return this.client.get<Array<VotingScheme>>(`/api/game/votingSchemes`);
}

public appInfo(): Observable<AppInfo> {
return this.client.get<AppInfo>(`/api/info`);
}

public createNewGame(name: string, scheme: VotingScheme): Observable<Game> {
return this.client.post<Game>(`/api/game/new`, {
name: name,
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/app/game/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@ export type GamePhase = 'VOTING' | 'REVEALED';

export interface GameState {
phase: GamePhase;
votesByParticipantId: {[key:string]:Vote};
votesByParticipantId: { [key: string]: Vote };
lastTransition: string
}

export type VoteOption = number | '?' | undefined;

export interface Vote {
voted: boolean;
vote?: VoteOption;
}

export interface AppInfo {
version: string;
}
3 changes: 3 additions & 0 deletions frontend/src/assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
"auto": "Auto",
"dark": "Dark",
"light": "Light"
},
"navigation": {
"appInfo": "Full House Version: {version}"
}
},
"game": {
Expand Down
4 changes: 1 addition & 3 deletions go.mk
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
GO ?= go
LINTER ?= golangci-lint
GO_TESTSUM ?= gotestsum
GIT_DIRTY := $(shell git diff --quiet || echo '-dirty')
VERSION := $(shell [ -z $$(git tag --points-at HEAD) ] && echo "unknown" || echo $$(git tag --points-at HEAD))
COMMIT := $(shell git rev-parse --short HEAD)$(GIT_DIRTY)
COMMIT := $(shell git rev-parse --short HEAD)
LDFLAGS += -ldflags '-extldflags "-static" -s -w -X=main.GitTag=$(VERSION) -X=main.GitCommit=$(COMMIT)' # -s -w reduces binary size by removing some debug information
BUILDFLAGS += -installsuffix cgo --tags release

Expand Down Expand Up @@ -40,7 +39,6 @@ prepare:

build:
$(GO) build -o $(CMD) -a $(BUILDFLAGS) $(LDFLAGS) $(CMD_SRC)
upx $(CMD) # reduce binary size

build-for-docker:
CGO_ENABLED=0 GOOS=linux $(GO) build -o $(CMD) -a $(BUILDFLAGS) $(LDFLAGS) $(CMD_SRC)
Expand Down
5 changes: 5 additions & 0 deletions pkg/fullhouse/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type Config struct {

type GameConfig struct {
Server ServerConfig `yaml:"server" validate:"required,dive"`
Metrics MetricsConfig `yaml:"metrics" validate:"required,dive"`
Mode Mode `yaml:"mode"`
VotingSchemes []VotingScheme `yaml:"votingSchemes" validate:"required,dive"`
}
Expand All @@ -80,3 +81,7 @@ const (
type ServerConfig struct {
Port int `yaml:"port" validate:"required,number"`
}

type MetricsConfig struct {
Port int `yaml:"port" validate:"required,number"`
}
63 changes: 45 additions & 18 deletions pkg/fullhouse/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package server

import (
"context"
"errors"
"fmt"
"fullhouse/pkg/fullhouse/config"
"fullhouse/pkg/fullhouse/game"
Expand All @@ -14,7 +13,6 @@ import (
ginzap "github.com/gin-contrib/zap"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"log"
"net/http"
"os"
"os/signal"
Expand All @@ -29,12 +27,17 @@ type Server struct {
websocketHandler *websocket.WebsocketHandler
ctx context.Context
}
type appInfo struct {
Version string `json:"version"`
}

var appVersion string
var info appInfo

func New(ctx context.Context, version string) Server {
handler := websocket.NewWebsocketHandler()
appVersion = version
info = appInfo{
Version: version,
}
return Server{
log: logger.New("server"),
manager: game.New(handler.Hub, ctx),
Expand All @@ -44,7 +47,7 @@ func New(ctx context.Context, version string) Server {
}

func (s *Server) Start(c config.Config) {
metrics.RegisterCommonMetrics(appVersion)
metrics.RegisterCommonMetrics(info.Version)

if c.FullHouse.Mode == config.PRODUCTION {
gin.SetMode(gin.ReleaseMode)
Expand All @@ -54,23 +57,24 @@ func (s *Server) Start(c config.Config) {

r := gin.New()

r.Use(ginzap.GinzapWithConfig(s.log.Desugar(), &ginzap.Config{
ginzapMiddleware := ginzap.GinzapWithConfig(s.log.Desugar(), &ginzap.Config{
TimeFormat: time.RFC3339,
UTC: false,
SkipPaths: []string{"/up", "/metrics"},
}))
})
r.Use(ginzapMiddleware)

r.Use(static.Serve("/", static.LocalFile("frontend", true)))
r.NoRoute(func(c *gin.Context) {
c.File(path.Join("frontend", "index.html"))
})

r.GET("/metrics", metrics.PrometheusHandler())
r.GET("/up", s.upHandler)

api := r.Group("/api")
api.POST("/participant/new", s.newParticipant)
api.Any("/ws", s.wsHandler)
api.GET("/info", s.infoHandler)

gameApi := api.Group("/game")
gameApi.GET("/votingSchemes", s.votingSchemes)
Expand All @@ -80,26 +84,46 @@ func (s *Server) Start(c config.Config) {
gameApi.POST("/:slug/progress", s.progressToNextPhase)
gameApi.GET("/:slug", s.getGame)

address := fmt.Sprintf(":%d", c.FullHouse.Server.Port)
srv := &http.Server{
Addr: address,
var servers []*http.Server

defaultServerAddress := fmt.Sprintf(":%d", c.FullHouse.Server.Port)
servers = append(servers, &http.Server{
Addr: defaultServerAddress,
Handler: r,
})

if c.FullHouse.Metrics.Port == c.FullHouse.Server.Port {
r.GET("/metrics", metrics.PrometheusHandler())
} else {
metricsHandler := gin.New()
metricsHandler.Use(ginzapMiddleware)
metricsHandler.GET("/metrics", metrics.PrometheusHandler())
metricsServerAddress := fmt.Sprintf(":%d", c.FullHouse.Metrics.Port)
servers = append(servers, &http.Server{
Addr: metricsServerAddress,
Handler: metricsHandler,
})
}

go func() {
if err := srv.ListenAndServe(); err != nil && errors.Is(err, http.ErrServerClosed) {
log.Fatalf("listen: %s\n", err)
}
}()
for _, server := range servers {
srv := server
go func() {
if err := srv.ListenAndServe(); err != nil {
s.log.Warnw("listen error", "server", srv.Addr, "error", err)
}
}()
}

quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
s.log.Info("shutting down server")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal("server forced to shutdown:", err)
for _, server := range servers {
if err := server.Shutdown(ctx); err != nil {
s.log.Fatalw("server forced to shutdown", "server", server.Addr, "error", err)
}
}
}

Expand Down Expand Up @@ -205,3 +229,6 @@ func getSessionIdCookie(ctx *gin.Context) (string, error) {
func (s *Server) wsHandler(c *gin.Context) {
s.websocketHandler.HandleMessages(c.Writer, c.Request)
}
func (s *Server) infoHandler(ctx *gin.Context) {
ctx.JSON(http.StatusOK, info)
}

0 comments on commit f286140

Please sign in to comment.