Add support for tracking custom metrics
Builds can now emit metrics that Hydra will store in its database and render as time series via flot charts. Typical applications are to keep track of performance indicators, coverage percentages, artifact sizes, and so on. For example, a coverage build can emit the coverage percentage as follows: echo "lineCoverage $pct %" > $out/nix-support/hydra-metrics Graphs of all metrics for a job can be seen at http://.../job/<project>/<jobset>/<job>#tabs-charts Specific metrics are also visible at http://.../job/<project>/<jobset>/<job>/metric/<metric> The latter URL also allows getting the data in JSON format (e.g. via "curl -H 'Accept: application/json'").
This commit is contained in:
@ -77,6 +77,9 @@ sub overview : Chained('job') PathPart('') Args(0) {
|
||||
, jobset => $c->stash->{jobset}->name
|
||||
, job => $c->stash->{job}->name
|
||||
})->count == 1 if $c->user_exists;
|
||||
|
||||
$c->stash->{metrics} = [ $job->buildmetrics->search(
|
||||
{ }, { select => ["name"], distinct => 1, order_by => "timestamp desc", }) ];
|
||||
}
|
||||
|
||||
|
||||
@ -110,6 +113,20 @@ sub output_sizes : Chained('job') PathPart('output-sizes') Args(0) {
|
||||
}
|
||||
|
||||
|
||||
sub metric : Chained('job') PathPart('metric') Args(1) {
|
||||
my ($self, $c, $metricName) = @_;
|
||||
|
||||
$c->stash->{template} = 'metric.tt';
|
||||
$c->stash->{metricName} = $metricName;
|
||||
|
||||
my @res = $c->stash->{job}->buildmetrics->search(
|
||||
{ name => $metricName },
|
||||
{ order_by => "timestamp", columns => [ "build", "name", "timestamp", "value", "unit" ] });
|
||||
|
||||
$self->status_ok($c, entity => [ map { { id => $_->get_column("build"), timestamp => $_ ->timestamp, value => $_->value, unit => $_->unit } } @res ]);
|
||||
}
|
||||
|
||||
|
||||
# Hydra::Base::Controller::ListBuilds needs this.
|
||||
sub get_builds : Chained('job') PathPart('') CaptureArgs(0) {
|
||||
my ($self, $c) = @_;
|
||||
|
187
src/lib/Hydra/Schema/BuildMetrics.pm
Normal file
187
src/lib/Hydra/Schema/BuildMetrics.pm
Normal file
@ -0,0 +1,187 @@
|
||||
use utf8;
|
||||
package Hydra::Schema::BuildMetrics;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Hydra::Schema::BuildMetrics
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 COMPONENTS LOADED
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L<Hydra::Component::ToJSON>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->load_components("+Hydra::Component::ToJSON");
|
||||
|
||||
=head1 TABLE: C<BuildMetrics>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("BuildMetrics");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 build
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 name
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 0
|
||||
|
||||
=head2 unit
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 value
|
||||
|
||||
data_type: 'double precision'
|
||||
is_nullable: 0
|
||||
|
||||
=head2 project
|
||||
|
||||
data_type: 'text'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 jobset
|
||||
|
||||
data_type: 'text'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 job
|
||||
|
||||
data_type: 'text'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 timestamp
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 0
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"build",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0 },
|
||||
"unit",
|
||||
{ data_type => "text", is_nullable => 1 },
|
||||
"value",
|
||||
{ data_type => "double precision", is_nullable => 0 },
|
||||
"project",
|
||||
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
|
||||
"jobset",
|
||||
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
|
||||
"job",
|
||||
{ data_type => "text", is_foreign_key => 1, is_nullable => 0 },
|
||||
"timestamp",
|
||||
{ data_type => "integer", is_nullable => 0 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</build>
|
||||
|
||||
=item * L</name>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("build", "name");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 build
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Builds>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"build",
|
||||
"Hydra::Schema::Builds",
|
||||
{ id => "build" },
|
||||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
|
||||
);
|
||||
|
||||
=head2 job
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Jobs>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"job",
|
||||
"Hydra::Schema::Jobs",
|
||||
{ jobset => "jobset", name => "job", project => "project" },
|
||||
{ is_deferrable => 0, on_delete => "NO ACTION", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
=head2 jobset
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Jobsets>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"jobset",
|
||||
"Hydra::Schema::Jobsets",
|
||||
{ name => "jobset", project => "project" },
|
||||
{ is_deferrable => 0, on_delete => "NO ACTION", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
=head2 project
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<Hydra::Schema::Projects>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"project",
|
||||
"Hydra::Schema::Projects",
|
||||
{ name => "project" },
|
||||
{ is_deferrable => 0, on_delete => "NO ACTION", on_update => "CASCADE" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:qoPm5/le+sVHigW4Dmum2Q
|
||||
|
||||
sub json_hint {
|
||||
return { columns => ['value', 'unit'] };
|
||||
}
|
||||
|
||||
1;
|
@ -341,6 +341,21 @@ __PACKAGE__->has_many(
|
||||
undef,
|
||||
);
|
||||
|
||||
=head2 buildmetrics
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::BuildMetrics>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"buildmetrics",
|
||||
"Hydra::Schema::BuildMetrics",
|
||||
{ "foreign.build" => "self.id" },
|
||||
undef,
|
||||
);
|
||||
|
||||
=head2 buildoutputs
|
||||
|
||||
Type: has_many
|
||||
@ -535,8 +550,8 @@ __PACKAGE__->many_to_many(
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:03:55
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EwxiaQpqbdzI9RvU0uUtLQ
|
||||
# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Y2lDtgY8EBLOuCHAI8fWRQ
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"dependents",
|
||||
@ -630,6 +645,7 @@ my %hint = (
|
||||
buildoutputs => 'name',
|
||||
buildinputs_builds => 'name',
|
||||
buildproducts => 'productnr',
|
||||
buildmetrics => 'name',
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -81,6 +81,25 @@ __PACKAGE__->set_primary_key("project", "jobset", "name");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 buildmetrics
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::BuildMetrics>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"buildmetrics",
|
||||
"Hydra::Schema::BuildMetrics",
|
||||
{
|
||||
"foreign.job" => "self.name",
|
||||
"foreign.jobset" => "self.jobset",
|
||||
"foreign.project" => "self.project",
|
||||
},
|
||||
undef,
|
||||
);
|
||||
|
||||
=head2 builds
|
||||
|
||||
Type: has_many
|
||||
@ -150,7 +169,7 @@ __PACKAGE__->has_many(
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-09-29 19:41:42
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:lnZSd0gDXgLk8WQeAFqByA
|
||||
# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:vDAo9bzLca+QWfhOb9OLMg
|
||||
|
||||
1;
|
||||
|
@ -184,6 +184,24 @@ __PACKAGE__->set_primary_key("project", "name");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 buildmetrics
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::BuildMetrics>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"buildmetrics",
|
||||
"Hydra::Schema::BuildMetrics",
|
||||
{
|
||||
"foreign.jobset" => "self.name",
|
||||
"foreign.project" => "self.project",
|
||||
},
|
||||
undef,
|
||||
);
|
||||
|
||||
=head2 builds
|
||||
|
||||
Type: has_many
|
||||
@ -320,8 +338,8 @@ __PACKAGE__->has_many(
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-04-23 23:13:51
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CO0aE+jrjB+UrwGRzWZLlw
|
||||
# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Coci9FdBAvUO9T3st2NEqA
|
||||
|
||||
my %hint = (
|
||||
columns => [
|
||||
|
@ -106,6 +106,21 @@ __PACKAGE__->set_primary_key("name");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 buildmetrics
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<Hydra::Schema::BuildMetrics>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"buildmetrics",
|
||||
"Hydra::Schema::BuildMetrics",
|
||||
{ "foreign.project" => "self.name" },
|
||||
undef,
|
||||
);
|
||||
|
||||
=head2 builds
|
||||
|
||||
Type: has_many
|
||||
@ -267,8 +282,8 @@ Composing rels: L</projectmembers> -> username
|
||||
__PACKAGE__->many_to_many("usernames", "projectmembers", "username");
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07033 @ 2014-04-23 23:13:08
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:fkd9ruEoVSBGIktmAj4u4g
|
||||
# Created by DBIx::Class::Schema::Loader v0.07043 @ 2015-07-30 16:52:20
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:67kWIE0IGmEJTvOIATAKaw
|
||||
|
||||
my %hint = (
|
||||
columns => [
|
||||
|
Reference in New Issue
Block a user