Commit a358c7f5 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab-ce master

parents c59205e4 327cae1d
...@@ -38,7 +38,7 @@ export default { ...@@ -38,7 +38,7 @@ export default {
GlModalDirective, GlModalDirective,
}, },
props: { props: {
externalDashboardPath: { externalDashboardUrl: {
type: String, type: String,
required: false, required: false,
default: '', default: '',
...@@ -299,10 +299,11 @@ export default { ...@@ -299,10 +299,11 @@ export default {
</gl-modal> </gl-modal>
</div> </div>
<gl-button <gl-button
v-if="externalDashboardPath.length" v-if="externalDashboardUrl.length"
class="js-external-dashboard-link prepend-left-8" class="js-external-dashboard-link prepend-left-8"
variant="primary" variant="primary"
:href="externalDashboardPath" :href="externalDashboardUrl"
target="_blank"
> >
{{ __('View full dashboard') }} {{ __('View full dashboard') }}
<icon name="external-link" /> <icon name="external-link" />
......
<script> <script>
import { mapState, mapActions } from 'vuex';
import { GlButton, GlFormGroup, GlFormInput, GlLink } from '@gitlab/ui'; import { GlButton, GlFormGroup, GlFormInput, GlLink } from '@gitlab/ui';
export default { export default {
...@@ -8,17 +9,24 @@ export default { ...@@ -8,17 +9,24 @@ export default {
GlFormInput, GlFormInput,
GlLink, GlLink,
}, },
props: { computed: {
externalDashboardPath: { ...mapState([
type: String, 'externalDashboardHelpPagePath',
required: false, 'externalDashboardUrl',
default: '', 'operationsSettingsEndpoint',
]),
userDashboardUrl: {
get() {
return this.externalDashboardUrl;
}, },
externalDashboardHelpPagePath: { set(url) {
type: String, this.setExternalDashboardUrl(url);
required: true,
}, },
}, },
},
methods: {
...mapActions(['setExternalDashboardUrl', 'updateExternalDashboardUrl']),
},
}; };
</script> </script>
...@@ -45,11 +53,12 @@ export default { ...@@ -45,11 +53,12 @@ export default {
:description="s__('ExternalMetrics|Enter the URL of the dashboard you want to link to')" :description="s__('ExternalMetrics|Enter the URL of the dashboard you want to link to')"
> >
<gl-form-input <gl-form-input
:value="externalDashboardPath" v-model="userDashboardUrl"
placeholder="https://my-org.gitlab.io/my-dashboards" placeholder="https://my-org.gitlab.io/my-dashboards"
@keydown.enter.native.prevent="updateExternalDashboardUrl"
/> />
</gl-form-group> </gl-form-group>
<gl-button variant="success"> <gl-button variant="success" @click="updateExternalDashboardUrl">
{{ __('Save Changes') }} {{ __('Save Changes') }}
</gl-button> </gl-button>
</form> </form>
......
import Vue from 'vue'; import Vue from 'vue';
import store from './store';
import ExternalDashboardForm from './components/external_dashboard.vue'; import ExternalDashboardForm from './components/external_dashboard.vue';
export default () => { export default () => {
...@@ -14,13 +15,9 @@ export default () => { ...@@ -14,13 +15,9 @@ export default () => {
return new Vue({ return new Vue({
el, el,
store: store(el.dataset),
render(createElement) { render(createElement) {
return createElement(ExternalDashboardForm, { return createElement(ExternalDashboardForm);
props: {
...el.dataset,
expanded: false,
},
});
}, },
}); });
}; };
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import createFlash from '~/flash';
import { refreshCurrentPage } from '~/lib/utils/url_utility';
import * as mutationTypes from './mutation_types';
export const setExternalDashboardUrl = ({ commit }, url) =>
commit(mutationTypes.SET_EXTERNAL_DASHBOARD_URL, url);
export const updateExternalDashboardUrl = ({ state, dispatch }) =>
axios
.patch(state.operationsSettingsEndpoint, {
project: {
metrics_setting_attributes: {
external_dashboard_url: state.externalDashboardUrl,
},
},
})
.then(() => dispatch('receiveExternalDashboardUpdateSuccess'))
.catch(error => dispatch('receiveExternalDashboardUpdateError', error));
export const receiveExternalDashboardUpdateSuccess = () => {
/**
* The operations_controller currently handles successful requests
* by creating a flash banner messsage to notify the user.
*/
refreshCurrentPage();
};
export const receiveExternalDashboardUpdateError = (_, error) => {
const { response } = error;
const message = response.data && response.data.message ? response.data.message : '';
createFlash(`${__('There was an error saving your changes.')} ${message}`, 'alert');
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import Vue from 'vue';
import Vuex from 'vuex';
import createState from './state';
import * as actions from './actions';
import mutations from './mutations';
Vue.use(Vuex);
export const createStore = initialState =>
new Vuex.Store({
state: createState(initialState),
actions,
mutations,
});
export default createStore;
/* eslint-disable import/prefer-default-export */
export const SET_EXTERNAL_DASHBOARD_URL = 'SET_EXTERNAL_DASHBOARD_URL';
import * as types from './mutation_types';
export default {
[types.SET_EXTERNAL_DASHBOARD_URL](state, url) {
state.externalDashboardUrl = url;
},
};
export default (initialState = {}) => ({
externalDashboardUrl: initialState.externalDashboardUrl || '',
operationsSettingsEndpoint: initialState.operationsSettingsEndpoint,
externalDashboardHelpPagePath: initialState.externalDashboardHelpPagePath,
});
.js-operation-settings{ data: { external_dashboard: { path: metrics_external_dashboard_url, .js-operation-settings{ data: { operations_settings_endpoint: project_settings_operations_path(@project),
external_dashboard: { url: metrics_external_dashboard_url,
help_page_path: help_page_path('user/project/operations/link_to_external_dashboard') } } } help_page_path: help_page_path('user/project/operations/link_to_external_dashboard') } } }
...@@ -47,6 +47,7 @@ The following metrics are available: ...@@ -47,6 +47,7 @@ The following metrics are available:
| successful_login_captcha_total | Gauge | 11.0 | Counter of successful CAPTCHA attempts during login | | successful_login_captcha_total | Gauge | 11.0 | Counter of successful CAPTCHA attempts during login |
| unicorn_active_connections | Gauge | 11.0 | The number of active Unicorn connections (workers) | | unicorn_active_connections | Gauge | 11.0 | The number of active Unicorn connections (workers) |
| unicorn_queued_connections | Gauge | 11.0 | The number of queued Unicorn connections | | unicorn_queued_connections | Gauge | 11.0 | The number of queued Unicorn connections |
| unicorn_workers | Gauge | 12.0 | The number of Unicorn workers |
## Sidekiq Metrics available for Geo **[PREMIUM]** ## Sidekiq Metrics available for Geo **[PREMIUM]**
...@@ -100,6 +101,10 @@ Some basic Ruby runtime metrics are available: ...@@ -100,6 +101,10 @@ Some basic Ruby runtime metrics are available:
| ruby_file_descriptors | Gauge | 11.1 | File descriptors per process | | ruby_file_descriptors | Gauge | 11.1 | File descriptors per process |
| ruby_memory_bytes | Gauge | 11.1 | Memory usage by process | | ruby_memory_bytes | Gauge | 11.1 | Memory usage by process |
| ruby_sampler_duration_seconds_total | Counter | 11.1 | Time spent collecting stats | | ruby_sampler_duration_seconds_total | Counter | 11.1 | Time spent collecting stats |
| ruby_process_cpu_seconds_total | Gauge | 12.0 | Total amount of CPU time per process |
| ruby_process_max_fds | Gauge | 12.0 | Maximum number of open file descriptors per process |
| ruby_process_resident_memory_bytes | Gauge | 12.0 | Memory usage by process, measured in bytes |
| ruby_process_start_time_seconds | Gauge | 12.0 | The elapsed time between system boot and the process started, measured in seconds |
[GC.stat]: https://ruby-doc.org/core-2.3.0/GC.html#method-c-stat [GC.stat]: https://ruby-doc.org/core-2.3.0/GC.html#method-c-stat
......
...@@ -23,25 +23,32 @@ module Gitlab ...@@ -23,25 +23,32 @@ module Gitlab
end end
def init_metrics def init_metrics
metrics = {} metrics = {
metrics[:sampler_duration] = ::Gitlab::Metrics.counter(with_prefix(:sampler, :duration_seconds_total), 'Sampler time', labels) file_descriptors: ::Gitlab::Metrics.gauge(with_prefix(:file, :descriptors), 'File descriptors used', labels, :livesum),
metrics[:total_time] = ::Gitlab::Metrics.counter(with_prefix(:gc, :duration_seconds_total), 'Total GC time', labels) memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:memory, :bytes), 'Memory used', labels, :livesum),
process_cpu_seconds_total: ::Gitlab::Metrics.gauge(with_prefix(:process, :cpu_seconds_total), 'Process CPU seconds total'),
process_max_fds: ::Gitlab::Metrics.gauge(with_prefix(:process, :max_fds), 'Process max fds'),
process_resident_memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:process, :resident_memory_bytes), 'Memory used', labels, :livesum),
process_start_time_seconds: ::Gitlab::Metrics.gauge(with_prefix(:process, :start_time_seconds), 'Process start time seconds'),
sampler_duration: ::Gitlab::Metrics.counter(with_prefix(:sampler, :duration_seconds_total), 'Sampler time', labels),
total_time: ::Gitlab::Metrics.counter(with_prefix(:gc, :duration_seconds_total), 'Total GC time', labels)
}
GC.stat.keys.each do |key| GC.stat.keys.each do |key|
metrics[key] = ::Gitlab::Metrics.gauge(with_prefix(:gc_stat, key), to_doc_string(key), labels, :livesum) metrics[key] = ::Gitlab::Metrics.gauge(with_prefix(:gc_stat, key), to_doc_string(key), labels, :livesum)
end end
metrics[:memory_usage] = ::Gitlab::Metrics.gauge(with_prefix(:memory, :bytes), 'Memory used', labels, :livesum)
metrics[:file_descriptors] = ::Gitlab::Metrics.gauge(with_prefix(:file, :descriptors), 'File descriptors used', labels, :livesum)
metrics metrics
end end
def sample def sample
start_time = System.monotonic_time start_time = System.monotonic_time
metrics[:memory_usage].set(labels.merge(worker_label), System.memory_usage)
metrics[:file_descriptors].set(labels.merge(worker_label), System.file_descriptor_count) metrics[:file_descriptors].set(labels.merge(worker_label), System.file_descriptor_count)
metrics[:process_cpu_seconds_total].set(labels.merge(worker_label), ::Gitlab::Metrics::System.cpu_time)
metrics[:process_max_fds].set(labels.merge(worker_label), ::Gitlab::Metrics::System.max_open_file_descriptors)
metrics[:process_start_time_seconds].set(labels.merge(worker_label), ::Gitlab::Metrics::System.process_start_time)
set_memory_usage_metrics
sample_gc sample_gc
metrics[:sampler_duration].increment(labels, System.monotonic_time - start_time) metrics[:sampler_duration].increment(labels, System.monotonic_time - start_time)
...@@ -61,6 +68,14 @@ module Gitlab ...@@ -61,6 +68,14 @@ module Gitlab
metrics[:total_time].increment(labels, GC::Profiler.total_time) metrics[:total_time].increment(labels, GC::Profiler.total_time)
end end
def set_memory_usage_metrics
memory_usage = System.memory_usage
memory_labels = labels.merge(worker_label)
metrics[:memory_bytes].set(memory_labels, memory_usage)
metrics[:process_resident_memory_bytes].set(memory_labels, memory_usage)
end
def worker_label def worker_label
return {} unless defined?(Unicorn::Worker) return {} unless defined?(Unicorn::Worker)
......
...@@ -4,16 +4,16 @@ module Gitlab ...@@ -4,16 +4,16 @@ module Gitlab
module Metrics module Metrics
module Samplers module Samplers
class UnicornSampler < BaseSampler class UnicornSampler < BaseSampler
def initialize(interval) def metrics
super(interval) @metrics ||= init_metrics
end end
def unicorn_active_connections def init_metrics
@unicorn_active_connections ||= ::Gitlab::Metrics.gauge(:unicorn_active_connections, 'Unicorn active connections', {}, :max) {
end unicorn_active_connections: ::Gitlab::Metrics.gauge(:unicorn_active_connections, 'Unicorn active connections', {}, :max),
unicorn_queued_connections: ::Gitlab::Metrics.gauge(:unicorn_queued_connections, 'Unicorn queued connections', {}, :max),
def unicorn_queued_connections unicorn_workers: ::Gitlab::Metrics.gauge(:unicorn_workers, 'Unicorn workers')
@unicorn_queued_connections ||= ::Gitlab::Metrics.gauge(:unicorn_queued_connections, 'Unicorn queued connections', {}, :max) }
end end
def enabled? def enabled?
...@@ -23,14 +23,13 @@ module Gitlab ...@@ -23,14 +23,13 @@ module Gitlab
def sample def sample
Raindrops::Linux.tcp_listener_stats(tcp_listeners).each do |addr, stats| Raindrops::Linux.tcp_listener_stats(tcp_listeners).each do |addr, stats|
unicorn_active_connections.set({ socket_type: 'tcp', socket_address: addr }, stats.active) set_unicorn_connection_metrics('tcp', addr, stats)
unicorn_queued_connections.set({ socket_type: 'tcp', socket_address: addr }, stats.queued)
end end
Raindrops::Linux.unix_listener_stats(unix_listeners).each do |addr, stats| Raindrops::Linux.unix_listener_stats(unix_listeners).each do |addr, stats|
unicorn_active_connections.set({ socket_type: 'unix', socket_address: addr }, stats.active) set_unicorn_connection_metrics('unix', addr, stats)
unicorn_queued_connections.set({ socket_type: 'unix', socket_address: addr }, stats.queued)
end end
metrics[:unicorn_workers].set({}, unicorn_workers_count)
end end
private private
...@@ -39,6 +38,13 @@ module Gitlab ...@@ -39,6 +38,13 @@ module Gitlab
@tcp_listeners ||= Unicorn.listener_names.grep(%r{\A[^/]+:\d+\z}) @tcp_listeners ||= Unicorn.listener_names.grep(%r{\A[^/]+:\d+\z})
end end
def set_unicorn_connection_metrics(type, addr, stats)
labels = { socket_type: type, socket_address: addr }
metrics[:unicorn_active_connections].set(labels, stats.active)
metrics[:unicorn_queued_connections].set(labels, stats.queued)
end
def unix_listeners def unix_listeners
@unix_listeners ||= Unicorn.listener_names - tcp_listeners @unix_listeners ||= Unicorn.listener_names - tcp_listeners
end end
...@@ -46,6 +52,10 @@ module Gitlab ...@@ -46,6 +52,10 @@ module Gitlab
def unicorn_with_listeners? def unicorn_with_listeners?
defined?(Unicorn) && Unicorn.listener_names.any? defined?(Unicorn) && Unicorn.listener_names.any?
end end
def unicorn_workers_count
`pgrep -f '[u]nicorn_rails worker.+ #{Rails.root.to_s}'`.split.count
end
end end
end end
end end
......
...@@ -23,6 +23,22 @@ module Gitlab ...@@ -23,6 +23,22 @@ module Gitlab
def self.file_descriptor_count def self.file_descriptor_count
Dir.glob('/proc/self/fd/*').length Dir.glob('/proc/self/fd/*').length
end end
def self.max_open_file_descriptors
match = File.read('/proc/self/limits').match(/Max open files\s*(\d+)/)
return unless match && match[1]
match[1].to_i
end
def self.process_start_time
fields = File.read('/proc/self/stat').split
# fields[21] is linux proc stat field "(22) starttime".
# The value is expressed in clock ticks, divide by clock ticks for seconds.
( fields[21].to_i || 0 ) / clk_tck
end
else else
def self.memory_usage def self.memory_usage
0.0 0.0
...@@ -31,6 +47,14 @@ module Gitlab ...@@ -31,6 +47,14 @@ module Gitlab
def self.file_descriptor_count def self.file_descriptor_count
0 0
end end
def self.max_open_file_descriptors
0
end
def self.process_start_time
0
end
end end
# THREAD_CPUTIME is not supported on OS X # THREAD_CPUTIME is not supported on OS X
...@@ -59,6 +83,10 @@ module Gitlab ...@@ -59,6 +83,10 @@ module Gitlab
def self.monotonic_time def self.monotonic_time
Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
end end
def self.clk_tck
@clk_tck ||= `getconf CLK_TCK`.to_i
end
end end
end end
end end
import { shallowMount } from '@vue/test-utils'; import { mount, shallowMount, createLocalVue } from '@vue/test-utils';
import { GlButton, GlLink, GlFormGroup, GlFormInput } from '@gitlab/ui'; import { GlButton, GlLink, GlFormGroup, GlFormInput } from '@gitlab/ui';
import ExternalDashboard from '~/operation_settings/components/external_dashboard.vue'; import ExternalDashboard from '~/operation_settings/components/external_dashboard.vue';
import store from '~/operation_settings/store';
import axios from '~/lib/utils/axios_utils';
import { refreshCurrentPage } from '~/lib/utils/url_utility';
import createFlash from '~/flash';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
jest.mock('~/lib/utils/axios_utils');
jest.mock('~/lib/utils/url_utility');
jest.mock('~/flash');
describe('operation settings external dashboard component', () => { describe('operation settings external dashboard component', () => {
let wrapper; let wrapper;
const externalDashboardPath = `http://mock-external-domain.com/external/dashboard/path`; const operationsSettingsEndpoint = `${TEST_HOST}/mock/ops/settings/endpoint`;
const externalDashboardUrl = `http://mock-external-domain.com/external/dashboard/url`;
const externalDashboardHelpPagePath = `${TEST_HOST}/help/page/path`; const externalDashboardHelpPagePath = `${TEST_HOST}/help/page/path`;
const localVue = createLocalVue();
beforeEach(() => { const mountComponent = (shallow = true) => {
wrapper = shallowMount(ExternalDashboard, { const config = [
propsData: { ExternalDashboard,
externalDashboardPath, {
localVue,
store: store({
operationsSettingsEndpoint,
externalDashboardUrl,
externalDashboardHelpPagePath, externalDashboardHelpPagePath,
}),
}, },
}); ];
wrapper = shallow ? shallowMount(...config) : mount(...config);
};
afterEach(() => {
if (wrapper.destroy) {
wrapper.destroy();
}
axios.patch.mockReset();
refreshCurrentPage.mockReset();
createFlash.mockReset();
}); });
it('renders header text', () => { it('renders header text', () => {
mountComponent();
expect(wrapper.find('.js-section-header').text()).toBe('External Dashboard'); expect(wrapper.find('.js-section-header').text()).toBe('External Dashboard');
}); });
...@@ -33,6 +58,7 @@ describe('operation settings external dashboard component', () => { ...@@ -33,6 +58,7 @@ describe('operation settings external dashboard component', () => {
let subHeader; let subHeader;
beforeEach(() => { beforeEach(() => {
mountComponent();
subHeader = wrapper.find('.js-section-sub-header'); subHeader = wrapper.find('.js-section-sub-header');
}); });
...@@ -51,18 +77,12 @@ describe('operation settings external dashboard component', () => { ...@@ -51,18 +77,12 @@ describe('operation settings external dashboard component', () => {
}); });
describe('form', () => { describe('form', () => {
let form;
beforeEach(() => {
form = wrapper.find('form');
});
describe('external dashboard url', () => {
describe('input label', () => { describe('input label', () => {
let formGroup; let formGroup;
beforeEach(() => { beforeEach(() => {
formGroup = form.find(GlFormGroup); mountComponent();
formGroup = wrapper.find(GlFormGroup);
}); });
it('uses label text', () => { it('uses label text', () => {
...@@ -80,11 +100,12 @@ describe('operation settings external dashboard component', () => { ...@@ -80,11 +100,12 @@ describe('operation settings external dashboard component', () => {
let input; let input;
beforeEach(() => { beforeEach(() => {
input = form.find(GlFormInput); mountComponent();
input = wrapper.find(GlFormInput);
}); });
it('defaults to externalDashboardPath prop', () => { it('defaults to externalDashboardUrl', () => {
expect(input.attributes().value).toBe(externalDashboardPath); expect(input.attributes().value).toBe(externalDashboardUrl);
}); });
it('uses a placeholder', () => { it('uses a placeholder', () => {
...@@ -93,15 +114,50 @@ describe('operation settings external dashboard component', () => { ...@@ -93,15 +114,50 @@ describe('operation settings external dashboard component', () => {
}); });
describe('submit button', () => { describe('submit button', () => {
let submit; const endpointRequest = [
operationsSettingsEndpoint,
beforeEach(() => { {
submit = form.find(GlButton); project: {
}); metrics_setting_attributes: {
external_dashboard_url: externalDashboardUrl,
},
},
},
];
it('renders button label', () => { it('renders button label', () => {
mountComponent();
const submit = wrapper.find(GlButton);
expect(submit.text()).toBe('Save Changes'); expect(submit.text()).toBe('Save Changes');
}); });
it('submits form on click', () => {
mountComponent(false);
axios.patch.mockResolvedValue();
wrapper.find(GlButton).trigger('click');
expect(axios.patch).toHaveBeenCalledWith(...endpointRequest);
return wrapper.vm.$nextTick().then(() => expect(refreshCurrentPage).toHaveBeenCalled());
});
it('creates flash banner on error', () => {
mountComponent(false);
const message = 'mockErrorMessage';
axios.patch.mockRejectedValue({ response: { data: { message } } });
wrapper.find(GlButton).trigger('click');
expect(axios.patch).toHaveBeenCalledWith(...endpointRequest);
return wrapper.vm
.$nextTick()
.then(jest.runAllTicks)
.then(() =>
expect(createFlash).toHaveBeenCalledWith(
`There was an error saving your changes. ${message}`,
'alert',
),
);
}); });
}); });
}); });
......
import mutations from '~/operation_settings/store/mutations';
import createState from '~/operation_settings/store/state';
describe('operation settings mutations', () => {
let localState;
beforeEach(() => {
localState = createState();
});
describe('SET_EXTERNAL_DASHBOARD_URL', () => {
it('sets externalDashboardUrl', () => {
const mockUrl = 'mockUrl';
mutations.SET_EXTERNAL_DASHBOARD_URL(localState, mockUrl);
expect(localState.externalDashboardUrl).toBe(mockUrl);
});
});
});
...@@ -393,7 +393,7 @@ describe('Dashboard', () => { ...@@ -393,7 +393,7 @@ describe('Dashboard', () => {
hasMetrics: true, hasMetrics: true,
showPanels: false, showPanels: false,
showTimeWindowDropdown: false, showTimeWindowDropdown: false,
externalDashboardPath: '/mockPath', externalDashboardUrl: '/mockUrl',
}, },
store, store,
}); });
...@@ -419,7 +419,7 @@ describe('Dashboard', () => { ...@@ -419,7 +419,7 @@ describe('Dashboard', () => {
hasMetrics: true, hasMetrics: true,
showPanels: false, showPanels: false,
showTimeWindowDropdown: false, showTimeWindowDropdown: false,
externalDashboardPath: '', externalDashboardUrl: '',
}, },
store, store,
}); });
......
...@@ -10,17 +10,20 @@ describe Gitlab::Metrics::Samplers::RubySampler do ...@@ -10,17 +10,20 @@ describe Gitlab::Metrics::Samplers::RubySampler do
describe '#sample' do describe '#sample' do
it 'samples various statistics' do it 'samples various statistics' do
expect(Gitlab::Metrics::System).to receive(:memory_usage) expect(Gitlab::Metrics::System).to receive(:cpu_time)
expect(Gitlab::Metrics::System).to receive(:file_descriptor_count) expect(Gitlab::Metrics::System).to receive(:file_descriptor_count)
expect(Gitlab::Metrics::System).to receive(:memory_usage)
expect(Gitlab::Metrics::System).to receive(:process_start_time)
expect(Gitlab::Metrics::System).to receive(:max_open_file_descriptors)
expect(sampler).to receive(:sample_gc) expect(sampler).to receive(:sample_gc)
sampler.sample sampler.sample
end end
it 'adds a metric containing the memory usage' do it 'adds a metric containing the process resident memory bytes' do
expect(Gitlab::Metrics::System).to receive(:memory_usage).and_return(9000) expect(Gitlab::Metrics::System).to receive(:memory_usage).and_return(9000)
expect(sampler.metrics[:memory_usage]).to receive(:set).with({}, 9000) expect(sampler.metrics[:process_resident_memory_bytes]).to receive(:set).with({}, 9000)
sampler.sample sampler.sample
end end
...@@ -34,6 +37,27 @@ describe Gitlab::Metrics::Samplers::RubySampler do ...@@ -34,6 +37,27 @@ describe Gitlab::Metrics::Samplers::RubySampler do
sampler.sample sampler.sample
end end
it 'adds a metric containing the process total cpu time' do
expect(Gitlab::Metrics::System).to receive(:cpu_time).and_return(0.51)
expect(sampler.metrics[:process_cpu_seconds_total]).to receive(:set).with({}, 0.51)
sampler.sample
end
it 'adds a metric containing the process start time' do
expect(Gitlab::Metrics::System).to receive(:process_start_time).and_return(12345)
expect(sampler.metrics[:process_start_time_seconds]).to receive(:set).with({}, 12345)
sampler.sample
end
it 'adds a metric containing the process max file descriptors' do
expect(Gitlab::Metrics::System).to receive(:max_open_file_descriptors).and_return(1024)
expect(sampler.metrics[:process_max_fds]).to receive(:set).with({}, 1024)
sampler.sample
end
it 'clears any GC profiles' do it 'clears any GC profiles' do
expect(GC::Profiler).to receive(:clear) expect(GC::Profiler).to receive(:clear)
......
...@@ -39,8 +39,8 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do ...@@ -39,8 +39,8 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do
it 'updates metrics type unix and with addr' do it 'updates metrics type unix and with addr' do
labels = { socket_type: 'unix', socket_address: socket_address } labels = { socket_type: 'unix', socket_address: socket_address }
expect(subject).to receive_message_chain(:unicorn_active_connections, :set).with(labels, 'active') expect(subject.metrics[:unicorn_active_connections]).to receive(:set).with(labels, 'active')
expect(subject).to receive_message_chain(:unicorn_queued_connections, :set).with(labels, 'queued') expect(subject.metrics[:unicorn_queued_connections]).to receive(:set).with(labels, 'queued')
subject.sample subject.sample
end end
...@@ -50,7 +50,6 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do ...@@ -50,7 +50,6 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do
context 'unicorn listens on tcp sockets' do context 'unicorn listens on tcp sockets' do
let(:tcp_socket_address) { '0.0.0.0:8080' } let(:tcp_socket_address) { '0.0.0.0:8080' }
let(:tcp_sockets) { [tcp_socket_address] } let(:tcp_sockets) { [tcp_socket_address] }
before do before do
allow(unicorn).to receive(:listener_names).and_return(tcp_sockets) allow(unicorn).to receive(:listener_names).and_return(tcp_sockets)
end end
...@@ -71,13 +70,29 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do ...@@ -71,13 +70,29 @@ describe Gitlab::Metrics::Samplers::UnicornSampler do
it 'updates metrics type unix and with addr' do it 'updates metrics type unix and with addr' do
labels = { socket_type: 'tcp', socket_address: tcp_socket_address } labels = { socket_type: 'tcp', socket_address: tcp_socket_address }
expect(subject).to receive_message_chain(:unicorn_active_connections, :set).with(labels, 'active') expect(subject.metrics[:unicorn_active_connections]).to receive(:set).with(labels, 'active')
expect(subject).to receive_message_chain(:unicorn_queued_connections, :set).with(labels, 'queued') expect(subject.metrics[:unicorn_queued_connections]).to receive(:set).with(labels, 'queued')
subject.sample subject.sample
end end
end end
end end
context 'additional metrics' do
let(:unicorn_workers) { 2 }
before do
allow(unicorn).to receive(:listener_names).and_return([""])
allow(::Gitlab::Metrics::System).to receive(:cpu_time).and_return(3.14)
allow(subject).to receive(:unicorn_workers_count).and_return(unicorn_workers)
end
it "sets additional metrics" do
expect(subject.metrics[:unicorn_workers]).to receive(:set).with({}, unicorn_workers)
subject.sample
end
end
end end
describe '#start' do describe '#start' do
......
...@@ -13,6 +13,18 @@ describe Gitlab::Metrics::System do ...@@ -13,6 +13,18 @@ describe Gitlab::Metrics::System do
expect(described_class.file_descriptor_count).to be > 0 expect(described_class.file_descriptor_count).to be > 0
end end
end end
describe '.max_open_file_descriptors' do
it 'returns the max allowed open file descriptors' do
expect(described_class.max_open_file_descriptors).to be > 0
end
end
describe '.process_start_time' do
it 'returns the process start time' do
expect(described_class.process_start_time).to be > 0
end
end
else else
describe '.memory_usage' do describe '.memory_usage' do
it 'returns 0.0' do it 'returns 0.0' do
...@@ -25,6 +37,18 @@ describe Gitlab::Metrics::System do ...@@ -25,6 +37,18 @@ describe Gitlab::Metrics::System do
expect(described_class.file_descriptor_count).to eq(0) expect(described_class.file_descriptor_count).to eq(0)
end end
end end
describe '.max_open_file_descriptors' do
it 'returns 0' do
expect(described_class.max_open_file_descriptors).to eq(0)
end
end
describe 'process_start_time' do
it 'returns 0' do
expect(described_class.process_start_time).to eq(0)
end
end
end end
describe '.cpu_time' do describe '.cpu_time' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment