forked from vieter-v/vieter
				
			Merge pull request 'Add Prometheus metrics endpoint' (#325) from Chewing_Bever/vieter:metrics into dev
Reviewed-on: vieter-v/vieter#325remotes/1739821333615734048/dev
						commit
						849bf54979
					
				|  | @ -8,15 +8,21 @@ branches: | |||
| depends_on: | ||||
|   - build | ||||
| 
 | ||||
| skip_clone: true | ||||
| 
 | ||||
| pipeline: | ||||
|   generate: | ||||
|   install-modules: | ||||
|     image: *vlang_image | ||||
|     pull: true | ||||
|     commands: | ||||
|       - curl -o vieter -L "https://s3.rustybever.be/vieter/commits/$CI_COMMIT_SHA/vieter-linux-amd64" | ||||
|       - chmod +x vieter | ||||
|       - export VMODULES=$PWD/.vmodules | ||||
|       - 'cd src && v install' | ||||
| 
 | ||||
|   generate: | ||||
|     image: *vlang_image | ||||
|     commands: | ||||
|       # - curl -o vieter -L "https://s3.rustybever.be/vieter/commits/$CI_COMMIT_SHA/vieter-linux-amd64" | ||||
|       # - chmod +x vieter | ||||
|       - export VMODULES=$PWD/.vmodules | ||||
|       - make | ||||
|       - ./vieter man man | ||||
|       - cd man | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | |||
| 
 | ||||
| ## [Unreleased](https://git.rustybever.be/vieter-v/vieter/src/branch/dev) | ||||
| 
 | ||||
| * Metrics endpoint for Prometheus integration | ||||
| 
 | ||||
| ## [0.5.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0) | ||||
| 
 | ||||
| ### Added | ||||
|  |  | |||
|  | @ -0,0 +1,19 @@ | |||
| module server | ||||
| 
 | ||||
| import metrics | ||||
| import web | ||||
| 
 | ||||
| // v1_metrics serves a Prometheus-compatible metrics endpoint. | ||||
| ['/api/v1/metrics'; get; markused] | ||||
| fn (mut app App) v1_metrics() web.Result { | ||||
| 	if !app.conf.collect_metrics { | ||||
| 		return app.status(.not_found) | ||||
| 	} | ||||
| 
 | ||||
| 	mut exporter := metrics.new_prometheus_exporter([0.01, 0.05, 0.1, 0.5, 1, 100]) | ||||
| 	exporter.load(app.collector) | ||||
| 
 | ||||
| 	// TODO stream to connection instead | ||||
| 	body := exporter.export_to_string() or { return app.status(.internal_server_error) } | ||||
| 	return app.body(.ok, 'text/plain', body) | ||||
| } | ||||
|  | @ -15,6 +15,7 @@ pub: | |||
| 	base_image           string = 'archlinux:base-devel' | ||||
| 	max_log_age          int    [empty_default] | ||||
| 	log_removal_schedule string = '0 0' | ||||
| 	collect_metrics      bool   [empty_default] | ||||
| } | ||||
| 
 | ||||
| // cmd returns the cli submodule that handles starting the server | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import util | |||
| import db | ||||
| import build { BuildJobQueue } | ||||
| import cron.expression | ||||
| import metrics | ||||
| 
 | ||||
| const ( | ||||
| 	log_file_name = 'vieter.log' | ||||
|  | @ -100,12 +101,19 @@ pub fn server(conf Config) ! { | |||
| 		util.exit_with_message(1, 'Failed to initialize database: $err.msg()') | ||||
| 	} | ||||
| 
 | ||||
| 	collector := if conf.collect_metrics { | ||||
| 		&metrics.MetricsCollector(metrics.new_default_collector()) | ||||
| 	} else { | ||||
| 		&metrics.MetricsCollector(metrics.new_null_collector()) | ||||
| 	} | ||||
| 
 | ||||
| 	mut app := &App{ | ||||
| 		logger: logger | ||||
| 		api_key: conf.api_key | ||||
| 		conf: conf | ||||
| 		repo: repo | ||||
| 		db: db | ||||
| 		collector: collector | ||||
| 		job_queue: build.new_job_queue(global_ce, conf.base_image) | ||||
| 	} | ||||
| 	app.init_job_queue() or { | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ Module { | |||
|     dependencies: [ | ||||
|         'https://git.rustybever.be/vieter-v/conf', | ||||
|         'https://git.rustybever.be/vieter-v/docker', | ||||
|         'https://git.rustybever.be/vieter-v/aur' | ||||
|         'https://git.rustybever.be/vieter-v/aur', | ||||
|         'https://git.rustybever.be/vieter-v/metrics' | ||||
|     ] | ||||
| } | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ import net.urllib | |||
| import time | ||||
| import json | ||||
| import log | ||||
| import metrics | ||||
| 
 | ||||
| // The Context struct represents the Context which hold the HTTP request and response. | ||||
| // It has fields for the query, form, files. | ||||
|  | @ -27,6 +28,8 @@ pub mut: | |||
| 	conn &net.TcpConn = unsafe { nil } | ||||
| 	// Gives access to a shared logger object | ||||
| 	logger shared log.Log | ||||
| 	// Used to collect metrics on the web server | ||||
| 	collector &metrics.MetricsCollector | ||||
| 	// time.ticks() from start of web connection handle. | ||||
| 	// You can use it to determine how much time is spent on your request. | ||||
| 	page_gen_start i64 | ||||
|  | @ -145,6 +148,15 @@ pub fn (ctx &Context) is_authenticated() bool { | |||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // body sends the given body as an HTTP response. | ||||
| pub fn (mut ctx Context) body(status http.Status, content_type string, body string) Result { | ||||
| 	ctx.status = status | ||||
| 	ctx.content_type = content_type | ||||
| 	ctx.send_response(body) | ||||
| 
 | ||||
| 	return Result{} | ||||
| } | ||||
| 
 | ||||
| // json<T> HTTP_OK with json_s as payload with content-type `application/json` | ||||
| pub fn (mut ctx Context) json<T>(status http.Status, j T) Result { | ||||
| 	ctx.status = status | ||||
|  | @ -319,6 +331,18 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) { | |||
| 			app.logger.flush() | ||||
| 		} | ||||
| 
 | ||||
| 		// Record how long request took to process | ||||
| 		labels := [ | ||||
| 			['method', app.req.method.str()]!, | ||||
| 			['path', app.req.url]!, | ||||
| 			['status', app.status.int().str()]!, | ||||
| 		] | ||||
| 		app.collector.counter_increment(name: 'http_requests_total', labels: labels) | ||||
| 		app.collector.histogram_record(time.ticks() - app.page_gen_start, | ||||
| 			name: 'http_requests_time_ms' | ||||
| 			labels: labels | ||||
| 		) | ||||
| 
 | ||||
| 		unsafe { | ||||
| 			free(app) | ||||
| 		} | ||||
|  | @ -384,6 +408,7 @@ fn handle_conn<T>(mut conn net.TcpConn, mut app T, routes map[string]Route) { | |||
| 		static_mime_types: app.static_mime_types | ||||
| 		reader: reader | ||||
| 		logger: app.logger | ||||
| 		collector: app.collector | ||||
| 		api_key: app.api_key | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -13,3 +13,4 @@ api_update_frequency = 2 | |||
| image_rebuild_frequency = 1 | ||||
| max_concurrent_builds = 3 | ||||
| max_log_age = 64 | ||||
| collect_metrics = true | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue